Coverage Report

Created: 2026-04-09 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/access/AccessControl.cpp
Line
Count
Source
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
#include <lib/support/TypeTraits.h>
28
29
#include <credentials/GroupDataProvider.h>
30
31
namespace chip {
32
namespace Access {
33
34
using chip::CATValues;
35
using chip::FabricIndex;
36
using chip::NodeId;
37
38
namespace {
39
40
Global<AccessControl> defaultAccessControl;
41
AccessControl * globalAccessControl = nullptr; // lazily defaulted to defaultAccessControl in GetAccessControl
42
43
static_assert(((unsigned(Privilege::kAdminister) & unsigned(Privilege::kManage)) == 0) &&
44
                  ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kOperate)) == 0) &&
45
                  ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kView)) == 0) &&
46
                  ((unsigned(Privilege::kAdminister) & unsigned(Privilege::kProxyView)) == 0) &&
47
                  ((unsigned(Privilege::kManage) & unsigned(Privilege::kOperate)) == 0) &&
48
                  ((unsigned(Privilege::kManage) & unsigned(Privilege::kView)) == 0) &&
49
                  ((unsigned(Privilege::kManage) & unsigned(Privilege::kProxyView)) == 0) &&
50
                  ((unsigned(Privilege::kOperate) & unsigned(Privilege::kView)) == 0) &&
51
                  ((unsigned(Privilege::kOperate) & unsigned(Privilege::kProxyView)) == 0) &&
52
                  ((unsigned(Privilege::kView) & unsigned(Privilege::kProxyView)) == 0),
53
              "Privilege bits must be unique");
