Skip to content

Commit 1133511

Browse files
mlepage-googlepull[bot]
authored andcommitted
Enforce length constraints in access control (#17817)
Add some APIs for length constraints. Use them in system module and in cluster to enforce length constraints.
1 parent 2b17bcd commit 1133511

File tree

7 files changed

+998
-92
lines changed

7 files changed

+998
-92
lines changed

src/access/AccessControl.cpp

+58
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,64 @@ CHIP_ERROR AccessControl::Finish()
193193
return retval;
194194
}
195195

196+
CHIP_ERROR AccessControl::CreateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t * index,
197+
const Entry & entry)
198+
{
199+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
200+
201+
size_t count = 0;
202+
size_t maxCount = 0;
203+
ReturnErrorOnFailure(mDelegate->GetEntryCount(fabric, count));
204+
ReturnErrorOnFailure(mDelegate->GetMaxEntriesPerFabric(maxCount));
205+
206+
VerifyOrReturnError((count + 1) <= maxCount, CHIP_ERROR_BUFFER_TOO_SMALL);
207+
208+
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
209+
210+
size_t i = 0;
211+
ReturnErrorOnFailure(mDelegate->CreateEntry(&i, entry, &fabric));
212+
213+
if (index)
214+
{
215+
*index = i;
216+
}
217+
218+
NotifyEntryChanged(subjectDescriptor, fabric, i, &entry, EntryListener::ChangeType::kAdded);
219+
return CHIP_NO_ERROR;
220+
}
221+
222+
CHIP_ERROR AccessControl::UpdateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index,
223+
const Entry & entry)
224+
{
225+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
226+
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
227+
ReturnErrorOnFailure(mDelegate->UpdateEntry(index, entry, &fabric));
228+
NotifyEntryChanged(subjectDescriptor, fabric, index, &entry, EntryListener::ChangeType::kUpdated);
229+
return CHIP_NO_ERROR;
230+
}
231+
232+
CHIP_ERROR AccessControl::DeleteEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index)
233+
{
234+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
235+
Entry entry;
236+
Entry * p = nullptr;
237+
if (mEntryListener != nullptr && ReadEntry(fabric, index, entry) == CHIP_NO_ERROR)
238+
{
239+
p = &entry;
240+
}
241+
ReturnErrorOnFailure(mDelegate->DeleteEntry(index, &fabric));
242+
if (p && p->HasDefaultDelegate())
243+
{
244+
// The entry was read prior to deletion so its latest value could be provided
245+
// to the listener after deletion. If it's been reset to its default delegate,
246+
// that best effort attempt to retain the latest value failed. This is
247+
// regrettable but OK.
248+
p = nullptr;
249+
}
250+
NotifyEntryChanged(subjectDescriptor, fabric, index, p, EntryListener::ChangeType::kRemoved);
251+
return CHIP_NO_ERROR;
252+
}
253+
196254
void AccessControl::AddEntryListener(EntryListener & listener)
197255
{
198256
if (mEntryListener == nullptr)

src/access/AccessControl.h

+39-44
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,29 @@ class AccessControl
345345
virtual CHIP_ERROR Finish() { return CHIP_NO_ERROR; }
346346

347347
// Capabilities
348-
virtual CHIP_ERROR GetMaxEntryCount(size_t & value) const
348+
virtual CHIP_ERROR GetMaxEntriesPerFabric(size_t & value) const
349+
{
350+
value = 0;
351+
return CHIP_NO_ERROR;
352+
}
353+
354+
virtual CHIP_ERROR GetMaxSubjectsPerEntry(size_t & value) const
349355
{
350356
value = 0;
351357
return CHIP_NO_ERROR;
352358
}
353359

354-
// TODO: add more capabilities
360+
virtual CHIP_ERROR GetMaxTargetsPerEntry(size_t & value) const
361+
{
362+
value = 0;
363+
return CHIP_NO_ERROR;
364+
}
365+
366+
virtual CHIP_ERROR GetMaxEntryCount(size_t & value) const
367+
{
368+
value = 0;
369+
return CHIP_NO_ERROR;
370+
}
355371

356372
// Actualities
357373
virtual CHIP_ERROR GetEntryCount(FabricIndex fabric, size_t & value) const
@@ -417,6 +433,24 @@ class AccessControl
417433
CHIP_ERROR Finish();
418434

419435
// Capabilities
436+
CHIP_ERROR GetMaxEntriesPerFabric(size_t & value) const
437+
{
438+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
439+
return mDelegate->GetMaxEntriesPerFabric(value);
440+
}
441+
442+
CHIP_ERROR GetMaxSubjectsPerEntry(size_t & value) const
443+
{
444+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
445+
return mDelegate->GetMaxSubjectsPerEntry(value);
446+
}
447+
448+
CHIP_ERROR GetMaxTargetsPerEntry(size_t & value) const
449+
{
450+
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
451+
return mDelegate->GetMaxTargetsPerEntry(value);
452+
}
453+
420454
CHIP_ERROR GetMaxEntryCount(size_t & value) const
421455
{
422456
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
@@ -457,19 +491,7 @@ class AccessControl
457491
* @param [out] index (If not nullptr) index of created entry (relative to fabric).
458492
* @param [in] entry Entry from which created entry is copied.
459493
*/
460-
CHIP_ERROR CreateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t * index, const Entry & entry)
461-
{
462-
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
463-
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
464-
size_t i;
465-
ReturnErrorOnFailure(mDelegate->CreateEntry(&i, entry, &fabric));
466-
if (index)
467-
{
468-
*index = i;
469-
}
470-
NotifyEntryChanged(subjectDescriptor, fabric, i, &entry, EntryListener::ChangeType::kAdded);
471-
return CHIP_NO_ERROR;
472-
}
494+
CHIP_ERROR CreateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t * index, const Entry & entry);
473495

