All Interaction Model operations in Matter must be verified by the the Access Control mechanism.
Whenever a client device and a server device want to interact with one another by reading (or subscribing) attributes or events, writing attributes, or invoking commands, the Access Control mechanism must verify that the client has sufficient privileges to perform the operation on the server device.
If no sufficient privilege is obtained, the operation cannot take place and it is denied (status 0x7E Access Denied
).
This guide describes how the Access Control mechanism works and how it is implemented, and provides examples of Access Control Lists (ACLs) for different use cases.
The initial Administer
privilege is obtained by the commissioner implicitly over the PASE commissioning channel. This implicit Administer
privilege is used to invoke commands on the server node during commissioning.
As part of commissioning, the commissioner invokes the AddNOC
command, providing a CaseAdminNode
argument. As well as commissioning the server node onto the fabric, that command automatically installs an ACL on the server node for Administer
privilege using the provided CaseAdminNode
argument as CASE subject. The subject may be a single node, or multiple nodes (by providing a CAT). These nodes become fabric administrators on that server node.
During and after commissioning, administrators manage ACLs for the fabric on the server node by reading and writing the fabric-scoped ACL
attribute of the Access Control Cluster, which is always present on endpoint 0 on the server node. It is these ACLs that govern which Interaction Model operations are allowed or denied on that server node, for subjects on the fabric, via CASE and group messaging.
Access Control Lists (ACLs) are fabric-scoped data structures with the following fields:
Privilege
AuthMode
Subjects
Targets
The Privilege
can be of the following types:
Note: An additional
ProxyView
privilege is not yet supported in the Matter SDK.
By default, the View
privilege is required to read attributes or events, and the Operate
privilege is required to write attributes or invoke commands.
Clusters may also require stricter privileges for certain operations on certain endpoints. For example, the Access Control Cluster requires the Administer
privilege for all its operations.
If applicable, the ACL grants the privilege, and all less strict privileges subsumed by it. Therefore an ACL for Manage
privilege will work for operations which require Operate
or View
privilege (but not Administer
privilege).
The AuthMode
, that is authentication modes, can be as follow:
The ACL applies only to subjects using that authentication mode.
The Subjects
field is a list containing zero, one, or more subject identifiers, which are:
AuthMode
AuthMode
A CASE subject may be a CAT, which has its own tag and version mechanism.
The ACL applies only to the listed subjects; if no subjects are listed, the ACL applies to any subjects using the authentication mode.
The Targets
field is a list containing zero, one, or more structured entries with fields:
All fields are nullable, but at least one must be present, and the endpoint and device type fields are mutually exclusive (only one of those two may be present):
Note: Specifying device type in targets is not yet supported in the Matter SDK.
The ACL applies only to the listed targets; if no targets are listed, the ACL applies to any targets on the server node.
The Matter Specification states that a Matter implementation must support at least:
There is no guarantee that an implementation supports any more than those minimums.
In the Matter SDK, using the example access control implementation, these values can be configured globally in CHIPConfig.h
or per-app in CHIPProjectAppConfig.h
by setting:
This section provides use case examples for different ACL scenarios.
A single controller commissions a server node, providing its own CASE node ID for the automatically installed ACL:
Administer
CASE
112233
The controller will be able to perform all Interaction Model operations on the server node, since it is the administrator.
The commissioner provides a CAT for the automatically installed ACL:
Administer
CASE
0xFFFFFFFD00010001
All controllers which have the CAT as part of their CASE credentials will be administrators for the server node.
The commissioner installs an ACL for non-administrative controllers:
View
CASE
4444
, 5555
, 6666
The non-administrative controllers are granted View
privilege for the entire server node. This doesn't mean they can view the entire server node, as some clusters may require stricter privileges for reading attributes or events.
The commissioner installs an ACL for group messaging:
Operate
Group
123
, 456
{cluster:onoff}
, {endpoint:1}
, {cluster:levelcontrol,endpoint:2}
Members of groups 123 and 456 are granted Operate
privilege for the on/off cluster on any endpoint, any cluster on endpoint 1, and the level control cluster on endpoint 2.
The following sections describe the requirements for managing ACLs using the CHIP-Tool.
The Access Control Cluster's ACL
attribute is a list.
Note: Currently, list operations for single entries (append, update, delete) are not yet supported in the Matter SDK, so the entire list must be written to the attribute to change any ACL.
The write operation may employ multiple messages, making it unreliable. In any case, ACLs are updated as they are processed, and take effect immediately.
The implication of this is that the administrator must ensure the first ACL in the list it is writing to the ACL
attribute is one granting itself Administer
privilege. Otherwise, the administrator may lose its administrative access during the write operation.
The tool requires all fields to be provided, even if null.
Incorrect: {"cluster": 6}
Correct: {"cluster": 6, "endpoint": null, "deviceType": null}
The ACL
attribute is fabric-scoped, so each ACL has a fabric index.
The tool requires this field to be provided, but ignores it when performing the actual write.
When reading ACLs, the proper fabric index is shown.
The tool requires numerical values for enums and identifiers.
Privilege values:
AuthMode values:
Values for some typical clusters:
This section provides examples of commands and ACL output for different operations with the CHIP-Tool.
During commissioning with the CHIP-Tool, an ACL that assigns Administer rights to the commissioner is automatically installed on the commissionee. This can be verified using the following command:
out/debug/standalone/chip-tool accesscontrol read acl 1 0
Assuming the CaseAdminNode
value is 112233
, the ACL command output for this case is the following:
Endpoint: 0 Cluster: 0x0000_001F Attribute 0x0000_0000 DataVersion: 2578401031 ACL: 1 entries [1]: { FabricIndex: 1 Privilege: 5 AuthMode: 2 Subjects: 1 entries [1]: 112233 Targets: null }
The following command example requests the installation of a CASE ACL through a write interaction:
out/debug/standalone/chip-tool accesscontrol write acl '[{"fabricIndex": 0, "privilege": 5, "authMode": 2, "subjects": [112233], "targets": null}, {"fabricIndex": 0, "privilege": 1, "authMode": 2, "subjects": [4444, 5555, 6666], "targets": null}]' 1 0
The resulting ACL command output for this case can look like the following one:
Endpoint: 0 Cluster: 0x0000_001F Attribute 0x0000_0000 DataVersion: 2578401034 ACL: 2 entries [1]: { FabricIndex: 1 Privilege: 5 AuthMode: 2 Subjects: 1 entries [1]: 112233 Targets: null } [2]: { FabricIndex: 1 Privilege: 1 AuthMode: 2 Subjects: 3 entries [1]: 4444 [2]: 5555 [3]: 6666 Targets: null }
The following command example requests the installation of a Group ACL through a write interaction:
out/debug/standalone/chip-tool accesscontrol write acl '[{"fabricIndex": 0, "privilege": 5, "authMode": 2, "subjects": [112233], "targets": null}, {"fabricIndex": 0, "privilege": 1, "authMode": 2, "subjects": [4444, 5555, 6666], "targets": null}, {"fabricIndex": 0, "privilege": 3, "authMode": 3, "subjects": [123, 456], "targets": [{"cluster": 6, "endpoint": null, "deviceType": null}, {"cluster": null, "endpoint": 1, "deviceType": null}, {"cluster": 8, "endpoint": 2, "deviceType": null}]}]' 1 0
The resulting ACL command output for this case can look like the following one:
Endpoint: 0 Cluster: 0x0000_001F Attribute 0x0000_0000DataVersion: 2578401041 ACL: 3 entries [1]: { FabricIndex: 1 Privilege: 5 AuthMode: 2 Subjects: 1 entries [1]: 112233 Targets: null } [2]: { FabricIndex: 1 Privilege: 1 AuthMode: 2 Subjects: 3 entries [1]: 4444 [2]: 5555 [3]: 6666 Targets: null } [3]: { FabricIndex: 1 Privilege: 3 AuthMode: 3 Subjects: 2 entries [1]: 123 [2]: 456 Targets: 3 entries [1]: { Cluster: 6 Endpoint: null DeviceType: null } [2]: { Cluster: null Endpoint: 1 DeviceType: null } [3]: { Cluster: 8 Endpoint: 2 DeviceType: null } }
This section provides examples of commands and ACL output for different operations with the CHIP-repl.
See the important notes in the Managing ACLs using CHIP-Tool section, as they also apply to the CHIP-repl.
Null fields may be omitted.
This means that the following entry is acceptable: Target(cluster=6, endpoint=Null, deviceType=Null)
. Just as the following one: Target(cluster=6)
.
The above assumes Target and Null are defined at global scope, which is not normally the case.
The ACL
attribute is fabric-scoped, so each ACL has a fabric index.
The CHIP-repl ignores it when performing the actual write. Because null fields can be omitted, simply do not provide it when writing ACLs.
When reading ACLs, the proper fabric index is shown.
The CHIP-repl accepts numerical values for enums and identifiers, but it also accepts strongly typed values:
The privileges are:
Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kView
Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kOperate
Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kManage
Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kAdminister
The authentication modes are:
Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCASE
Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kGroup
Some typical clusters:
This section provides examples of commands and ACL output for different operations with the CHIP-repl.
After commissioning with chip-repl, assuming CaseAdminNode
is 1, the automatically installed ACL is:
await devCtrl.ReadAttribute(1, [ (0, Clusters.AccessControl.Attributes.Acl ) ] )
{ │ 0: { │ │ <class 'chip.clusters.Objects.AccessControl'>: { │ │ │ <class 'chip.clusters.Attribute.DataVersion'>: 556798280, │ │ │ <class 'chip.clusters.Objects.AccessControl.Attributes.Acl'>: [ │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kAdminister: 5>, │ │ │ │ │ authMode=<AuthMode.kCase: 2>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 1 │ │ │ │ │ ], │ │ │ │ │ targets=Null │ │ │ │ ) │ │ │ ] │ │ } │ } }
await devCtrl.WriteAttribute(1, [ (0, Clusters.AccessControl.Attributes.Acl( [ Clusters.AccessControl.Structs.AccessControlEntryStruct( privilege = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kAdminister, authMode = Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCase, subjects = [ 1 ] ), Clusters.AccessControl.Structs.AccessControlEntryStruct( privilege = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kView, authMode = Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCase, subjects = [ 4444, 5555, 6666 ], ), ] ) ) ] )
{ │ 0: { │ │ <class 'chip.clusters.Objects.AccessControl'>: { │ │ │ <class 'chip.clusters.Attribute.DataVersion'>: 556798289, │ │ │ <class 'chip.clusters.Objects.AccessControl.Attributes.Acl'>: [ │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kAdminister: 5>, │ │ │ │ │ authMode=<AuthMode.kCase: 2>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 1 │ │ │ │ │ ], │ │ │ │ │ targets=Null │ │ │ │ ), │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kView: 1>, │ │ │ │ │ authMode=<AuthMode.kCase: 2>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 4444, │ │ │ │ │ │ 5555, │ │ │ │ │ │ 6666 │ │ │ │ │ ], │ │ │ │ │ targets=Null │ │ │ │ ) │ │ │ ] │ │ } │ } }
await devCtrl.WriteAttribute(1, [ (0, Clusters.AccessControl.Attributes.Acl( [ Clusters.AccessControl.Structs.AccessControlEntryStruct( privilege = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kAdminister, authMode = Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCase, subjects = [ 1 ] ), Clusters.AccessControl.Structs.AccessControlEntryStruct( privilege = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kView, authMode = Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCase, subjects = [ 4444, 5555, 6666 ], ), Clusters.AccessControl.Structs.AccessControlEntryStruct( privilege = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kOperate, authMode = Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kGroup, subjects = [ 123, 456 ], targets = [ Clusters.AccessControl.Structs.AccessControlTargetStruct( cluster = Clusters.OnOff.id, ), Clusters.AccessControl.Structs.AccessControlTargetStruct( endpoint = 1, ), Clusters.AccessControl.Structs.AccessControlTargetStruct( cluster = Clusters.LevelControl.id, endpoint = 2, ), ] ), ] ) ) ] )
{ │ 0: { │ │ <class 'chip.clusters.Objects.AccessControl'>: { │ │ │ <class 'chip.clusters.Attribute.DataVersion'>: 556798301, │ │ │ <class 'chip.clusters.Objects.AccessControl.Attributes.Acl'>: [ │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kAdminister: 5>, │ │ │ │ │ authMode=<AuthMode.kCase: 2>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 1 │ │ │ │ │ ], │ │ │ │ │ targets=Null │ │ │ │ ), │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kView: 1>, │ │ │ │ │ authMode=<AuthMode.kCase: 2>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 4444, │ │ │ │ │ │ 5555, │ │ │ │ │ │ 6666 │ │ │ │ │ ], │ │ │ │ │ targets=Null │ │ │ │ ), │ │ │ │ AccessControlEntry( │ │ │ │ │ fabricIndex=1, │ │ │ │ │ privilege=<Privilege.kOperate: 3>, │ │ │ │ │ authMode=<AuthMode.kGroup: 3>, │ │ │ │ │ subjects=[ │ │ │ │ │ │ 123, │ │ │ │ │ │ 456 │ │ │ │ │ ], │ │ │ │ │ targets=[ │ │ │ │ │ │ Target( │ │ │ │ │ │ │ cluster=6, │ │ │ │ │ │ │ endpoint=Null, │ │ │ │ │ │ │ deviceType=Null │ │ │ │ │ │ ), │ │ │ │ │ │ Target( │ │ │ │ │ │ │ cluster=Null, │ │ │ │ │ │ │ endpoint=1, │ │ │ │ │ │ │ deviceType=Null │ │ │ │ │ │ ), │ │ │ │ │ │ Target( │ │ │ │ │ │ │ cluster=8, │ │ │ │ │ │ │ endpoint=2, │ │ │ │ │ │ │ deviceType=Null │ │ │ │ │ │ ) │ │ │ │ │ ] │ │ │ │ ) │ │ │ ] │ │ } │ } }