Skip to content

Commit 315b7ed

Browse files
committed
ACL: Add support for NOC CAT
1 parent 11d5048 commit 315b7ed

File tree

2 files changed

+189
-19
lines changed

2 files changed

+189
-19
lines changed

matter/src/acl.rs

Lines changed: 173 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
* limitations under the License.
1616
*/
1717

18-
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
18+
use std::{
19+
fmt::Display,
20+
sync::{Arc, Mutex, MutexGuard, RwLock},
21+
};
1922

2023
use crate::{
2124
data_model::objects::{Access, Privilege},
@@ -24,6 +27,7 @@ use crate::{
2427
interaction_model::messages::GenericPath,
2528
sys::Psm,
2629
tlv::{FromTLV, TLVElement, TLVList, TLVWriter, TagType, ToTLV},
30+
transport::session::MAX_CAT_IDS_PER_NOC,
2731
utils::writebuf::WriteBuf,
2832
};
2933
use log::error;
@@ -67,23 +71,110 @@ impl ToTLV for AuthMode {
6771
}
6872
}
6973

74+
/// An accessor can have as many identities: one node id and Upto MAX_CAT_IDS_PER_NOC
75+
const MAX_ACCESSOR_SUBJECTS: usize = 1 + MAX_CAT_IDS_PER_NOC;
76+
/// The CAT Prefix used in Subjects
77+
pub const NOC_CAT_SUBJECT_PREFIX: u64 = 0xFFFF_FFFD_0000_0000;
78+
const NOC_CAT_ID_MASK: u64 = 0xFFFF_0000;
79+
const NOC_CAT_VERSION_MASK: u64 = 0xFFFF;
80+
81+
fn is_noc_cat(id: u64) -> bool {
82+
(id & NOC_CAT_SUBJECT_PREFIX) == NOC_CAT_SUBJECT_PREFIX
83+
}
84+
85+
fn get_noc_cat_id(id: u64) -> u64 {
86+
(id & NOC_CAT_ID_MASK) >> 16
87+
}
88+
89+
fn get_noc_cat_version(id: u64) -> u64 {
90+
id & NOC_CAT_VERSION_MASK
91+
}
92+
93+
pub fn gen_noc_cat(id: u16, version: u16) -> u64 {
94+
NOC_CAT_SUBJECT_PREFIX | ((id as u64) << 16) | version as u64
95+
}
96+
97+
pub struct AccessorSubjects([u64; MAX_ACCESSOR_SUBJECTS]);
98+
99+
impl AccessorSubjects {
100+
pub fn new(id: u64) -> Self {
101+
let mut a = Self(Default::default());
102+
a.0[0] = id;
103+
a
104+
}
105+
106+
pub fn add(&mut self, subject: u64) -> Result<(), Error> {
107+
for (i, val) in self.0.iter().enumerate() {
108+
if *val == 0 {
109+
self.0[i] = subject;
110+
return Ok(());
111+
}
112+
}
113+
Err(Error::NoSpace)
114+
}
115+
116+
/// Match the match_subject with any of the current subjects
117+
/// If a NOC CAT is specified, CAT aware matching is also performed
118+
pub fn matches(&self, acl_subject: u64) -> bool {
119+
for v in self.0.iter() {
120+
if *v == 0 {
121+
continue;
122+
}
123+
124+
if *v == acl_subject {
125+
return true;
126+
} else {
127+
// NOC CAT match
128+
if is_noc_cat(*v)
129+
&& is_noc_cat(acl_subject)
130+
&& (get_noc_cat_id(*v) == get_noc_cat_id(acl_subject))
131+
&& (get_noc_cat_version(*v) >= get_noc_cat_version(acl_subject))
132+
{
133+
return true;
134+
}
135+
}
136+
}
137+
138+
false
139+
}
140+
}
141+
142+
impl Display for AccessorSubjects {
143+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
144+
write!(f, "[")?;
145+
for i in self.0 {
146+
if is_noc_cat(i) {
147+
write!(f, "CAT({} - {})", get_noc_cat_id(i), get_noc_cat_version(i))?;
148+
} else if i != 0 {
149+
write!(f, "{}, ", i)?;
150+
}
151+
}
152+
write!(f, "]")
153+
}
154+
}
155+
70156
/// The Accessor Object
71157
pub struct Accessor {
72158
/// The fabric index of the accessor
73159
pub fab_idx: u8,
74-
/// Accessor's identified: could be node-id, NoC CAT, group id
75-
id: u64,
160+
/// Accessor's subject: could be node-id, NoC CAT, group id
161+
subjects: AccessorSubjects,
76162
/// The Authmode of this session
77163
auth_mode: AuthMode,
78164
// TODO: Is this the right place for this though, or should we just use a global-acl-handle-get
79165
acl_mgr: Arc<AclMgr>,
80166
}
81167

