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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use crate::{constants, wrappers, Ace, Trustee};
use std::fmt;
use std::io;
use winapi::shared::winerror::ERROR_INVALID_PARAMETER;
use winapi::um::winnt::ACL;
/// An entry in an access control list (ACL).
#[repr(C)]
pub struct Acl {
_opaque: [u8; 0],
}
impl Acl {
fn internal_type_reference(&self) -> &ACL {
unsafe { &*(self as *const _ as *const _) }
}
/// Determine what rights the given `Trustee` has under this ACL
///
/// ```
/// use windows_permissions::{LocalBox, Trustee, Sid, SecurityDescriptor};
/// use windows_permissions::constants::AccessRights;
///
/// // Allow a particular user FA (File All) and give all users FR (File Read)
/// let sd = "D:(A;;FA;;;S-1-5-20-12345)(A;;FR;;;WD)"
/// .parse::<LocalBox<SecurityDescriptor>>().unwrap();
/// let acl = sd.dacl().unwrap();
///
/// let sid1: LocalBox<Sid> = "S-1-5-20-12345".parse().unwrap();
/// let sid2: LocalBox<Sid> = "WD".parse().unwrap();
///
/// let trustee1: Trustee = sid1.as_ref().into();
/// let trustee2: Trustee = sid2.as_ref().into();
///
/// assert_eq!(acl.effective_rights(&trustee1).unwrap(), AccessRights::FileAllAccess);
/// assert_eq!(acl.effective_rights(&trustee2).unwrap(), AccessRights::FileGenericRead);
/// ```
pub fn effective_rights(&self, trustee: &Trustee) -> io::Result<constants::AccessRights> {
wrappers::GetEffectiveRightsFromAcl(self, trustee)
}
/// Determine the number of ACEs in this ACL
///
/// ```
/// use windows_permissions::{LocalBox, SecurityDescriptor};
///
/// let sd = "D:(A;;GA;;;S-1-5-20-12345)(A;;GR;;;WD)"
/// .parse::<LocalBox<SecurityDescriptor>>().unwrap();
///
/// assert_eq!(sd.dacl().unwrap().len(), 2);
/// ```
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u32 {
wrappers::GetAclInformationSize(self)
.expect("GetAclInformation failed on valid ACL")
.AceCount
}
/// Get an ACE by index
///
/// Returns `None` if there are too few ACEs to satisfy the request.
///
/// ```
/// use windows_permissions::{LocalBox, Sid, SecurityDescriptor};
/// use windows_permissions::constants::{AceType::*, AccessRights};
///
/// let sd = "D:(A;;GA;;;S-1-5-20-12345)(A;;GR;;;WD)"
/// .parse::<LocalBox<SecurityDescriptor>>().unwrap();
/// let acl = sd.dacl().unwrap();
///
/// let sid1: LocalBox<Sid> = "S-1-5-20-12345".parse().unwrap();
/// let sid2: LocalBox<Sid> = "WD".parse().unwrap();
///
/// assert_eq!(acl.get_ace(0).unwrap().ace_type(), ACCESS_ALLOWED_ACE_TYPE);
/// assert_eq!(acl.get_ace(0).unwrap().mask(), AccessRights::GenericAll);
/// assert_eq!(acl.get_ace(0).unwrap().sid(), Some(&*sid1));
///
/// assert_eq!(acl.get_ace(1).unwrap().ace_type(), ACCESS_ALLOWED_ACE_TYPE);
/// assert_eq!(acl.get_ace(1).unwrap().mask(), AccessRights::GenericRead);
/// assert_eq!(acl.get_ace(1).unwrap().sid(), Some(&*sid2));
///
/// assert!(acl.get_ace(2).is_none());
/// ```
pub fn get_ace(&self, index: u32) -> Option<&Ace> {
match wrappers::GetAce(self, index) {
Ok(ace) => Some(ace),
Err(ref e) if e.raw_os_error() == Some(ERROR_INVALID_PARAMETER as i32) => None,
other_err => {
other_err.expect("GetAce returned error on valid Ace");
unreachable!() // Because other_err will always fail the expect
}
}
}
/// Get the ACL's revision level
///
/// ```
/// use windows_permissions::{LocalBox, SecurityDescriptor, Acl};
/// use windows_permissions::constants::AclRevision::*;
///
/// let simple_acl_sd: LocalBox<SecurityDescriptor> = "D:(A;;;;;WD)".parse().unwrap();
/// let complex_acl_sd: LocalBox<SecurityDescriptor> = "D:(OA;;;294be2fb-d1ca-4aa2-aa06-ab98a8b5556d;;WD)".parse().unwrap();
///
/// assert_eq!(simple_acl_sd.dacl().unwrap().revision_level(), ACL_REVISION);
/// assert_eq!(complex_acl_sd.dacl().unwrap().revision_level(), ACL_REVISION_DS);
/// ```
pub fn revision_level(&self) -> constants::AclRevision {
constants::AclRevision::from_raw(self.internal_type_reference().AclRevision)
.expect("Unknown revision level")
}
}
impl fmt::Debug for Acl {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut map = fmt.debug_map();
map.entry(&"len", &self.len());
map.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::constants::AceType;
use crate::{LocalBox, SecurityDescriptor};
#[test]
fn get_len() -> io::Result<()> {
let limit = 100;
for dacl_count in 0..limit {
let sacl_count = limit - dacl_count - 1;
// Looks like "D:(A;;;;;WD)(A;;;;;WD)(...)S:(AU;;;;;WD)(...)"
// A (SDDL_ACCESS_ALLOWED) isn't valid for SACLs, AU (SDDL_AUDIT) is valid
let mut sddl_string = String::new();
sddl_string.push_str("D:");
sddl_string.push_str(&"(A;;;;;WD)".repeat(dacl_count));
sddl_string.push_str("S:");
sddl_string.push_str(&"(AU;;;;;WD)".repeat(sacl_count));
let sd: LocalBox<SecurityDescriptor> = sddl_string.parse()?;
assert_eq!(sd.dacl().unwrap().len(), dacl_count as u32);
assert_eq!(sd.sacl().unwrap().len(), sacl_count as u32);
}
Ok(())
}
#[test]
fn get_from_sddl() -> io::Result<()> {
let mut sddl = "D:".to_string();
let limit = 10;
for i in 0..limit {
sddl.push_str(&format!("(A;;;;;S-1-5-{})", i));
}
let sd: LocalBox<SecurityDescriptor> = sddl.parse()?;
let dacl = sd.dacl().unwrap();
// Try to get each one
for i in 0..limit {
let ace = dacl.get_ace(i).unwrap();
assert_eq!(ace.ace_type(), AceType::ACCESS_ALLOWED_ACE_TYPE);
}
// Off the end
assert!(dacl.get_ace(limit).is_none());
Ok(())
}
}