sddl 0.1.6

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 flags: Vec<_> = self.flags.iter().collect();
            for flag in flags.into_iter().rev() {
                sddl.push_str(match flag {
                    AccessMaskFlag::GENERIC_READ => SDDL_GENERIC_READ,
                    AccessMaskFlag::GENERIC_WRITE => SDDL_GENERIC_WRITE,
                    AccessMaskFlag::GENERIC_EXECUTE => SDDL_GENERIC_EXECUTE,
                    AccessMaskFlag::GENERIC_ALL => SDDL_GENERIC_ALL,
                    AccessMaskFlag::MAXIMUM_ALLOWED => "MA",
                    AccessMaskFlag::ACCESS_SYSTEM_SECURITY => "AS",
                    AccessMaskFlag::SYNCHRONIZE => "SY",
                    AccessMaskFlag::WRITE_OWNER => SDDL_WRITE_OWNER,
                    AccessMaskFlag::WRITE_DACL => SDDL_WRITE_DAC,
                    AccessMaskFlag::READ_CONTROL => SDDL_READ_CONTROL,
                    AccessMaskFlag::STANDARD_DELETE => SDDL_STANDARD_DELETE,
                    AccessMaskFlag::CONTROL_ACCESS => SDDL_CONTROL_ACCESS,
                    AccessMaskFlag::LIST_OBJECT => SDDL_LIST_OBJECT,
                    AccessMaskFlag::DELETE_TREE => SDDL_DELETE_TREE,
                    AccessMaskFlag::WRITE_PROPERTY => SDDL_WRITE_PROPERTY,
                    AccessMaskFlag::READ_PROPERTY => SDDL_READ_PROPERTY,
                    AccessMaskFlag::SELF_WRITE => SDDL_SELF_WRITE,
                    AccessMaskFlag::LIST_CHILDREN => SDDL_LIST_CHILDREN,
                    AccessMaskFlag::DELETE_CHILD => SDDL_DELETE_CHILD,
                    AccessMaskFlag::CREATE_CHILD => SDDL_CREATE_CHILD,
                });
            }
            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);
    }
}