LCOV - code coverage report
Current view: top level - access - AccessControl.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 207 259 79.9 %
Date: 2024-02-15 08:20:41 Functions: 16 19 84.2 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : // Included for the default AccessControlDelegate logging enables/disables.
      20             : // See `chip_access_control_policy_logging_verbosity` in `src/app/BUILD.gn` for
      21             : // the levels available.
      22             : #include <app/AppConfig.h>
      23             : 
      24             : #include "AccessControl.h"
      25             : 
      26             : #include <lib/core/Global.h>
      27             : 
      28             : namespace chip {
      29             : namespace Access {
      30             : 
      31             : using chip::CATValues;
      32             : using chip::FabricIndex;
      33             : using chip::NodeId;
      34             : 
      35             : namespace {
      36             : 
      37             : Global<AccessControl> defaultAccessControl;
      38             : AccessControl * globalAccessControl = nullptr; // lazily defaulted to defaultAccessControl in GetAccessControl
      39             : 
      40             : static_assert(((unsigned(Privilege::kAdminister) & unsigned(Privilege::kManage)) == 0) &&
      41             :                   ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kOperate)) == 0) &&
      42             :                   ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kView)) == 0) &&
      43             :                   ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kProxyView)) == 0) &&
      44             :                   ((unsigned(Privilege::kManage) & unsigned(Privilege::kOperate)) == 0) &&
      45             :                   ((unsigned(Privilege::kManage) & unsigned(Privilege::kView)) == 0) &&
      46             :                   ((unsigned(Privilege::kManage) & unsigned(Privilege::kProxyView)) == 0) &&
      47             :                   ((unsigned(Privilege::kOperate) & unsigned(Privilege::kView)) == 0) &&
      48             :                   ((unsigned(Privilege::kOperate) & unsigned(Privilege::kProxyView)) == 0) &&
      49             :                   ((unsigned(Privilege::kView) & unsigned(Privilege::kProxyView)) == 0),
      50             :               "Privilege bits must be unique");
      51             : 
      52         128 : bool CheckRequestPrivilegeAgainstEntryPrivilege(Privilege requestPrivilege, Privilege entryPrivilege)
      53             : {
      54         128 :     switch (entryPrivilege)
      55             :     {
      56          11 :     case Privilege::kView:
      57          11 :         return requestPrivilege == Privilege::kView;
      58          10 :     case Privilege::kProxyView:
      59          10 :         return requestPrivilege == Privilege::kProxyView || requestPrivilege == Privilege::kView;
      60          25 :     case Privilege::kOperate:
      61          25 :         return requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView;
      62          35 :     case Privilege::kManage:
      63          35 :         return requestPrivilege == Privilege::kManage || requestPrivilege == Privilege::kOperate ||
      64          35 :             requestPrivilege == Privilege::kView;
      65          47 :     case Privilege::kAdminister:
      66          38 :         return requestPrivilege == Privilege::kAdminister || requestPrivilege == Privilege::kManage ||
      67          85 :             requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView ||
      68          47 :             requestPrivilege == Privilege::kProxyView;
      69             :     }
      70           0 :     return false;
      71             : }
      72             : 
      73       41567 : constexpr bool IsValidCaseNodeId(NodeId aNodeId)
      74             : {
      75       41567 :     if (IsOperationalNodeId(aNodeId))
      76             :     {
      77       41082 :         return true;
      78             :     }
      79             : 
      80         485 :     if (IsCASEAuthTag(aNodeId) && (GetCASEAuthTagVersion(CASEAuthTagFromNodeId(aNodeId)) != 0))
      81             :     {
      82         307 :         return true;
      83             :     }
      84             : 
      85         178 :     return false;
      86             : }
      87             : 
      88         286 : constexpr bool IsValidGroupNodeId(NodeId aNodeId)
      89             : {
      90         286 :     return IsGroupId(aNodeId) && IsValidGroupId(GroupIdFromNodeId(aNodeId));
      91             : }
      92             : 
      93             : #if CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
      94             : 
      95       47858 : char GetAuthModeStringForLogging(AuthMode authMode)
      96             : {
      97       47858 :     switch (authMode)
      98             :     {
      99         295 :     case AuthMode::kNone:
     100         295 :         return 'n';
     101         227 :     case AuthMode::kPase:
     102         227 :         return 'p';
     103       47033 :     case AuthMode::kCase:
     104       47033 :         return 'c';
     105         303 :     case AuthMode::kGroup:
     106         303 :         return 'g';
     107             :     }
     108           0 :     return 'u';
     109             : }
     110             : 
     111             : constexpr int kCharsPerCatForLogging = 11; // including final null terminator
     112             : 
     113        5494 : char * GetCatStringForLogging(char * buf, size_t size, const CATValues & cats)
     114             : {
     115        5494 :     if (size == 0)
     116             :     {
     117           0 :         return nullptr;
     118             :     }
     119        5494 :     char * p         = buf;
     120        5494 :     char * const end = buf + size;
     121        5494 :     *p               = '\0';
     122             :     // Format string chars needed:
     123             :     //   1 for comma (optional)
     124             :     //   2 for 0x prefix
     125             :     //   8 for 32-bit hex value
     126             :     //   1 for null terminator (at end)
     127             :     static constexpr char fmtWithoutComma[] = "0x%08" PRIX32;
     128             :     static constexpr char fmtWithComma[]    = ",0x%08" PRIX32;
     129        5494 :     constexpr int countWithoutComma         = 10;
     130        5494 :     constexpr int countWithComma            = countWithoutComma + 1;
     131        5494 :     bool withComma                          = false;
     132        5509 :     for (auto cat : cats.values)
     133             :     {
     134        5509 :         if (cat == chip::kUndefinedCAT)
     135             :         {
     136        5494 :             break;
     137             :         }
     138          15 :         snprintf(p, static_cast<size_t>(end - p), withComma ? fmtWithComma : fmtWithoutComma, cat);
     139          15 :         p += withComma ? countWithComma : countWithoutComma;
     140          15 :         if (p >= end)
     141             :         {
     142             :             // Output was truncated.
     143           0 :             p = end - ((size < 4) ? size : 4);
     144           0 :             while (*p)
     145             :             {
     146             :                 // Indicate truncation if possible.
     147           0 :                 *p++ = '.';
     148             :             }
     149           0 :             break;
     150             :         }
     151          15 :         withComma = true;
     152             :     }
     153        5494 :     return buf;
     154             : }
     155             : 
     156       47858 : char GetPrivilegeStringForLogging(Privilege privilege)
     157             : {
     158       47858 :     switch (privilege)
     159             :     {
     160       46375 :     case Privilege::kView:
     161       46375 :         return 'v';
     162          77 :     case Privilege::kProxyView:
     163          77 :         return 'p';
     164         550 :     case Privilege::kOperate:
     165         550 :         return 'o';
     166         134 :     case Privilege::kManage:
     167         134 :         return 'm';
     168         722 :     case Privilege::kAdminister:
     169         722 :         return 'a';
     170             :     }
     171           0 :     return 'u';
     172             : }
     173             : 
     174             : #endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     175             : 
     176             : } // namespace
     177             : 
     178             : Global<AccessControl::Entry::Delegate> AccessControl::Entry::mDefaultDelegate;
     179             : Global<AccessControl::EntryIterator::Delegate> AccessControl::EntryIterator::mDefaultDelegate;
     180             : 
     181         260 : CHIP_ERROR AccessControl::Init(AccessControl::Delegate * delegate, DeviceTypeResolver & deviceTypeResolver)
     182             : {
     183         260 :     VerifyOrReturnError(!IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     184             : 
     185         260 :     ChipLogProgress(DataManagement, "AccessControl: initializing");
     186             : 
     187         260 :     VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     188         260 :     CHIP_ERROR retval = delegate->Init();
     189         260 :     if (retval == CHIP_NO_ERROR)
     190             :     {
     191         260 :         mDelegate           = delegate;
     192         260 :         mDeviceTypeResolver = &deviceTypeResolver;
     193             :     }
     194             : 
     195         260 :     return retval;
     196             : }
     197             : 
     198         260 : void AccessControl::Finish()
     199             : {
     200         260 :     VerifyOrReturn(IsInitialized());
     201         260 :     ChipLogProgress(DataManagement, "AccessControl: finishing");
     202         260 :     mDelegate->Finish();
     203         260 :     mDelegate = nullptr;
     204             : }
     205             : 
     206           0 : CHIP_ERROR AccessControl::CreateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t * index,
     207             :                                       const Entry & entry)
     208             : {
     209           0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     210             : 
     211           0 :     size_t count    = 0;
     212           0 :     size_t maxCount = 0;
     213           0 :     ReturnErrorOnFailure(mDelegate->GetEntryCount(fabric, count));
     214           0 :     ReturnErrorOnFailure(mDelegate->GetMaxEntriesPerFabric(maxCount));
     215             : 
     216           0 :     VerifyOrReturnError((count + 1) <= maxCount, CHIP_ERROR_BUFFER_TOO_SMALL);
     217             : 
     218           0 :     ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
     219             : 
     220           0 :     size_t i = 0;
     221           0 :     ReturnErrorOnFailure(mDelegate->CreateEntry(&i, entry, &fabric));
     222             : 
     223           0 :     if (index)
     224             :     {
     225           0 :         *index = i;
     226             :     }
     227             : 
     228           0 :     NotifyEntryChanged(subjectDescriptor, fabric, i, &entry, EntryListener::ChangeType::kAdded);
     229           0 :     return CHIP_NO_ERROR;
     230             : }
     231             : 
     232           0 : CHIP_ERROR AccessControl::UpdateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index,
     233             :                                       const Entry & entry)
     234             : {
     235           0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     236           0 :     ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
     237           0 :     ReturnErrorOnFailure(mDelegate->UpdateEntry(index, entry, &fabric));
     238           0 :     NotifyEntryChanged(subjectDescriptor, fabric, index, &entry, EntryListener::ChangeType::kUpdated);
     239           0 :     return CHIP_NO_ERROR;
     240             : }
     241             : 
     242           9 : CHIP_ERROR AccessControl::DeleteEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index)
     243             : {
     244           9 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     245           9 :     Entry entry;
     246           9 :     Entry * p = nullptr;
     247           9 :     if (mEntryListener != nullptr && ReadEntry(fabric, index, entry) == CHIP_NO_ERROR)
     248             :     {
     249           0 :         p = &entry;
     250             :     }
     251           9 :     ReturnErrorOnFailure(mDelegate->DeleteEntry(index, &fabric));
     252           9 :     if (p && p->HasDefaultDelegate())
     253             :     {
     254             :         // The entry was read prior to deletion so its latest value could be provided
     255             :         // to the listener after deletion. If it's been reset to its default delegate,
     256             :         // that best effort attempt to retain the latest value failed. This is
     257             :         // regrettable but OK.
     258           0 :         p = nullptr;
     259             :     }
     260           9 :     NotifyEntryChanged(subjectDescriptor, fabric, index, p, EntryListener::ChangeType::kRemoved);
     261           9 :     return CHIP_NO_ERROR;
     262           9 : }
     263             : 
     264           1 : void AccessControl::AddEntryListener(EntryListener & listener)
     265             : {
     266           1 :     if (mEntryListener == nullptr)
     267             :     {
     268           1 :         mEntryListener = &listener;
     269           1 :         listener.mNext = nullptr;
     270           1 :         return;
     271             :     }
     272             : 
     273           0 :     for (EntryListener * l = mEntryListener; /**/; l = l->mNext)
     274             :     {
     275           0 :         if (l == &listener)
     276             :         {
     277           0 :             return;
     278             :         }
     279             : 
     280           0 :         if (l->mNext == nullptr)
     281             :         {
     282           0 :             l->mNext       = &listener;
     283           0 :             listener.mNext = nullptr;
     284           0 :             return;
     285             :         }
     286             :     }
     287             : }
     288             : 
     289           0 : void AccessControl::RemoveEntryListener(EntryListener & listener)
     290             : {
     291           0 :     if (mEntryListener == &listener)
     292             :     {
     293           0 :         mEntryListener = listener.mNext;
     294           0 :         listener.mNext = nullptr;
     295           0 :         return;
     296             :     }
     297             : 
     298           0 :     for (EntryListener * l = mEntryListener; l != nullptr; l = l->mNext)
     299             :     {
     300           0 :         if (l->mNext == &listener)
     301             :         {
     302           0 :             l->mNext       = listener.mNext;
     303           0 :             listener.mNext = nullptr;
     304           0 :             return;
     305             :         }
     306             :     }
     307             : }
     308             : 
     309        5494 : CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
     310             :                                 Privilege requestPrivilege)
     311             : {
     312        5494 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     313             : 
     314             : #if CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     315             :     {
     316        5494 :         constexpr size_t kMaxCatsToLog = 6;
     317             :         char catLogBuf[kMaxCatsToLog * kCharsPerCatForLogging];
     318        5494 :         ChipLogProgress(DataManagement,
     319             :                         "AccessControl: checking f=%u a=%c s=0x" ChipLogFormatX64 " t=%s c=" ChipLogFormatMEI " e=%u p=%c",
     320             :                         subjectDescriptor.fabricIndex, GetAuthModeStringForLogging(subjectDescriptor.authMode),
     321             :                         ChipLogValueX64(subjectDescriptor.subject),
     322             :                         GetCatStringForLogging(catLogBuf, sizeof(catLogBuf), subjectDescriptor.cats),
     323             :                         ChipLogValueMEI(requestPath.cluster), requestPath.endpoint, GetPrivilegeStringForLogging(requestPrivilege));
     324             :     }
     325             : #endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     326             : 
     327             :     {
     328        5494 :         CHIP_ERROR result = mDelegate->Check(subjectDescriptor, requestPath, requestPrivilege);
     329        5494 :         if (result != CHIP_ERROR_NOT_IMPLEMENTED)
     330             :         {
     331             : #if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
     332        5437 :             ChipLogProgress(DataManagement, "AccessControl: %s (delegate)",
     333             :                             (result == CHIP_NO_ERROR)                  ? "allowed"
     334             :                                 : (result == CHIP_ERROR_ACCESS_DENIED) ? "denied"
     335             :                                                                        : "error");
     336             : #else
     337             :             if (result != CHIP_NO_ERROR)
     338             :             {
     339             :                 ChipLogProgress(DataManagement, "AccessControl: %s (delegate)",
     340             :                                 (result == CHIP_ERROR_ACCESS_DENIED) ? "denied" : "error");
     341             :             }
     342             : #endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
     343        5437 :             return result;
     344             :         }
     345             :     }
     346             : 
     347             :     // Operational PASE not supported for v1.0, so PASE implies commissioning, which has highest privilege.
     348             :     // Currently, subject descriptor is only PASE if this node is the responder (aka commissionee);
     349             :     // if this node is the initiator (aka commissioner) then the subject descriptor remains blank.
     350          57 :     if (subjectDescriptor.authMode == AuthMode::kPase)
     351             :     {
     352             : #if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     353           5 :         ChipLogProgress(DataManagement, "AccessControl: implicit admin (PASE)");
     354             : #endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     355           5 :         return CHIP_NO_ERROR;
     356             :     }
     357             : 
     358          52 :     EntryIterator iterator;
     359          52 :     ReturnErrorOnFailure(Entries(iterator, &subjectDescriptor.fabricIndex));
     360             : 
     361          52 :     Entry entry;
     362         221 :     while (iterator.Next(entry) == CHIP_NO_ERROR)
     363             :     {
     364         191 :         AuthMode authMode = AuthMode::kNone;
     365         191 :         ReturnErrorOnFailure(entry.GetAuthMode(authMode));
     366             :         // Operational PASE not supported for v1.0.
     367         191 :         VerifyOrReturnError(authMode == AuthMode::kCase || authMode == AuthMode::kGroup, CHIP_ERROR_INCORRECT_STATE);
     368         191 :         if (authMode != subjectDescriptor.authMode)
     369             :         {
     370         169 :             continue;
     371             :         }
     372             : 
     373         128 :         Privilege privilege = Privilege::kView;
     374         128 :         ReturnErrorOnFailure(entry.GetPrivilege(privilege));
     375         128 :         if (!CheckRequestPrivilegeAgainstEntryPrivilege(requestPrivilege, privilege))
     376             :         {
     377          32 :             continue;
     378             :         }
     379             : 
     380          96 :         size_t subjectCount = 0;
     381          96 :         ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
     382          96 :         if (subjectCount > 0)
     383             :         {
     384          89 :             bool subjectMatched = false;
     385         167 :             for (size_t i = 0; i < subjectCount; ++i)
     386             :             {
     387         105 :                 NodeId subject = kUndefinedNodeId;
     388         105 :                 ReturnErrorOnFailure(entry.GetSubject(i, subject));
     389         105 :                 if (IsOperationalNodeId(subject))
     390             :                 {
     391          53 :                     VerifyOrReturnError(authMode == AuthMode::kCase, CHIP_ERROR_INCORRECT_STATE);
     392          53 :                     if (subject == subjectDescriptor.subject)
     393             :                     {
     394          13 :                         subjectMatched = true;
     395          27 :                         break;
     396             :                     }
     397             :                 }
     398          52 :                 else if (IsCASEAuthTag(subject))
     399             :                 {
     400          45 :                     VerifyOrReturnError(authMode == AuthMode::kCase, CHIP_ERROR_INCORRECT_STATE);
     401          45 :                     if (subjectDescriptor.cats.CheckSubjectAgainstCATs(subject))
     402             :                     {
     403           8 :                         subjectMatched = true;
     404           8 :                         break;
     405             :                     }
     406             :                 }
     407           7 :                 else if (IsGroupId(subject))
     408             :                 {
     409           7 :                     VerifyOrReturnError(authMode == AuthMode::kGroup, CHIP_ERROR_INCORRECT_STATE);
     410           7 :                     if (subject == subjectDescriptor.subject)
     411             :                     {
     412           6 :                         subjectMatched = true;
     413           6 :                         break;
     414             :                     }
     415             :                 }
     416             :                 else
     417             :                 {
     418             :                     // Operational PASE not supported for v1.0.
     419           0 :                     return CHIP_ERROR_INCORRECT_STATE;
     420             :                 }
     421             :             }
     422          89 :             if (!subjectMatched)
     423             :             {
     424          62 :                 continue;
     425             :             }
     426             :         }
     427             : 
     428          34 :         size_t targetCount = 0;
     429          34 :         ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
     430          34 :         if (targetCount > 0)
     431             :         {
     432          22 :             bool targetMatched = false;
     433          43 :             for (size_t i = 0; i < targetCount; ++i)
     434             :             {
     435          31 :                 Entry::Target target;
     436          31 :                 ReturnErrorOnFailure(entry.GetTarget(i, target));
     437          31 :                 if ((target.flags & Entry::Target::kCluster) && target.cluster != requestPath.cluster)
     438             :                 {
     439          21 :                     continue;
     440             :                 }
     441          15 :                 if ((target.flags & Entry::Target::kEndpoint) && target.endpoint != requestPath.endpoint)
     442             :                 {
     443           5 :                     continue;
     444             :                 }
     445          10 :                 if (target.flags & Entry::Target::kDeviceType &&
     446           0 :                     !mDeviceTypeResolver->IsDeviceTypeOnEndpoint(target.deviceType, requestPath.endpoint))
     447             :                 {
     448           0 :                     continue;
     449             :                 }
     450          10 :                 targetMatched = true;
     451          10 :                 break;
     452             :             }
     453          22 :             if (!targetMatched)
     454             :             {
     455          12 :                 continue;
     456             :             }
     457             :         }
     458             :         // Entry passed all checks: access is allowed.
     459             : 
     460             : #if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
     461          22 :         ChipLogProgress(DataManagement, "AccessControl: allowed");
     462             : #endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
     463             : 
     464          22 :         return CHIP_NO_ERROR;
     465             :     }
     466             : 
     467             :     // No entry was found which passed all checks: access is denied.
     468          30 :     ChipLogProgress(DataManagement, "AccessControl: denied");
     469          30 :     return CHIP_ERROR_ACCESS_DENIED;
     470          52 : }
     471             : 
     472             : #if CHIP_ACCESS_CONTROL_DUMP_ENABLED
     473             : CHIP_ERROR AccessControl::Dump(const Entry & entry)
     474             : {
     475             :     CHIP_ERROR err;
     476             : 
     477             :     ChipLogDetail(DataManagement, "----- BEGIN ENTRY -----");
     478             : 
     479             :     {
     480             :         FabricIndex fabricIndex;
     481             :         SuccessOrExit(err = entry.GetFabricIndex(fabricIndex));
     482             :         ChipLogDetail(DataManagement, "fabricIndex: %u", fabricIndex);
     483             :     }
     484             : 
     485             :     {
     486             :         Privilege privilege;
     487             :         SuccessOrExit(err = entry.GetPrivilege(privilege));
     488             :         ChipLogDetail(DataManagement, "privilege: %d", to_underlying(privilege));
     489             :     }
     490             : 
     491             :     {
     492             :         AuthMode authMode;
     493             :         SuccessOrExit(err = entry.GetAuthMode(authMode));
     494             :         ChipLogDetail(DataManagement, "authMode: %d", to_underlying(authMode));
     495             :     }
     496             : 
     497             :     {
     498             :         size_t count;
     499             :         SuccessOrExit(err = entry.GetSubjectCount(count));
     500             :         if (count)
     501             :         {
     502             :             ChipLogDetail(DataManagement, "subjects: %u", static_cast<unsigned>(count));
     503             :             for (size_t i = 0; i < count; ++i)
     504             :             {
     505             :                 NodeId subject;
     506             :                 SuccessOrExit(err = entry.GetSubject(i, subject));
     507             :                 ChipLogDetail(DataManagement, "  %u: 0x" ChipLogFormatX64, static_cast<unsigned>(i), ChipLogValueX64(subject));
     508             :             }
     509             :         }
     510             :     }
     511             : 
     512             :     {
     513             :         size_t count;
     514             :         SuccessOrExit(err = entry.GetTargetCount(count));
     515             :         if (count)
     516             :         {
     517             :             ChipLogDetail(DataManagement, "targets: %u", static_cast<unsigned>(count));
     518             :             for (size_t i = 0; i < count; ++i)
     519             :             {
     520             :                 Entry::Target target;
     521             :                 SuccessOrExit(err = entry.GetTarget(i, target));
     522             :                 if (target.flags & Entry::Target::kCluster)
     523             :                 {
     524             :                     ChipLogDetail(DataManagement, "  %u: cluster: 0x" ChipLogFormatMEI, static_cast<unsigned>(i),
     525             :                                   ChipLogValueMEI(target.cluster));
     526             :                 }
     527             :                 if (target.flags & Entry::Target::kEndpoint)
     528             :                 {
     529             :                     ChipLogDetail(DataManagement, "  %u: endpoint: %u", static_cast<unsigned>(i), target.endpoint);
     530             :                 }
     531             :                 if (target.flags & Entry::Target::kDeviceType)
     532             :                 {
     533             :                     ChipLogDetail(DataManagement, "  %u: deviceType: 0x" ChipLogFormatMEI, static_cast<unsigned>(i),
     534             :                                   ChipLogValueMEI(target.deviceType));
     535             :                 }
     536             :             }
     537             :         }
     538             :     }
     539             : 
     540             :     ChipLogDetail(DataManagement, "----- END ENTRY -----");
     541             : 
     542             :     return CHIP_NO_ERROR;
     543             : 
     544             : exit:
     545             :     ChipLogError(DataManagement, "AccessControl: dump failed %" CHIP_ERROR_FORMAT, err.Format());
     546             :     return err;
     547             : }
     548             : #endif
     549             : 
     550       42364 : bool AccessControl::IsValid(const Entry & entry)
     551             : {
     552       42364 :     const char * log = "unexpected error";
     553             :     IgnoreUnusedVariable(log); // logging may be disabled
     554             : 
     555       42364 :     AuthMode authMode       = AuthMode::kNone;
     556       42364 :     FabricIndex fabricIndex = kUndefinedFabricIndex;
     557       42364 :     Privilege privilege     = static_cast<Privilege>(0);
     558       42364 :     size_t subjectCount     = 0;
     559       42364 :     size_t targetCount      = 0;
     560             : 
     561       42364 :     CHIP_ERROR err = CHIP_NO_ERROR;
     562       42364 :     SuccessOrExit(err = entry.GetAuthMode(authMode));
     563       42364 :     SuccessOrExit(err = entry.GetFabricIndex(fabricIndex));
     564       42364 :     SuccessOrExit(err = entry.GetPrivilege(privilege));
     565       42364 :     SuccessOrExit(err = entry.GetSubjectCount(subjectCount));
     566       42364 :     SuccessOrExit(err = entry.GetTargetCount(targetCount));
     567             : 
     568             : #if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     569       42364 :     ChipLogProgress(DataManagement, "AccessControl: validating f=%u p=%c a=%c s=%d t=%d", fabricIndex,
     570             :                     GetPrivilegeStringForLogging(privilege), GetAuthModeStringForLogging(authMode), static_cast<int>(subjectCount),
     571             :                     static_cast<int>(targetCount));
     572             : #endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     573             : 
     574             :     // Fabric index must be defined.
     575       42364 :     VerifyOrExit(fabricIndex != kUndefinedFabricIndex, log = "invalid fabric index");
     576             : 
     577       42362 :     if (authMode != AuthMode::kCase)
     578             :     {
     579             :         // Operational PASE not supported for v1.0 (so must be group).
     580         734 :         VerifyOrExit(authMode == AuthMode::kGroup, log = "invalid auth mode");
     581             : 
     582             :         // Privilege must not be administer.
     583         290 :         VerifyOrExit(privilege != Privilege::kAdminister, log = "invalid privilege");
     584             :     }
     585             : 
     586       83393 :     for (size_t i = 0; i < subjectCount; ++i)
     587             :     {
     588             :         NodeId subject;
     589       42229 :         SuccessOrExit(err = entry.GetSubject(i, subject));
     590       41853 :         const bool kIsCase  = authMode == AuthMode::kCase;
     591       41853 :         const bool kIsGroup = authMode == AuthMode::kGroup;
     592             : #if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     593       41853 :         ChipLogProgress(DataManagement, "  validating subject 0x" ChipLogFormatX64, ChipLogValueX64(subject));
     594             : #endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
     595       41853 :         VerifyOrExit((kIsCase && IsValidCaseNodeId(subject)) || (kIsGroup && IsValidGroupNodeId(subject)), log = "invalid subject");
     596             :     }
     597             : 
     598       42776 :     for (size_t i = 0; i < targetCount; ++i)
     599             :     {
     600       41360 :         Entry::Target target;
     601       81484 :         SuccessOrExit(err = entry.GetTarget(i, target));
     602       41360 :         const bool kHasCluster    = target.flags & Entry::Target::kCluster;
     603       41360 :         const bool kHasEndpoint   = target.flags & Entry::Target::kEndpoint;
     604       41360 :         const bool kHasDeviceType = target.flags & Entry::Target::kDeviceType;
     605       41360 :         VerifyOrExit((kHasCluster || kHasEndpoint || kHasDeviceType) && !(kHasEndpoint && kHasDeviceType) &&
     606             :                          (!kHasCluster || IsValidClusterId(target.cluster)) &&
     607             :                          (!kHasEndpoint || IsValidEndpointId(target.endpoint)) &&
     608             :                          (!kHasDeviceType || IsValidDeviceTypeId(target.deviceType)),
     609             :                      log = "invalid target");
     610             :     }
     611             : 
     612        1416 :     return true;
     613             : 
     614       40948 : exit:
     615       40948 :     if (err != CHIP_NO_ERROR)
     616             :     {
     617           0 :         ChipLogError(DataManagement, "AccessControl: %s %" CHIP_ERROR_FORMAT, log, err.Format());
     618             :     }
     619             :     else
     620             :     {
     621       40948 :         ChipLogError(DataManagement, "AccessControl: %s", log);
     622             :     }
     623       40948 :     return false;
     624             : }
     625             : 
     626           9 : void AccessControl::NotifyEntryChanged(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index,
     627             :                                        const Entry * entry, EntryListener::ChangeType changeType)
     628             : {
     629           9 :     for (EntryListener * listener = mEntryListener; listener != nullptr; listener = listener->mNext)
     630             :     {
     631           0 :         listener->OnEntryChanged(subjectDescriptor, fabric, index, entry, changeType);
     632             :     }
     633           9 : }
     634             : 
     635        5956 : AccessControl & GetAccessControl()
     636             : {
     637        5956 :     return (globalAccessControl) ? *globalAccessControl : defaultAccessControl.get();
     638             : }
     639             : 
     640         258 : void SetAccessControl(AccessControl & accessControl)
     641             : {
     642         258 :     ChipLogProgress(DataManagement, "AccessControl: setting");
     643         258 :     globalAccessControl = &accessControl;
     644         258 : }
     645             : 
     646         258 : void ResetAccessControlToDefault()
     647             : {
     648         258 :     globalAccessControl = nullptr;
     649         258 : }
     650             : 
     651             : } // namespace Access
     652             : } // namespace chip

Generated by: LCOV version 1.14