54
55
bool CheckRequestPrivilegeAgainstEntryPrivilege(Privilege requestPrivilege, Privilege entryPrivilege)
56
0
{
57
0
    switch (entryPrivilege)
58
0
    {
59
0
    case Privilege::kView:
60
0
        return requestPrivilege == Privilege::kView;
61
0
    case Privilege::kProxyView:
62
0
        return requestPrivilege == Privilege::kProxyView || requestPrivilege == Privilege::kView;
63
0
    case Privilege::kOperate:
64
0
        return requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView;
65
0
    case Privilege::kManage:
66
0
        return requestPrivilege == Privilege::kManage || requestPrivilege == Privilege::kOperate ||
67
0
            requestPrivilege == Privilege::kView;
68
0
    case Privilege::kAdminister:
69
0
        return requestPrivilege == Privilege::kAdminister || requestPrivilege == Privilege::kManage ||
70
0
            requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView ||
71
0
            requestPrivilege == Privilege::kProxyView;
72
0
    }
73
0
    return false;
74
0
}
75
76
constexpr bool IsValidCaseNodeId(NodeId aNodeId)
77
0
{
78
0
    if (IsOperationalNodeId(aNodeId))
79
0
    {
80
0
        return true;
81
0
    }
82
83
0
    if (IsCASEAuthTag(aNodeId) && (GetCASEAuthTagVersion(CASEAuthTagFromNodeId(aNodeId)) != 0))
84
0
    {
85
0
        return true;
86
0
    }
87
88
0
    return false;
89
0
}
90
91
constexpr bool IsValidGroupNodeId(NodeId aNodeId)
92
0
{
93
0
    return IsGroupId(aNodeId) && IsValidGroupId(GroupIdFromNodeId(aNodeId));
94
0
}
95
96
#if CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
97
98
char GetAuthModeStringForLogging(AuthMode authMode)
99
0
{
100
0
    switch (authMode)
101
0
    {
102
0
    case AuthMode::kNone:
103
0
        return 'n';
104
0
    case AuthMode::kInternalDeviceAccess:
105
0
        return 'i';
106
0
    case AuthMode::kPase:
107
0
        return 'p';
108
0
    case AuthMode::kCase:
109
0
        return 'c';
110
0
    case AuthMode::kGroup:
111
0
        return 'g';
112
0
    }
113
0
    return 'u';
114
0
}
115
116
constexpr int kCharsPerCatForLogging = 11; // including final null terminator
117
118
char * GetCatStringForLogging(char * buf, size_t size, const CATValues & cats)
119
0
{
120
0
    if (size == 0)
121
0
    {
122
0
        return nullptr;
123
0
    }
124
0
    char * p         = buf;
125
0
    char * const end = buf + size;
126
0
    *p               = '\0';
127
    // Format string chars needed:
128
    //   1 for comma (optional)
129
    //   2 for 0x prefix
130
    //   8 for 32-bit hex value
131
    //   1 for null terminator (at end)
132
0
    static constexpr char fmtWithoutComma[] = "0x%08" PRIX32;
133
0
    static constexpr char fmtWithComma[]    = ",0x%08" PRIX32;
134
0
    constexpr int countWithoutComma         = 10;
135
0
    constexpr int countWithComma            = countWithoutComma + 1;
136
0
    bool withComma                          = false;
137
0
    for (auto cat : cats.values)
138
0
    {
139
0
        if (cat == chip::kUndefinedCAT)
140
0
        {
141
0
            break;
142
0
        }
143
0
        snprintf(p, static_cast<size_t>(end - p), withComma ? fmtWithComma : fmtWithoutComma, cat);
144
0
        p += withComma ? countWithComma : countWithoutComma;
145
0
        if (p >= end)
146
0
        {
147
            // Output was truncated.
148
0
            p = end - ((size < 4) ? size : 4);
149
0
            while (*p)
150
0
            {
151
                // Indicate truncation if possible.
152
0
                *p++ = '.';
153
0
            }
154
0
            break;
155
0
        }
156
0
        withComma = true;
157
0
    }
158
0
    return buf;
159
0
}
160
161
char GetPrivilegeStringForLogging(Privilege privilege)
162
0
{
163
0
    switch (privilege)
164
0
    {
165
0
    case Privilege::kView:
166
0
        return 'v';
167
0
    case Privilege::kProxyView:
168
0
        return 'p';
169
0
    case Privilege::kOperate:
170
0
        return 'o';
171
0
    case Privilege::kManage:
172
0
        return 'm';
173
0
    case Privilege::kAdminister:
174
0
        return 'a';
175
0
    }
176
0
    return 'u';
177
0
}
178
179
char GetAuxiliaryTypeStringForLogging(AuxiliaryType auxiliaryType)
180
0
{
181
0
    switch (auxiliaryType)
182
0
    {
183
0
    case AuxiliaryType::kSystem:
184
0
        return 's';
185
0
    case AuxiliaryType::kGroupcast:
186
0
        return 'g';
187
0
    default:
188
0
        return 'u';
189
0
    }
190
0
}
191
192
char GetRequestTypeStringForLogging(RequestType requestType)
193
0
{
194
0
    switch (requestType)
195
0
    {
196
0
    case RequestType::kAttributeReadRequest:
197
0
        return 'r';
198
0
    case RequestType::kAttributeWriteRequest:
199
0
        return 'w';
200
0
    case RequestType::kCommandInvokeRequest:
201
0
        return 'i';
202
0
    case RequestType::kEventReadRequest:
203
0
        return 'e';
204
0
    default:
205
0
        return '?';
206
0
    }
207
0
}
208
209
#endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
210
211
} // namespace
212
213
Global<AccessControl::Entry::Delegate> AccessControl::Entry::mDefaultDelegate;
214
Global<AccessControl::EntryIterator::Delegate> AccessControl::EntryIterator::mDefaultDelegate;
215
216
CHIP_ERROR AccessControl::Init(AccessControl::Delegate * delegate, DeviceTypeResolver & deviceTypeResolver)
217
0
{
218
0
    VerifyOrReturnError(!IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
219
220
0
    ChipLogProgress(DataManagement, "AccessControl: initializing");
221
222
0
    VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
223
0
    CHIP_ERROR retval = delegate->Init();
224
0
    if (retval == CHIP_NO_ERROR)
225
0
    {
226
0
        mDelegate           = delegate;
227
0
        mDeviceTypeResolver = &deviceTypeResolver;
228
0
    }
229
230
0
    return retval;
231
0
}
232
233
void AccessControl::Finish()
234
0
{
235
0
    VerifyOrReturn(IsInitialized());
236
0
    ChipLogProgress(DataManagement, "AccessControl: finishing");
237
0
    mDelegate->Finish();
238
0
    mDelegate = nullptr;
239
240
0
    if (IsGroupAuxiliaryDelegateRegistered())
241
0
    {
242
0
        mGroupAuxDelegate->Finish();
243
0
        UnregisterGroupAuxiliaryDelegate();
244
0
    }
245
0
}
246
247
CHIP_ERROR AccessControl::CreateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t * index,
248
                                      const Entry & entry)
