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