Merge pull request #21 from kedars/feature/cat_in_acl

Feature/CAT in ACL
This commit is contained in:
Kedar Sovani 2023-02-12 14:19:00 +05:30 committed by GitHub
commit d518be9db3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 349 additions and 38 deletions

View file

@ -15,7 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
use std::sync::{Arc, Mutex, MutexGuard, RwLock}; use std::{
fmt::Display,
sync::{Arc, Mutex, MutexGuard, RwLock},
};
use crate::{ use crate::{
data_model::objects::{Access, Privilege}, data_model::objects::{Access, Privilege},
@ -24,6 +27,7 @@ use crate::{
interaction_model::messages::GenericPath, interaction_model::messages::GenericPath,
sys::Psm, sys::Psm,
tlv::{FromTLV, TLVElement, TLVList, TLVWriter, TagType, ToTLV}, tlv::{FromTLV, TLVElement, TLVList, TLVWriter, TagType, ToTLV},
transport::session::MAX_CAT_IDS_PER_NOC,
utils::writebuf::WriteBuf, utils::writebuf::WriteBuf,
}; };
use log::error; use log::error;
@ -67,12 +71,96 @@ impl ToTLV for AuthMode {
} }
} }
/// An accessor can have as many identities: one node id and Upto MAX_CAT_IDS_PER_NOC
const MAX_ACCESSOR_SUBJECTS: usize = 1 + MAX_CAT_IDS_PER_NOC;
/// The CAT Prefix used in Subjects
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;
fn is_noc_cat(id: u64) -> bool {
(id & NOC_CAT_SUBJECT_PREFIX) == NOC_CAT_SUBJECT_PREFIX
}
fn get_noc_cat_id(id: u64) -> u64 {
(id & NOC_CAT_ID_MASK) >> 16
}
fn get_noc_cat_version(id: u64) -> u64 {
id & NOC_CAT_VERSION_MASK
}
/// Generate CAT that is embeddedable in the NoC
/// This only generates the 32-bit CAT ID
pub fn gen_noc_cat(id: u16, version: u16) -> u32 {
((id as u32) << 16) | version as u32
}
pub struct AccessorSubjects([u64; MAX_ACCESSOR_SUBJECTS]);
impl AccessorSubjects {
pub fn new(id: u64) -> Self {
let mut a = Self(Default::default());
a.0[0] = id;
a
}
pub fn add_catid(&mut self, subject: u32) -> Result<(), Error> {
for (i, val) in self.0.iter().enumerate() {
if *val == 0 {
self.0[i] = NOC_CAT_SUBJECT_PREFIX | (subject as u64);
return Ok(());
}
}
Err(Error::NoSpace)
}
/// Match the match_subject with any of the current subjects
/// If a NOC CAT is specified, CAT aware matching is also performed
pub fn matches(&self, acl_subject: u64) -> bool {
for v in self.0.iter() {
if *v == 0 {
continue;
}
if *v == acl_subject {
return true;
} else {
// NOC CAT match
if is_noc_cat(*v)
&& is_noc_cat(acl_subject)
&& (get_noc_cat_id(*v) == get_noc_cat_id(acl_subject))
&& (get_noc_cat_version(*v) >= get_noc_cat_version(acl_subject))
{
return true;
}
}
}
false
}
}
impl Display for AccessorSubjects {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "[")?;
for i in self.0 {
if is_noc_cat(i) {
write!(f, "CAT({} - {})", get_noc_cat_id(i), get_noc_cat_version(i))?;
} else if i != 0 {
write!(f, "{}, ", i)?;
}
}
write!(f, "]")
}
}
/// The Accessor Object /// The Accessor Object
pub struct Accessor { pub struct Accessor {
/// The fabric index of the accessor /// The fabric index of the accessor
pub fab_idx: u8, pub fab_idx: u8,
/// Accessor's identified: could be node-id, NoC CAT, group id /// Accessor's subject: could be node-id, NoC CAT, group id
id: u64, subjects: AccessorSubjects,
/// The Authmode of this session /// The Authmode of this session
auth_mode: AuthMode, auth_mode: AuthMode,
// TODO: Is this the right place for this though, or should we just use a global-acl-handle-get // TODO: Is this the right place for this though, or should we just use a global-acl-handle-get
@ -80,10 +168,15 @@ pub struct Accessor {
} }
impl Accessor { impl Accessor {
pub fn new(fab_idx: u8, id: u64, auth_mode: AuthMode, acl_mgr: Arc<AclMgr>) -> Self { pub fn new(
fab_idx: u8,
subjects: AccessorSubjects,
auth_mode: AuthMode,
acl_mgr: Arc<AclMgr>,
) -> Self {
Self { Self {
fab_idx, fab_idx,
id, subjects,
auth_mode, auth_mode,
acl_mgr, acl_mgr,
} }
@ -196,6 +289,10 @@ impl AclEntry {
Ok(()) Ok(())
} }
pub fn add_subject_catid(&mut self, cat_id: u32) -> Result<(), Error> {
self.add_subject(NOC_CAT_SUBJECT_PREFIX | cat_id as u64)
}
pub fn add_target(&mut self, target: Target) -> Result<(), Error> { pub fn add_target(&mut self, target: Target) -> Result<(), Error> {
let index = self let index = self
.targets .targets
@ -215,7 +312,7 @@ impl AclEntry {
let mut entries_exist = false; let mut entries_exist = false;
for i in self.subjects.iter().flatten() { for i in self.subjects.iter().flatten() {
entries_exist = true; entries_exist = true;
if accessor.id == *i { if accessor.subjects.matches(*i) {
allow = true; allow = true;
} }
} }
@ -467,8 +564,8 @@ impl AclMgr {
} }
} }
error!( error!(
"ACL Disallow for src id {} fab idx {}", "ACL Disallow for subjects {} fab idx {}",
req.accessor.id, req.accessor.fab_idx req.accessor.subjects, req.accessor.fab_idx
); );
error!("{}", self); error!("{}", self);
false false
@ -490,6 +587,7 @@ impl std::fmt::Display for AclMgr {
#[allow(clippy::bool_assert_comparison)] #[allow(clippy::bool_assert_comparison)]
mod tests { mod tests {
use crate::{ use crate::{
acl::{gen_noc_cat, AccessorSubjects},
data_model::objects::{Access, Privilege}, data_model::objects::{Access, Privilege},
interaction_model::messages::GenericPath, interaction_model::messages::GenericPath,
}; };
@ -501,7 +599,7 @@ mod tests {
fn test_basic_empty_subject_target() { fn test_basic_empty_subject_target() {
let am = Arc::new(AclMgr::new_with(false).unwrap()); let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all(); am.erase_all();
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone()); let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None); let path = GenericPath::new(Some(1), Some(1234), None);
let mut req = AccessReq::new(&accessor, &path, Access::READ); let mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA); req.set_target_perms(Access::RWVA);
@ -529,7 +627,7 @@ mod tests {
fn test_subject() { fn test_subject() {
let am = Arc::new(AclMgr::new_with(false).unwrap()); let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all(); am.erase_all();
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone()); let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None); let path = GenericPath::new(Some(1), Some(1234), None);
let mut req = AccessReq::new(&accessor, &path, Access::READ); let mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA); req.set_target_perms(Access::RWVA);
@ -547,11 +645,81 @@ mod tests {
assert_eq!(req.allow(), true); assert_eq!(req.allow(), true);
} }
#[test]
fn test_cat() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all();
let allow_cat = 0xABCD;
let disallow_cat = 0xCAFE;
let v2 = 2;
let v3 = 3;
// Accessor has nodeif and CAT 0xABCD_0002
let mut subjects = AccessorSubjects::new(112233);
subjects.add_catid(gen_noc_cat(allow_cat, v2)).unwrap();
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None);
let mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA);
// Deny for CAT id mismatch
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
new.add_subject_catid(gen_noc_cat(disallow_cat, v2))
.unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), false);
// Deny of CAT version mismatch
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
new.add_subject_catid(gen_noc_cat(allow_cat, v3)).unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), false);
// Allow for CAT match
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
new.add_subject_catid(gen_noc_cat(allow_cat, v2)).unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), true);
}
#[test]
fn test_cat_version() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all();
let allow_cat = 0xABCD;
let disallow_cat = 0xCAFE;
let v2 = 2;
let v3 = 3;
// Accessor has nodeif and CAT 0xABCD_0003
let mut subjects = AccessorSubjects::new(112233);
subjects.add_catid(gen_noc_cat(allow_cat, v3)).unwrap();
let accessor = Accessor::new(2, subjects, AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None);
let mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA);
// Deny for CAT id mismatch
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
new.add_subject_catid(gen_noc_cat(disallow_cat, v2))
.unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), false);
// Allow for CAT match and version more than ACL version
let mut new = AclEntry::new(2, Privilege::VIEW, AuthMode::Case);
new.add_subject_catid(gen_noc_cat(allow_cat, v2)).unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), true);
}
#[test] #[test]
fn test_target() { fn test_target() {
let am = Arc::new(AclMgr::new_with(false).unwrap()); let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all(); am.erase_all();
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone()); let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None); let path = GenericPath::new(Some(1), Some(1234), None);
let mut req = AccessReq::new(&accessor, &path, Access::READ); let mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA); req.set_target_perms(Access::RWVA);
@ -612,7 +780,8 @@ mod tests {
fn test_privilege() { fn test_privilege() {
let am = Arc::new(AclMgr::new_with(false).unwrap()); let am = Arc::new(AclMgr::new_with(false).unwrap());
am.erase_all(); am.erase_all();
let accessor = Accessor::new(2, 112233, AuthMode::Case, am.clone());
let accessor = Accessor::new(2, AccessorSubjects::new(112233), AuthMode::Case, am.clone());
let path = GenericPath::new(Some(1), Some(1234), None); let path = GenericPath::new(Some(1), Some(1234), None);
// Create an Exact Match ACL with View privilege // Create an Exact Match ACL with View privilege

View file

@ -318,6 +318,19 @@ impl DistNames {
} }
}) })
} }
fn u32_arr(&self, match_id: DnTags, output: &mut [u32]) {
let mut out_index = 0;
for (_, val) in self.dn.iter().filter(|(id, _)| *id == match_id as u8) {
if let DistNameValue::Uint(a) = val {
if out_index < output.len() {
// CatIds are actually just 32-bit
output[out_index] = *a as u32;
out_index += 1;
}
}
}
}
} }
const PRINTABLE_STR_THRESHOLD: u8 = 0x80; const PRINTABLE_STR_THRESHOLD: u8 = 0x80;
@ -543,6 +556,10 @@ impl Cert {
self.subject.u64(DnTags::NodeId).ok_or(Error::NoNodeId) self.subject.u64(DnTags::NodeId).ok_or(Error::NoNodeId)
} }
pub fn get_cat_ids(&self, output: &mut [u32]) {
self.subject.u32_arr(DnTags::NocCat, output)
}
pub fn get_fabric_id(&self) -> Result<u64, Error> { pub fn get_fabric_id(&self) -> Result<u64, Error> {
self.subject.u64(DnTags::FabricId).ok_or(Error::NoFabricId) self.subject.u64(DnTags::FabricId).ok_or(Error::NoFabricId)
} }

