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