Skip to content

Commit d518be9

Browse files
authored
Merge pull request #21 from kedars/feature/cat_in_acl
Feature/CAT in ACL
2 parents 108385c + 707d370 commit d518be9

File tree

9 files changed

+349
-38
lines changed

9 files changed

+349
-38
lines changed

matter/src/acl.rs

Lines changed: 181 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,112 @@ 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+
/// Generate CAT that is embeddedable in the NoC
94+
/// This only generates the 32-bit CAT ID
95+
pub fn gen_noc_cat(id: u16, version: u16) -> u32 {
96+
((id as u32) << 16) | version as u32
97+
}
98+
99+
pub struct AccessorSubjects([u64; MAX_ACCESSOR_SUBJECTS]);
100+
101+
impl AccessorSubjects {
102+
pub fn new(id: u64) -> Self {
103+
let mut a = Self(Default::default());
104+
a.0[0] = id;
105+
a
106+
}
107+
108+
pub fn add_catid(&mut self, subject: u32) -> Result<(), Error> {
109+
for (i, val) in self.0.iter().enumerate() {
110+
if *val == 0 {
111+
self.0[i] = NOC_CAT_SUBJECT_PREFIX | (subject as u64);
112+
return Ok(());
113+
}
114+
}
115+
Err(Error::NoSpace)
116+
}
117+
118+
/// Match the match_subject with any of the current subjects
119+
/// If a NOC CAT is specified, CAT aware matching is also performed
120+
pub fn matches(&self, acl_subject: u64) -> bool {
121+
for v in self.0.iter() {
122+
if *v == 0 {
123+
continue;
124+
}
125+
126+
if *v == acl_subject {
127+
return true;
128+
} else {
129+
// NOC CAT match
130+
if is_noc_cat(*v)
131+
&& is_noc_cat(acl_subject)
132+
&& (get_noc_cat_id(*v) == get_noc_cat_id(acl_subject))
133+
&& (get_noc_cat_version(*v) >= get_noc_cat_version(acl_subject))
134+
{
135+
return true;
136+
}
137+
}
138+
}
139+
140+
false
141+
}
142+
}
143+
144+
impl Display for AccessorSubjects {
145+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
146+
write!(f, "[")?;
147+
for i in self.0 {
148+
if is_noc_cat(i) {
149+
write!(f, "CAT({} - {})", get_noc_cat_id(i), get_noc_cat_version(i))?;
150+
} else if i != 0 {
151+
write!(f, "{}, ", i)?;
152+
}
153+
}
154+
write!(f, "]")
155+
}
156+
}
157+
70158
/// The Accessor Object
71159
pub struct Accessor {
72160
/// The fabric index of the accessor
73161
pub fab_idx: u8,
74-
/// Accessor's identified: could be node-id, NoC CAT, group id
75-
id: u64,
162+
/// Accessor's subject: could be node-id, NoC CAT, group id
163+
subjects: AccessorSubjects,
76164
/// The Authmode of this session
77165
auth_mode: AuthMode,
78166
// TODO: Is this the right place for this though, or should we just use a global-acl-handle-get
79167
acl_mgr: Arc<AclMgr>,
80168
}
81169

