diff --git a/matter/src/acl.rs b/matter/src/acl.rs index 8625a7b..a3553c0 100644 --- a/matter/src/acl.rs +++ b/matter/src/acl.rs @@ -78,14 +78,17 @@ pub const NOC_CAT_SUBJECT_PREFIX: u64 = 0xFFFF_FFFD_0000_0000; const NOC_CAT_ID_MASK: u64 = 0xFFFF_0000; const NOC_CAT_VERSION_MASK: u64 = 0xFFFF; +/// Is this identifier a NOC CAT fn is_noc_cat(id: u64) -> bool { (id & NOC_CAT_SUBJECT_PREFIX) == NOC_CAT_SUBJECT_PREFIX } +/// Get the 16-bit NOC CAT id from the identifier fn get_noc_cat_id(id: u64) -> u64 { (id & NOC_CAT_ID_MASK) >> 16 } +/// Get the 16-bit NOC CAT version from the identifier fn get_noc_cat_version(id: u64) -> u64 { id & NOC_CAT_VERSION_MASK } @@ -96,6 +99,7 @@ pub fn gen_noc_cat(id: u16, version: u16) -> u32 { ((id as u32) << 16) | version as u32 } +/// The Subjects that identify the Accessor pub struct AccessorSubjects([u64; MAX_ACCESSOR_SUBJECTS]); impl AccessorSubjects { @@ -816,4 +820,34 @@ mod tests { req.set_target_perms(Access::RWVA); assert_eq!(req.allow(), true); } + + #[test] + fn test_delete_for_fabric() { + let am = Arc::new(AclMgr::new_with(false).unwrap()); + am.erase_all(); + let path = GenericPath::new(Some(1), Some(1234), None); + let accessor2 = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone()); + let mut req2 = AccessReq::new(&accessor2, &path, Access::READ); + req2.set_target_perms(Access::RWVA); + let accessor3 = Accessor::new(3, AccessorSubjects::new(112233), AuthMode::Case, am.clone()); + let mut req3 = AccessReq::new(&accessor3, &path, Access::READ); + req3.set_target_perms(Access::RWVA); + + // Allow for subject match - target is wildcard - Fabric idx 2 + let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case); + new.add_subject(112233).unwrap(); + am.add(new).unwrap(); + + // Allow for subject match - target is wildcard - Fabric idx 3 + let mut new = AclEntry::new(3, Privilege::VIEW, AuthMode::Case); + new.add_subject(112233).unwrap(); + am.add(new).unwrap(); + + // Req for Fabric idx 2 gets denied, and that for Fabric idx 3 is allowed + assert_eq!(req2.allow(), true); + assert_eq!(req3.allow(), true); + am.delete_for_fabric(2).unwrap(); + assert_eq!(req2.allow(), false); + assert_eq!(req3.allow(), true); + } } diff --git a/matter/src/data_model/sdm/noc.rs b/matter/src/data_model/sdm/noc.rs index 7785fbd..0b94500 100644 --- a/matter/src/data_model/sdm/noc.rs +++ b/matter/src/data_model/sdm/noc.rs @@ -263,6 +263,24 @@ impl NocCluster { Ok(()) } + fn handle_command_rmfabric(&mut self, cmd_req: &mut CommandReq) -> Result<(), IMStatusCode> { + cmd_enter!("Remove Fabric"); + let req = + RemoveFabricReq::from_tlv(&cmd_req.data).map_err(|_| IMStatusCode::InvalidCommand)?; + if self.fabric_mgr.remove(req.fab_idx).is_ok() { + let _ = self.acl_mgr.delete_for_fabric(req.fab_idx); + cmd_req.trans.terminate(); + } else { + NocCluster::create_nocresponse( + cmd_req.resp, + NocStatus::InvalidFabricIndex, + req.fab_idx, + "".to_string(), + ); + } + Ok(()) + } + fn handle_command_addnoc(&mut self, cmd_req: &mut CommandReq) -> Result<(), IMStatusCode> { cmd_enter!("AddNOC"); if let Err(e) = self._handle_command_addnoc(cmd_req) { @@ -437,6 +455,7 @@ impl ClusterType for NocCluster { Commands::AttReq => self.handle_command_attrequest(cmd_req), Commands::CertChainReq => self.handle_command_certchainrequest(cmd_req), Commands::UpdateFabricLabel => self.handle_command_updatefablabel(cmd_req), + Commands::RemoveFabric => self.handle_command_rmfabric(cmd_req), _ => Err(IMStatusCode::UnsupportedCommand), } } @@ -566,6 +585,11 @@ struct CertChainReq { cert_type: u8, } +#[derive(FromTLV)] +struct RemoveFabricReq { + fab_idx: u8, +} + fn get_certchainrequest_params(data: &TLVElement) -> Result { let cert_type = CertChainReq::from_tlv(data)?.cert_type; diff --git a/matter/src/fabric.rs b/matter/src/fabric.rs index 3299f4d..1715db7 100644 --- a/matter/src/fabric.rs +++ b/matter/src/fabric.rs @@ -195,6 +195,17 @@ impl Fabric { } } + fn rm_store(&self, index: usize, psm: &MutexGuard) { + psm.rm(fb_key!(index, ST_RCA)); + psm.rm(fb_key!(index, ST_ICA)); + psm.rm(fb_key!(index, ST_NOC)); + psm.rm(fb_key!(index, ST_IPK)); + psm.rm(fb_key!(index, ST_LBL)); + psm.rm(fb_key!(index, ST_PBKEY)); + psm.rm(fb_key!(index, ST_PRKEY)); + psm.rm(fb_key!(index, ST_VID)); + } + fn store(&self, index: usize, psm: &MutexGuard) -> Result<(), Error> { let mut key = [0u8; MAX_CERT_TLV_LEN]; let len = self.root_ca.as_tlv(&mut key)?; @@ -335,6 +346,19 @@ impl FabricMgr { Ok(index as u8) } + pub fn remove(&self, fab_idx: u8) -> Result<(), Error> { + let fab_idx = fab_idx as usize; + let mut mgr = self.inner.write().unwrap(); + let psm = self.psm.lock().unwrap(); + if let Some(f) = &mgr.fabrics[fab_idx] { + f.rm_store(fab_idx, &psm); + mgr.fabrics[fab_idx] = None; + Ok(()) + } else { + Err(Error::NotFound) + } + } + pub fn match_dest_id(&self, random: &[u8], target: &[u8]) -> Result { let mgr = self.inner.read()?; for i in 0..MAX_SUPPORTED_FABRICS { diff --git a/matter/src/interaction_model/core.rs b/matter/src/interaction_model/core.rs index dd77c78..adbda94 100644 --- a/matter/src/interaction_model/core.rs +++ b/matter/src/interaction_model/core.rs @@ -68,6 +68,16 @@ impl<'a> Transaction<'a> { } } + /// Terminates the transaction, no communication (even ACKs) happens hence forth + pub fn terminate(&mut self) { + self.state = TransactionState::Terminate + } + + pub fn is_terminate(&self) -> bool { + self.state == TransactionState::Terminate + } + + /// Marks the transaction as completed from the application's perspective pub fn complete(&mut self) { self.state = TransactionState::Complete } @@ -200,7 +210,9 @@ impl proto_demux::HandleProto for InteractionModel { info!("Sending response"); tlv::print_tlv_list(ctx.tx.as_borrow_slice()); } - if trans.is_complete() { + if trans.is_terminate() { + ctx.exch_ctx.exch.terminate(); + } else if trans.is_complete() { ctx.exch_ctx.exch.close(); } Ok(result) diff --git a/matter/src/interaction_model/mod.rs b/matter/src/interaction_model/mod.rs index c4d401b..98af4af 100644 --- a/matter/src/interaction_model/mod.rs +++ b/matter/src/interaction_model/mod.rs @@ -30,6 +30,7 @@ use self::{ pub enum TransactionState { Ongoing, Complete, + Terminate, } pub struct Transaction<'a> { pub state: TransactionState, diff --git a/matter/src/sys/posix.rs b/matter/src/sys/posix.rs index ea1b9d6..2736e51 100644 --- a/matter/src/sys/posix.rs +++ b/matter/src/sys/posix.rs @@ -17,7 +17,7 @@ use std::{ convert::TryInto, - fs::{DirBuilder, File}, + fs::{remove_file, DirBuilder, File}, io::{Read, Write}, sync::{Arc, Mutex, Once}, }; @@ -89,4 +89,8 @@ impl Psm { *val = u64::from_be_bytes(vec.as_slice().try_into()?); Ok(()) } + + pub fn rm(&self, key: &str) { + let _ = remove_file(psm_path!(key)); + } } diff --git a/matter/src/transport/exchange.rs b/matter/src/transport/exchange.rs index 2e67636..54f8a7a 100644 --- a/matter/src/transport/exchange.rs +++ b/matter/src/transport/exchange.rs @@ -48,10 +48,15 @@ impl Default for Role { } } +/// State of the exchange #[derive(Debug, PartialEq)] enum State { + /// The exchange is open and active Open, + /// The exchange is closed, but keys are active since retransmissions/acks may be pending Close, + /// The exchange is terminated, keys are destroyed, no communication can happen + Terminate, } impl Default for State { @@ -100,6 +105,11 @@ impl Exchange { } } + pub fn terminate(&mut self) { + self.data = DataOption::None; + self.state = State::Terminate; + } + pub fn close(&mut self) { self.data = DataOption::None; self.state = State::Close; @@ -111,7 +121,7 @@ impl Exchange { pub fn is_purgeable(&self) -> bool { // No Users, No pending ACKs/Retrans - self.state == State::Close && self.mrp.is_empty() + self.state == State::Terminate || (self.state == State::Close && self.mrp.is_empty()) } pub fn get_id(&self) -> u16 { @@ -170,6 +180,11 @@ impl Exchange { mut proto_tx: BoxSlab, session: &mut SessionHandle, ) -> Result<(), Error> { + if self.state == State::Terminate { + info!("Skipping tx for terminated exchange {}", self.id); + return Ok(()); + } + trace!("payload: {:x?}", proto_tx.as_borrow_slice()); info!( "{} with proto id: {} opcode: {}",