249
0
{
250
0
    VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
251
252
0
    size_t count    = 0;
253
0
    size_t maxCount = 0;
254
0
    ReturnErrorOnFailure(mDelegate->GetEntryCount(fabric, count));
255
0
    ReturnErrorOnFailure(mDelegate->GetMaxEntriesPerFabric(maxCount));
256
257
0
    VerifyOrReturnError((count + 1) <= maxCount, CHIP_ERROR_BUFFER_TOO_SMALL);
258
259
0
    VerifyOrReturnError(entry.IsValid(), CHIP_ERROR_INVALID_ARGUMENT);
260
261
0
    size_t i = 0;
262
0
    ReturnErrorOnFailure(mDelegate->CreateEntry(&i, entry, &fabric));
263
264
0
    if (index)
265
0
    {
266
0
        *index = i;
267
0
    }
268
269
0
    NotifyEntryChanged(subjectDescriptor, fabric, i, &entry, EntryListener::ChangeType::kAdded);
270
0
    return CHIP_NO_ERROR;
271
0
}
272
273
CHIP_ERROR AccessControl::UpdateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index,
274
                                      const Entry & entry)
275
0
{
276
0
    VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
277
0
    VerifyOrReturnError(entry.IsValid(), CHIP_ERROR_INVALID_ARGUMENT);
278
0
    ReturnErrorOnFailure(mDelegate->UpdateEntry(index, entry, &fabric));
279
0
    NotifyEntryChanged(subjectDescriptor, fabric, index, &entry, EntryListener::ChangeType::kUpdated);
280
0
    return CHIP_NO_ERROR;
281
0
}
282
283
CHIP_ERROR AccessControl::DeleteEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index)
284
0
{
285
0
    VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
286
0
    Entry entry;
287
0
    Entry * p = nullptr;
288
0
    if (mEntryListener != nullptr && ReadEntry(fabric, index, entry) == CHIP_NO_ERROR)
289
0
    {
290
0
        p = &entry;
291
0
    }
292
0
    ReturnErrorOnFailure(mDelegate->DeleteEntry(index, &fabric));
293
0
    if (p && p->HasDefaultDelegate())
294
0
    {
295
        // The entry was read prior to deletion so its latest value could be provided
296
        // to the listener after deletion. If it's been reset to its default delegate,
297
        // that best effort attempt to retain the latest value failed. This is
298
        // regrettable but OK.
299
0
        p = nullptr;
300
0
    }
301
0
    NotifyEntryChanged(subjectDescriptor, fabric, index, p, EntryListener::ChangeType::kRemoved);
302
0
    return CHIP_NO_ERROR;
303
0
}
304
305
void AccessControl::AddEntryListener(EntryListener & listener)
306
0
{
307
0
    if (mEntryListener == nullptr)
308
0
    {
309
0
        mEntryListener = &listener;
310
0
        listener.mNext = nullptr;
311
0
        return;
312
0
    }
313
314
0
    for (EntryListener * l = mEntryListener; /**/; l = l->mNext)
315
0
    {
316
0
        if (l == &listener)
317
0
        {
318
0
            return;
319
0
        }
320
321
0
        if (l->mNext == nullptr)
322
0
        {
323
0
            l->mNext       = &listener;
324
0
            listener.mNext = nullptr;
325
0
            return;
326
0
        }
327
0
    }
328
0
}
329
330
void AccessControl::RemoveEntryListener(EntryListener & listener)
331
0
{
332
0
    if (mEntryListener == &listener)
333
0
    {
334
0
        mEntryListener = listener.mNext;
335
0
        listener.mNext = nullptr;
336
0
        return;
337
0
    }
338
339
0
    for (EntryListener * l = mEntryListener; l != nullptr; l = l->mNext)
340
0
    {
341
0
        if (l->mNext == &listener)
342
0
        {
343
0
            l->mNext       = listener.mNext;
344
0
            listener.mNext = nullptr;
345
0
            return;
346
0
        }
347
0
    }
348
0
}
349
350
bool AccessControl::IsAccessRestrictionListSupported() const
351
0
{
352
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
353
    return mAccessRestrictionProvider != nullptr;
354
#else
355
0
    return false;
356
0
#endif
357
0
}
358
359
CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
360
                                Privilege requestPrivilege)
