smb_dtyp/security/
sid.rs

1//! MS-DTYP 2.4.2.2
2
3use std::str::FromStr;
4
5use binrw::prelude::*;
6
7use crate::binrw_util::prelude::*;
8
9/// SID (Security identifier)
10///
11/// [MS-DTYP 2.4.2](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/78eb9013-1c3a-4970-ad1f-2b1dad588a25>)
12///
13/// This SID implementation supports binary read/write, and from/to string operations.
14/// ```
15/// use smb_dtyp::SID;
16/// use std::str::FromStr;
17/// const SID_STRING: &str = "S-1-5-21-782712087-4182988437-2163400469-1002";
18/// let sid = SID::from_str(SID_STRING).unwrap();
19/// let sid_string = sid.to_string();
20/// assert_eq!(sid_string, SID_STRING);
21/// ```
22#[binrw::binrw]
23#[derive(Debug, PartialEq, Eq, Clone)]
24#[brw(little)]
25pub struct SID {
26    #[bw(calc = 1)]
27    #[br(assert(revision == 1))]
28    revision: u8,
29    #[bw(try_calc = sub_authority.len().try_into())]
30    sub_authority_count: u8,
31    #[brw(big)] // WE LOVE MICROSOFT!
32    #[br(parse_with = read_u48)]
33    #[bw(write_with = write_u48)]
34    pub identifier_authority: u64,
35    #[br(count = sub_authority_count)]
36    pub sub_authority: Vec<u32>,
37}
38impl SID {
39    /// Minimum size of this structure in bytes, when serialized.
40    pub const MIN_SIZE: usize = std::mem::size_of::<u8>()  // Revision
41        + std::mem::size_of::<u8>()  // SubAuthorityCount
42        + 6; // IdentifierAuthority (48 bits)
43
44    /// SID common string prefix
45    const PREFIX: &'static str = "S-1-";
46
47    /// Administrators group SID
48    pub const S_ADMINISTRATORS: &'static str = "S-1-5-32-544";
49    /// Local System user SID
50    pub const S_LOCAL_SYSTEM: &'static str = "S-1-5-18";
51    /// Everyone group SID
52    pub const S_EVERYONE: &'static str = "S-1-1-0";
53}
54
55impl FromStr for SID {
56    type Err = &'static str;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        // 1. starts with S-1-:
60        if !s.starts_with(Self::PREFIX) {
61            return Err("SID must start with S-1-");
62        }
63        let mut s = s[Self::PREFIX.len()..].split('-');
64        // 2. authority is a number, possibly in hex.
65        let identifier_authority = match s.next() {
66            Some("0x") => {
67                // hex is only for sub-authorities > 32 bits!
68                let p = u64::from_str_radix(
69                    s.next().ok_or("Identifier authority format is incorrect")?,
70                    16,
71                )
72                .map_err(|_| "Identifier authority format is incorrect")?;
73                if p >> 32 == 0 {
74                    p
75                } else {
76                    return Err("Identifier authority format is incorrect");
77                }
78            }
79            Some(x) => x
80                .parse()
81                .map_err(|_| "Identifier authority format is incorrect")?,
82            None => return Err("SID format is incorrect - missing authority"),
83        };
84        // 3. sub-authorities are numbers.
85        let sub_authority = s
86            .map(|x| x.parse().map_err(|_| "Sub-authority format is incorrect"))
87            .collect::<Result<_, _>>()?;
88        Ok(SID {
89            identifier_authority,
90            sub_authority,
91        })
92    }
93}
94
95impl std::fmt::Display for SID {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        // MS-DTYP 2.4.2.1: SID String Format
98        write!(f, "S-1-")?;
99        if self.identifier_authority >> 32 == 0 {
100            write!(f, "{}", self.identifier_authority)?;
101        } else {
102            write!(f, "0x{:x}", self.identifier_authority)?;
103        }
104        for sub_authority in &self.sub_authority {
105            write!(f, "-{sub_authority}")?;
106        }
107        Ok(())
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use smb_tests::*;
115
116    const SID_STRING: &str = "S-1-5-21-782712087-4182988437-2163400469-1002";
117
118    #[test]
119    fn test_sid_to_from_string() {
120        let sid_value: SID = SID {
121            identifier_authority: 5,
122            sub_authority: vec![21, 782712087, 4182988437, 2163400469, 1002],
123        };
124        assert_eq!(SID_STRING.parse::<SID>().unwrap(), sid_value);
125        assert_eq!(sid_value.to_string(), SID_STRING);
126
127        let invalid_sids = ["", "S-1", "S-1-", "S-1-2-", "S-1-4f4"];
128        for sid in invalid_sids {
129            assert!(sid.parse::<SID>().is_err())
130        }
131    }
132
133    test_binrw! {
134        SID: SID_STRING.parse::<SID>().unwrap()
135            => "010500000000000515000000173da72e955653f915dff280ea030000"
136    }
137}