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