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}