474496
/**
475497
* Creates an entry in the access control list.
@@ -519,14 +541,7 @@ class AccessControl
519541
* @param [in] index Index of entry to update (relative to fabric).
520542
* @param [in] entry Entry from which updated entry is copied.
521543
*/
522-
CHIP_ERROR UpdateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index, const Entry & entry)
523-
{
524-
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
525-
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
526-
ReturnErrorOnFailure(mDelegate->UpdateEntry(index, entry, &fabric));
527-
NotifyEntryChanged(subjectDescriptor, fabric, index, &entry, EntryListener::ChangeType::kUpdated);
528-
return CHIP_NO_ERROR;
529-
}
544+
CHIP_ERROR UpdateEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index, const Entry & entry);
530545

531546
/**
532547
* Updates an entry in the access control list.
@@ -549,27 +564,7 @@ class AccessControl
549564
* @param [in] fabric Index of fabric in which to delete entry.
550565
* @param [in] index Index of entry to delete (relative to fabric).
551566
*/
552-
CHIP_ERROR DeleteEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index)
553-
{
554-
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
555-
Entry entry;
556-
Entry * p = nullptr;
557-
if (mEntryListener != nullptr && ReadEntry(fabric, index, entry) == CHIP_NO_ERROR)
558-
{
559-
p = &entry;
560-
}
561-
ReturnErrorOnFailure(mDelegate->DeleteEntry(index, &fabric));
562-
if (p && p->HasDefaultDelegate())
563-
{
564-
// The entry was read prior to deletion so its latest value could be provided
565-
// to the listener after deletion. If it's been reset to its default delegate,
566-
// that best effort attempt to retain the latest value failed. This is
567-
// regretable but OK.
568-
p = nullptr;
569-
}
570-
NotifyEntryChanged(subjectDescriptor, fabric, index, p, EntryListener::ChangeType::kRemoved);
571-
return CHIP_NO_ERROR;
572-
}
567+
CHIP_ERROR DeleteEntry(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index);
573568

