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