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