Skip to main content

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}