win_authorization_sys/security/sddl.rs
1// Copyright (c) Mike Grier.
2// Split from crates/windows-namespace-request-sys/src/security/sddl.rs at 11020848.
3
4//! SDDL text to and from a [`SecurityDescriptor`].
5//!
6//! Both directions are public, so a caller can build a descriptor from the text form and read one
7//! back as text, and neither needs a raw pointer. The conversion is the documented
8//! `ConvertStringSecurityDescriptorToSecurityDescriptorW` and its reverse,
9//! `ConvertSecurityDescriptorToStringSecurityDescriptorW`; what they return is memory Windows
10//! allocated with `LocalAlloc`, which is copied into this crate's own owned type or string and then
11//! freed with `LocalFree`, so nothing the caller holds points at it.
12//!
13//! The text form is not canonical: two strings can describe the same descriptor, and a descriptor
14//! read back as text is Windows' rendering of it, not necessarily the string it was built from.
15
16use std::ops::BitOr;
17use std::ptr;
18use std::slice;
19
20use win_error_codes::Win32Error;
21use windows_sys::Win32::Foundation::{ERROR_INVALID_PARAMETER, FALSE, LocalFree};
22use windows_sys::Win32::Security::Authorization::{
23 ConvertSecurityDescriptorToStringSecurityDescriptorW,
24 ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,
25};
26use windows_sys::Win32::Security::{
27 DACL_SECURITY_INFORMATION, GROUP_SECURITY_INFORMATION, LABEL_SECURITY_INFORMATION,
28 OBJECT_SECURITY_INFORMATION, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR,
29 SACL_SECURITY_INFORMATION,
30};
31use wtf_string::Wtf16String;
32
33use super::{SecurityCaptureError, SecurityCaptureFailure, SecurityDescriptor};
34
35#[cfg(test)]
36mod tests;
37
38/// Which parts of a descriptor to write as text.
39///
40/// A newtype over `OBJECT_SECURITY_INFORMATION`, because the value is a combination and Windows
41/// defines more bits than are named here. Nothing is defaulted on a caller's behalf: a mask that
42/// selects nothing is Windows' to answer.
43///
44/// # Example
45///
46/// ```
47/// use win_authorization_sys::SecurityInformation;
48///
49/// let both = SecurityInformation::OWNER | SecurityInformation::DACL;
50/// assert!(both.contains(SecurityInformation::OWNER));
51/// assert!(both.contains(SecurityInformation::DACL));
52/// assert!(!both.contains(SecurityInformation::SACL));
53/// ```
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
55pub struct SecurityInformation(OBJECT_SECURITY_INFORMATION);
56
57impl SecurityInformation {
58 /// No part: what Windows makes of an empty selection is its to say.
59 pub const NONE: Self = Self(0);
60
61 /// `OWNER_SECURITY_INFORMATION`: the owner SID.
62 pub const OWNER: Self = Self(OWNER_SECURITY_INFORMATION);
63
64 /// `GROUP_SECURITY_INFORMATION`: the primary group SID.
65 pub const GROUP: Self = Self(GROUP_SECURITY_INFORMATION);
66
67 /// `DACL_SECURITY_INFORMATION`: the discretionary access-control list.
68 pub const DACL: Self = Self(DACL_SECURITY_INFORMATION);
69
70 /// `SACL_SECURITY_INFORMATION`: the system access-control list.
71 pub const SACL: Self = Self(SACL_SECURITY_INFORMATION);
72
73 /// `LABEL_SECURITY_INFORMATION`: the mandatory integrity label.
74 pub const LABEL: Self = Self(LABEL_SECURITY_INFORMATION);
75
76 /// A selection from its raw bits, for a part Windows defines that this crate has no name for.
77 #[must_use]
78 pub const fn from_bits(bits: OBJECT_SECURITY_INFORMATION) -> Self {
79 Self(bits)
80 }
81
82 /// The raw bits.
83 #[must_use]
84 pub const fn bits(self) -> OBJECT_SECURITY_INFORMATION {
85 self.0
86 }
87
88 /// Whether every bit of `other` is set. Everything contains [`NONE`](Self::NONE).
89 #[must_use]
90 pub const fn contains(self, other: Self) -> bool {
91 self.0 & other.0 == other.0
92 }
93}
94
95impl BitOr for SecurityInformation {
96 type Output = Self;
97
98 fn bitor(self, other: Self) -> Self {
99 Self(self.0 | other.0)
100 }
101}
102
103/// Memory Windows allocated with `LocalAlloc` and handed to this crate, freed when it goes out of
104/// scope, so no return path can leak it.
105pub(super) struct LocalAllocation(pub(super) *mut core::ffi::c_void);
106
107impl Drop for LocalAllocation {
108 fn drop(&mut self) {
109 if !self.0.is_null() {
110 // SAFETY: the pointer is one Windows allocated with `LocalAlloc` for this crate to
111 // free, and it is freed exactly once, here.
112 unsafe { LocalFree(self.0) };
113 }
114 }
115}
116
117impl SecurityDescriptor {
118 /// A descriptor from its SDDL text, such as `D:P(A;;FA;;;WD)`.
119 ///
120 /// The result is the self-relative form this type always holds, copied out of the memory
121 /// Windows allocated for it, which is then freed.
122 ///
123 /// The string is not interpreted here: Windows parses it, accepts what it defines and refuses
124 /// the rest, and a string with no parts, such as an empty one, is its to answer. A string with
125 /// an interior NUL is refused before the call, because Windows would read only up to it and
126 /// build a different descriptor than the string says.
127 ///
128 /// # Example
129 ///
130 /// A DACL granting everyone full access, read back through the descriptor:
131 ///
132 /// ```
133 /// use win_authorization_sys::{AclState, SecurityDescriptor};
134 /// use wtf_string::Wtf16String;
135 ///
136 /// let descriptor = SecurityDescriptor::from_sddl(&Wtf16String::from("D:P(A;;GA;;;WD)"))?;
137 /// assert_eq!(descriptor.dacl()?, AclState::Populated(1));
138 /// # Ok::<(), Box<dyn std::error::Error>>(())
139 /// ```
140 ///
141 /// # Errors
142 ///
143 /// [`SecurityCaptureFailure::InvalidSddl`] with Windows' own code, unaltered, when it refuses
144 /// the string; [`SecurityCaptureFailure::InteriorNul`] for a string with an interior NUL; or
145 /// the failures of capturing the descriptor Windows returned, including
146 /// [`SecurityCaptureFailure::Allocate`] when the memory for the copy cannot be allocated.
147 pub fn from_sddl(sddl: &Wtf16String) -> Result<Self, SecurityCaptureError> {
148 if sddl.has_interior_nul() {
149 return Err(SecurityCaptureError::new(
150 SecurityCaptureFailure::InteriorNul,
151 Win32Error::from_code(ERROR_INVALID_PARAMETER).to_io_error(),
152 ));
153 }
154
155 let mut raw: PSECURITY_DESCRIPTOR = ptr::null_mut();
156 // SAFETY: the string is terminated and has no interior NUL, and is borrowed for the call;
157 // `raw` is a writable pointer for the descriptor, and a null size is accepted.
158 let converted = unsafe {
159 ConvertStringSecurityDescriptorToSecurityDescriptorW(
160 sddl.as_terminated_ptr(),
161 SDDL_REVISION_1,
162 &raw mut raw,
163 ptr::null_mut(),
164 )
165 };
166 if converted == FALSE {
167 return Err(SecurityCaptureError::last_os(
168 SecurityCaptureFailure::InvalidSddl,
169 ));
170 }
171
172 let allocation = LocalAllocation(raw);
173 // SAFETY: Windows returned a valid self-relative descriptor in memory that stays allocated
174 // until `allocation` is dropped, after the capture has copied out of it.
175 let captured = unsafe { Self::capture(allocation.0) };
176 drop(allocation);
177 captured
178 }
179
180 /// The descriptor's parts selected by `information`, as SDDL text.
181 ///
182 /// Windows' rendering of the descriptor, which is not necessarily the string it was built
183 /// from: the text form is not canonical.
184 ///
185 /// # Example
186 ///
187 /// ```
188 /// use win_authorization_sys::{SecurityDescriptor, SecurityInformation};
189 /// use wtf_string::Wtf16String;
190 ///
191 /// let descriptor = SecurityDescriptor::from_sddl(&Wtf16String::from("D:P(A;;GA;;;WD)"))?;
192 /// let text = descriptor.to_sddl(SecurityInformation::DACL)?;
193 /// assert_eq!(text.to_string_lossy(), "D:P(A;;GA;;;WD)");
194 /// # Ok::<(), Box<dyn std::error::Error>>(())
195 /// ```
196 ///
197 /// The length Windows reports for the text is not used. It is documented to count the
198 /// terminator, and was measured to be the string's length plus one, plus two, and plus none,
199 /// depending on the string; the text is read up to the terminator the documentation promises.
200 ///
201 /// # Errors
202 ///
203 /// [`SecurityCaptureFailure::ToSddl`] with Windows' own code, unaltered, when it refuses.
204 pub fn to_sddl(
205 &self,
206 information: SecurityInformation,
207 ) -> Result<Wtf16String, SecurityCaptureError> {
208 let mut text: *mut u16 = ptr::null_mut();
209 // SAFETY: the descriptor validated at capture and is borrowed for the call; the text
210 // pointer is writable, and a null length is accepted.
211 let converted = unsafe {
212 ConvertSecurityDescriptorToStringSecurityDescriptorW(
213 self.as_ptr().cast_mut(),
214 SDDL_REVISION_1,
215 information.bits(),
216 &raw mut text,
217 ptr::null_mut(),
218 )
219 };
220 if converted == FALSE {
221 return Err(SecurityCaptureError::last_os(
222 SecurityCaptureFailure::ToSddl,
223 ));
224 }
225
226 let allocation = LocalAllocation(text.cast());
227 let mut count = 0_usize;
228 // SAFETY: Windows returned a NUL-terminated string, which stays allocated until
229 // `allocation` is dropped, after this has been copied; the read stops at the terminator.
230 while unsafe { *text.add(count) } != 0 {
231 count += 1;
232 }
233 // SAFETY: the `count` code units before the terminator were just read.
234 let units = unsafe { slice::from_raw_parts(text, count) };
235 let result = Wtf16String::from_units(units);
236 drop(allocation);
237 Ok(result)
238 }
239}