16
16
* limitations under the License.
17
17
*/
18
18
19
+ #include < access/AccessRestrictionProvider.h>
20
+ #include < access/Privilege.h>
19
21
#include < app/AppConfig.h>
20
22
#include < app/ConcreteEventPath.h>
23
+ #include < app/GlobalAttributes.h>
21
24
#include < app/InteractionModelEngine.h>
22
25
#include < app/RequiredPrivilege.h>
23
26
#include < app/data-model-provider/ActionReturnStatus.h>
27
+ #include < app/data-model-provider/MetadataTypes.h>
24
28
#include < app/data-model-provider/Provider.h>
25
29
#include < app/icd/server/ICDServerConfig.h>
26
30
#include < app/reporting/Engine.h>
27
31
#include < app/reporting/reporting.h>
28
32
#include < app/util/MatterCallbacks.h>
33
+ #include < lib/core/CHIPError.h>
29
34
#include < lib/core/DataModelTypes.h>
35
+ #include < lib/support/CodeUtils.h>
36
+ #include < optional>
30
37
#include < protocols/interaction_model/StatusCode.h>
31
38
32
39
#if CHIP_CONFIG_ENABLE_ICD_SERVER
@@ -52,9 +59,81 @@ Status EventPathValid(DataModel::Provider * model, const ConcreteEventPath & eve
52
59
return Status::Success;
53
60
}
54
61
55
- DataModel::ActionReturnStatus RetrieveClusterData (DataModel::Provider * dataModel,
56
- const Access::SubjectDescriptor & subjectDescriptor, bool isFabricFiltered,
57
- AttributeReportIBs::Builder & reportBuilder,
62
+ // / Returns the status of ACL validation.
63
+ // / If the return value has a status set, that means the ACL check failed,
64
+ // / the read must not be performed, and the returned status (which may
65
+ // / be success, when dealing with non-concrete paths) should be used
66
+ // / as the status for the read.
67
+ // /
68
+ // / If the returned value is std::nullopt, that means the ACL check passed and the
69
+ // / read should proceed.
70
+ std::optional<CHIP_ERROR> ValidateReadAttributeACL (DataModel::Provider * dataModel, const SubjectDescriptor & subjectDescriptor,
71
+ const ConcreteReadAttributePath & path)
72
+ {
73
+
74
+ RequestPath requestPath{ .cluster = path.mClusterId ,
75
+ .endpoint = path.mEndpointId ,
76
+ .requestType = RequestType::kAttributeReadRequest ,
77
+ .entityId = path.mAttributeId };
78
+
79
+ std::optional<DataModel::AttributeInfo> info = dataModel->GetAttributeInfo (path);
80
+
81
+ // If the attribute exists, we know whether it is readable (readPrivilege has value)
82
+ // and what the required access privilege is. However for attributes missing from the metatada
83
+ // (e.g. global attributes) or completely missing attributes we do not actually know of a required
84
+ // privilege and default to kView (this is correct for global attributes and a reasonable check
85
+ // for others)
86
+ Privilege requiredPrivilege = Privilege::kView ;
87
+ if (info.has_value () && info->readPrivilege .has_value ())
88
+ {
89
+ // attribute exists and is readable, set the correct read privilege
90
+ requiredPrivilege = *info->readPrivilege ;
91
+ }
92
+
93
+ CHIP_ERROR err = GetAccessControl ().Check (subjectDescriptor, requestPath, requiredPrivilege);
94
+ if (err == CHIP_NO_ERROR)
95
+ {
96
+ if (IsSupportedGlobalAttributeNotInMetadata (path.mAttributeId ))
97
+ {
98
+ // Global attributes passing a kView check is ok
99
+ return std::nullopt;
100
+ }
101
+
102
+ // We want to return "success" (i.e. nulopt) IF AND ONLY IF the attribute exists and is readable (has read privilege).
103
+ // Since the Access control check above may have passed with kView, we do another check here:
104
+ // - Attribute exists (info has value)
105
+ // - Attribute is readable (readProvilege has value) and not "write only"
106
+ // If the attribute exists and is not readable, we will return UnsupportedRead (spec 8.4.3.2: "Else if the path indicates
107
+ // attribute data that is not readable, an AttributeStatusIB SHALL be generated with the UNSUPPORTED_READ Status Code.")
108
+ //
109
+ // TODO:: https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/9024 requires interleaved ordering that
110
+ // is NOT implemented here. Spec requires:
111
+ // - check cluster access check (done here as kView at least)
112
+ // - unsupported endpoint/cluster/attribute check (NOT done here) when the attribute is missing.
113
+ // this SHOULD be done here when info does not have a value. This was not done as a first pass to
114
+ // minimize amount of delta in the initial PR.
115
+ // - "write-only" attributes should return UNSUPPORTED_READ (this is done here)
116
+ if (info.has_value () && !info->readPrivilege .has_value ())
117
+ {
118
+ return CHIP_IM_GLOBAL_STATUS (UnsupportedRead);
119
+ }
120
+
121
+ return std::nullopt;
122
+ }
123
+ VerifyOrReturnError ((err == CHIP_ERROR_ACCESS_DENIED) || (err == CHIP_ERROR_ACCESS_RESTRICTED_BY_ARL), err);
124
+
125
+ // Implementation of 8.4.3.2 of the spec for path expansion
126
+ if (path.mExpanded )
127
+ {
128
+ return CHIP_NO_ERROR;
129
+ }
130
+
131
+ // access denied and access restricted have specific codes for IM
132
+ return err == CHIP_ERROR_ACCESS_DENIED ? CHIP_IM_GLOBAL_STATUS (UnsupportedAccess) : CHIP_IM_GLOBAL_STATUS (AccessRestricted);
133
+ }
134
+
135
+ DataModel::ActionReturnStatus RetrieveClusterData (DataModel::Provider * dataModel, const SubjectDescriptor & subjectDescriptor,
136
+ bool isFabricFiltered, AttributeReportIBs::Builder & reportBuilder,
58
137
const ConcreteReadAttributePath & path, AttributeEncodeState * encoderState)
59
138
{
60
139
ChipLogDetail (DataManagement, " <RE:Run> Cluster %" PRIx32 " , Attribute %" PRIx32 " is dirty" , path.mClusterId ,
@@ -64,10 +143,7 @@ DataModel::ActionReturnStatus RetrieveClusterData(DataModel::Provider * dataMode
64
143
65
144
DataModel::ReadAttributeRequest readRequest;
66
145
67
- if (isFabricFiltered)
68
- {
69
- readRequest.readFlags .Set (DataModel::ReadFlags::kFabricFiltered );
70
- }
146
+ readRequest.readFlags .Set (DataModel::ReadFlags::kFabricFiltered , isFabricFiltered);
71
147
readRequest.subjectDescriptor = &subjectDescriptor;
72
148
readRequest.path = path;
73
149
@@ -84,9 +160,17 @@ DataModel::ActionReturnStatus RetrieveClusterData(DataModel::Provider * dataMode
84
160
TLV::TLVWriter checkpoint;
85
161
reportBuilder.Checkpoint (checkpoint);
86
162
163
+ DataModel::ActionReturnStatus status (CHIP_NO_ERROR);
87
164
AttributeValueEncoder attributeValueEncoder (reportBuilder, subjectDescriptor, path, version, isFabricFiltered, encoderState);
88
165
89
- DataModel::ActionReturnStatus status = dataModel->ReadAttribute (readRequest, attributeValueEncoder);
166
+ if (auto access_status = ValidateReadAttributeACL (dataModel, subjectDescriptor, path); access_status.has_value ())
167
+ {
168
+ status = *access_status;
169
+ }
170
+ else
171
+ {
172
+ status = dataModel->ReadAttribute (readRequest, attributeValueEncoder);
173
+ }
90
174
91
175
if (status.IsSuccess ())
92
176
{
@@ -435,13 +519,13 @@ CHIP_ERROR Engine::CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool &
435
519
aHasEncodedData = true ;
436
520
}
437
521
438
- Access:: RequestPath requestPath{ .cluster = current->mValue .mClusterId ,
439
- .endpoint = current->mValue .mEndpointId ,
440
- .requestType = RequestType::kEventReadRequest ,
441
- .entityId = current->mValue .mEventId };
442
- Access:: Privilege requestPrivilege = RequiredPrivilege::ForReadEvent (path);
522
+ RequestPath requestPath{ .cluster = current->mValue .mClusterId ,
523
+ .endpoint = current->mValue .mEndpointId ,
524
+ .requestType = RequestType::kEventReadRequest ,
525
+ .entityId = current->mValue .mEventId };
526
+ Privilege requestPrivilege = RequiredPrivilege::ForReadEvent (path);
443
527
444
- err = Access:: GetAccessControl ().Check (apReadHandler->GetSubjectDescriptor (), requestPath, requestPrivilege);
528
+ err = GetAccessControl ().Check (apReadHandler->GetSubjectDescriptor (), requestPath, requestPrivilege);
445
529
if ((err != CHIP_ERROR_ACCESS_DENIED) && (err != CHIP_ERROR_ACCESS_RESTRICTED_BY_ARL))
446
530
{
447
531
ReturnErrorOnFailure (err);
@@ -580,13 +664,13 @@ CHIP_ERROR Engine::BuildSingleReportDataEventReports(ReportDataMessage::Builder
580
664
CHIP_ERROR Engine::BuildAndSendSingleReportData (ReadHandler * apReadHandler)
581
665
{
582
666
CHIP_ERROR err = CHIP_NO_ERROR;
583
- chip:: System::PacketBufferTLVWriter reportDataWriter;
667
+ System::PacketBufferTLVWriter reportDataWriter;
584
668
ReportDataMessage::Builder reportDataBuilder;
585
- chip:: System::PacketBufferHandle bufHandle = nullptr ;
586
- uint16_t reservedSize = 0 ;
587
- bool hasMoreChunks = false ;
588
- bool needCloseReadHandler = false ;
589
- size_t reportBufferMaxSize = 0 ;
669
+ System::PacketBufferHandle bufHandle = nullptr ;
670
+ uint16_t reservedSize = 0 ;
671
+ bool hasMoreChunks = false ;
672
+ bool needCloseReadHandler = false ;
673
+ size_t reportBufferMaxSize = 0 ;
590
674
591
675
// Reserved size for the MoreChunks boolean flag, which takes up 1 byte for the control tag and 1 byte for the context tag.
592
676
const uint32_t kReservedSizeForMoreChunksFlag = 1 + 1 ;
@@ -623,7 +707,7 @@ CHIP_ERROR Engine::BuildAndSendSingleReportData(ReadHandler * apReadHandler)
623
707
// Always limit the size of the generated packet to fit within the max size returned by the ReadHandler regardless
624
708
// of the available buffer capacity.
625
709
// Also, we need to reserve some extra space for the MIC field.
626
- reportDataWriter.ReserveBuffer (static_cast <uint32_t >(reservedSize + chip:: Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
710
+ reportDataWriter.ReserveBuffer (static_cast <uint32_t >(reservedSize + Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
627
711
628
712
// Create a report data.
629
713
err = reportDataBuilder.Init (&reportDataWriter);
0 commit comments