574569
/**
575570
* Deletes an entry from the access control list.

src/access/examples/ExampleAccessControlDelegate.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,24 @@ class AccessControlDelegate : public AccessControl::Delegate
971971
return CHIP_NO_ERROR;
972972
}
973973

974+
CHIP_ERROR GetMaxEntriesPerFabric(size_t & value) const override
975+
{
976+
value = EntryStorage::kEntriesPerFabric;
977+
return CHIP_NO_ERROR;
978+
}
979+
980+
CHIP_ERROR GetMaxSubjectsPerEntry(size_t & value) const override
981+
{
982+
value = EntryStorage::kMaxSubjects;
983+
return CHIP_NO_ERROR;
984+
}
985+
986+
CHIP_ERROR GetMaxTargetsPerEntry(size_t & value) const override
987+
{
988+
value = EntryStorage::kMaxTargets;
989+
return CHIP_NO_ERROR;
990+
}
991+
974992
CHIP_ERROR GetMaxEntryCount(size_t & value) const override
975993
{
976994
value = ArraySize(EntryStorage::acl);

src/app/clusters/access-control-server/access-control-server.cpp

+18-19
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,20 @@ CHIP_ERROR AccessControlAttribute::Read(const ConcreteReadAttributePath & aPath,
104104
return ReadAcl(aEncoder);
105105
case AccessControlCluster::Attributes::Extension::Id:
106106
return ReadExtension(aEncoder);
107-
// TODO(#14455): use API to get actual capabilities
108107
case AccessControlCluster::Attributes::SubjectsPerAccessControlEntry::Id: {
109-
uint16_t value = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY;
110-
return aEncoder.Encode(value);
108+
size_t value = 0;
109+
ReturnErrorOnFailure(GetAccessControl().GetMaxSubjectsPerEntry(value));
110+
return aEncoder.Encode(static_cast<uint16_t>(value));
111111
}
112-
// TODO(#14455): use API to get actual capabilities
113112
case AccessControlCluster::Attributes::TargetsPerAccessControlEntry::Id: {
114-
uint16_t value = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY;
115-
return aEncoder.Encode(value);
113+
size_t value = 0;
114+
ReturnErrorOnFailure(GetAccessControl().GetMaxTargetsPerEntry(value));
115+
return aEncoder.Encode(static_cast<uint16_t>(value));
116116
}
117-
// TODO(#14455): use API to get actual capabilities
118117
case AccessControlCluster::Attributes::AccessControlEntriesPerFabric::Id: {
119-
uint16_t value = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC;
120-
return aEncoder.Encode(value);
118+
size_t value = 0;
119+
ReturnErrorOnFailure(GetAccessControl().GetMaxEntriesPerFabric(value));
120+
return aEncoder.Encode(static_cast<uint16_t>(value));
121121
}
122122
case AccessControlCluster::Attributes::ClusterRevision::Id:
123123
return aEncoder.Encode(kClusterRevision);
@@ -193,23 +193,20 @@ CHIP_ERROR AccessControlAttribute::WriteAcl(const ConcreteDataAttributePath & aP
193193
{
194194
FabricIndex accessingFabricIndex = aDecoder.AccessingFabricIndex();
195195

196+
size_t oldCount;
197+
ReturnErrorOnFailure(GetAccessControl().GetEntryCount(accessingFabricIndex, oldCount));
198+
size_t maxCount;
199+
ReturnErrorOnFailure(GetAccessControl().GetMaxEntriesPerFabric(maxCount));
200+
196201
if (!aPath.IsListItemOperation())
197202
{
198203
DataModel::DecodableList<AclStorage::DecodableEntry> list;
199204
ReturnErrorOnFailure(aDecoder.Decode(list));
200205

201-
size_t allCount;
202-
size_t oldCount;
203206
size_t newCount;
204-
size_t maxCount;
205-
206-
ReturnErrorOnFailure(GetAccessControl().GetEntryCount(allCount));
207-
ReturnErrorOnFailure(GetAccessControl().GetEntryCount(accessingFabricIndex, oldCount));
208207
ReturnErrorOnFailure(list.ComputeSize(&newCount));
209-
ReturnErrorOnFailure(GetAccessControl().GetMaxEntryCount(maxCount));
210-
VerifyOrReturnError(allCount >= oldCount, CHIP_ERROR_INTERNAL);
211-
VerifyOrReturnError(static_cast<size_t>(allCount - oldCount + newCount) <= maxCount,
212-
CHIP_IM_GLOBAL_STATUS(ConstraintError));
208+
209+
VerifyOrReturnError(newCount <= maxCount, CHIP_IM_GLOBAL_STATUS(ResourceExhausted));
213210

214211
auto iterator = list.begin();
215212
size_t i = 0;
@@ -237,6 +234,8 @@ CHIP_ERROR AccessControlAttribute::WriteAcl(const ConcreteDataAttributePath & aP
237234
}
238235
else if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
239236
{
237+
VerifyOrReturnError((oldCount + 1) <= maxCount, CHIP_IM_GLOBAL_STATUS(ResourceExhausted));
238+
240239
AclStorage::DecodableEntry decodableEntry;
241240
ReturnErrorOnFailure(aDecoder.Decode(decodableEntry));
242241

src/app/tests/suites/TestAccessControlCluster.yaml

+101
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,107 @@ tests:
423423
},
424424
]
425425

426+
- label: "Write too many entries"
427+
command: "writeAttribute"
428+
attribute: "ACL"
429+
arguments:
430+
value: [
431+
{
432+
FabricIndex: 0,
433+
Privilege: 5, # administer
434+
AuthMode: 2, # case
435+
Subjects: null,
436+
Targets:
437+
[
438+
{ Cluster: null, Endpoint: 0, DeviceType: null },
439+
{ Cluster: 1, Endpoint: null, DeviceType: null },
440+
{ Cluster: 2, Endpoint: 3, DeviceType: null },
441+
],
442+
},
443+
{
444+
FabricIndex: 0,
445+
Privilege: 1, # view
446+
AuthMode: 2, # case
447+
Subjects: [4, 5, 6, 7],
448+
Targets:
449+
[
450+
{ Cluster: null, Endpoint: 8, DeviceType: null },
451+
{ Cluster: 9, Endpoint: null, DeviceType: null },
452+
{ Cluster: 10, Endpoint: 11, DeviceType: null },
453+
],
454+
},
455+
{
456+
FabricIndex: 0,
457+
Privilege: 3, # operate
458+
AuthMode: 3, # group
459+
Subjects: [12, 13, 14, 15],
460+
Targets:
461+
[
462+
{ Cluster: null, Endpoint: 16, DeviceType: null },
463+
{ Cluster: 17, Endpoint: null, DeviceType: null },
464+
{ Cluster: 18, Endpoint: 19, DeviceType: null },
465+
],
466+
},
467+
{
468+
FabricIndex: 0,
469+
Privilege: 1, # view
470+
AuthMode: 2, # case
471+
Subjects: [20, 21, 22, 23],
472+
Targets:
473+
[
474+
{ Cluster: null, Endpoint: 24, DeviceType: null },
475+
{ Cluster: 25, Endpoint: null, DeviceType: null },
476+
{ Cluster: 26, Endpoint: 27, DeviceType: null },
477+
],
478+
},
479+
]
480+
response:
481+
error: RESOURCE_EXHAUSTED
482+
483+
- label: "Verify"
484+
command: "readAttribute"
485+
attribute: "ACL"
486+
response:
487+
value: [
488+
{
489+
FabricIndex: 1,
490+
Privilege: 5, # administer
491+
AuthMode: 2, # case
492+
Subjects: null,
493+
Targets:
494+
[
495+
{ Cluster: null, Endpoint: 0, DeviceType: null },
496+
{ Cluster: 1, Endpoint: null, DeviceType: null },
497+
{ Cluster: 2, Endpoint: 3, DeviceType: null },
498+
],
499+
},
500+
{
501+
FabricIndex: 1,
502+
Privilege: 1, # view
503+
AuthMode: 2, # case
504+
Subjects: [4, 5, 6, 7],
505+
Targets:
506+
[
507+
{ Cluster: null, Endpoint: 8, DeviceType: null },
508+
{ Cluster: 9, Endpoint: null, DeviceType: null },
509+
{ Cluster: 10, Endpoint: 11, DeviceType: null },
510+
],
511+
},
512+
{
513+
FabricIndex: 1,
514+
Privilege: 3, # operate
515+
AuthMode: 3, # group
516+
Subjects: [12, 13, 14, 15],
517+
Targets:
518+
[
519+
{ Cluster: null, Endpoint: 16, DeviceType: null },
520+
{ Cluster: 17, Endpoint: null, DeviceType: null },
521+
{ Cluster: 18, Endpoint: 19, DeviceType: null },
522+
],
523+
},
524+
]
525+
526+
# note missing last entry
426527
- label: "Restore ACL"
427528
command: "writeAttribute"
428529
attribute: "ACL"

0 commit comments

Comments
 (0)