From a51525c8fed5c7f8fd73bb42a9b67b7b9a8e8e65 Mon Sep 17 00:00:00 2001 From: Marc Lepage Date: Tue, 30 Nov 2021 11:46:07 -0500 Subject: [PATCH] Add access control implementation for reading ACL Add an actual implementation, incomplete of course. This supports reading the entire ACL attribute (all indexes, all fabrics). It doesn't support reading the extension attribute (which doesn't have any dependent code yet), or parts of ACL entries, etc. And it doesn't support writing (that's next priority). Access Control isn't yet set up in common code, hooked up in the IM, or persisted to flash. Therefore, it's set up here for now, and a few ACL entries are created which will be removed before more set up and hook up and persistence is done. (Just need some entries during development.) Tested by running chip-tool against chip-all-clusters-app to read those temporary ACL entries and inspect they are correctly read. This work is toward issue #10254. --- src/app/BUILD.gn | 1 + .../access-control-server.cpp | 240 +++++++++++++++++- 2 files changed, 240 insertions(+), 1 deletion(-) diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 64d255211680f4..f789fc2d3092e6 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -130,6 +130,7 @@ static_library("app") { public_deps = [ ":app_buildconfig", + "${chip_root}/src/access", "${chip_root}/src/app/util:device_callbacks_manager", "${chip_root}/src/lib/support", "${chip_root}/src/messaging", diff --git a/src/app/clusters/access-control-server/access-control-server.cpp b/src/app/clusters/access-control-server/access-control-server.cpp index 25dde950bbd703..2edd1716194128 100644 --- a/src/app/clusters/access-control-server/access-control-server.cpp +++ b/src/app/clusters/access-control-server/access-control-server.cpp @@ -15,4 +15,242 @@ * limitations under the License. */ -void MatterAccessControlPluginServerInitCallback() {} +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace AccessControl { +namespace Structs { +namespace AccessControlEntry { + +struct AccessControlEntryCodec +{ + Access::AccessControl::Entry entry; + + // TODO: use enum classes once they settle down + static int Encode(Access::Privilege privilege) + { + switch (privilege) + { + case Access::Privilege::kView: + return 1; + case Access::Privilege::kProxyView: + return 2; + case Access::Privilege::kOperate: + return 3; + case Access::Privilege::kManage: + return 4; + case Access::Privilege::kAdminister: + return 5; + default: + return 0; + } + } + + // TODO: use enum classes once they settle down + static int Encode(Access::AuthMode authMode) + { + switch (authMode) + { + case Access::AuthMode::kPase: + return 1; + case Access::AuthMode::kCase: + return 2; + case Access::AuthMode::kGroup: + return 3; + default: + return 0; + } + } + + CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) const + { + TLV::TLVType outer; + ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer)); + + FabricIndex fabricIndex; + ReturnErrorOnFailure(entry.GetFabricIndex(fabricIndex)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kFabricIndex)), fabricIndex)); + + Access::Privilege privilege; + ReturnErrorOnFailure(entry.GetPrivilege(privilege)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kPrivilege)), Encode(privilege))); + + Access::AuthMode authMode; + ReturnErrorOnFailure(entry.GetAuthMode(authMode)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kAuthMode)), Encode(authMode))); + + size_t subjectCount = 0; + ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount)); + if (subjectCount > 0) + { + TLV::TLVType inner; + ReturnErrorOnFailure( + writer.StartContainer(TLV::ContextTag(to_underlying(Fields::kSubjects)), TLV::kTLVType_Array, inner)); + for (size_t i = 0; i < subjectCount; ++i) + { + NodeId subject; + ReturnErrorOnFailure(entry.GetSubject(i, subject)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::AnonymousTag, subject)); + } + ReturnErrorOnFailure(writer.EndContainer(inner)); + } + + size_t targetCount = 0; + ReturnErrorOnFailure(entry.GetTargetCount(targetCount)); + if (targetCount > 0) + { + TLV::TLVType inner; + ReturnErrorOnFailure( + writer.StartContainer(TLV::ContextTag(to_underlying(Fields::kTargets)), TLV::kTLVType_Array, inner)); + for (size_t i = 0; i < targetCount; ++i) + { + TLV::TLVType innermost; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, innermost)); + Access::AccessControl::Entry::Target target; + ReturnErrorOnFailure(entry.GetTarget(i, target)); + if (target.flags & Access::AccessControl::Entry::Target::kCluster) + { + ReturnErrorOnFailure( + DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kCluster)), target.cluster)); + } + if (target.flags & Access::AccessControl::Entry::Target::kEndpoint) + { + ReturnErrorOnFailure( + DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kEndpoint)), target.endpoint)); + } + if (target.flags & Access::AccessControl::Entry::Target::kDeviceType) + { + ReturnErrorOnFailure( + DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kDeviceType)), target.deviceType)); + } + ReturnErrorOnFailure(writer.EndContainer(innermost)); + } + ReturnErrorOnFailure(writer.EndContainer(inner)); + } + + ReturnErrorOnFailure(writer.EndContainer(outer)); + return CHIP_NO_ERROR; + } +}; + +} // namespace AccessControlEntry +} // namespace Structs +} // namespace AccessControl +} // namespace Clusters +} // namespace app +} // namespace chip + +using namespace chip; +using namespace chip::app; +using namespace chip::Access; + +class AccessControlAttribute : public AttributeAccessInterface +{ + using AccessControlEntryCodec = chip::app::Clusters::AccessControl::Structs::AccessControlEntry::AccessControlEntryCodec; + +public: + AccessControlAttribute() : AttributeAccessInterface(Optional(0), Clusters::AccessControl::Id) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + +private: + CHIP_ERROR ReadAcl(AttributeValueEncoder & aEncoder); + CHIP_ERROR ReadExtension(AttributeValueEncoder & aEncoder); +}; + +CHIP_ERROR AccessControlAttribute::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + // TODO: probably need to handle list indexes, fabric filtering, etc. + // but don't worry about all that right now + + switch (aPath.mAttributeId) + { + case Clusters::AccessControl::Attributes::Acl::Id: + return ReadAcl(aEncoder); + case Clusters::AccessControl::Attributes::Extension::Id: + return ReadExtension(aEncoder); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AccessControlAttribute::ReadAcl(AttributeValueEncoder & aEncoder) +{ + AccessControlEntryCodec codec; + AccessControl::EntryIterator iterator; + + GetAccessControl().Entries(iterator); + + CHIP_ERROR err = aEncoder.EncodeList([&](const TagBoundEncoder & encoder) -> CHIP_ERROR { + while (iterator.Next(codec.entry) == CHIP_NO_ERROR) + { + encoder.Encode(codec); + } + return CHIP_NO_ERROR; + }); + + return err; +} + +CHIP_ERROR AccessControlAttribute::ReadExtension(AttributeValueEncoder & aEncoder) +{ + // TODO: implement extension support + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +AccessControlAttribute gAttribute; + +AccessControl gAccessControl(Examples::GetAccessControlDelegate()); + +void MatterAccessControlPluginServerInitCallback() +{ + registerAttributeAccessOverride(&gAttribute); + + // TODO: move access control setup to a better place but it's fine here for now + gAccessControl.Init(); + SetAccessControl(gAccessControl); + + // TODO: create a few entries during development, these will be removed, + // it's OK because access control isn't persisted and isn't hooked up in IM + AccessControl::Entry entry; + + GetAccessControl().PrepareEntry(entry); + entry.SetFabricIndex(1); + entry.SetPrivilege(Privilege::kAdminister); + entry.SetAuthMode(AuthMode::kCase); + GetAccessControl().CreateEntry(nullptr, entry); + + GetAccessControl().PrepareEntry(entry); + entry.SetFabricIndex(1); + entry.SetPrivilege(Privilege::kManage); + entry.SetAuthMode(AuthMode::kPase); + entry.AddSubject(nullptr, 0x0000000011112222); + entry.AddSubject(nullptr, 0x0000000033334444); + entry.AddTarget(nullptr, { AccessControl::Entry::Target::kCluster, 10, 0, 0 }); + entry.AddTarget(nullptr, { AccessControl::Entry::Target::kEndpoint, 0, 1, 0 }); + GetAccessControl().CreateEntry(nullptr, entry); + + GetAccessControl().PrepareEntry(entry); + entry.SetFabricIndex(1); + entry.SetPrivilege(Privilege::kOperate); + entry.SetAuthMode(AuthMode::kGroup); + entry.AddSubject(nullptr, 0x0000000055556666); + entry.AddSubject(nullptr, 0x0000000077778888); + entry.AddTarget(nullptr, { AccessControl::Entry::Target::kCluster | AccessControl::Entry::Target::kEndpoint, 11, 2, 0 }); + entry.AddTarget(nullptr, { AccessControl::Entry::Target::kDeviceType, 0, 0, 100 }); + GetAccessControl().CreateEntry(nullptr, entry); +}