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