361
0
{
362
0
    VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
363
0
    VerifyOrReturnError(IsValidPrivilege(requestPrivilege), CHIP_ERROR_INVALID_ARGUMENT);
364
365
0
    CHIP_ERROR result = CheckACL(subjectDescriptor, requestPath, requestPrivilege);
366
367
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
368
    if (result == CHIP_NO_ERROR)
369
    {
370
        result = CheckARL(subjectDescriptor, requestPath, requestPrivilege);
371
    }
372
#endif
373
374
0
    if ((CHIP_NO_ERROR != result) && (Access::AuthMode::kGroup == subjectDescriptor.authMode) &&
375
0
        (Access::RequestType::kCommandInvokeRequest == requestPath.requestType) &&
376
0
        (Access::Privilege::kOperate == requestPrivilege) && IsGroupId(subjectDescriptor.subject))
377
0
    {
378
0
        Credentials::GroupDataProvider * groups = Credentials::GetGroupDataProvider();
379
0
        VerifyOrReturnError(nullptr != groups, result);
380
0
        Credentials::GroupDataProvider::GroupInfo info;
381
0
        GroupId gid = GroupIdFromNodeId(subjectDescriptor.subject);
382
0
        ReturnErrorOnFailure(groups->GetGroupInfo(subjectDescriptor.fabricIndex, gid, info));
383
0
        if (info.HasAuxiliaryACL() && IsGroupAuxiliaryDelegateRegistered())
384
0
        {
385
0
            return mGroupAuxDelegate->Check(subjectDescriptor, requestPath, requestPrivilege);
386
0
        }
387
0
    }
388
389
0
    return result;
390
0
}
391
392
CHIP_ERROR AccessControl::CheckACL(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
393
                                   Privilege requestPrivilege)