82168
impl Accessor {
83-
pub fn new(fab_idx: u8, id: u64, auth_mode: AuthMode, acl_mgr: Arc<AclMgr>) -> Self {
169+
pub fn new(
170+
fab_idx: u8,
171+
subjects: AccessorSubjects,
172+
auth_mode: AuthMode,
173+
acl_mgr: Arc<AclMgr>,
174+
) -> Self {
84175
Self {
85176
fab_idx,
86-
id,
177+
subjects,
87178
auth_mode,
88179
acl_mgr,
89180
}
@@ -215,7 +306,7 @@ impl AclEntry {
215306
let mut entries_exist = false;
216307
for i in self.subjects.iter().flatten() {
217308
entries_exist = true;
218-
if accessor.id == *i {
309+
if accessor.subjects.matches(*i) {
219310
allow = true;
220311
}
221312
}
@@ -467,8 +558,8 @@ impl AclMgr {
467558
}
468559
}
469560
error!(
470-
"ACL Disallow for src id {} fab idx {}",
471-
req.accessor.id, req.accessor.fab_idx
561+
"ACL Disallow for subjects {} fab idx {}",
562+
req.accessor.subjects, req.accessor.fab_idx
472563
);
473564
error!("{}", self);
474565
false
@@ -490,6 +581,7 @@ impl std::fmt::Display for AclMgr {
490581
#[allow(clippy::bool_assert_comparison)]
491582
mod tests {
492583
use crate::{
584+
acl::{gen_noc_cat, AccessorSubjects},
493585
data_model::objects::{Access, Privilege},
494586
interaction_model::messages::GenericPath,
495587
};
@@ -501,7 +593,7 @@ mod tests {
501593
fn test_basic_empty_subject_target() {
502594
let am = Arc::new(AclMgr::new_with(false).unwrap());
503595
am.erase_all();
504-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
596+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
505597
let path = GenericPath::new(Some(1), Some(1234), None);
506598
let mut req = AccessReq::new(&accessor, &path, Access::READ);
507599
req.set_target_perms(Access::RWVA);
@@ -529,7 +621,7 @@ mod tests {
529621
fn test_subject() {
530622
let am = Arc::new(AclMgr::new_with(false).unwrap());
531623
am.erase_all();
532-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
624+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
533625
let path = GenericPath::new(Some(1), Some(1234), None);
534626
let mut req = AccessReq::new(&accessor, &path, Access::READ);
535627
req.set_target_perms(Access::RWVA);
@@ -547,11 +639,79 @@ mod tests {
547639
assert_eq!(req.allow(), true);
548640
}
549641

642+
#[test]
643+
fn test_cat() {
644+
let am = Arc::new(AclMgr::new_with(false).unwrap());
645+
am.erase_all();
646+
647+
let allow_cat = 0xABCD;
648+
let disallow_cat = 0xCAFE;
649+
let v2 = 2;
650+
let v3 = 3;
651+
// Accessor has nodeif and CAT 0xABCD_0002
652+
let mut subjects = AccessorSubjects::new(112233);
653+
subjects.add(gen_noc_cat(allow_cat, v2)).unwrap();
654+
655+
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
656+
let path = GenericPath::new(Some(1), Some(1234), None);
657+
let mut req = AccessReq::new(&accessor, &path, Access::READ);
658+
req.set_target_perms(Access::RWVA);
659+
660+
// Deny for CAT id mismatch
661+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
662+
new.add_subject(gen_noc_cat(disallow_cat, v2)).unwrap();
663+
am.add(new).unwrap();
664+
assert_eq!(req.allow(), false);
665+
666+
// Deny of CAT version mismatch
667+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
668+
new.add_subject(gen_noc_cat(allow_cat, v3)).unwrap();
669+
am.add(new).unwrap();
670+
assert_eq!(req.allow(), false);
671+
672+
// Allow for CAT match
673+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
674+
new.add_subject(gen_noc_cat(allow_cat, v2)).unwrap();
675+
am.add(new).unwrap();
676+
assert_eq!(req.allow(), true);
677+
}
678+
679+
#[test]
680+
fn test_cat_version() {
681+
let am = Arc::new(AclMgr::new_with(false).unwrap());
682+
am.erase_all();
683+
684+
let allow_cat = 0xABCD;
685+
let disallow_cat = 0xCAFE;
686+
let v2 = 2;
687+
let v3 = 3;
688+
// Accessor has nodeif and CAT 0xABCD_0003
689+
let mut subjects = AccessorSubjects::new(112233);
690+
subjects.add(gen_noc_cat(allow_cat, v3)).unwrap();
691+
692+
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
693+
let path = GenericPath::new(Some(1), Some(1234), None);
694+
let mut req = AccessReq::new(&accessor, &path, Access::READ);
695+
req.set_target_perms(Access::RWVA);
696+
697+
// Deny for CAT id mismatch
698+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
699+
new.add_subject(gen_noc_cat(disallow_cat, v2)).unwrap();
700+
am.add(new).unwrap();
701+
assert_eq!(req.allow(), false);
702+
703+
// Allow for CAT match and version more than ACL version
704+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
705+
new.add_subject(gen_noc_cat(allow_cat, v2)).unwrap();
706+
am.add(new).unwrap();
707+
assert_eq!(req.allow(), true);
708+
}
709+
550710
#[test]
551711
fn test_target() {
552712
let am = Arc::new(AclMgr::new_with(false).unwrap());
553713
am.erase_all();
554-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
714+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
555715
let path = GenericPath::new(Some(1), Some(1234), None);
556716
let mut req = AccessReq::new(&accessor, &path, Access::READ);
557717
req.set_target_perms(Access::RWVA);
@@ -612,7 +772,8 @@ mod tests {
612772
fn test_privilege() {
613773
let am = Arc::new(AclMgr::new_with(false).unwrap());
614774
am.erase_all();
615-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
775+
776+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
616777
let path = GenericPath::new(Some(1), Some(1234), None);
617778

618779
// Create an Exact Match ACL with View privilege

matter/src/data_model/core.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use super::{
2323
system_model::descriptor::DescriptorCluster,
2424
};
2525
use crate::{
26-
acl::{AccessReq, Accessor, AclMgr, AuthMode},
26+
acl::{AccessReq, Accessor, AccessorSubjects, AclMgr, AuthMode},
2727
error::*,
2828
fabric::FabricMgr,
2929
interaction_model::{
@@ -219,14 +219,23 @@ impl DataModel {
219219

220220
fn sess_to_accessor(&self, sess: &Session) -> Accessor {
221221
match sess.get_session_mode() {
222-
SessionMode::Case(c) => Accessor::new(
223-
c,
224-
sess.get_peer_node_id().unwrap_or_default(),
225-
AuthMode::Case,
222+
SessionMode::Case(c) => {
223+
let subject = AccessorSubjects::new(sess.get_peer_node_id().unwrap_or_default());
224+
Accessor::new(c, subject, AuthMode::Case, self.acl_mgr.clone())
225+
}
226+
SessionMode::Pase => Accessor::new(
227+
0,
228+
AccessorSubjects::new(1),
229+
AuthMode::Pase,
230+
self.acl_mgr.clone(),
231+
),
232+
233+
SessionMode::PlainText => Accessor::new(
234+
0,
235+
AccessorSubjects::new(1),
236+
AuthMode::Invalid,
226237
self.acl_mgr.clone(),
227238
),
228-
SessionMode::Pase => Accessor::new(0, 1, AuthMode::Pase, self.acl_mgr.clone()),
229-
SessionMode::PlainText => Accessor::new(0, 1, AuthMode::Invalid, self.acl_mgr.clone()),
230239
}
231240
}
232241

0 commit comments

Comments
 (0)