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