ACL: Add support for NOC CAT
This commit is contained in:
parent
11d50485a4
commit
315b7edbc8
2 changed files with 189 additions and 19 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,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
|
/// 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 +166,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,
|
||||||
}
|
}
|
||||||
|
@ -215,7 +306,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 +558,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 +581,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 +593,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 +621,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 +639,79 @@ 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(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]
|
#[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 +772,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
|
||||||
|
|
|
@ -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,23 @@ 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 subject = AccessorSubjects::new(sess.get_peer_node_id().unwrap_or_default());
|
||||||
sess.get_peer_node_id().unwrap_or_default(),
|
Accessor::new(c, subject, AuthMode::Case, self.acl_mgr.clone())
|
||||||
AuthMode::Case,
|
}
|
||||||
|
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()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue