use std::str::FromStr;
use binrw::prelude::*;
use crate::binrw_util::prelude::*;
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq, Clone)]
#[brw(little)]
pub struct SID {
#[bw(calc = 1)]
#[br(assert(revision == 1))]
revision: u8,
#[bw(try_calc = sub_authority.len().try_into())]
sub_authority_count: u8,
#[brw(big)] #[br(parse_with = read_u48)]
#[bw(write_with = write_u48)]
pub identifier_authority: u64,
#[br(count = sub_authority_count)]
pub sub_authority: Vec<u32>,
}
impl SID {
pub const MIN_SIZE: usize = std::mem::size_of::<u8>() + std::mem::size_of::<u8>() + 6;
const PREFIX: &'static str = "S-1-";
pub const S_ADMINISTRATORS: &'static str = "S-1-5-32-544";
pub const S_LOCAL_SYSTEM: &'static str = "S-1-5-18";
pub const S_EVERYONE: &'static str = "S-1-1-0";
}
impl FromStr for SID {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.starts_with(Self::PREFIX) {
return Err("SID must start with S-1-");
}
let mut s = s[Self::PREFIX.len()..].split('-');
let identifier_authority = match s.next() {
Some("0x") => {
let p = u64::from_str_radix(
s.next().ok_or("Identifier authority format is incorrect")?,
16,
)
.map_err(|_| "Identifier authority format is incorrect")?;
if p >> 32 == 0 {
p
} else {
return Err("Identifier authority format is incorrect");
}
}
Some(x) => x
.parse()
.map_err(|_| "Identifier authority format is incorrect")?,
None => return Err("SID format is incorrect - missing authority"),
};
let sub_authority = s
.map(|x| x.parse().map_err(|_| "Sub-authority format is incorrect"))
.collect::<Result<_, _>>()?;
Ok(SID {
identifier_authority,
sub_authority,
})
}
}
impl std::fmt::Display for SID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "S-1-")?;
if self.identifier_authority >> 32 == 0 {
write!(f, "{}", self.identifier_authority)?;
} else {
write!(f, "0x{:x}", self.identifier_authority)?;
}
for sub_authority in &self.sub_authority {
write!(f, "-{sub_authority}")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use smb_tests::*;
const SID_STRING: &str = "S-1-5-21-782712087-4182988437-2163400469-1002";
#[test]
fn test_sid_to_from_string() {
let sid_value: SID = SID {
identifier_authority: 5,
sub_authority: vec![21, 782712087, 4182988437, 2163400469, 1002],
};
assert_eq!(SID_STRING.parse::<SID>().unwrap(), sid_value);
assert_eq!(sid_value.to_string(), SID_STRING);
let invalid_sids = ["", "S-1", "S-1-", "S-1-2-", "S-1-4f4"];
for sid in invalid_sids {
assert!(sid.parse::<SID>().is_err())
}
}
test_binrw! {
SID: SID_STRING.parse::<SID>().unwrap()
=> "010500000000000515000000173da72e955653f915dff280ea030000"
}
}