394
0
{
395
0
#if CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
396
0
    {
397
0
        constexpr size_t kMaxCatsToLog = 6;
398
0
        char catLogBuf[kMaxCatsToLog * kCharsPerCatForLogging];
399
0
        ChipLogProgress(DataManagement,
400
0
                        "AccessControl: checking f=%u a=%c s=0x" ChipLogFormatX64 " t=%s c=" ChipLogFormatMEI " e=%u p=%c r=%c",
401
0
                        subjectDescriptor.fabricIndex, GetAuthModeStringForLogging(subjectDescriptor.authMode),
402
0
                        ChipLogValueX64(subjectDescriptor.subject),
403
0
                        GetCatStringForLogging(catLogBuf, sizeof(catLogBuf), subjectDescriptor.cats),
404
0
                        ChipLogValueMEI(requestPath.cluster), requestPath.endpoint, GetPrivilegeStringForLogging(requestPrivilege),
405
0
                        GetRequestTypeStringForLogging(requestPath.requestType));
406
0
    }
407
0
#endif // CHIP_PROGRESS_LOGGING && CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
408
409
0
    {
410
0
        CHIP_ERROR result = mDelegate->Check(subjectDescriptor, requestPath, requestPrivilege);
411
0
        if (result != CHIP_ERROR_NOT_IMPLEMENTED)
412
0
        {
413
0
#if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
414
0
            ChipLogProgress(DataManagement, "AccessControl: %s (delegate)",
415
0
                            (result == CHIP_NO_ERROR)                  ? "allowed"
416
0
                                : (result == CHIP_ERROR_ACCESS_DENIED) ? "denied"
417
0
                                                                       : "error");
418
#else
419
            if (result != CHIP_NO_ERROR)
420
            {
421
                ChipLogProgress(DataManagement, "AccessControl: %s (delegate)",
422
                                (result == CHIP_ERROR_ACCESS_DENIED) ? "denied" : "error");
423
            }
424
#endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
425
426
0
            return result;
427
0
        }
428
0
    }
429
430
    // Operational PASE not supported for v1.0, so PASE implies commissioning, which has highest privilege.
431
    // Currently, subject descriptor is only PASE if this node is the responder (aka commissionee);
432
    // if this node is the initiator (aka commissioner) then the subject descriptor remains blank.
433
0
    if (subjectDescriptor.authMode == AuthMode::kPase)
434
0
    {
435
0
#if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
436
0
        ChipLogProgress(DataManagement, "AccessControl: implicit admin (PASE)");
437
0
#endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
438
0
        return CHIP_NO_ERROR;
439
0
    }
440
441
0
    EntryIterator iterator;
442
0
    ReturnErrorOnFailure(Entries(iterator, &subjectDescriptor.fabricIndex));
443
444
0
    Entry entry;
445
0
    while (iterator.Next(entry) == CHIP_NO_ERROR)
446
0
    {
447
0
        AuthMode authMode = AuthMode::kNone;
448
0
        ReturnErrorOnFailure(entry.GetAuthMode(authMode));
449
        // Operational PASE not supported for v1.0.
450
0
        VerifyOrReturnError(authMode == AuthMode::kCase || authMode == AuthMode::kGroup, CHIP_ERROR_INCORRECT_STATE);
451
0
        if (authMode != subjectDescriptor.authMode)
452
0
        {
453
0
            continue;
454
0
        }
455
456
0
        Privilege privilege = Privilege::kView;
457
0
        ReturnErrorOnFailure(entry.GetPrivilege(privilege));
458
0
        if (!CheckRequestPrivilegeAgainstEntryPrivilege(requestPrivilege, privilege))
459
0
        {
460
0
            continue;
461
0
        }
462
463
0
        size_t subjectCount = 0;
464
0
        ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
465
0
        if (subjectCount > 0)
466
0
        {
467
0
            bool subjectMatched = false;
468
0
            for (size_t i = 0; i < subjectCount; ++i)
469
0
            {
470
0
                NodeId subject = kUndefinedNodeId;
471
0
                ReturnErrorOnFailure(entry.GetSubject(i, subject));
472
0
                if (IsOperationalNodeId(subject))
473
0
                {
474
0
                    VerifyOrReturnError(authMode == AuthMode::kCase, CHIP_ERROR_INCORRECT_STATE);
475
0
                    if (subject == subjectDescriptor.subject)
476
0
                    {
477
0
                        subjectMatched = true;
478
0
                        break;
479
0
                    }
480
0
                }
481
0
                else if (IsCASEAuthTag(subject))
482
0
                {
483
0
                    VerifyOrReturnError(authMode == AuthMode::kCase, CHIP_ERROR_INCORRECT_STATE);
484
0
                    if (subjectDescriptor.cats.CheckSubjectAgainstCATs(subject))
485
0
                    {
486
0
                        subjectMatched = true;
487
0
                        break;
488
0
                    }
489
0
                }
490
0
                else if (IsGroupId(subject))
491
0
                {
492
0
                    VerifyOrReturnError(authMode == AuthMode::kGroup, CHIP_ERROR_INCORRECT_STATE);
493
0
                    if (subject == subjectDescriptor.subject)
494
0
                    {
495
0
                        subjectMatched = true;
496
0
                        break;
497
0
                    }
498
0
                }
499
0
                else
500
0
                {
501
                    // Operational PASE not supported for v1.0.
502
0
                    return CHIP_ERROR_INCORRECT_STATE;
503
0
                }
504
0
            }
505
0
            if (!subjectMatched)
506
0
            {
507
0
                continue;
508
0
            }
509
0
        }
510
511
0
        size_t targetCount = 0;
512
0
        ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
513
0
        if (targetCount > 0)
514
0
        {
515
0
            bool targetMatched = false;
516
0
            for (size_t i = 0; i < targetCount; ++i)
517
0
            {
518
0
                Entry::Target target;
519
0
                ReturnErrorOnFailure(entry.GetTarget(i, target));
520
0
                if ((target.flags & Entry::Target::kCluster) && target.cluster != requestPath.cluster)
521
0
                {
522
0
                    continue;
523
0
                }
524
0
                if ((target.flags & Entry::Target::kEndpoint) && target.endpoint != requestPath.endpoint)
525
0
                {
526
0
                    continue;
527
0
                }
528
0
                if (target.flags & Entry::Target::kDeviceType &&
529
0
                    !mDeviceTypeResolver->IsDeviceTypeOnEndpoint(target.deviceType, requestPath.endpoint))
530
0
                {
531
0
                    continue;
532
0
                }
533
0
                targetMatched = true;
534
0
                break;
535
0
            }
536
0
            if (!targetMatched)
537
0
            {
538
0
                continue;
539
0
            }
540
0
        }
541
        // Entry passed all checks: access is allowed.
542
543
0
#if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
544
0
        ChipLogProgress(DataManagement, "AccessControl: allowed");
545
0
#endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 0
546
547
0
        return CHIP_NO_ERROR;
548
0
    }
549
550
    // No entry was found which passed all checks: access is denied.
551
0
    ChipLogProgress(DataManagement, "AccessControl: denied");
552
0
    return CHIP_ERROR_ACCESS_DENIED;
553
0
}
554
555
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
556
CHIP_ERROR AccessControl::CheckARL(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
557
                                   Privilege requestPrivilege)
