smb_dtyp/security/
acl.rs

1//! MS-DTYP 2.4.5: ACL
2
3use binrw::prelude::*;
4
5use crate::binrw_util::prelude::*;
6
7use super::ACE;
8
9#[binrw::binrw]
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct ACL {
12    pub acl_revision: AclRevision,
13    #[bw(calc = 0)]
14    #[br(assert(sbz1 == 0))]
15    sbz1: u8,
16    #[bw(calc = PosMarker::default())]
17    _acl_size: PosMarker<u16>,
18    #[bw(calc = ace.len() as u16)]
19    ace_count: u16,
20    #[bw(calc = 0)]
21    #[br(assert(sbz2 == 0))]
22    sbz2: u16,
23
24    #[br(count = ace_count)]
25    #[bw(write_with = PosMarker::write_size_plus, args(&_acl_size, Self::HEADER_SIZE))]
26    pub ace: Vec<ACE>,
27}
28
29impl ACL {
30    const HEADER_SIZE: u64 = 8;
31
32    /// Orders the ACEs in the ACL according to the standard order.
33    ///
34    /// Note that since we do not have sufficient information about the inheritance,
35    /// we only apply order which is independent of inheritance.
36    ///
37    /// The following steps describe the preferred order:
38    /// 1. ✅ All explicit ACEs are placed in a group before any inherited ACEs.
39    /// 2. ✅ Within the group of explicit ACEs, access-denied ACEs are placed before access-allowed ACEs.
40    /// 3. ❌ Inherited ACEs are placed in the order in which they are inherited. ACEs inherited from the child object's parent come first, then ACEs inherited from the grandparent, and so on up the tree of objects.
41    /// 4. ❌ For each level of inherited ACEs, access-denied ACEs are placed before access-allowed ACEs.
42    ///
43    /// See more information on [Order of ACEs in a DACL - MSDN](<https://learn.microsoft.com/en-us/windows/win32/secauthz/order-of-aces-in-a-dacl>)
44    pub fn order_aces(&mut self) {
45        self.ace.sort_by(Self::sort_aces_by);
46    }
47
48    /// Whether ACE ordering rules apply to this ACL.
49    ///
50    /// See [`order_aces`][ACL::order_aces] for the ordering rules.
51    pub fn is_ace_sorted(&self) -> bool {
52        self.ace
53            .is_sorted_by(|a, b| Self::sort_aces_by(a, b).is_le())
54    }
55
56    /// Sorting function for ACEs.
57    ///
58    /// See [`order_aces`][ACL::order_aces] for the ordering rules.
59    fn sort_aces_by(a: &ACE, b: &ACE) -> std::cmp::Ordering {
60        let a_inherited = a.ace_flags.inherited();
61        let b_inherited = b.ace_flags.inherited();
62        if a_inherited != b_inherited {
63            return a_inherited.cmp(&b_inherited); // (1)
64        }
65        if a_inherited {
66            return std::cmp::Ordering::Equal; // keep original order for inherited ACEs (3)
67        }
68        let a_denied = a.value.is_access_allowed();
69        let b_denied = b.value.is_access_allowed();
70        a_denied.cmp(&b_denied) // (2) on explicit ACEs, access-denied first <=> access-allowed last
71        // Note: the sort is stable, so we keep the original order for inherited ACEs
72    }
73
74    /// Insert an ACE into the ACL, maintaining the correct order.
75    /// See [`order_aces`][ACL::order_aces] for the ordering rules.
76    pub fn insert_ace(&mut self, ace: ACE) {
77        self.ace.push(ace);
78        self.order_aces();
79    }
80}
81
82#[binrw::binrw]
83#[derive(Debug, PartialEq, Eq, Copy, Clone)]
84#[brw(repr(u8))]
85pub enum AclRevision {
86    /// Windows NT 4.0
87    Nt4 = 2,
88    /// Active directory
89    DS = 4,
90}
91
92#[cfg(test)]
93mod tests {
94    use crate::security::{AccessAce, AccessMask, AceFlags, AceValue, SID};
95    use std::str::FromStr;
96
97    use super::*;
98    #[test]
99    fn test_sort_acls() {
100        let fake_access_ace = AccessAce {
101            access_mask: AccessMask::new(),
102            sid: SID::from_str(SID::S_EVERYONE).unwrap(),
103        };
104        let explicit_deny_first = ACE {
105            ace_flags: AceFlags::new().with_inherited(false),
106            value: AceValue::AccessDenied(fake_access_ace.clone()),
107        };
108        let explicit_allow_second = ACE {
109            ace_flags: AceFlags::new().with_inherited(false),
110            value: AceValue::AccessAllowed(fake_access_ace.clone()),
111        };
112        // Let's make sure inherited remain untouched (in allow/deny difference)
113        let inherited_last_1 = ACE {
114            ace_flags: AceFlags::new().with_inherited(true),
115            value: AceValue::AccessAllowed(fake_access_ace.clone()),
116        };
117        let inherited_last_2 = ACE {
118            ace_flags: AceFlags::new().with_inherited(true),
119            value: AceValue::AccessDenied(fake_access_ace.clone()),
120        };
121        let dacl = ACL {
122            acl_revision: AclRevision::Nt4,
123            ace: vec![
124                inherited_last_1.clone(),      // should go third - before inherited_last_2
125                explicit_allow_second.clone(), // should go second
126                explicit_deny_first.clone(),   // should go first
127                inherited_last_2.clone(), // should stay in place - inherited_last_1 before inherited_last_2
128            ],
129        };
130
131        assert!(!dacl.is_ace_sorted());
132
133        let mut new_dacl = dacl.clone();
134        new_dacl.order_aces();
135
136        assert!(new_dacl.is_ace_sorted());
137
138        assert_eq!(
139            new_dacl,
140            ACL {
141                acl_revision: AclRevision::Nt4,
142                ace: vec![
143                    explicit_deny_first,
144                    explicit_allow_second,
145                    inherited_last_1,
146                    inherited_last_2,
147                ]
148            }
149        );
150    }
151}