ACL: Add support for NOC CAT

This commit is contained in:
Kedar Sovani 2023-02-09 19:51:04 +05:30
parent 11d50485a4
commit 315b7edbc8
2 changed files with 189 additions and 19 deletions

View file

@ -15,7 +15,10 @@
* limitations under the License.
*/
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
use std::{
fmt::Display,
sync::{Arc, Mutex, MutexGuard, RwLock},
};
use crate::{
data_model::objects::{Access, Privilege},
@ -24,6 +27,7 @@ use crate::{
interaction_model::messages::GenericPath,
sys::Psm,
tlv::{FromTLV, TLVElement, TLVList, TLVWriter, TagType, ToTLV},
transport::session::MAX_CAT_IDS_PER_NOC,
utils::writebuf::WriteBuf,
};
use log::error;
@ -67,12 +71,94 @@ 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
}
pub fn gen_noc_cat(id: u16, version: u16) -> u64 {
NOC_CAT_SUBJECT_PREFIX | ((id as u64) << 16) | version as u64
}
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(&mut self, subject: u64) -> Result<(), Error> {
for (i, val) in self.0.iter().enumerate() {
if *val == 0 {
self.0[i] = subject;
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
pub struct Accessor {
/// The fabric index of the accessor
pub fab_idx: u8,
/// Accessor's identified: could be node-id, NoC CAT, group id
id: u64,
/// Accessor's subject: could be node-id, NoC CAT, group id
subjects: AccessorSubjects,
/// The Authmode of this session
auth_mode: AuthMode,
// TODO: Is this the right place for this though, or should we just use a global-acl-handle-get
@ -80,10 +166,15 @@ pub struct 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 {
fab_idx,
id,
subjects,
auth_mode,
acl_mgr,
}
@ -215,7 +306,7 @@ impl AclEntry {
let mut entries_exist = false;
for i in self.subjects.iter().flatten() {
entries_exist = true;
if accessor.id == *i {
if accessor.subjects.matches(*i) {
allow = true;
}
}
@ -467,8 +558,8 @@ impl AclMgr {
}
}
error!(
"ACL Disallow for src id {} fab idx {}",
req.accessor.id, req.accessor.fab_idx
"ACL Disallow for subjects {} fab idx {}",
req.accessor.subjects, req.accessor.fab_idx
);
error!("{}", self);
false
@ -490,6 +581,7 @@ impl std::fmt::Display for AclMgr {
#[allow(clippy::bool_assert_comparison)]
mod tests {
use crate::{
acl::{gen_noc_cat, AccessorSubjects},
data_model::objects::{Access, Privilege},
interaction_model::messages::GenericPath,
};
@ -501,7 +593,7 @@ mod tests {
fn test_basic_empty_subject_target() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
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 mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA);
@ -529,7 +621,7 @@ mod tests {
fn test_subject() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
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 mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA);
@ -547,11 +639,79 @@ mod tests {
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(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(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(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(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(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(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(gen_noc_cat(allow_cat, v2)).unwrap();
am.add(new).unwrap();
assert_eq!(req.allow(), true);
}
#[test]
fn test_target() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
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 mut req = AccessReq::new(&accessor, &path, Access::READ);
req.set_target_perms(Access::RWVA);
@ -612,7 +772,8 @@ mod tests {
fn test_privilege() {
let am = Arc::new(AclMgr::new_with(false).unwrap());
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);
// Create an Exact Match ACL with View privilege

View file

@ -23,7 +23,7 @@ use super::{
system_model::descriptor::DescriptorCluster,
};
use crate::{
acl::{AccessReq, Accessor, AclMgr, AuthMode},
acl::{AccessReq, Accessor, AccessorSubjects, AclMgr, AuthMode},
error::*,
fabric::FabricMgr,
interaction_model::{
@ -219,14 +219,23 @@ impl DataModel {
fn sess_to_accessor(&self, sess: &Session) -> Accessor {
match sess.get_session_mode() {
SessionMode::Case(c) => Accessor::new(
c,
sess.get_peer_node_id().unwrap_or_default(),
AuthMode::Case,
SessionMode::Case(c) => {
let subject = AccessorSubjects::new(sess.get_peer_node_id().unwrap_or_default());
Accessor::new(c, 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(),
),
SessionMode::Pase => Accessor::new(0, 1, AuthMode::Pase, self.acl_mgr.clone()),
SessionMode::PlainText => Accessor::new(0, 1, AuthMode::Invalid, self.acl_mgr.clone()),
}
}