Skip to main content

windows_permissions/structures/
ace.rs

1use crate::constants::{AccessRights, AceFlags, AceType};
2use crate::Sid;
3use std::fmt;
4use std::mem;
5use std::ptr::NonNull;
6use winapi::um::winnt::ACE_HEADER;
7
8/// An access control list.
9///
10/// See [MSDN](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header)
11/// for layout details, or [ACCESS_ALLOWED_ACE on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace)
12/// for an example.
13#[repr(C)]
14pub struct Ace {
15    header: ACE_HEADER,
16}
17
18impl Drop for Ace {
19    fn drop(&mut self) {
20        unreachable!("Ace should only be borrowed")
21    }
22}
23
24impl Ace {
25    /// Get a reference from an ACE pointer.
26    ///
27    /// # Safety
28    ///
29    /// - `ptr` must point to a valid ACE structure
30    /// - The ACE header must be followed by the correct ACE structure
31    /// - The entire structure must remain alive at least as long as `'s`
32    pub unsafe fn ref_from_nonnull<'s>(ptr: NonNull<ACE_HEADER>) -> &'s Self {
33        mem::transmute(ptr)
34    }
35
36    /// Determine the type of ACE
37    pub fn ace_type(&self) -> AceType {
38        AceType::from_raw(self.header.AceType).expect("ACE had invalid header byte")
39    }
40
41    /// Get the option flags set on the ACE
42    pub fn flags(&self) -> AceFlags {
43        debug_assert!(AceFlags::from_bits(self.header.AceFlags).is_some());
44        AceFlags::from_bits_truncate(self.header.AceFlags)
45    }
46
47    /// Get the access mask if it is available for this ACE type
48    pub fn mask(&self) -> AccessRights {
49        use winapi::um::winnt::*;
50
51        macro_rules! mask_mapping {
52            ($slf:ident ; $($t:ident => $b:ty),*) => {{
53                match $slf.ace_type() {
54                    $(
55                    AceType::$t => AccessRights::from_bits_truncate(
56                        (*(&$slf.header as *const _ as *mut $b)).Mask,
57                    ),
58                    )*
59                }
60            }}
61        }
62
63        unsafe {
64            mask_mapping! {self;
65                ACCESS_ALLOWED_ACE_TYPE => ACCESS_ALLOWED_ACE,
66                ACCESS_ALLOWED_CALLBACK_ACE_TYPE => ACCESS_ALLOWED_CALLBACK_ACE,
67                ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE => ACCESS_ALLOWED_CALLBACK_OBJECT_ACE,
68                ACCESS_ALLOWED_OBJECT_ACE_TYPE => ACCESS_ALLOWED_OBJECT_ACE,
69                ACCESS_DENIED_ACE_TYPE => ACCESS_DENIED_ACE,
70                ACCESS_DENIED_CALLBACK_ACE_TYPE => ACCESS_DENIED_ACE,
71                ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE => ACCESS_DENIED_CALLBACK_OBJECT_ACE,
72                ACCESS_DENIED_OBJECT_ACE_TYPE => ACCESS_DENIED_OBJECT_ACE,
73                SYSTEM_AUDIT_ACE_TYPE => SYSTEM_AUDIT_ACE,
74                SYSTEM_AUDIT_CALLBACK_ACE_TYPE => SYSTEM_AUDIT_CALLBACK_ACE,
75                SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE => SYSTEM_AUDIT_CALLBACK_ACE,
76                SYSTEM_AUDIT_OBJECT_ACE_TYPE => SYSTEM_AUDIT_OBJECT_ACE,
77                SYSTEM_MANDATORY_LABEL_ACE_TYPE => SYSTEM_MANDATORY_LABEL_ACE,
78                SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE => SYSTEM_RESOURCE_ATTRIBUTE_ACE,
79                SYSTEM_SCOPED_POLICY_ID_ACE_TYPE => SYSTEM_SCOPED_POLICY_ID_ACE
80            }
81        }
82    }
83
84    /// Get the SID if it is available for this ACE type
85    pub fn sid(&self) -> Option<&Sid> {
86        use winapi::um::winnt::*;
87
88        macro_rules! get_sid {
89            ($slf:ident ; $ace_type:ty ; $sid_field:ident ) => {
90                Some(
91                    &*(&(*(&$slf.header as *const ACE_HEADER as *const $ace_type)).$sid_field
92                        as *const _ as *const Sid),
93                )
94            };
95            ($slf:ident ; $ace_type:ty) => {
96                get_sid!($slf ; $ace_type ; SidStart)
97            };
98            ($slf:ident ; $ace_type:ty ; $field_none:ident , $field_one:ident, $field_both:ident) => {{
99                let flags = (*(&$slf.header as *const ACE_HEADER as *const $ace_type)).Flags;
100                let obj_pres = flags & winapi::um::winnt::ACE_OBJECT_TYPE_PRESENT != 0;
101                let inh_pres = flags & winapi::um::winnt::ACE_INHERITED_OBJECT_TYPE_PRESENT != 0;
102                match (obj_pres, inh_pres) {
103                    (false, false) => get_sid!($slf ; $ace_type ; $field_none),
104                    (true,  false) => get_sid!($slf ; $ace_type ; $field_one),
105                    (false, true ) => get_sid!($slf ; $ace_type ; $field_one),
106                    (true,  true ) => get_sid!($slf ; $ace_type ; $field_both),
107                }}
108            };
109        }
110
111        unsafe {
112            match self.ace_type() {
113                AceType::ACCESS_ALLOWED_ACE_TYPE => get_sid!(self; ACCESS_ALLOWED_ACE),
114                AceType::ACCESS_ALLOWED_CALLBACK_ACE_TYPE => {
115                    get_sid!(self; ACCESS_ALLOWED_CALLBACK_ACE)
116                }
117                AceType::ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE => {
118                    get_sid!(self; ACCESS_ALLOWED_CALLBACK_OBJECT_ACE;
119                    ObjectType, InheritedObjectType, SidStart)
120                }
121                AceType::ACCESS_ALLOWED_OBJECT_ACE_TYPE => {
122                    get_sid!(self; ACCESS_ALLOWED_OBJECT_ACE;
123                    ObjectType, InheritedObjectType, SidStart)
124                }
125                AceType::ACCESS_DENIED_ACE_TYPE => get_sid!(self; ACCESS_DENIED_ACE),
126                AceType::ACCESS_DENIED_CALLBACK_ACE_TYPE => get_sid!(self; ACCESS_DENIED_ACE),
127                AceType::ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE => {
128                    get_sid!(self; ACCESS_DENIED_CALLBACK_OBJECT_ACE;
129                    ObjectType, InheritedObjectType, SidStart)
130                }
131                AceType::ACCESS_DENIED_OBJECT_ACE_TYPE => get_sid!(self; ACCESS_DENIED_OBJECT_ACE;
132                    ObjectType, InheritedObjectType, SidStart),
133                AceType::SYSTEM_AUDIT_ACE_TYPE => get_sid!(self; SYSTEM_AUDIT_ACE),
134                AceType::SYSTEM_AUDIT_CALLBACK_ACE_TYPE => {
135                    get_sid!(self; SYSTEM_AUDIT_CALLBACK_ACE)
136                }
137                AceType::SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE => {
138                    get_sid!(self; SYSTEM_AUDIT_CALLBACK_ACE; SidStart)
139                }
140                AceType::SYSTEM_AUDIT_OBJECT_ACE_TYPE => get_sid!(self; SYSTEM_AUDIT_OBJECT_ACE;
141                    ObjectType, InheritedObjectType, SidStart),
142                AceType::SYSTEM_MANDATORY_LABEL_ACE_TYPE => {
143                    get_sid!(self; SYSTEM_MANDATORY_LABEL_ACE)
144                }
145                AceType::SYSTEM_SCOPED_POLICY_ID_ACE_TYPE => {
146                    get_sid!(self; SYSTEM_SCOPED_POLICY_ID_ACE)
147                }
148                AceType::SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE => None, // TODO: Resource attributes are more complex
149            }
150        }
151    }
152}
153
154impl fmt::Debug for Ace {
155    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
156        let mut map = fmt.debug_map();
157        map.entry(&"ace_type", &self.ace_type());
158        map.entry(&"flags", &self.flags());
159        map.finish()
160    }
161}
162
163#[cfg(test)]
164mod test {
165    use super::*;
166    use crate::{wrappers, LocalBox, SecurityDescriptor};
167
168    enum DaclSacl {
169        Dacl,
170        Sacl,
171    }
172
173    use DaclSacl::{Dacl, Sacl};
174
175    #[test]
176    fn mandatory_label() {
177        let access_rights = [
178            ("NR", AccessRights::MandatoryLabelNoReadUp),
179            ("NW", AccessRights::MandatoryLabelNoWriteUp),
180            ("NX", AccessRights::MandatoryLabelNoExecuteUp),
181        ];
182
183        let test_setups = [
184            ("(ML;;", ";;;LW)", winapi::um::winnt::WinLowLabelSid),
185            ("(ML;;", ";;;ME)", winapi::um::winnt::WinMediumLabelSid),
186            ("(ML;;", ";;;HI)", winapi::um::winnt::WinHighLabelSid),
187        ];
188
189        for (sddl1, sddl2, sid_type) in test_setups.iter() {
190            for (mask_sddl, mask_value) in access_rights.iter() {
191                let sd: LocalBox<SecurityDescriptor> = format!("S:{}{}{}", sddl1, mask_sddl, sddl2)
192                    .parse()
193                    .unwrap();
194
195                let ace = sd.sacl().unwrap().get_ace(0).unwrap();
196
197                assert_eq!(ace.ace_type(), AceType::SYSTEM_MANDATORY_LABEL_ACE_TYPE);
198                assert_eq!(ace.mask(), *mask_value);
199                assert_eq!(
200                    ace.sid().unwrap(),
201                    &*wrappers::CreateWellKnownSid(*sid_type, None).unwrap()
202                );
203            }
204        }
205    }
206
207    #[test]
208    fn resource_attribute() {
209        // These are weird enough that they get their own tests
210        let sd: LocalBox<SecurityDescriptor> =
211            r#"S:(RA;;;;;WD;("Secrecy",TU,0,3))"#.parse().unwrap();
212        let ace = sd.sacl().unwrap().get_ace(0).unwrap();
213
214        assert_eq!(ace.mask(), AccessRights::empty());
215        assert_eq!(ace.sid(), None);
216    }
217
218    #[test]
219    fn standard_ace() {
220        use crate::constants::AccessRights;
221        use crate::constants::AceType::*;
222
223        let access_rights = [
224            ("", AccessRights::empty()),
225            ("GA", AccessRights::GenericAll),
226            ("GR", AccessRights::GenericRead),
227            ("GW", AccessRights::GenericWrite),
228            ("GX", AccessRights::GenericExecute),
229            ("RC", AccessRights::ReadControl),
230            ("SD", AccessRights::Delete),
231            ("WD", AccessRights::WriteDac),
232            ("WO", AccessRights::WriteOwner),
233            ("FA", AccessRights::FileAllAccess),
234            ("FR", AccessRights::FileGenericRead),
235            ("FW", AccessRights::FileGenericWrite),
236            ("FX", AccessRights::FileGenericExecute),
237            ("KA", AccessRights::KeyAllAccess),
238            ("KR", AccessRights::KeyRead),
239            ("KW", AccessRights::KeyWrite),
240            ("KX", AccessRights::KeyExecute),
241            (
242                "GRGWRCSDWD",
243                AccessRights::GenericRead
244                    | AccessRights::GenericWrite
245                    | AccessRights::ReadControl
246                    | AccessRights::Delete
247                    | AccessRights::WriteDac,
248            ),
249        ];
250
251        // In order: (SDDL ACE, expected type, acl to use)
252        //
253        // The GUIDs are required because Windows automatically replaces object-
254        // based ACEs with their non-object counterparts when no GUID is
255        // specified. The GUID here is randomly generated, and doesn't refer
256        // to anything in particular.
257        //
258        // ACEs with "CALLBACK" in their type take a conditional function.
259        // "(TRUE)" is a valid (if useless) parameter.
260        //
261        // RA requires an extra parameter defining a resource attribute.
262        //
263        // TODO: I couldn't get SP to work on my machine.
264        let test_cases = [
265            ("(A;;", ";;;", ")", ACCESS_ALLOWED_ACE_TYPE, Dacl),
266            ("(D;;", ";;;", ")", ACCESS_DENIED_ACE_TYPE, Dacl),
267            ("(AU;;", ";;;", ")", SYSTEM_AUDIT_ACE_TYPE, Sacl),
268            (
269                "(XU;;",
270                ";;;",
271                ";(TRUE))",
272                SYSTEM_AUDIT_CALLBACK_ACE_TYPE,
273                Sacl,
274            ),
275            (
276                "(XA;;",
277                ";;;",
278                ";(TRUE))",
279                ACCESS_ALLOWED_CALLBACK_ACE_TYPE,
280                Dacl,
281            ),
282            (
283                "(XD;;",
284                ";;;",
285                ";(TRUE))",
286                ACCESS_DENIED_CALLBACK_ACE_TYPE,
287                Dacl,
288            ),
289            (
290                "(OA;;",
291                ";c434c045-9b91-4504-a2a0-aea9e781ec69;;",
292                ")",
293                ACCESS_ALLOWED_OBJECT_ACE_TYPE,
294                Dacl,
295            ),
296            (
297                "(OD;;",
298                ";c434c045-9b91-4504-a2a0-aea9e781ec69;;",
299                ")",
300                ACCESS_DENIED_OBJECT_ACE_TYPE,
301                Dacl,
302            ),
303            (
304                "(OU;;",
305                ";c434c045-9b91-4504-a2a0-aea9e781ec69;;",
306                ")",
307                SYSTEM_AUDIT_OBJECT_ACE_TYPE,
308                Sacl,
309            ),
310            (
311                "(ZA;;",
312                ";c434c045-9b91-4504-a2a0-aea9e781ec69;;",
313                ";(TRUE))",
314                ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE,
315                Dacl,
316            ),
317        ];
318
319        for (base_sddl_1, base_sddl_2, base_sddl_3, ace_type, which_acl) in test_cases.iter() {
320            for (access_rights_sddl, access_rights_value) in access_rights.iter() {
321                for (sid, _, _) in Sid::test_sids() {
322                    let mut sddl_string = String::new();
323                    sddl_string.push_str(base_sddl_1);
324                    sddl_string.push_str(access_rights_sddl);
325                    sddl_string.push_str(base_sddl_2);
326                    sddl_string.push_str(&sid.to_string());
327                    sddl_string.push_str(base_sddl_3);
328
329                    //eprintln!("Testing {} yields {:?}, {:?}", sddl_string, ace_type, access_rights_value);
330
331                    dbg!(&sddl_string);
332
333                    let sd: LocalBox<SecurityDescriptor> = match which_acl {
334                        Dacl => format!("D:{}", sddl_string),
335                        Sacl => format!("S:{}", sddl_string),
336                    }
337                    .parse()
338                    .unwrap();
339
340                    let acl = match which_acl {
341                        Dacl => sd.dacl(),
342                        Sacl => sd.sacl(),
343                    }
344                    .unwrap();
345
346                    assert_eq!(acl.len(), 1);
347
348                    let ace = acl.get_ace(0).unwrap();
349
350                    assert_eq!(ace.ace_type(), *ace_type);
351                    assert_eq!(ace.sid(), Some(&*sid));
352                    assert_eq!(ace.mask(), *access_rights_value);
353                }
354            }
355        }
356    }
357
358    #[test]
359    fn get_flags_dacl() {
360        let test_cases = [
361            ("", AceFlags::empty(), Dacl),
362            ("", AceFlags::empty(), Sacl),
363            ("CI", AceFlags::ContainerInherit, Dacl),
364            ("OI", AceFlags::ObjectInherit, Dacl),
365            ("NP", AceFlags::NoPropagateInherit, Dacl),
366            ("IO", AceFlags::InheritOnly, Dacl),
367            ("ID", AceFlags::Inherited, Dacl),
368            ("SA", AceFlags::SuccessfulAccess, Sacl),
369            ("FA", AceFlags::FailedAccess, Sacl),
370            (
371                "CIOINPIOID",
372                AceFlags::ContainerInherit
373                    | AceFlags::ObjectInherit
374                    | AceFlags::NoPropagateInherit
375                    | AceFlags::InheritOnly
376                    | AceFlags::Inherited,
377                Dacl,
378            ),
379            (
380                "SAFA",
381                AceFlags::SuccessfulAccess | AceFlags::FailedAccess,
382                Sacl,
383            ),
384        ];
385
386        for (sddl, flag, which_acl) in test_cases.iter() {
387            eprintln!("Testing {} yields {:?}", sddl, flag);
388
389            let sd: LocalBox<SecurityDescriptor> = match which_acl {
390                Dacl => format!("D:(A;{};;;;WD)", sddl),
391                Sacl => format!("S:(AU;{};;;;WD)", sddl),
392            }
393            .parse()
394            .unwrap();
395
396            let acl = match which_acl {
397                Dacl => sd.dacl(),
398                Sacl => sd.sacl(),
399            }
400            .unwrap();
401
402            assert_eq!(acl.get_ace(0).unwrap().flags(), *flag);
403        }
404    }
405}