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 : #include "ExampleAccessControlDelegate.h"
20 :
21 : #include <lib/core/CHIPConfig.h>
22 :
23 : #include <algorithm>
24 : #include <cstdint>
25 : #include <string>
26 : #include <type_traits>
27 :
28 : namespace {
29 :
30 : using chip::ClusterId;
31 : using chip::DeviceTypeId;
32 : using chip::EndpointId;
33 : using chip::FabricIndex;
34 : using chip::NodeId;
35 :
36 : using chip::kUndefinedNodeId;
37 :
38 : using chip::Access::AccessControl;
39 : using chip::Access::AuthMode;
40 : using chip::Access::Privilege;
41 : using chip::Access::RequestPath;
42 : using chip::Access::SubjectDescriptor;
43 :
44 : using Entry = chip::Access::AccessControl::Entry;
45 : using EntryIterator = chip::Access::AccessControl::EntryIterator;
46 : using Target = Entry::Target;
47 :
48 : // Pool sizes
49 : constexpr int kEntryStoragePoolSize = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_STORAGE_POOL_SIZE;
50 : constexpr int kEntryDelegatePoolSize = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_DELEGATE_POOL_SIZE;
51 : constexpr int kEntryIteratorDelegatePoolSize = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_ITERATOR_DELEGATE_POOL_SIZE;
52 :
53 : /*
54 : +---+ +---+ +---+ +---+
55 : | 1 | | 2 | | A | | C | ENTRIES
56 : +---+ +---+ +---+ +---+
57 : | | | |
58 : | +-+ +-+ |
59 : +----+ | | +-----+
60 : | | | |
61 : v v v v
62 : +---+---+---+---+
63 : | 1 | 2 | A | C | ENTRY DELEGATE POOL
64 : +---+---+---+---+
65 : | | | |
66 : +-----------+ | | +-----------+
67 : | +-----------+ +-------+ |
68 : | | | |
69 : v v v v
70 : +---+---+---+---+ +---+---+---+---+
71 : | 0 | 1 | 2 | X | | A | X | C | X | ACL ENTRY STORAGE & POOL
72 : +---+---+---+---+ +---+---+---+---+
73 : ^ ^
74 : | |
75 : | +-----------+
76 : +---------------+ |
77 : | |
78 : +---+---+---+---+
79 : | 0 | 2 | X | X | ENTRY ITERATOR DELEGATE POOL
80 : +---+---+---+---+
81 : ^ ^
82 : | |
83 : | +-------+
84 : | |
85 : +---+ +---+
86 : | 0 | | 2 | ENTRY ITERATORS
87 : +---+ +---+
88 : */
89 :
90 : class SubjectStorage
91 : {
92 : public:
93 6826 : bool IsEmpty() const { return mNode == kUndefinedNodeId; }
94 :
95 7112 : void Clear() { mNode = kUndefinedNodeId; }
96 :
97 1975 : CHIP_ERROR Get(NodeId & node) const
98 : {
99 1975 : if (!IsEmpty())
100 : {
101 1974 : node = mNode;
102 1974 : return CHIP_NO_ERROR;
103 : }
104 1 : return CHIP_ERROR_SENTINEL;
105 : }
106 :
107 32 : CHIP_ERROR Set(NodeId node)
108 : {
109 32 : if (!IsEmpty())
110 : {
111 32 : if (IsValid(node))
112 : {
113 32 : mNode = node;
114 32 : return CHIP_NO_ERROR;
115 : }
116 0 : return CHIP_ERROR_INVALID_ARGUMENT;
117 : }
118 0 : return CHIP_ERROR_SENTINEL;
119 : }
120 :
121 652 : CHIP_ERROR Add(NodeId node)
122 : {
123 652 : if (IsValid(node))
124 : {
125 652 : mNode = node;
126 652 : return CHIP_NO_ERROR;
127 : }
128 0 : return CHIP_ERROR_INVALID_ARGUMENT;
129 : }
130 :
131 : private:
132 684 : static bool IsValid(NodeId node) { return node != kUndefinedNodeId; }
133 :
134 : static_assert(sizeof(NodeId) == 8, "Expecting 8 byte node ID");
135 :
136 : NodeId mNode;
137 : };
138 :
139 : class TargetStorage
140 : {
141 : public:
142 6296 : bool IsEmpty() const { return mCluster == kClusterEmpty && mDeviceType == kDeviceTypeEmpty; }
143 :
144 5335 : void Clear()
145 : {
146 5335 : mCluster = kClusterEmpty;
147 5335 : mDeviceType = kDeviceTypeEmpty;
148 5335 : }
149 :
150 1671 : CHIP_ERROR Get(Target & target) const
151 : {
152 1671 : if (!IsEmpty())
153 : {
154 1670 : Decode(target);
155 1670 : return CHIP_NO_ERROR;
156 : }
157 1 : return CHIP_ERROR_SENTINEL;
158 : }
159 :
160 356 : CHIP_ERROR Set(const Target & target)
161 : {
162 356 : if (!IsEmpty())
163 : {
164 356 : if (IsValid(target))
165 : {
166 356 : Encode(target);
167 356 : return CHIP_NO_ERROR;
168 : }
169 0 : return CHIP_ERROR_INVALID_ARGUMENT;
170 : }
171 0 : return CHIP_ERROR_SENTINEL;
172 : }
173 :
174 530 : CHIP_ERROR Add(const Target & target)
175 : {
176 530 : if (IsValid(target))
177 : {
178 530 : Encode(target);
179 530 : return CHIP_NO_ERROR;
180 : }
181 0 : return CHIP_ERROR_INVALID_ARGUMENT;
182 : }
183 :
184 : private:
185 : // TODO: eventually this functionality should live where the type itself is defined
186 771 : static bool IsValidCluster(ClusterId cluster)
187 : {
188 771 : const auto id = cluster & kClusterIdMask;
189 771 : const auto vendor = cluster & kClusterVendorMask;
190 771 : return ((id <= kClusterIdMaxStd) || (kClusterIdMinMs <= id && id <= kClusterIdMaxMs)) && (vendor <= kClusterVendorMax);
191 : }
192 :
193 : // TODO: eventually this functionality should live where the type itself is defined
194 324 : static constexpr bool IsValidEndpoint(EndpointId endpoint) { return true; }
195 :
196 : // TODO: eventually this functionality should live where the type itself is defined
197 272 : static bool IsValidDeviceType(DeviceTypeId deviceType)
198 : {
199 272 : const auto id = deviceType & kDeviceTypeIdMask;
200 272 : const auto vendor = deviceType & kDeviceTypeVendorMask;
201 272 : return (id <= kDeviceTypeIdMax) && (vendor <= kDeviceTypeVendorMax);
202 : }
203 :
204 : // TODO: eventually this functionality should live where the type itself is defined
205 886 : static bool IsValid(const Target & target)
206 : {
207 886 : constexpr Target::Flags kNotAll = Target::kEndpoint | Target::kDeviceType;
208 886 : constexpr Target::Flags kAtLeastOne = kNotAll | Target::kCluster;
209 886 : constexpr Target::Flags kNone = ~kAtLeastOne;
210 886 : return ((target.flags & kNone) == 0) && ((target.flags & kAtLeastOne) != 0) && ((target.flags & kNotAll) != kNotAll) &&
211 886 : !((target.flags & Target::kCluster) && !IsValidCluster(target.cluster)) &&
212 2658 : !((target.flags & Target::kEndpoint) && !IsValidEndpoint(target.endpoint)) &&
213 1772 : !((target.flags & Target::kDeviceType) && !IsValidDeviceType(target.deviceType));
214 : }
215 :
216 1670 : void Decode(Target & target) const
217 : {
218 1670 : auto & flags = target.flags;
219 1670 : auto & cluster = target.cluster;
220 1670 : auto & endpoint = target.endpoint;
221 1670 : auto & deviceType = target.deviceType;
222 1670 : flags = 0;
223 1670 : if (mCluster != kClusterEmpty)
224 : {
225 1481 : cluster = mCluster;
226 1481 : flags |= Target::kCluster;
227 : }
228 1670 : if (mDeviceType != kDeviceTypeEmpty)
229 : {
230 1083 : if ((mDeviceType & kDeviceTypeIdMask) == kEndpointMagic)
231 : {
232 539 : endpoint = static_cast<EndpointId>(mDeviceType >> kEndpointShift);
233 539 : flags |= Target::kEndpoint;
234 : }
235 : else
236 : {
237 544 : deviceType = mDeviceType;
238 544 : flags |= Target::kDeviceType;
239 : }
240 : }
241 1670 : }
242 :
243 886 : void Encode(const Target & target)
244 : {
245 886 : const auto flags = target.flags;
246 886 : const auto cluster = target.cluster;
247 886 : const auto endpoint = target.endpoint;
248 886 : const auto deviceType = target.deviceType;
249 886 : if (flags & Target::kCluster)
250 : {
251 771 : mCluster = cluster;
252 : }
253 : else
254 : {
255 115 : mCluster = kClusterEmpty;
256 : }
257 886 : if (flags & Target::kEndpoint)
258 : {
259 324 : mDeviceType = (static_cast<DeviceTypeId>(endpoint) << kEndpointShift) | kEndpointMagic;
260 : }
261 562 : else if (flags & Target::kDeviceType)
262 : {
263 272 : mDeviceType = deviceType;
264 : }
265 : else
266 : {
267 290 : mDeviceType = kDeviceTypeEmpty;
268 : }
269 886 : }
270 :
271 : static_assert(sizeof(ClusterId) == 4, "Expecting 4 byte cluster ID");
272 : static_assert(sizeof(EndpointId) == 2, "Expecting 2 byte endpoint ID");
273 : static_assert(sizeof(DeviceTypeId) == 4, "Expecting 4 byte device type ID");
274 :
275 : // TODO: some (not all) of these values should live where the type itself is defined
276 :
277 : // (mCluster == kClusterEmpty) --> mCluster contains no cluster
278 : static constexpr ClusterId kClusterEmpty = 0xFFFFFFFF;
279 :
280 : // (mCluster & kClusterIdMask) --> cluster id portion
281 : static constexpr ClusterId kClusterIdMask = 0x0000FFFF;
282 :
283 : // ((mCluster & kClusterIdMask) < kClusterIdMaxStd) --> invalid
284 : static constexpr ClusterId kClusterIdMaxStd = 0x00007FFF;
285 :
286 : // ((mCluster & kClusterIdMask) < kClusterIdMinMs) --> invalid
287 : static constexpr ClusterId kClusterIdMinMs = 0x0000FC00;
288 :
289 : // ((mCluster & kClusterIdMask) < kClusterIdMaxMs) --> invalid
290 : static constexpr ClusterId kClusterIdMaxMs = 0x0000FFFE;
291 :
292 : // (mCluster & kClusterVendorMask) --> cluster vendor portion
293 : static constexpr ClusterId kClusterVendorMask = 0xFFFF0000;
294 :
295 : // ((mCluster & kClusterVendorMask) > kClusterVendorMax) --> invalid
296 : static constexpr ClusterId kClusterVendorMax = 0xFFFE0000;
297 :
298 : // (mDeviceType == kDeviceTypeEmpty) --> mDeviceType contains neither endpoint nor device type
299 : static constexpr DeviceTypeId kDeviceTypeEmpty = 0xFFFFFFFF;
300 :
301 : // (mDeviceType & kDeviceTypeIdMask) --> device type id portion
302 : static constexpr DeviceTypeId kDeviceTypeIdMask = 0x0000FFFF;
303 :
304 : // ((mDeviceType & kDeviceTypeIdMask) < kDeviceTypeIdMax) --> invalid
305 : static constexpr DeviceTypeId kDeviceTypeIdMax = 0x0000BFFF;
306 :
307 : // (mDeviceType & kDeviceTypeVendorMask) --> device type vendor portion
308 : static constexpr DeviceTypeId kDeviceTypeVendorMask = 0xFFFF0000;
309 :
310 : // ((mDeviceType & kDeviceTypeVendorMask) > kDeviceTypeVendorMax) --> invalid
311 : static constexpr DeviceTypeId kDeviceTypeVendorMax = 0xFFFE0000;
312 :
313 : // ((mDeviceType & kDeviceTypeIdMask) == kEndpointMagic) --> mDeviceType contains endpoint
314 : static constexpr DeviceTypeId kEndpointMagic = 0x0000EEEE;
315 :
316 : // (mDeviceType >> kEndpointShift) --> extract endpoint from mDeviceType
317 : static constexpr int kEndpointShift = 16;
318 :
319 : ClusterId mCluster;
320 : DeviceTypeId mDeviceType;
321 : };
322 :
323 : class EntryStorage
324 : {
325 : public:
326 : // ACL support
327 : static constexpr size_t kNumberOfFabrics = CHIP_CONFIG_MAX_FABRICS;
328 : static constexpr size_t kEntriesPerFabric = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC;
329 : static EntryStorage acl[kNumberOfFabrics * kEntriesPerFabric];
330 :
331 : // Find the next unused entry storage in the access control list, if one exists.
332 1009 : static EntryStorage * FindUnusedInAcl()
333 : {
334 3645 : for (auto & storage : acl)
335 : {
336 3645 : if (!storage.InUse())
337 : {
338 1009 : return &storage;
339 : }
340 : }
341 0 : return nullptr;
342 : }
343 :
344 : // Find the specified used entry storage in the access control list, if it exists.
345 22433 : static EntryStorage * FindUsedInAcl(size_t index, const FabricIndex * fabricIndex)
346 : {
347 22433 : if (fabricIndex != nullptr)
348 : {
349 27 : ConvertIndex(index, *fabricIndex, ConvertDirection::kRelativeToAbsolute);
350 : }
351 22433 : if (index < ArraySize(acl))
352 : {
353 22424 : auto * storage = acl + index;
354 22424 : if (storage->InUse())
355 : {
356 1799 : return storage;
357 : }
358 : }
359 20634 : return nullptr;
360 : }
361 :
362 : // Pool support
363 : static EntryStorage pool[kEntryStoragePoolSize];
364 :
365 : // Find an unused entry storage in the pool, if one is available.
366 : // The candidate is preferred if provided and it is in the pool,
367 : // regardless of whether it is already in use.
368 651 : static EntryStorage * Find(EntryStorage * candidate)
369 : {
370 651 : if (candidate && candidate->InPool())
371 : {
372 523 : return candidate;
373 : }
374 128 : for (auto & storage : pool)
375 : {
376 128 : if (!storage.InUse())
377 : {
378 128 : return &storage;
379 : }
380 : }
381 0 : return nullptr;
382 : }
383 :
384 5283 : bool InPool() const
385 : {
386 5283 : constexpr auto * end = pool + ArraySize(pool);
387 5283 : return pool <= this && this < end;
388 : }
389 :
390 : EntryStorage() = default;
391 :
392 1251 : void Init()
393 : {
394 1251 : if (!mInUse)
395 : {
396 650 : Clear();
397 650 : mInUse = true;
398 : }
399 1251 : }
400 :
401 29808 : bool InUse() const { return mInUse; }
402 :
403 1251 : void Release()
404 : {
405 1251 : if (InPool())
406 : {
407 651 : mInUse = false;
408 : }
409 1251 : }
410 :
411 1778 : void Clear()
412 : {
413 1778 : mInUse = false;
414 1778 : mFabricIndex = chip::kUndefinedFabricIndex;
415 1778 : mAuthMode = AuthMode::kPase;
416 1778 : mPrivilege = Privilege::kView;
417 8890 : for (auto & subject : mSubjects)
418 : {
419 7112 : subject.Clear();
420 : }
421 7112 : for (auto & target : mTargets)
422 : {
423 5334 : target.Clear();
424 : }
425 1778 : }
426 :
427 : enum class ConvertDirection
428 : {
429 : kAbsoluteToRelative,
430 : kRelativeToAbsolute
431 : };
432 :
433 : // Entries have a position in the access control list, denoted by an "absolute" index.
434 : // Because entries are scoped to a fabric, a "fabric relative" index can be inferred.
435 : //
436 : // For example: suppose there are 8 entries for fabrics A, B, and C (as fabric indexes 1, 2, 3).
437 : //
438 : // 0 1 2 3 4 5 6 7 ABSOLUTE INDEX
439 : // +---+---+---+---+---+---+---+---+
440 : // | A0| A1| B0| A2| B1| B2| C0| C1| FABRIC RELATIVE INDEX
441 : // +---+---+---+---+---+---+---+---+
442 : //
443 : // While the entry at (absolute) index 2 is the third entry, it is the first entry scoped to
444 : // fabric B. So relative to fabric index 2, the entry is at (relative) index 0.
445 : //
446 : // The opposite is true: the second entry scoped to fabric B, at (relative) index 1, is the
447 : // fifth entry overall, at (absolute) index 4.
448 : //
449 : // Not all conversions are possible. For example, absolute index 3 is not scoped to fabric B, so
450 : // attempting to convert it to be relative to fabric index 2 will fail. Likewise, fabric B does
451 : // not contain a fourth entry, so attempting to convert index 3 (relative to fabric index 2) to
452 : // an absolute index will also fail. Such failures are denoted by use of an index that is one
453 : // past the end of the access control list. (So in this example, failure produces index 8.)
454 54 : static void ConvertIndex(size_t & index, const FabricIndex fabricIndex, ConvertDirection direction)
455 : {
456 54 : size_t absoluteIndex = 0;
457 54 : size_t relativeIndex = 0;
458 54 : size_t & fromIndex = (direction == ConvertDirection::kAbsoluteToRelative) ? absoluteIndex : relativeIndex;
459 54 : size_t & toIndex = (direction == ConvertDirection::kAbsoluteToRelative) ? relativeIndex : absoluteIndex;
460 54 : bool found = false;
461 299 : for (const auto & storage : acl)
462 : {
463 299 : if (!storage.InUse())
464 : {
465 9 : break;
466 : }
467 290 : if (storage.mFabricIndex == fabricIndex)
468 : {
469 126 : if (index == fromIndex)
470 : {
471 45 : found = true;
472 45 : break;
473 : }
474 81 : relativeIndex++;
475 : }
476 245 : absoluteIndex++;
477 : }
478 54 : index = found ? toIndex : ArraySize(acl);
479 54 : }
480 :
481 : static constexpr size_t kMaxSubjects = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY;
482 : static constexpr size_t kMaxTargets = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY;
483 :
484 : bool mInUse;
485 : FabricIndex mFabricIndex;
486 : AuthMode mAuthMode;
487 : Privilege mPrivilege;
488 : SubjectStorage mSubjects[kMaxSubjects];
489 : TargetStorage mTargets[kMaxTargets];
490 : };
491 :
492 : class EntryDelegate : public Entry::Delegate
493 : {
494 : public:
495 : // Pool support
496 : static EntryDelegate pool[kEntryDelegatePoolSize];
497 :
498 : // Find an unused entry delegate in the pool, if one is available.
499 : // The candidate is preferred if it is in the pool, regardless of whether
500 : // it is already in use.
501 1251 : static EntryDelegate * Find(Entry::Delegate & candidate)
502 : {
503 1251 : if (InPool(candidate))
504 : {
505 999 : return &static_cast<EntryDelegate &>(candidate);
506 : }
507 252 : for (auto & delegate : pool)
508 : {
509 252 : if (!delegate.InUse())
510 : {
511 252 : return &delegate;
512 : }
513 : }
514 0 : return nullptr;
515 : }
516 :
517 2667 : static bool InPool(const Entry::Delegate & delegate)
518 : {
519 2667 : constexpr auto * end = pool + ArraySize(pool);
520 2667 : return pool <= &delegate && &delegate < end;
521 : }
522 :
523 1251 : void Release() override
524 : {
525 1251 : mStorage->Release();
526 1251 : mStorage = nullptr;
527 1251 : }
528 :
529 2047 : CHIP_ERROR GetAuthMode(AuthMode & authMode) const override
530 : {
531 2047 : authMode = mStorage->mAuthMode;
532 2047 : return CHIP_NO_ERROR;
533 : }
534 :
535 1856 : CHIP_ERROR GetFabricIndex(FabricIndex & fabricIndex) const override
536 : {
537 1856 : fabricIndex = mStorage->mFabricIndex;
538 1856 : return CHIP_NO_ERROR;
539 : }
540 :
541 1983 : CHIP_ERROR GetPrivilege(Privilege & privilege) const override
542 : {
543 1983 : privilege = mStorage->mPrivilege;
544 1983 : return CHIP_NO_ERROR;
545 : }
546 :
547 654 : CHIP_ERROR SetAuthMode(AuthMode authMode) override
548 : {
549 654 : ReturnErrorOnFailure(EnsureStorageInPool());
550 654 : mStorage->mAuthMode = authMode;
551 654 : return CHIP_NO_ERROR;
552 : }
553 :
554 653 : CHIP_ERROR SetFabricIndex(FabricIndex fabricIndex) override
555 : {
556 653 : ReturnErrorOnFailure(EnsureStorageInPool());
557 653 : mStorage->mFabricIndex = fabricIndex;
558 653 : return CHIP_NO_ERROR;
559 : }
560 :
561 628 : CHIP_ERROR SetPrivilege(Privilege privilege) override
562 : {
563 628 : ReturnErrorOnFailure(EnsureStorageInPool());
564 628 : mStorage->mPrivilege = privilege;
565 628 : return CHIP_NO_ERROR;
566 : }
567 :
568 2636 : CHIP_ERROR GetSubjectCount(size_t & count) const override
569 : {
570 2636 : count = 0;
571 4819 : for (const auto & subject : mStorage->mSubjects)
572 : {
573 4819 : if (subject.IsEmpty())
574 : {
575 2636 : break;
576 : }
577 2183 : count++;
578 : }
579 2636 : return CHIP_NO_ERROR;
580 : }
581 :
582 1975 : CHIP_ERROR GetSubject(size_t index, NodeId & subject) const override
583 : {
584 1975 : if (index < EntryStorage::kMaxSubjects)
585 : {
586 1975 : return mStorage->mSubjects[index].Get(subject);
587 : }
588 0 : return CHIP_ERROR_SENTINEL;
589 : }
590 :
591 32 : CHIP_ERROR SetSubject(size_t index, NodeId subject) override
592 : {
593 32 : ReturnErrorOnFailure(EnsureStorageInPool());
594 32 : if (index < EntryStorage::kMaxSubjects)
595 : {
596 32 : return mStorage->mSubjects[index].Set(subject);
597 : }
598 0 : return CHIP_ERROR_SENTINEL;
599 : }
600 :
601 652 : CHIP_ERROR AddSubject(size_t * index, NodeId subject) override
602 : {
603 652 : ReturnErrorOnFailure(EnsureStorageInPool());
604 652 : size_t count = 0;
605 652 : GetSubjectCount(count);
606 652 : if (count < EntryStorage::kMaxSubjects)
607 : {
608 652 : CHIP_ERROR err = mStorage->mSubjects[count].Add(subject);
609 652 : if (err == CHIP_NO_ERROR && index != nullptr)
610 : {
611 0 : *index = count;
612 : }
613 652 : return err;
614 : }
615 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
616 : }
617 :
618 2 : CHIP_ERROR RemoveSubject(size_t index) override
619 : {
620 2 : ReturnErrorOnFailure(EnsureStorageInPool());
621 2 : size_t count = 0;
622 2 : GetSubjectCount(count);
623 2 : if (index < count)
624 : {
625 : // The storage at the specified index will be deleted by copying any subsequent storage
626 : // over it, ideally also including one unused storage past the ones in use. If all
627 : // storage was in use, this isn't possible, so the final storage is manually cleared.
628 2 : auto * dest = mStorage->mSubjects + index;
629 2 : const auto * src = dest + 1;
630 2 : const auto n = std::min(count, EntryStorage::kMaxSubjects - 1) - index;
631 2 : memmove(dest, src, n * sizeof(*dest));
632 2 : if (count == EntryStorage::kMaxSubjects)
633 : {
634 0 : mStorage->mSubjects[EntryStorage::kMaxSubjects - 1].Clear();
635 : }
636 2 : return CHIP_NO_ERROR;
637 : }
638 0 : return CHIP_ERROR_SENTINEL;
639 : }
640 :
641 2451 : CHIP_ERROR GetTargetCount(size_t & count) const override
642 : {
643 2451 : count = 0;
644 4403 : for (const auto & target : mStorage->mTargets)
645 : {
646 4269 : if (target.IsEmpty())
647 : {
648 2317 : break;
649 : }
650 1952 : count++;
651 : }
652 2451 : return CHIP_NO_ERROR;
653 : }
654 :
655 1671 : CHIP_ERROR GetTarget(size_t index, Target & target) const override
656 : {
657 1671 : if (index < EntryStorage::kMaxTargets)
658 : {
659 1671 : return mStorage->mTargets[index].Get(target);
660 : }
661 0 : return CHIP_ERROR_SENTINEL;
662 : }
663 :
664 356 : CHIP_ERROR SetTarget(size_t index, const Target & target) override
665 : {
666 356 : ReturnErrorOnFailure(EnsureStorageInPool());
667 356 : if (index < EntryStorage::kMaxTargets)
668 : {
669 356 : return mStorage->mTargets[index].Set(target);
670 : }
671 0 : return CHIP_ERROR_SENTINEL;
672 : }
673 :
674 530 : CHIP_ERROR AddTarget(size_t * index, const Target & target) override
675 : {
676 530 : ReturnErrorOnFailure(EnsureStorageInPool());
677 530 : size_t count = 0;
678 530 : GetTargetCount(count);
679 530 : if (count < EntryStorage::kMaxTargets)
680 : {
681 530 : CHIP_ERROR err = mStorage->mTargets[count].Add(target);
682 530 : if (err == CHIP_NO_ERROR && index != nullptr)
683 : {
684 0 : *index = count;
685 : }
686 530 : return err;
687 : }
688 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
689 : }
690 :
691 1 : CHIP_ERROR RemoveTarget(size_t index) override
692 : {
693 1 : ReturnErrorOnFailure(EnsureStorageInPool());
694 1 : size_t count = 0;
695 1 : GetTargetCount(count);
696 1 : if (index < count)
697 : {
698 : // The storage at the specified index will be deleted by copying any subsequent storage
699 : // over it, ideally also including one unused storage past the ones in use. If all
700 : // storage was in use, this isn't possible, so the final storage is manually cleared.
701 1 : auto * dest = mStorage->mTargets + index;
702 1 : const auto * src = dest + 1;
703 1 : const auto n = std::min(count, EntryStorage::kMaxTargets - 1) - index;
704 1 : memmove(dest, src, n * sizeof(*dest));
705 1 : if (count == EntryStorage::kMaxTargets)
706 : {
707 1 : mStorage->mTargets[EntryStorage::kMaxTargets - 1].Clear();
708 : }
709 1 : return CHIP_NO_ERROR;
710 : }
711 0 : return CHIP_ERROR_SENTINEL;
712 : }
713 :
714 1251 : void Init(Entry & entry, EntryStorage & storage)
715 : {
716 1251 : entry.SetDelegate(*this);
717 1251 : storage.Init();
718 1251 : mEntry = &entry;
719 1251 : mStorage = &storage;
720 1251 : }
721 :
722 252 : bool InUse() const { return mStorage != nullptr; }
723 :
724 1416 : const EntryStorage * GetStorage() const { return mStorage; }
725 :
726 650 : EntryStorage * GetStorage() { return mStorage; }
727 :
728 : // A storage is about to be deleted. If this delegate was
729 : // using it, make a best effort to copy it to the pool.
730 1000 : void FixBeforeDelete(EntryStorage & storage)
731 : {
732 1000 : if (mStorage == &storage)
733 : {
734 : // Best effort, OK if it fails.
735 0 : EnsureStorageInPool();
736 : }
737 1000 : }
738 :
739 : // A storage was deleted, and others shuffled into its place.
740 : // Fix this delegate (if necessary) to ensure it's using the
741 : // correct storage.
742 1000 : void FixAfterDelete(EntryStorage & storage)
743 : {
744 1000 : constexpr auto & acl = EntryStorage::acl;
745 1398 : constexpr auto * end = acl + ArraySize(acl);
746 1000 : if (mStorage == &storage)
747 : {
748 0 : mEntry->ResetDelegate();
749 : }
750 1000 : else if (&storage < mStorage && mStorage < end)
751 : {
752 0 : mStorage--;
753 : }
754 1000 : }
755 :
756 : // Ensure the delegate is using storage from the pool (not the access control list),
757 : // by copying (from the access control list to the pool) if necessary.
758 3508 : CHIP_ERROR EnsureStorageInPool()
759 : {
760 3508 : if (mStorage->InPool())
761 : {
762 3507 : return CHIP_NO_ERROR;
763 : }
764 1 : if (auto * storage = EntryStorage::Find(nullptr))
765 : {
766 1 : *storage = *mStorage;
767 1 : mStorage = storage;
768 1 : return CHIP_NO_ERROR;
769 : }
770 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
771 : }
772 :
773 : private:
774 : Entry * mEntry = nullptr;
775 : EntryStorage * mStorage = nullptr;
776 : };
777 :
778 : class EntryIteratorDelegate : public EntryIterator::Delegate
779 : {
780 : public:
781 : // Pool support
782 : static EntryIteratorDelegate pool[kEntryIteratorDelegatePoolSize];
783 :
784 : // Find an unused entry iterator delegate in the pool, if one is available.
785 : // The candidate is preferred if it is in the pool, regardless of whether
786 : // it is already in use.
787 56 : static EntryIteratorDelegate * Find(EntryIterator::Delegate & candidate)
788 : {
789 56 : if (InPool(candidate))
790 : {
791 3 : return static_cast<EntryIteratorDelegate *>(&candidate);
792 : }
793 53 : for (auto & delegate : pool)
794 : {
795 53 : if (!delegate.InUse())
796 : {
797 53 : return &delegate;
798 : }
799 : }
800 0 : return nullptr;
801 : }
802 :
803 56 : static bool InPool(const EntryIterator::Delegate & delegate)
804 : {
805 56 : constexpr auto * end = pool + ArraySize(pool);
806 56 : return pool <= &delegate && &delegate < end;
807 : }
808 :
809 56 : void Release() override { mInUse = false; }
810 :
811 243 : CHIP_ERROR Next(Entry & entry) override
812 : {
813 243 : constexpr auto & acl = EntryStorage::acl;
814 243 : constexpr auto * end = acl + ArraySize(acl);
815 : while (true)
816 : {
817 434 : if (mStorage == nullptr)
818 : {
819 : // Start at beginning of access control list...
820 56 : mStorage = acl;
821 : }
822 378 : else if (mStorage < end)
823 : {
824 : // ...and continue iterating entries...
825 378 : mStorage++;
826 : }
827 434 : if (mStorage == end || !mStorage->InUse())
828 : {
829 : // ...but only used ones...
830 34 : mStorage = end;
831 34 : break;
832 : }
833 400 : if (mFabricFiltered && mStorage->mFabricIndex != mFabricIndex)
834 : {
835 : // ...skipping those that aren't scoped to a specified fabric...
836 191 : continue;
837 : }
838 209 : if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
839 : {
840 : // ...returning any next entry via a delegate.
841 209 : delegate->Init(entry, *mStorage);
842 209 : return CHIP_NO_ERROR;
843 : }
844 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
845 191 : }
846 34 : return CHIP_ERROR_SENTINEL;
847 : }
848 :
849 56 : void Init(EntryIterator & iterator, const FabricIndex * fabricIndex)
850 : {
851 56 : iterator.SetDelegate(*this);
852 56 : mInUse = true;
853 56 : mFabricFiltered = fabricIndex != nullptr;
854 56 : if (mFabricFiltered)
855 : {
856 55 : mFabricIndex = *fabricIndex;
857 : }
858 56 : mStorage = nullptr;
859 56 : }
860 :
861 53 : bool InUse() const { return mInUse; }
862 :
863 : // A storage was deleted, and others shuffled into its place.
864 : // Fix this delegate (if necessary) to ensure it's using the
865 : // correct storage.
866 1000 : void FixAfterDelete(EntryStorage & storage)
867 : {
868 1000 : constexpr auto & acl = EntryStorage::acl;
869 1153 : constexpr auto * end = acl + ArraySize(acl);
870 1000 : if (&storage <= mStorage && mStorage < end)
871 : {
872 0 : if (mStorage == acl)
873 : {
874 0 : mStorage = nullptr;
875 : }
876 : else
877 : {
878 0 : mStorage--;
879 : }
880 : }
881 1000 : }
882 :
883 : private:
884 : bool mInUse = false;
885 : bool mFabricFiltered;
886 : FabricIndex mFabricIndex;
887 : EntryStorage * mStorage;
888 : };
889 :
890 0 : CHIP_ERROR CopyViaInterface(const Entry & entry, EntryStorage & storage)
891 : {
892 : #if CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_FLEXIBLE_COPY_SUPPORT
893 : // NOTE: uses sizeof(EntryStorage) on stack as a temporary and is only necessary if using this
894 : // file with other Entry::Delegate implementations that are not EntryDelegate in this file
895 : EntryStorage temp;
896 : temp.Clear();
897 :
898 : FabricIndex fabricIndex = kUndefinedFabricIndex;
899 : ReturnErrorOnFailure(entry.GetFabricIndex(fabricIndex));
900 : temp.mFabricIndex = fabricIndex;
901 :
902 : AuthMode authMode = AuthMode::kNone;
903 : ReturnErrorOnFailure(entry.GetAuthMode(authMode));
904 : temp.mAuthMode = authMode;
905 :
906 : Privilege privilege = Privilege::kView;
907 : ReturnErrorOnFailure(entry.GetPrivilege(privilege));
908 : temp.mPrivilege = privilege;
909 :
910 : size_t subjectCount = 0;
911 : ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
912 : ReturnErrorCodeIf(subjectCount > EntryStorage::kMaxSubjects, CHIP_ERROR_BUFFER_TOO_SMALL);
913 : for (size_t i = 0; i < subjectCount; ++i)
914 : {
915 : NodeId subject = kUndefinedNodeId;
916 : ReturnErrorOnFailure(entry.GetSubject(i, subject));
917 : temp.mSubjects[i].Add(subject);
918 : }
919 :
920 : size_t targetCount = 0;
921 : ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
922 : ReturnErrorCodeIf(targetCount > EntryStorage::kMaxTargets, CHIP_ERROR_BUFFER_TOO_SMALL);
923 : for (size_t i = 0; i < targetCount; ++i)
924 : {
925 : Target target;
926 : ReturnErrorOnFailure(entry.GetTarget(i, target));
927 : temp.mTargets[i].Add(target);
928 : }
929 :
930 : temp.mInUse = true;
931 : storage = temp;
932 : return CHIP_NO_ERROR;
933 : #else
934 : // NOTE: save space by not implementing function
935 0 : VerifyOrDie(false);
936 : return CHIP_ERROR_NOT_IMPLEMENTED;
937 : #endif
938 : }
939 :
940 1416 : CHIP_ERROR Copy(const Entry & entry, EntryStorage & storage)
941 : {
942 : #if CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_FAST_COPY_SUPPORT
943 1416 : auto & delegate = entry.GetDelegate();
944 1416 : if (EntryDelegate::InPool(delegate))
945 : {
946 : // NOTE: if an entry's delegate is in the pool, it must be an EntryDelegate,
947 : // which must have a storage that is in use, which can be copied as POD
948 1416 : storage = *static_cast<const EntryDelegate &>(delegate).GetStorage();
949 1416 : return CHIP_NO_ERROR;
950 : }
951 : #endif
952 0 : return CopyViaInterface(entry, storage);
953 : }
954 :
955 : class AccessControlDelegate : public AccessControl::Delegate
956 : {
957 : public:
958 2 : CHIP_ERROR Init() override
959 : {
960 2 : ChipLogProgress(DataManagement, "Examples::AccessControlDelegate::Init");
961 130 : for (auto & storage : EntryStorage::acl)
962 : {
963 128 : storage.Clear();
964 : }
965 2 : return CHIP_NO_ERROR;
966 : }
967 :
968 2 : void Finish() override { ChipLogProgress(DataManagement, "Examples::AccessControlDelegate::Finish"); }
969 :
970 0 : CHIP_ERROR GetMaxEntriesPerFabric(size_t & value) const override
971 : {
972 0 : value = EntryStorage::kEntriesPerFabric;
973 0 : return CHIP_NO_ERROR;
974 : }
975 :
976 0 : CHIP_ERROR GetMaxSubjectsPerEntry(size_t & value) const override
977 : {
978 0 : value = EntryStorage::kMaxSubjects;
979 0 : return CHIP_NO_ERROR;
980 : }
981 :
982 0 : CHIP_ERROR GetMaxTargetsPerEntry(size_t & value) const override
983 : {
984 0 : value = EntryStorage::kMaxTargets;
985 0 : return CHIP_NO_ERROR;
986 : }
987 :
988 0 : CHIP_ERROR GetMaxEntryCount(size_t & value) const override
989 : {
990 0 : value = ArraySize(EntryStorage::acl);
991 0 : return CHIP_NO_ERROR;
992 : }
993 :
994 3 : CHIP_ERROR GetEntryCount(FabricIndex fabric, size_t & value) const override
995 : {
996 3 : value = 0;
997 22 : for (const auto & storage : EntryStorage::acl)
998 : {
999 22 : if (!storage.InUse())
1000 : {
1001 3 : break;
1002 : }
1003 19 : if (storage.mFabricIndex == fabric)
1004 : {
1005 14 : value++;
1006 : }
1007 : }
1008 3 : return CHIP_NO_ERROR;
1009 : }
1010 :
1011 2 : CHIP_ERROR GetEntryCount(size_t & value) const override
1012 : {
1013 2 : value = 0;
1014 7 : for (const auto & storage : EntryStorage::acl)
1015 : {
1016 7 : if (!storage.InUse())
1017 : {
1018 2 : break;
1019 : }
1020 5 : value++;
1021 : }
1022 2 : return CHIP_NO_ERROR;
1023 : }
1024 :
1025 650 : CHIP_ERROR PrepareEntry(Entry & entry) override
1026 : {
1027 650 : if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
1028 : {
1029 650 : if (auto * storage = EntryStorage::Find(delegate->GetStorage()))
1030 : {
1031 650 : delegate->Init(entry, *storage);
1032 650 : return CHIP_NO_ERROR;
1033 : }
1034 : }
1035 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
1036 : }
1037 :
1038 1009 : CHIP_ERROR CreateEntry(size_t * index, const Entry & entry, FabricIndex * fabricIndex) override
1039 : {
1040 1009 : if (auto * storage = EntryStorage::FindUnusedInAcl())
1041 : {
1042 1009 : CHIP_ERROR err = Copy(entry, *storage);
1043 1009 : if (err == CHIP_NO_ERROR)
1044 : {
1045 1009 : if (fabricIndex != nullptr)
1046 : {
1047 27 : *fabricIndex = storage->mFabricIndex;
1048 : }
1049 1009 : if (index != nullptr)
1050 : {
1051 31 : *index = size_t(storage - EntryStorage::acl);
1052 31 : if (fabricIndex != nullptr)
1053 : {
1054 27 : EntryStorage::ConvertIndex(*index, *fabricIndex, EntryStorage::ConvertDirection::kAbsoluteToRelative);
1055 : }
1056 : }
1057 : }
1058 1009 : return err;
1059 : }
1060 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
1061 : }
1062 :
1063 465 : CHIP_ERROR ReadEntry(size_t index, Entry & entry, const FabricIndex * fabricIndex) const override
1064 : {
1065 465 : if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
1066 : {
1067 392 : if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
1068 : {
1069 392 : delegate->Init(entry, *storage);
1070 392 : return CHIP_NO_ERROR;
1071 : }
1072 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
1073 : }
1074 73 : return CHIP_ERROR_SENTINEL;
1075 : }
1076 :
1077 407 : CHIP_ERROR UpdateEntry(size_t index, const Entry & entry, const FabricIndex * fabricIndex) override
1078 : {
1079 407 : if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
1080 : {
1081 407 : return Copy(entry, *storage);
1082 : }
1083 0 : return CHIP_ERROR_SENTINEL;
1084 : }
1085 :
1086 21561 : CHIP_ERROR DeleteEntry(size_t index, const FabricIndex * fabricIndex) override
1087 : {
1088 21561 : if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
1089 : {
1090 : // Best effort attempt to preserve any outstanding delegates...
1091 2000 : for (auto & delegate : EntryDelegate::pool)
1092 : {
1093 1000 : delegate.FixBeforeDelete(*storage);
1094 : }
1095 :
1096 : // ...then go through the access control list starting at the deleted storage...
1097 1000 : constexpr auto & acl = EntryStorage::acl;
1098 6698 : constexpr auto * end = acl + ArraySize(acl);
1099 2849 : for (auto * next = storage + 1; storage < end; ++storage, ++next)
1100 : {
1101 : // ...copying over each storage with its next one...
1102 2849 : if (next < end && next->InUse())
1103 : {
1104 1849 : *storage = *next;
1105 : }
1106 : else
1107 : {
1108 : // ...clearing the last previously used one...
1109 1000 : storage->Clear();
1110 1000 : break;
1111 : }
1112 : }
1113 :
1114 : // ...then fix up all the delegates so they still use the proper storage.
1115 1000 : storage = acl + index;
1116 2000 : for (auto & delegate : EntryDelegate::pool)
1117 : {
1118 1000 : delegate.FixAfterDelete(*storage);
1119 : }
1120 2000 : for (auto & delegate : EntryIteratorDelegate::pool)
1121 : {
1122 1000 : delegate.FixAfterDelete(*storage);
1123 : }
1124 :
1125 1000 : return CHIP_NO_ERROR;
1126 : }
1127 :
1128 20561 : return CHIP_ERROR_SENTINEL;
1129 : }
1130 :
1131 56 : CHIP_ERROR Entries(EntryIterator & iterator, const FabricIndex * fabricIndex) const override
1132 : {
1133 56 : if (auto * delegate = EntryIteratorDelegate::Find(iterator.GetDelegate()))
1134 : {
1135 56 : delegate->Init(iterator, fabricIndex);
1136 56 : return CHIP_NO_ERROR;
1137 : }
1138 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
1139 : }
1140 :
1141 57 : CHIP_ERROR Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
1142 : Privilege requestPrivilege) override
1143 : {
1144 57 : return CHIP_ERROR_NOT_IMPLEMENTED;
1145 : }
1146 : };
1147 :
1148 : static_assert(std::is_pod<SubjectStorage>(), "Storage type must be POD");
1149 : static_assert(std::is_pod<TargetStorage>(), "Storage type must be POD");
1150 : static_assert(std::is_pod<EntryStorage>(), "Storage type must be POD");
1151 :
1152 : EntryStorage EntryStorage::acl[];
1153 : EntryStorage EntryStorage::pool[];
1154 : EntryDelegate EntryDelegate::pool[];
1155 : EntryIteratorDelegate EntryIteratorDelegate::pool[];
1156 :
1157 : } // namespace
1158 :
1159 : namespace chip {
1160 : namespace Access {
1161 : namespace Examples {
1162 :
1163 2 : AccessControl::Delegate * GetAccessControlDelegate()
1164 : {
1165 2 : static AccessControlDelegate accessControlDelegate;
1166 2 : return &accessControlDelegate;
1167 : }
1168 :
1169 : } // namespace Examples
1170 : } // namespace Access
1171 : } // namespace chip
|