558
{
559
    CHIP_ERROR result = CHIP_NO_ERROR;
560
561
    VerifyOrReturnError(requestPath.requestType != RequestType::kRequestTypeUnknown, CHIP_ERROR_INVALID_ARGUMENT);
562
563
    if (!IsAccessRestrictionListSupported())
564
    {
565
        // Access Restriction support is compiled in, but not configured/enabled. Nothing to restrict.
566
        return CHIP_NO_ERROR;
567
    }
568
569
    if (subjectDescriptor.isCommissioning)
570
    {
571
        result = mAccessRestrictionProvider->CheckForCommissioning(subjectDescriptor, requestPath);
572
    }
573
    else
574
    {
575
        result = mAccessRestrictionProvider->Check(subjectDescriptor, requestPath);
576
    }
577
578
    if (result != CHIP_NO_ERROR)
579
    {
580
        ChipLogProgress(DataManagement, "AccessControl: %s",
581
                        (result == CHIP_ERROR_ACCESS_RESTRICTED_BY_ARL) ? "denied (restricted)" : "denied (restriction error)");
582
        return result;
583
    }
584
585
    return result;
586
}
587
#endif
588
589
#if CHIP_ACCESS_CONTROL_DUMP_ENABLED
590
CHIP_ERROR AccessControl::Dump(const Entry & entry)
591
{
592
    CHIP_ERROR err;
593
594
    ChipLogDetail(DataManagement, "----- BEGIN ENTRY -----");
595
596
    {
597
        FabricIndex fabricIndex;
598
        SuccessOrExit(err = entry.GetFabricIndex(fabricIndex));
599
        ChipLogDetail(DataManagement, "fabricIndex: %u", fabricIndex);
600
    }
601
602
    {
603
        Privilege privilege;
604
        SuccessOrExit(err = entry.GetPrivilege(privilege));
605
        ChipLogDetail(DataManagement, "privilege: %d", to_underlying(privilege));
606
    }
607
608
    {
609
        AuthMode authMode;
610
        SuccessOrExit(err = entry.GetAuthMode(authMode));
611
        ChipLogDetail(DataManagement, "authMode: %d", to_underlying(authMode));
612
    }
613
614
    {
615
        AuxiliaryType auxiliaryType;
616
        // Auxiliary type is optional, so it not being implemented
617
        // is still a valid configuration, all other errors should
618
        // be handled appropriately.
619
        err = GetAuxiliaryType(auxiliaryType);
620
        if (err != CHIP_ERROR_NOT_IMPLEMENTED)
621
        {
622
            SuccessOrExit(err);
623
            ChipLogDetail(DataManagement, "auxiliaryType: %d", to_underlying(auxiliaryType));
624
        }
625
    }
626
627
    {
628
        size_t count;
629
        SuccessOrExit(err = entry.GetSubjectCount(count));
630
        if (count)
631
        {
632
            ChipLogDetail(DataManagement, "subjects: %u", static_cast<unsigned>(count));
633
            for (size_t i = 0; i < count; ++i)
634
            {
635
                NodeId subject;
636
                SuccessOrExit(err = entry.GetSubject(i, subject));
637
                ChipLogDetail(DataManagement, "  %u: 0x" ChipLogFormatX64, static_cast<unsigned>(i), ChipLogValueX64(subject));
638
            }
639
        }
640
    }
641
642
    {
643
        size_t count;
644
        SuccessOrExit(err = entry.GetTargetCount(count));
645
        if (count)
646
        {
647
            ChipLogDetail(DataManagement, "targets: %u", static_cast<unsigned>(count));
648
            for (size_t i = 0; i < count; ++i)
649
            {
650
                Entry::Target target;
651
                SuccessOrExit(err = entry.GetTarget(i, target));
652
                if (target.flags & Entry::Target::kCluster)
653
                {
654
                    ChipLogDetail(DataManagement, "  %u: cluster: 0x" ChipLogFormatMEI, static_cast<unsigned>(i),
655
                                  ChipLogValueMEI(target.cluster));
656
                }
657
                if (target.flags & Entry::Target::kEndpoint)
658
                {
659
                    ChipLogDetail(DataManagement, "  %u: endpoint: %u", static_cast<unsigned>(i), target.endpoint);
660
                }
661
                if (target.flags & Entry::Target::kDeviceType)
662
                {
663
                    ChipLogDetail(DataManagement, "  %u: deviceType: 0x" ChipLogFormatMEI, static_cast<unsigned>(i),
664
                                  ChipLogValueMEI(target.deviceType));
665
                }
666
            }
667
        }
668
    }
669
670
    ChipLogDetail(DataManagement, "----- END ENTRY -----");
671
672
    return CHIP_NO_ERROR;
673
674
exit:
675
    ChipLogError(DataManagement, "AccessControl: dump failed %" CHIP_ERROR_FORMAT, err.Format());
676
    return err;
677
}
678
#endif
679
680
bool AccessControl::Entry::IsValid() const
681
0
{
682
0
    const char * log = "unexpected error";
683
0
    IgnoreUnusedVariable(log); // logging may be disabled
684
685
0
    AuthMode authMode           = AuthMode::kNone;
686
0
    FabricIndex fabricIndex     = kUndefinedFabricIndex;
687
0
    Privilege privilege         = static_cast<Privilege>(0);
688
0
    AuxiliaryType auxiliaryType = AuxiliaryType::kSystem;
689
0
    size_t subjectCount         = 0;
690
0
    size_t targetCount          = 0;
691
692
0
    CHIP_ERROR err = CHIP_NO_ERROR;
693
0
    SuccessOrExit(err = GetAuthMode(authMode));
694
0
    SuccessOrExit(err = GetFabricIndex(fabricIndex));
695
0
    SuccessOrExit(err = GetPrivilege(privilege));
696
0
    SuccessOrExit(err = GetSubjectCount(subjectCount));
697
0
    SuccessOrExit(err = GetTargetCount(targetCount));
698
699
    // Auxiliary type is optional, so it not being implemented
700
    // is still a valid configuration, all other errors should
701
    // be handled appropriately.
702
0
    err = GetAuxiliaryType(auxiliaryType);
703
0
    if (err != CHIP_ERROR_NOT_IMPLEMENTED)
704
0
    {
705
0
        SuccessOrExit(err);
706
0
    }
707
708
0
#if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
709
0
    ChipLogProgress(DataManagement, "AccessControl: validating f=%u p=%c a=%c x=%d s=%d t=%d", fabricIndex,
710
0
                    GetPrivilegeStringForLogging(privilege), GetAuthModeStringForLogging(authMode),
711
0
                    GetAuxiliaryTypeStringForLogging(auxiliaryType), static_cast<int>(subjectCount), static_cast<int>(targetCount));
712
0
#endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
713
714
    // Fabric index must be defined.
715
0
    VerifyOrExit(fabricIndex != kUndefinedFabricIndex, log = "invalid fabric index");
716
717
0
    if (authMode != AuthMode::kCase)
718
0
    {
719
        // Operational PASE not supported for v1.0 (so must be group).
720
0
        VerifyOrExit(authMode == AuthMode::kGroup, log = "invalid auth mode");
721
722
        // Privilege must not be administer.
723
0
        VerifyOrExit(privilege != Privilege::kAdminister, log = "invalid privilege");
724
0
    }
725
726
0
    for (size_t i = 0; i < subjectCount; ++i)
727
0
    {
728
0
        NodeId subject;
729
0
        SuccessOrExit(err = GetSubject(i, subject));
730
0
        const bool kIsCase  = authMode == AuthMode::kCase;
731
0
        const bool kIsGroup = authMode == AuthMode::kGroup;
732
0
#if CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
733
0
        ChipLogProgress(DataManagement, "  validating subject 0x" ChipLogFormatX64, ChipLogValueX64(subject));
734
0
#endif // CHIP_CONFIG_ACCESS_CONTROL_POLICY_LOGGING_VERBOSITY > 1
735
0
        VerifyOrExit((kIsCase && IsValidCaseNodeId(subject)) || (kIsGroup && IsValidGroupNodeId(subject)), log = "invalid subject");
736
0
    }
737
738
0
    for (size_t i = 0; i < targetCount; ++i)
739
0
    {
740
0
        Entry::Target target;
741
0
        SuccessOrExit(err = GetTarget(i, target));
742
0
        const bool kHasCluster    = target.flags & Entry::Target::kCluster;
743
0
        const bool kHasEndpoint   = target.flags & Entry::Target::kEndpoint;
744
0
        const bool kHasDeviceType = target.flags & Entry::Target::kDeviceType;
745
0
        VerifyOrExit((kHasCluster || kHasEndpoint || kHasDeviceType) && !(kHasEndpoint && kHasDeviceType) &&
746
0
                         (!kHasCluster || IsValidClusterId(target.cluster)) &&
747
0
                         (!kHasEndpoint || IsValidEndpointId(target.endpoint)) &&
748
0
                         (!kHasDeviceType || IsValidDeviceTypeId(target.deviceType)),
749
0
                     log = "invalid target");
750
0
    }
751
752
0
    return true;
753
754
0
exit:
755
0
    if (err != CHIP_NO_ERROR)
756
0
    {
757
0
        ChipLogError(DataManagement, "AccessControl: %s %" CHIP_ERROR_FORMAT, log, err.Format());
758
0
    }
759
0
    else
760
0
    {
761
0
        ChipLogError(DataManagement, "AccessControl: %s", log);
762
0
    }
763
0
    return false;
764
0
}
765
766
void AccessControl::NotifyEntryChanged(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index,
767
                                       const Entry * entry, EntryListener::ChangeType changeType)
768
0
{
769
0
    for (EntryListener * listener = mEntryListener; listener != nullptr; listener = listener->mNext)
770
0
    {
771
0
        listener->OnEntryChanged(subjectDescriptor, fabric, index, entry, changeType);
772
0
    }
773
0
}
774
775
AccessControl & GetAccessControl()
776
0
{
777
0
    return (globalAccessControl) ? *globalAccessControl : defaultAccessControl.get();
778
0
}
779
780
void SetAccessControl(AccessControl & accessControl)
781
0
{
782
0
    ChipLogProgress(DataManagement, "AccessControl: setting");
783
0
    globalAccessControl = &accessControl;
784
0
}
785
786
void ResetAccessControlToDefault()
787
0
{
788
0
    globalAccessControl = nullptr;
789
0
}
790
791
} // namespace Access
792
} // namespace chip