Merge pull request #21 from kedars/feature/cat_in_acl
Feature/CAT in ACL
This commit is contained in:
commit
d518be9db3
9 changed files with 349 additions and 38 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)],
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Reference in a new issue