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