| /* |
| * |
| * Copyright (c) 2022 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <app/server/DefaultAclStorage.h> |
| |
| #include <lib/support/DefaultStorageKeyAllocator.h> |
| |
| using namespace chip; |
| using namespace chip::app; |
| using namespace chip::Access; |
| |
| using EncodableEntry = AclStorage::EncodableEntry; |
| using Entry = AccessControl::Entry; |
| using EntryListener = AccessControl::EntryListener; |
| using StagingAuthMode = Clusters::AccessControl::AccessControlEntryAuthModeEnum; |
| using StagingPrivilege = Clusters::AccessControl::AccessControlEntryPrivilegeEnum; |
| using StagingTarget = Clusters::AccessControl::Structs::AccessControlTargetStruct::Type; |
| using Target = AccessControl::Entry::Target; |
| |
| namespace { |
| |
| /* |
| Size calculation for TLV encoded entry. |
| |
| Because EncodeForWrite is used without an accessing fabric, the fabric index is |
| not encoded. However, let's assume it is. This yields 17 bytes total overhead, |
| but it's wise to add a few more for safety. |
| |
| Each subject may require up to 9 bytes. Each target may require up to 14 bytes |
| (only one of endpoint or device type will be encoded). |
| |
| DATA C T L V NOTES |
| structure (anonymous) 1 0x15 |
| field 1 privilege 1 1 1 |
| field 2 authmode 1 1 1 |
| field 3 subjects 1 1 |
| uint64 1 8 per subject |
| end list 1 0x18 |
| field 4 targets 1 1 |
| structure (anonymous) 1 per target |
| field 0 cluster 1 1 4 |
| field 1 endpoint 1 1 2 only field 1 or 2 |
| field 2 devicetype 1 1 4 only field 1 or 2 |
| end structure 1 |
| end list 1 0x18 |
| field 254 fabric index 1 1 1 not written |
| end structure 1 0x18 |
| */ |
| |
| // TODO(#14455): get actual values for max subjects/targets |
| constexpr int kEncodedEntryOverheadBytes = 17 + 8; |
| constexpr int kEncodedEntrySubjectBytes = 9 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY; |
| constexpr int kEncodedEntryTargetBytes = 14 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY; |
| constexpr int kEncodedEntryTotalBytes = kEncodedEntryOverheadBytes + kEncodedEntrySubjectBytes + kEncodedEntryTargetBytes; |
| |
| class : public EntryListener |
| { |
| public: |
| void OnEntryChanged(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index, const Entry * entry, |
| ChangeType changeType) override |
| { |
| CHIP_ERROR err; |
| |
| uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; |
| |
| VerifyOrExit(mPersistentStorage != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| if (changeType == ChangeType::kRemoved) |
| { |
| // Shuffle down entries past index, then delete entry at last index. |
| while (true) |
| { |
| uint16_t size = static_cast<uint16_t>(sizeof(buffer)); |
| err = mPersistentStorage->SyncGetKeyValue( |
| DefaultStorageKeyAllocator::AccessControlAclEntry(fabric, index + 1).KeyName(), buffer, size); |
| if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| break; |
| } |
| SuccessOrExit(err); |
| SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue( |
| DefaultStorageKeyAllocator::AccessControlAclEntry(fabric, index).KeyName(), buffer, size)); |
| index++; |
| } |
| SuccessOrExit(err = mPersistentStorage->SyncDeleteKeyValue( |
| DefaultStorageKeyAllocator::AccessControlAclEntry(fabric, index).KeyName())); |
| } |
| else |
| { |
| // Write added/updated entry at index. |
| VerifyOrExit(entry != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| TLV::TLVWriter writer; |
| writer.Init(buffer); |
| EncodableEntry encodableEntry(*entry); |
| SuccessOrExit(err = encodableEntry.EncodeForWrite(writer, TLV::AnonymousTag())); |
| SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue( |
| DefaultStorageKeyAllocator::AccessControlAclEntry(fabric, index).KeyName(), buffer, |
| static_cast<uint16_t>(writer.GetLengthWritten()))); |
| } |
| |
| return; |
| |
| exit: |
| ChipLogError(DataManagement, "DefaultAclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| |
| // Must initialize before use. |
| void Init(PersistentStorageDelegate & persistentStorage) { mPersistentStorage = &persistentStorage; } |
| |
| private: |
| PersistentStorageDelegate * mPersistentStorage = nullptr; |
| |
| } sEntryListener; |
| |
| } // namespace |
| |
| namespace chip { |
| namespace app { |
| |
| CHIP_ERROR DefaultAclStorage::Init(PersistentStorageDelegate & persistentStorage, ConstFabricIterator first, |
| ConstFabricIterator last) |
| { |
| ChipLogProgress(DataManagement, "DefaultAclStorage: initializing"); |
| |
| CHIP_ERROR err; |
| |
| [[maybe_unused]] size_t count = 0; |
| |
| for (auto it = first; it != last; ++it) |
| { |
| auto fabric = it->GetFabricIndex(); |
| for (size_t index = 0; /**/; ++index) |
| { |
| uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; |
| uint16_t size = static_cast<uint16_t>(sizeof(buffer)); |
| err = persistentStorage.SyncGetKeyValue(DefaultStorageKeyAllocator::AccessControlAclEntry(fabric, index).KeyName(), |
| buffer, size); |
| if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| break; |
| } |
| SuccessOrExit(err); |
| |
| TLV::TLVReader reader; |
| reader.Init(buffer, size); |
| SuccessOrExit(err = reader.Next()); |
| |
| DecodableEntry decodableEntry; |
| SuccessOrExit(err = decodableEntry.Decode(reader)); |
| |
| Entry & entry = decodableEntry.GetEntry(); |
| SuccessOrExit(err = entry.SetFabricIndex(fabric)); |
| |
| SuccessOrExit(err = GetAccessControl().CreateEntry(nullptr, fabric, nullptr, entry)); |
| count++; |
| } |
| } |
| |
| ChipLogProgress(DataManagement, "DefaultAclStorage: %u entries loaded", (unsigned) count); |
| |
| sEntryListener.Init(persistentStorage); |
| GetAccessControl().AddEntryListener(sEntryListener); |
| |
| return CHIP_NO_ERROR; |
| |
| exit: |
| ChipLogError(DataManagement, "DefaultAclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); |
| return err; |
| } |
| |
| } // namespace app |
| } // namespace chip |