mod flag;
pub use flag::AccessMaskFlag;
use std::{fmt::{Display, LowerHex, Octal}, str::FromStr};
use binrw::{BinRead, BinReaderExt, BinWrite, BinWriterExt};
use crate::*;
flag_wrapper!(AccessMask, AccessMaskFlag, u32);
macro_rules! access_mask {
( $($variant:ident)|* ) => {
AccessMask::from_bitflags(make_bitflags!(AccessMaskFlag::{$($variant)|*}))
};
($flag:ident) => {
AccessMask::from_bitflags(make_bitflags!(AccessMaskFlag::{$flag}))
};
}
pub mod constants {
use super::AccessMask;
use super::AccessMaskFlag;
use enumflags2::make_bitflags;
pub const SYSTEM_MANDATORY_LABEL_NO_WRITE_UP: AccessMaskFlag = AccessMaskFlag::CREATE_CHILD;
pub const SYSTEM_MANDATORY_LABEL_NO_READ_UP: AccessMaskFlag = AccessMaskFlag::DELETE_CHILD;
pub const SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP: AccessMaskFlag = AccessMaskFlag::LIST_CHILDREN;
pub const ADS_RIGHT_DS_CREATE_CHILD: AccessMaskFlag = AccessMaskFlag::CREATE_CHILD;
pub const ADS_RIGHT_DS_DELETE_CHILD: AccessMaskFlag = AccessMaskFlag::DELETE_CHILD;
pub const ADS_RIGHT_ACTRL_DS_LIST: AccessMaskFlag = AccessMaskFlag::LIST_CHILDREN;
pub const ADS_RIGHT_DS_SELF: AccessMaskFlag = AccessMaskFlag::SELF_WRITE;
pub const ADS_RIGHT_DS_READ_PROP: AccessMaskFlag = AccessMaskFlag::READ_PROPERTY;
pub const ADS_RIGHT_DS_WRITE_PROP: AccessMaskFlag = AccessMaskFlag::WRITE_PROPERTY;
pub const ADS_RIGHT_DS_DELETE_TREE: AccessMaskFlag = AccessMaskFlag::DELETE_TREE;
pub const ADS_RIGHT_DS_LIST_OBJECT: AccessMaskFlag = AccessMaskFlag::LIST_OBJECT;
pub const ADS_RIGHT_DS_CONTROL_ACCESS: AccessMaskFlag = AccessMaskFlag::CONTROL_ACCESS;
pub const FILE_ALL: AccessMask = access_mask!(
SYNCHRONIZE
| WRITE_OWNER
| WRITE_DACL
| READ_CONTROL
| STANDARD_DELETE
| CONTROL_ACCESS
| LIST_OBJECT
| DELETE_TREE
| WRITE_PROPERTY
| READ_PROPERTY
| SELF_WRITE
| LIST_CHILDREN
| DELETE_CHILD
| CREATE_CHILD
);
pub const FILE_EXECUTE: AccessMask =
access_mask!(SYNCHRONIZE | READ_CONTROL | LIST_OBJECT | WRITE_PROPERTY);
pub const FILE_WRITE: AccessMask = access_mask!(
SYNCHRONIZE | READ_CONTROL | CONTROL_ACCESS | READ_PROPERTY | LIST_CHILDREN | DELETE_CHILD
);
pub const FILE_READ: AccessMask =
access_mask!(SYNCHRONIZE | READ_CONTROL | LIST_OBJECT | SELF_WRITE | CREATE_CHILD);
pub const KEY_ALL: AccessMask = access_mask!(
WRITE_OWNER
| WRITE_DACL
| READ_CONTROL
| STANDARD_DELETE
| WRITE_PROPERTY
| READ_PROPERTY
| SELF_WRITE
| LIST_CHILDREN
| DELETE_CHILD
| CREATE_CHILD
);
pub const KEY_READ: AccessMask =
access_mask!(READ_CONTROL | READ_PROPERTY | SELF_WRITE | CREATE_CHILD);
pub const KEY_WRITE: AccessMask = access_mask!(READ_CONTROL | LIST_CHILDREN | DELETE_CHILD);
pub const KEY_EXECUTE: AccessMask =
access_mask!(READ_CONTROL | READ_PROPERTY | SELF_WRITE | CREATE_CHILD);
}
impl AccessMask {
pub fn try_from_alias(alias: &str) -> Result<Self, Error> {
match AccessMaskFlag::from_str(alias) {
Ok(flag) => Ok(Self::from_bitflag(flag)),
Err(why) => match alias {
"CA" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_CONTROL_ACCESS)),
"LO" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_LIST_OBJECT)),
"DT" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_DELETE_TREE)),
"WP" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_WRITE_PROP)),
"RP" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_READ_PROP)),
"SW" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_SELF)),
"LC" => Ok(Self::from_bitflag(constants::ADS_RIGHT_ACTRL_DS_LIST)),
"DC" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_DELETE_CHILD)),
"CC" => Ok(Self::from_bitflag(constants::ADS_RIGHT_DS_CREATE_CHILD)),
"NW" => Ok(Self::from_bitflag(constants::SYSTEM_MANDATORY_LABEL_NO_WRITE_UP)),
"NR" => Ok(Self::from_bitflag(constants::SYSTEM_MANDATORY_LABEL_NO_READ_UP)),
"NX" => Ok(Self::from_bitflag(constants::SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP)),
"FA" => Ok(constants::FILE_ALL),
"FR" => Ok(constants::FILE_READ),
"FW" => Ok(constants::FILE_WRITE),
"FX" => Ok(constants::FILE_EXECUTE),
"KA" => Ok(constants::KEY_ALL),
"KR" => Ok(constants::KEY_READ),
"KW" => Ok(constants::KEY_WRITE),
"KX" => Ok(constants::KEY_EXECUTE),
_ => Err(Error::IllegalAlias(why))
}
}
}
}
impl Display for AccessMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::constants::*;
if self == &FILE_ALL {
SDDL_FILE_ALL.fmt(f)
} else if self == &FILE_READ {
SDDL_FILE_READ.fmt(f)
} else if self == &FILE_WRITE {
SDDL_FILE_WRITE.fmt(f)
} else if self == &FILE_EXECUTE {
SDDL_FILE_EXECUTE.fmt(f)
} else if self == &KEY_ALL {
SDDL_KEY_ALL.fmt(f)
} else if self == &KEY_READ {
SDDL_KEY_READ.fmt(f)
} else if self == &KEY_WRITE {
SDDL_KEY_WRITE.fmt(f)
} else if self == &KEY_EXECUTE {
SDDL_KEY_EXECUTE.fmt(f)
} else {
let mut sddl = String::with_capacity(32);
let mut check_flag = |mask: AccessMaskFlag, s: &str| {
if self.flags.contains(mask) {
sddl.push_str(s);
}
};
check_flag(AccessMaskFlag::GENERIC_READ, SDDL_GENERIC_READ);
check_flag(AccessMaskFlag::GENERIC_WRITE, SDDL_GENERIC_WRITE);
check_flag(AccessMaskFlag::GENERIC_EXECUTE, SDDL_GENERIC_EXECUTE);
check_flag(AccessMaskFlag::GENERIC_ALL, SDDL_GENERIC_ALL);
check_flag(AccessMaskFlag::MAXIMUM_ALLOWED, "MA");
check_flag(AccessMaskFlag::ACCESS_SYSTEM_SECURITY, "AS");
check_flag(AccessMaskFlag::SYNCHRONIZE, "SY");
check_flag(AccessMaskFlag::WRITE_OWNER, SDDL_WRITE_OWNER);
check_flag(AccessMaskFlag::WRITE_DACL, SDDL_WRITE_DAC);
check_flag(AccessMaskFlag::READ_CONTROL, SDDL_READ_CONTROL);
check_flag(AccessMaskFlag::STANDARD_DELETE, SDDL_STANDARD_DELETE);
sddl.fmt(f)
}
}
}
impl LowerHex for AccessMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "0x")?;
}
write!(f, "{:08x}", self.raw_value())
}
}
impl Octal for AccessMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "0o")?;
}
write!(f, "{:o}", self.raw_value())
}
}
impl std::fmt::UpperHex for AccessMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "0x")?;
}
write!(f, "{:08X}", self.raw_value())
}
}
#[cfg(test)]
mod tests {
use crate::access_mask::{AccessMask, AccessMaskFlag};
use super::constants::*;
#[test]
fn test_simple_mask() {
let parser = crate::parser::AccessMaskParser::new();
assert_eq!(
parser.parse(None, "0x80000000").unwrap(),
AccessMask::from_bitflag(AccessMaskFlag::GENERIC_READ)
);
assert_ne!(
parser.parse(None, "0x80000001").unwrap(),
AccessMask::from_bitflag(AccessMaskFlag::GENERIC_READ)
);
}
#[test]
fn test_complex_mask1() {
let parser = crate::parser::AccessMaskParser::new();
assert_eq!(
parser.parse(None, "GRGXWP").unwrap(),
AccessMask::from(AccessMaskFlag::GENERIC_READ | AccessMaskFlag::GENERIC_EXECUTE | AccessMaskFlag::WRITE_PROPERTY)
);
}
#[test]
fn test_complex_mask2() {
let parser = crate::parser::AccessMaskParser::new();
assert_eq!(parser.parse(None, "FA").unwrap(), FILE_ALL);
}
#[test]
fn test_statics() {
assert_eq!(FILE_ALL.flags.bits(), 0x001F01FF);
assert_eq!(FILE_EXECUTE.flags.bits(), 0x001200A0);
assert_eq!(FILE_WRITE.flags.bits(), 0x00120116);
assert_eq!(FILE_READ.flags.bits(), 0x00120089);
assert_eq!(KEY_ALL.flags.bits(), 0x000F003F);
assert_eq!(KEY_READ.flags.bits(), 0x00020019);
assert_eq!(KEY_EXECUTE.flags.bits(), 0x00020019);
assert_eq!(KEY_WRITE.flags.bits(), 0x00020006);
}
}