sddl 0.1.3

a library to parse and analyse SDDL Strings
Documentation
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;

    ///  A principal with a lower mandatory level than the object cannot
    ///  write to the object.
    pub const SYSTEM_MANDATORY_LABEL_NO_WRITE_UP: AccessMaskFlag = AccessMaskFlag::CREATE_CHILD;

    ///  A principal with a lower mandatory level than the object cannot
    ///  read the object.
    pub const SYSTEM_MANDATORY_LABEL_NO_READ_UP: AccessMaskFlag = AccessMaskFlag::DELETE_CHILD;

    ///  A principal with a lower mandatory level than the object cannot
    ///  execute the object.
    pub const SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP: AccessMaskFlag = AccessMaskFlag::LIST_CHILDREN;

    /// The ObjectType GUID identifies a type of child object. The ACE
    /// controls the trustee's right to create this type of child object.
    pub const ADS_RIGHT_DS_CREATE_CHILD: AccessMaskFlag = AccessMaskFlag::CREATE_CHILD;

    /// The ObjectType GUID identifies a type of child object. The ACE
    /// controls the trustee's right to delete this type of child object.
    pub const ADS_RIGHT_DS_DELETE_CHILD: AccessMaskFlag = AccessMaskFlag::DELETE_CHILD;
    pub const ADS_RIGHT_ACTRL_DS_LIST: AccessMaskFlag = AccessMaskFlag::LIST_CHILDREN;

    /// The ObjectType GUID identifies a validated write.
    pub const ADS_RIGHT_DS_SELF: AccessMaskFlag = AccessMaskFlag::SELF_WRITE;

    /// The ObjectType GUID identifies a property set or property of the
    /// object. The ACE controls the trustee's right to read the property
    /// or property set.
    pub const ADS_RIGHT_DS_READ_PROP: AccessMaskFlag = AccessMaskFlag::READ_PROPERTY;

    /// The ObjectType GUID identifies a property set or property of the
    /// object. The ACE controls the trustee's right to write the property
    /// or property set.
    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;

    /// The ObjectType GUID identifies an extended access right.
    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);
    }
}