View file

@ -23,7 +23,7 @@ use super::{
system_model::descriptor::DescriptorCluster, system_model::descriptor::DescriptorCluster,
}; };
use crate::{ use crate::{
acl::{AccessReq, Accessor, AclMgr, AuthMode}, acl::{AccessReq, Accessor, AccessorSubjects, AclMgr, AuthMode},
error::*, error::*,
fabric::FabricMgr, fabric::FabricMgr,
interaction_model::{ interaction_model::{
@ -219,14 +219,29 @@ impl DataModel {
fn sess_to_accessor(&self, sess: &Session) -> Accessor { fn sess_to_accessor(&self, sess: &Session) -> Accessor {
match sess.get_session_mode() { match sess.get_session_mode() {
SessionMode::Case(c) => Accessor::new( SessionMode::Case(c) => {
c, let mut subject =
sess.get_peer_node_id().unwrap_or_default(), AccessorSubjects::new(sess.get_peer_node_id().unwrap_or_default());
AuthMode::Case, for i in c.cat_ids {
if i != 0 {
let _ = subject.add_catid(i);
}
}
Accessor::new(c.fab_idx, subject, AuthMode::Case, self.acl_mgr.clone())
}
SessionMode::Pase => Accessor::new(
0,
AccessorSubjects::new(1),
AuthMode::Pase,
self.acl_mgr.clone(),
),
SessionMode::PlainText => Accessor::new(
0,
AccessorSubjects::new(1),
AuthMode::Invalid,
self.acl_mgr.clone(), self.acl_mgr.clone(),
), ),
SessionMode::Pase => Accessor::new(0, 1, AuthMode::Pase, self.acl_mgr.clone()),
SessionMode::PlainText => Accessor::new(0, 1, AuthMode::Invalid, self.acl_mgr.clone()),
} }
} }

View file

@ -89,10 +89,15 @@ impl FailSafe {
match c.noc_state { match c.noc_state {
NocState::NocNotRecvd => return Err(Error::Invalid), NocState::NocNotRecvd => return Err(Error::Invalid),
NocState::AddNocRecvd(idx) | NocState::UpdateNocRecvd(idx) => { NocState::AddNocRecvd(idx) | NocState::UpdateNocRecvd(idx) => {
if SessionMode::Case(idx) != session_mode { if let SessionMode::Case(c) = session_mode {
error!( if c.fab_idx != idx {
"Received disarm in separate session from previous Add/Update NOC" error!(
); "Received disarm in separate session from previous Add/Update NOC"
);
return Err(Error::Invalid);
}
} else {
error!("Received disarm in a non-CASE session");
return Err(Error::Invalid); return Err(Error::Invalid);
} }
} }

View file

@ -248,11 +248,11 @@ impl NocCluster {
.map_err(|_| IMStatusCode::InvalidDataType)?; .map_err(|_| IMStatusCode::InvalidDataType)?;
let (result, fab_idx) = let (result, fab_idx) =
if let SessionMode::Case(fab_idx) = cmd_req.trans.session.get_session_mode() { if let SessionMode::Case(c) = cmd_req.trans.session.get_session_mode() {
if self.fabric_mgr.set_label(fab_idx, label).is_err() { if self.fabric_mgr.set_label(c.fab_idx, label).is_err() {
(NocStatus::LabelConflict, fab_idx) (NocStatus::LabelConflict, c.fab_idx)
} else { } else {
(NocStatus::Ok, fab_idx) (NocStatus::Ok, c.fab_idx)
} }
} else { } else {
// Update Fabric Label not allowed // Update Fabric Label not allowed

View file

@ -33,7 +33,7 @@ use crate::{
network::Address, network::Address,
proto_demux::{ProtoCtx, ResponseRequired}, proto_demux::{ProtoCtx, ResponseRequired},
queue::{Msg, WorkQ}, queue::{Msg, WorkQ},
session::{CloneData, SessionMode}, session::{CaseDetails, CloneData, NocCatIds, SessionMode},
}, },
utils::writebuf::WriteBuf, utils::writebuf::WriteBuf,
}; };
@ -155,6 +155,8 @@ impl Case {
} }
// Only now do we add this message to the TT Hash // Only now do we add this message to the TT Hash
let mut peer_catids: NocCatIds = Default::default();
initiator_noc.get_cat_ids(&mut peer_catids);
case_session.tt_hash.update(ctx.rx.as_borrow_slice())?; case_session.tt_hash.update(ctx.rx.as_borrow_slice())?;
let clone_data = Case::get_session_clone_data( let clone_data = Case::get_session_clone_data(
fabric.ipk.op_key(), fabric.ipk.op_key(),
@ -162,6 +164,7 @@ impl Case {
initiator_noc.get_node_id()?, initiator_noc.get_node_id()?,
ctx.exch_ctx.sess.get_peer_addr(), ctx.exch_ctx.sess.get_peer_addr(),
&case_session, &case_session,
&peer_catids,
)?; )?;
// Queue a transport mgr request to add a new session // Queue a transport mgr request to add a new session
WorkQ::get()?.sync_send(Msg::NewSession(clone_data))?; WorkQ::get()?.sync_send(Msg::NewSession(clone_data))?;
@ -281,6 +284,7 @@ impl Case {
peer_nodeid: u64, peer_nodeid: u64,
peer_addr: Address, peer_addr: Address,
case_session: &CaseSession, case_session: &CaseSession,
peer_catids: &NocCatIds,
) -> Result<CloneData, Error> { ) -> Result<CloneData, Error> {
let mut session_keys = [0_u8; 3 * crypto::SYMM_KEY_LEN_BYTES]; let mut session_keys = [0_u8; 3 * crypto::SYMM_KEY_LEN_BYTES];
Case::get_session_keys( Case::get_session_keys(
@ -296,7 +300,10 @@ impl Case {
case_session.peer_sessid, case_session.peer_sessid,
case_session.local_sessid, case_session.local_sessid,
peer_addr, peer_addr,
SessionMode::Case(case_session.local_fabric_idx as u8), SessionMode::Case(CaseDetails::new(
case_session.local_fabric_idx as u8,
peer_catids,
)),
); );
clone_data.dec_key.copy_from_slice(&session_keys[0..16]); clone_data.dec_key.copy_from_slice(&session_keys[0..16]);

View file

@ -37,12 +37,30 @@ use super::{
packet::{Packet, PacketPool}, packet::{Packet, PacketPool},
}; };
pub const MAX_CAT_IDS_PER_NOC: usize = 3;
pub type NocCatIds = [u32; MAX_CAT_IDS_PER_NOC];
const MATTER_AES128_KEY_SIZE: usize = 16; const MATTER_AES128_KEY_SIZE: usize = 16;
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct CaseDetails {
pub fab_idx: u8,
pub cat_ids: NocCatIds,
}
impl CaseDetails {
pub fn new(fab_idx: u8, cat_ids: &NocCatIds) -> Self {
Self {
fab_idx,
cat_ids: *cat_ids,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum SessionMode { pub enum SessionMode {
// The Case session will capture the local fabric index // The Case session will capture the local fabric index
Case(u8), Case(CaseDetails),
Pase, Pase,
PlainText, PlainText,
} }
@ -188,9 +206,16 @@ impl Session {
self.peer_nodeid self.peer_nodeid
} }
pub fn get_peer_cat_ids(&self) -> Option<&NocCatIds> {
match &self.mode {
SessionMode::Case(a) => Some(&a.cat_ids),
_ => None,
}
}
pub fn get_local_fabric_idx(&self) -> Option<u8> { pub fn get_local_fabric_idx(&self) -> Option<u8> {
match self.mode { match self.mode {
SessionMode::Case(a) => Some(a), SessionMode::Case(a) => Some(a.fab_idx),
_ => None, _ => None,
} }
} }

View file

@ -32,14 +32,14 @@ use matter::{
secure_channel::pake::PaseMgr, secure_channel::pake::PaseMgr,
tlv::{TLVWriter, TagType, ToTLV}, tlv::{TLVWriter, TagType, ToTLV},
transport::packet::Packet, transport::packet::Packet,
transport::proto_demux::HandleProto,
transport::{ transport::{
exchange::{self, Exchange, ExchangeCtx}, exchange::{self, Exchange, ExchangeCtx},
network::Address, network::Address,
packet::PacketPool, packet::PacketPool,
proto_demux::ProtoCtx, proto_demux::ProtoCtx,
session::{CloneData, SessionMgr, SessionMode}, session::{CloneData, NocCatIds, SessionMgr, SessionMode},
}, },
transport::{proto_demux::HandleProto, session::CaseDetails},
utils::writebuf::WriteBuf, utils::writebuf::WriteBuf,
}; };
use std::{ use std::{
@ -69,6 +69,7 @@ pub struct ImInput<'a> {
action: OpCode, action: OpCode,
data: &'a dyn ToTLV, data: &'a dyn ToTLV,
peer_id: u64, peer_id: u64,
cat_ids: NocCatIds,
} }
pub const IM_ENGINE_PEER_ID: u64 = 445566; pub const IM_ENGINE_PEER_ID: u64 = 445566;
@ -78,12 +79,17 @@ impl<'a> ImInput<'a> {
action, action,
data, data,
peer_id: IM_ENGINE_PEER_ID, peer_id: IM_ENGINE_PEER_ID,
cat_ids: Default::default(),
} }
} }
pub fn set_peer_node_id(&mut self, peer: u64) { pub fn set_peer_node_id(&mut self, peer: u64) {
self.peer_id = peer; self.peer_id = peer;
} }
pub fn set_cat_ids(&mut self, cat_ids: &NocCatIds) {
self.cat_ids = *cat_ids;
}
} }
impl ImEngine { impl ImEngine {
@ -146,7 +152,7 @@ impl ImEngine {
std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
5542, 5542,
)), )),
SessionMode::Case(1), SessionMode::Case(CaseDetails::new(1, &input.cat_ids)),
); );
let sess_idx = sess_mgr.clone_session(&clone_data).unwrap(); let sess_idx = sess_mgr.clone_session(&clone_data).unwrap();
let sess = sess_mgr.get_session_handle(sess_idx); let sess = sess_mgr.get_session_handle(sess_idx);

View file

@ -16,7 +16,7 @@
*/ */
use matter::{ use matter::{
acl::{AclEntry, AuthMode, Target}, acl::{gen_noc_cat, AclEntry, AuthMode, Target},
data_model::{ data_model::{
objects::{AttrValue, EncodeValue, Privilege}, objects::{AttrValue, EncodeValue, Privilege},
system_model::access_control, system_model::access_control,
@ -30,6 +30,7 @@ use matter::{
messages::{msg, GenericPath}, messages::{msg, GenericPath},
}, },
tlv::{self, ElementType, FromTLV, TLVArray, TLVElement, TLVWriter, TagType}, tlv::{self, ElementType, FromTLV, TLVArray, TLVElement, TLVWriter, TagType},
transport::session::NocCatIds,
}; };
use crate::{ use crate::{
@ -77,6 +78,7 @@ fn gen_read_reqs_output<'a>(
fn handle_write_reqs( fn handle_write_reqs(
im: &mut ImEngine, im: &mut ImEngine,
peer_node_id: u64, peer_node_id: u64,
peer_cat_ids: Option<&NocCatIds>,
input: &[AttrData], input: &[AttrData],
expected: &[AttrStatus], expected: &[AttrStatus],
) { ) {
@ -85,6 +87,9 @@ fn handle_write_reqs(
let mut input = ImInput::new(OpCode::WriteRequest, &write_req); let mut input = ImInput::new(OpCode::WriteRequest, &write_req);
input.set_peer_node_id(peer_node_id); input.set_peer_node_id(peer_node_id);
if let Some(cat_ids) = peer_cat_ids {
input.set_cat_ids(cat_ids);
}
let (_, out_buf) = im.process(&input, &mut out_buf); let (_, out_buf) = im.process(&input, &mut out_buf);
tlv::print_tlv_list(out_buf); tlv::print_tlv_list(out_buf);
@ -263,7 +268,7 @@ fn wc_write_attribute() {
// Test 1: Wildcard write to an attribute without permission should return // Test 1: Wildcard write to an attribute without permission should return
// no error // no error
handle_write_reqs(&mut im, peer, input0, &[]); handle_write_reqs(&mut im, peer, None, input0, &[]);
{ {
let node = im.dm.node.read().unwrap(); let node = im.dm.node.read().unwrap();
let echo = node.get_cluster(0, echo_cluster::ID).unwrap(); let echo = node.get_cluster(0, echo_cluster::ID).unwrap();
@ -287,6 +292,7 @@ fn wc_write_attribute() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input0, input0,
&[AttrStatus::new(&ep0_att, IMStatusCode::Sucess, 0)], &[AttrStatus::new(&ep0_att, IMStatusCode::Sucess, 0)],
); );
@ -307,6 +313,7 @@ fn wc_write_attribute() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input1, input1,
&[ &[
AttrStatus::new(&ep0_att, IMStatusCode::Sucess, 0), AttrStatus::new(&ep0_att, IMStatusCode::Sucess, 0),
@ -350,7 +357,7 @@ fn exact_write_attribute() {
// Test 1: Exact write to an attribute without permission should return // Test 1: Exact write to an attribute without permission should return
// Unsupported Access Error // Unsupported Access Error
handle_write_reqs(&mut im, peer, input, expected_fail); handle_write_reqs(&mut im, peer, None, input, expected_fail);
assert_eq!( assert_eq!(
AttrValue::Uint16(ATTR_WRITE_DEFAULT_VALUE), AttrValue::Uint16(ATTR_WRITE_DEFAULT_VALUE),
read_cluster_id_write_attr(&im, 0) read_cluster_id_write_attr(&im, 0)
@ -363,7 +370,62 @@ fn exact_write_attribute() {
// Test 1: Exact write to an attribute with permission should grant // Test 1: Exact write to an attribute with permission should grant
// access // access
handle_write_reqs(&mut im, peer, input, expected_success); handle_write_reqs(&mut im, peer, None, input, expected_success);
assert_eq!(AttrValue::Uint16(val0), read_cluster_id_write_attr(&im, 0));
}
#[test]
/// Ensure that an write attribute without a wildcard returns an error when the
/// ACL disallows the access, and returns success once access is granted to the CAT ID
/// The Accessor CAT version is one more than that in the ACL
fn exact_write_attribute_noc_cat() {
let _ = env_logger::try_init();
let val0 = 10;
let attr_data0 = |tag, t: &mut TLVWriter| {
let _ = t.u16(tag, val0);
};
let ep0_att = GenericPath::new(
Some(0),
Some(echo_cluster::ID),
Some(echo_cluster::Attributes::AttWrite as u32),
);
let input = &[AttrData::new(
None,
AttrPath::new(&ep0_att),
EncodeValue::Closure(&attr_data0),
)];
let expected_fail = &[AttrStatus::new(
&ep0_att,
IMStatusCode::UnsupportedAccess,
0,
)];
let expected_success = &[AttrStatus::new(&ep0_att, IMStatusCode::Sucess, 0)];
let peer = 98765;
/* CAT in NOC is 1 more, in version, than that in ACL */
let noc_cat = gen_noc_cat(0xABCD, 2);
let cat_in_acl = gen_noc_cat(0xABCD, 1);
let cat_ids = [noc_cat, 0, 0];
let mut im = ImEngine::new();
// Test 1: Exact write to an attribute without permission should return
// Unsupported Access Error
handle_write_reqs(&mut im, peer, Some(&cat_ids), input, expected_fail);
assert_eq!(
AttrValue::Uint16(ATTR_WRITE_DEFAULT_VALUE),
read_cluster_id_write_attr(&im, 0)
);
// Add ACL to allow our peer to access any endpoint
let mut acl = AclEntry::new(1, Privilege::ADMIN, AuthMode::Case);
acl.add_subject_catid(cat_in_acl).unwrap();
im.acl_mgr.add(acl).unwrap();
// Test 1: Exact write to an attribute with permission should grant
// access
handle_write_reqs(&mut im, peer, Some(&cat_ids), input, expected_success);
assert_eq!(AttrValue::Uint16(val0), read_cluster_id_write_attr(&im, 0)); assert_eq!(AttrValue::Uint16(val0), read_cluster_id_write_attr(&im, 0));
} }
@ -399,6 +461,7 @@ fn insufficient_perms_write() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input0, input0,
&[AttrStatus::new( &[AttrStatus::new(
&ep0_att, &ep0_att,
@ -466,6 +529,7 @@ fn write_with_runtime_acl_add() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
// write to echo-cluster attribute, write to acl attribute, write to echo-cluster attribute // write to echo-cluster attribute, write to acl attribute, write to echo-cluster attribute
&[input0, acl_input, input0], &[input0, acl_input, input0],
&[ &[
@ -623,6 +687,7 @@ fn test_write_data_ver() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input_correct_dataver, input_correct_dataver,
&[AttrStatus::new(&ep0_attwrite, IMStatusCode::Sucess, 0)], &[AttrStatus::new(&ep0_attwrite, IMStatusCode::Sucess, 0)],
); );
@ -638,6 +703,7 @@ fn test_write_data_ver() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input_correct_dataver, input_correct_dataver,
&[AttrStatus::new( &[AttrStatus::new(
&ep0_attwrite, &ep0_attwrite,
@ -660,6 +726,7 @@ fn test_write_data_ver() {
handle_write_reqs( handle_write_reqs(
&mut im, &mut im,
peer, peer,
None,
input_correct_dataver, input_correct_dataver,
&[AttrStatus::new(&ep0_attwrite, IMStatusCode::Sucess, 0)], &[AttrStatus::new(&ep0_attwrite, IMStatusCode::Sucess, 0)],
); );