82170
impl Accessor {
83-
pub fn new(fab_idx: u8, id: u64, auth_mode: AuthMode, acl_mgr: Arc<AclMgr>) -> Self {
171+
pub fn new(
172+
fab_idx: u8,
173+
subjects: AccessorSubjects,
174+
auth_mode: AuthMode,
175+
acl_mgr: Arc<AclMgr>,
176+
) -> Self {
84177
Self {
85178
fab_idx,
86-
id,
179+
subjects,
87180
auth_mode,
88181
acl_mgr,
89182
}
@@ -196,6 +289,10 @@ impl AclEntry {
196289
Ok(())
197290
}
198291

292+
pub fn add_subject_catid(&mut self, cat_id: u32) -> Result<(), Error> {
293+
self.add_subject(NOC_CAT_SUBJECT_PREFIX | cat_id as u64)
294+
}
295+
199296
pub fn add_target(&mut self, target: Target) -> Result<(), Error> {
200297
let index = self
201298
.targets
@@ -215,7 +312,7 @@ impl AclEntry {
215312
let mut entries_exist = false;
216313
for i in self.subjects.iter().flatten() {
217314
entries_exist = true;
218-
if accessor.id == *i {
315+
if accessor.subjects.matches(*i) {
219316
allow = true;
220317
}
221318
}
@@ -467,8 +564,8 @@ impl AclMgr {
467564
}
468565
}
469566
error!(
470-
"ACL Disallow for src id {} fab idx {}",
471-
req.accessor.id, req.accessor.fab_idx
567+
"ACL Disallow for subjects {} fab idx {}",
568+
req.accessor.subjects, req.accessor.fab_idx
472569
);
473570
error!("{}", self);
474571
false
@@ -490,6 +587,7 @@ impl std::fmt::Display for AclMgr {
490587
#[allow(clippy::bool_assert_comparison)]
491588
mod tests {
492589
use crate::{
590+
acl::{gen_noc_cat, AccessorSubjects},
493591
data_model::objects::{Access, Privilege},
494592
interaction_model::messages::GenericPath,
495593
};
@@ -501,7 +599,7 @@ mod tests {
501599
fn test_basic_empty_subject_target() {
502600
let am = Arc::new(AclMgr::new_with(false).unwrap());
503601
am.erase_all();
504-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
602+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
505603
let path = GenericPath::new(Some(1), Some(1234), None);
506604
let mut req = AccessReq::new(&accessor, &path, Access::READ);
507605
req.set_target_perms(Access::RWVA);
@@ -529,7 +627,7 @@ mod tests {
529627
fn test_subject() {
530628
let am = Arc::new(AclMgr::new_with(false).unwrap());
531629
am.erase_all();
532-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
630+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
533631
let path = GenericPath::new(Some(1), Some(1234), None);
534632
let mut req = AccessReq::new(&accessor, &path, Access::READ);
535633
req.set_target_perms(Access::RWVA);
@@ -547,11 +645,81 @@ mod tests {
547645
assert_eq!(req.allow(), true);
548646
}
549647

648+
#[test]
649+
fn test_cat() {
650+
let am = Arc::new(AclMgr::new_with(false).unwrap());
651+
am.erase_all();
652+
653+
let allow_cat = 0xABCD;
654+
let disallow_cat = 0xCAFE;
655+
let v2 = 2;
656+
let v3 = 3;
657+
// Accessor has nodeif and CAT 0xABCD_0002
658+
let mut subjects = AccessorSubjects::new(112233);
659+
subjects.add_catid(gen_noc_cat(allow_cat, v2)).unwrap();
660+
661+
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
662+
let path = GenericPath::new(Some(1), Some(1234), None);
663+
let mut req = AccessReq::new(&accessor, &path, Access::READ);
664+
req.set_target_perms(Access::RWVA);
665+
666+
// Deny for CAT id mismatch
667+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
668+
new.add_subject_catid(gen_noc_cat(disallow_cat, v2))
669+
.unwrap();
670+
am.add(new).unwrap();
671+
assert_eq!(req.allow(), false);
672+
673+
// Deny of CAT version mismatch
674+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
675+
new.add_subject_catid(gen_noc_cat(allow_cat, v3)).unwrap();
676+
am.add(new).unwrap();
677+
assert_eq!(req.allow(), false);
678+
679+
// Allow for CAT match
680+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
681+
new.add_subject_catid(gen_noc_cat(allow_cat, v2)).unwrap();
682+
am.add(new).unwrap();
683+
assert_eq!(req.allow(), true);
684+
}
685+
686+
#[test]
687+
fn test_cat_version() {
688+
let am = Arc::new(AclMgr::new_with(false).unwrap());
689+
am.erase_all();
690+
691+
let allow_cat = 0xABCD;
692+
let disallow_cat = 0xCAFE;
693+
let v2 = 2;
694+
let v3 = 3;
695+
// Accessor has nodeif and CAT 0xABCD_0003
696+
let mut subjects = AccessorSubjects::new(112233);
697+
subjects.add_catid(gen_noc_cat(allow_cat, v3)).unwrap();
698+
699+
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
700+
let path = GenericPath::new(Some(1), Some(1234), None);
701+
let mut req = AccessReq::new(&accessor, &path, Access::READ);
702+
req.set_target_perms(Access::RWVA);
703+
704+
// Deny for CAT id mismatch
705+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
706+
new.add_subject_catid(gen_noc_cat(disallow_cat, v2))
707+
.unwrap();
708+
am.add(new).unwrap();
709+
assert_eq!(req.allow(), false);
710+
711+
// Allow for CAT match and version more than ACL version
712+
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
713+
new.add_subject_catid(gen_noc_cat(allow_cat, v2)).unwrap();
714+
am.add(new).unwrap();
715+
assert_eq!(req.allow(), true);
716+
}
717+
550718
#[test]
551719
fn test_target() {
552720
let am = Arc::new(AclMgr::new_with(false).unwrap());
553721
am.erase_all();
554-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
722+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
555723
let path = GenericPath::new(Some(1), Some(1234), None);
556724
let mut req = AccessReq::new(&accessor, &path, Access::READ);
557725
req.set_target_perms(Access::RWVA);
@@ -612,7 +780,8 @@ mod tests {
612780
fn test_privilege() {
613781
let am = Arc::new(AclMgr::new_with(false).unwrap());
614782
am.erase_all();
615-
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
783+
784+
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
616785
let path = GenericPath::new(Some(1), Some(1234), None);
617786

618787
// Create an Exact Match ACL with View privilege

matter/src/cert/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,19 @@ impl DistNames {
318318
}
319319
})
320320
}
321+
322+
fn u32_arr(&self, match_id: DnTags, output: &mut [u32]) {
323+
let mut out_index = 0;
324+
for (_, val) in self.dn.iter().filter(|(id, _)| *id == match_id as u8) {
325+
if let DistNameValue::Uint(a) = val {
326+
if out_index < output.len() {
327+
// CatIds are actually just 32-bit
328+
output[out_index] = *a as u32;
329+
out_index += 1;
330+
}
331+
}
332+
}
333+
}
321334
}
322335

323336
const PRINTABLE_STR_THRESHOLD: u8 = 0x80;
@@ -543,6 +556,10 @@ impl Cert {
543556
self.subject.u64(DnTags::NodeId).ok_or(Error::NoNodeId)
544557
}
545558

559+
pub fn get_cat_ids(&self, output: &mut [u32]) {
560+
self.subject.u32_arr(DnTags::NocCat, output)
561+
}
562+
546563
pub fn get_fabric_id(&self) -> Result<u64, Error> {
547564
self.subject.u64(DnTags::FabricId).ok_or(Error::NoFabricId)
548565
}

matter/src/data_model/core.rs

Lines changed: 22 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,29 @@ 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 mut subject =
224+
AccessorSubjects::new(sess.get_peer_node_id().unwrap_or_default());
225+
for i in c.cat_ids {
226+
if i != 0 {
227+
let _ = subject.add_catid(i);
228+
}
229+
}
230+
Accessor::new(c.fab_idx, subject, AuthMode::Case, self.acl_mgr.clone())
231+
}
232+
SessionMode::Pase => Accessor::new(
233+
0,
234+
AccessorSubjects::new(1),
235+
AuthMode::Pase,
236+
self.acl_mgr.clone(),
237+
),
238+
239+
SessionMode::PlainText => Accessor::new(
240+
0,
241+
AccessorSubjects::new(1),
242+
AuthMode::Invalid,
226243
self.acl_mgr.clone(),
227244
),
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()),
230245
}
231246
}
232247

matter/src/data_model/sdm/failsafe.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,15 @@ impl FailSafe {
8989
match c.noc_state {
9090
NocState::NocNotRecvd => return Err(Error::Invalid),
9191
NocState::AddNocRecvd(idx) | NocState::UpdateNocRecvd(idx) => {
92-
if SessionMode::Case(idx) != session_mode {
93-
error!(
94-
"Received disarm in separate session from previous Add/Update NOC"
95-
);
92+
if let SessionMode::Case(c) = session_mode {
93+
if c.fab_idx != idx {
94+
error!(
95+
"Received disarm in separate session from previous Add/Update NOC"
96+
);
97+
return Err(Error::Invalid);
98+
}
99+
} else {
100+
error!("Received disarm in a non-CASE session");
96101
return Err(Error::Invalid);
97102
}
98103
}

matter/src/data_model/sdm/noc.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,11 @@ impl NocCluster {
248248
.map_err(|_| IMStatusCode::InvalidDataType)?;
249249

250250
let (result, fab_idx) =
251-
if let SessionMode::Case(fab_idx) = cmd_req.trans.session.get_session_mode() {
252-
if self.fabric_mgr.set_label(fab_idx, label).is_err() {
253-
(NocStatus::LabelConflict, fab_idx)
251+
if let SessionMode::Case(c) = cmd_req.trans.session.get_session_mode() {
252+
if self.fabric_mgr.set_label(c.fab_idx, label).is_err() {
253+
(NocStatus::LabelConflict, c.fab_idx)
254254
} else {
255-
(NocStatus::Ok, fab_idx)
255+
(NocStatus::Ok, c.fab_idx)
256256
}
257257
} else {
258258
// Update Fabric Label not allowed

0 commit comments

Comments
 (0)