Skip to main content

windows_permissions/structures/
sid.rs

1use crate::{wrappers, LocalBox};
2use std::fmt;
3use std::hash::Hash;
4use std::io;
5use std::str::FromStr;
6
7/// A SID (Security Identifier) that can be used with Windows API calls.
8#[repr(C)]
9pub struct Sid {
10    _opaque: [u8; 0],
11}
12
13impl Sid {
14    /// Create a new SID from raw parts
15    ///
16    /// ```
17    /// use windows_permissions::Sid;
18    ///
19    /// let sid_8 = Sid::new([1, 2, 3, 4, 5, 6], &[1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
20    ///
21    /// assert_eq!(sid_8.id_authority(), &[1, 2, 3, 4, 5, 6]);
22    /// assert_eq!(sid_8.sub_authority_count(), 8);
23    /// assert_eq!(sid_8.sub_authorities(), &[1, 2, 3, 4, 5, 6, 7, 8]);
24    /// ```
25    ///
26    /// No more than 8 sub-authorities can be made using this function. If more
27    /// are needed, you can parse SDDL or use a wrapper function directly.
28    ///
29    /// ```
30    /// use windows_permissions::Sid;
31    ///
32    /// assert!(Sid::new([1, 2, 3, 4, 5, 6], &[1, 2, 3, 4, 5, 6, 7, 8]).is_ok());
33    /// assert!(Sid::new([1, 2, 3, 4, 5, 6], &[1, 2, 3, 4, 5, 6, 7, 8, 9]).is_err());
34    /// ```
35    ///
36    pub fn new(id_auth: [u8; 6], sub_auths: &[u32]) -> io::Result<LocalBox<Sid>> {
37        wrappers::AllocateAndInitializeSid(id_auth, sub_auths)
38    }
39
40    /// Create a new well-known SID
41    ///
42    /// This is equivalent to calling [`wrappers::CreateWellKnownSid`] with
43    /// `None` as the domain.
44    ///
45    /// ```
46    /// use windows_permissions::{Sid, LocalBox};
47    /// use winapi::um::winnt::WinWorldSid;
48    ///
49    /// let win_world_sid = Sid::well_known_sid(WinWorldSid).unwrap();
50    /// let another_sid = "S-1-1-0".parse().unwrap();
51    ///
52    /// assert_eq!(win_world_sid, another_sid);
53    /// ```
54    pub fn well_known_sid(well_known_sid_type: u32) -> io::Result<LocalBox<Sid>> {
55        wrappers::CreateWellKnownSid(well_known_sid_type, None)
56    }
57
58    /// Get the number of sub-authorities in the SID
59    ///
60    /// ```
61    /// use windows_permissions::{Sid, LocalBox};
62    ///
63    /// let sid1: LocalBox<Sid> = "S-1-5-1".parse().unwrap();
64    /// let sid2: LocalBox<Sid> = "S-1-5-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15".parse().unwrap();
65    ///
66    /// assert_eq!(sid1.sub_authority_count(), 1);
67    /// assert_eq!(sid2.sub_authority_count(), 15);
68    /// ```
69    pub fn sub_authority_count(&self) -> u8 {
70        wrappers::GetSidSubAuthorityCount(self)
71    }
72
73    /// Get the ID authority of the SID
74    ///
75    /// ```
76    /// use windows_permissions::{Sid, LocalBox};
77    ///
78    /// let sid1: LocalBox<Sid> = "S-1-5-12-62341".parse().unwrap();
79    /// let sid2: LocalBox<Sid> = "S-1-211111900160837-1".parse().unwrap();
80    ///
81    /// assert_eq!(sid1.id_authority(), &[0, 0, 0, 0, 0, 5]);
82    /// assert_eq!(sid2.id_authority(), &[0xC0, 0x01, 0x51, 0xD1, 0x23, 0x45]);
83    /// ```
84    pub fn id_authority(&self) -> &[u8; 6] {
85        wrappers::GetSidIdentifierAuthority(self)
86    }
87
88    /// Get a sub-authority of the SID if it is available
89    ///
90    /// Returns `None` if the SID has too few sub-authorities.
91    ///
92    /// ```
93    /// use windows_permissions::{Sid, LocalBox};
94    ///
95    /// let sid: LocalBox<Sid> = "S-1-5-12-62341".parse().unwrap();
96    ///
97    /// assert_eq!(sid.sub_authority(0), Some(12));
98    /// assert_eq!(sid.sub_authority(1), Some(62341));
99    /// assert_eq!(sid.sub_authority(2), None);
100    /// ```
101    pub fn sub_authority(&self, index: u8) -> Option<u32> {
102        wrappers::GetSidSubAuthorityChecked(self, index)
103    }
104
105    /// Generate a list of the sub-authorities in the SID
106    ///
107    /// Changes in the returned `Vec` are not reflected in the SID
108    pub fn sub_authorities(&self) -> Vec<u32> {
109        let mut vec = Vec::with_capacity(self.sub_authority_count() as usize);
110
111        for index in 0..self.sub_authority_count() {
112            vec.push(self.sub_authority(index).expect("Already checked count"));
113        }
114
115        vec
116    }
117
118    /// Get the numeric value of an ID authority
119    ///
120    /// ```
121    /// use windows_permissions::Sid;
122    ///
123    /// assert_eq!(
124    ///     Sid::id_auth_to_number([0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]),
125    ///     0x123456789ABCu64
126    /// );
127    /// ```
128    pub fn id_auth_to_number(id_auth: [u8; 6]) -> u64 {
129        id_auth[5] as u64
130            | (id_auth[4] as u64) << 8
131            | (id_auth[3] as u64) << 16
132            | (id_auth[2] as u64) << 24
133            | (id_auth[1] as u64) << 32
134            | (id_auth[0] as u64) << 40
135    }
136}
137
138#[cfg(test)]
139impl Sid {
140    /// Return an iterator that yields a whole bunch of SIDs you can test
141    /// against, along with the things that got fed into `Sid::new` for each
142    ///
143    /// Only built on `cfg(test)`.
144    pub fn test_sids() -> impl Iterator<Item = (LocalBox<Sid>, [u8; 6], &'static [u32])> {
145        extern crate itertools;
146        use itertools::Itertools;
147
148        const ID_AUTHS: &[[u8; 6]] = &[
149            [0, 0, 0, 0, 0, 0],
150            [0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
151            [0xba, 0xd5, 0x1d, 0xba, 0xd5, 0x1d],
152            [0xc0, 0x00, 0x15, 0x1d, 0xab, 0xcd],
153        ];
154
155        const SUB_AUTHS: &[[u32; 8]] = &[
156            [0, 0, 0, 0, 0, 0, 0, 0],
157            [
158                0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
159                0xffffffff,
160            ],
161            [1, 2, 3, 4, 5, 6, 7, 8],
162        ];
163
164        ID_AUTHS
165            .iter()
166            .cartesian_product(SUB_AUTHS)
167            .cartesian_product(1..=8)
168            .map(|((id, sa), sa_len)| {
169                let chopped_sa = &sa[..sa_len];
170                (
171                    Sid::new(id.clone(), chopped_sa).unwrap(),
172                    id.clone(),
173                    chopped_sa,
174                )
175            })
176    }
177}
178
179impl fmt::Debug for Sid {
180    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
181        fmt.debug_map()
182            .entry(&"string_sid", &self.to_string())
183            .entry(&"id_auth", &self.id_authority())
184            .entry(&"sub_auth_count", &self.sub_authority_count())
185            .entry(&"sub_auths", &self.sub_authorities())
186            .finish()
187    }
188}
189
190impl fmt::Display for Sid {
191    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
192        write!(
193            fmt,
194            "{}",
195            wrappers::ConvertSidToStringSid(&self)
196                .expect("Passed a safe Sid to ConvertSidToStringSid but got an error")
197                .to_string_lossy()
198        )
199    }
200}
201
202impl Hash for Sid {
203    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
204        self.id_authority().hash(state);
205        for index in 0..self.sub_authority_count() {
206            self.sub_authority(index)
207                .expect("Already checked count")
208                .hash(state);
209        }
210    }
211}
212
213impl FromStr for LocalBox<Sid> {
214    type Err = io::Error;
215
216    fn from_str(s: &str) -> Result<Self, Self::Err> {
217        wrappers::ConvertStringSidToSid(s)
218    }
219}
220
221impl Eq for Sid {}
222impl PartialEq for Sid {
223    fn eq(&self, other: &Sid) -> bool {
224        wrappers::EqualSid(self, other)
225    }
226}
227
228impl Clone for LocalBox<Sid> {
229    fn clone(&self) -> Self {
230        wrappers::CopySid(self)
231            // internally, CopySid is just memmove with a length check.
232            // This cannot panic unless allocation fails.
233            .expect("Failed to clone SID")
234    }
235}
236
237#[cfg(test)]
238mod test {
239    use super::*;
240
241    #[test]
242    fn create_and_read_sids() {
243        for (sid, id_auth, sub_auths) in Sid::test_sids() {
244            assert_eq!(*sid.id_authority(), id_auth);
245            assert_eq!(sid.sub_authority_count() as usize, sub_auths.len());
246
247            for i in 0..sub_auths.len() {
248                assert_eq!(sid.sub_authority(i as u8), Some(sub_auths[i]));
249            }
250        }
251    }
252}