Skip to main content

windows_namespace_request_sys/
security.rs

1// Copyright (c) Mike Grier.
2
3//! Owned capture of a caller's `lpSecurityAttributes`.
4//!
5//! A `SECURITY_ATTRIBUTES` is not a value. It points at a security descriptor,
6//! and that descriptor may itself be **absolute**: a structure of raw pointers
7//! to an owner SID, a group SID, a DACL and a SACL that are quite possibly on
8//! the caller's stack. Carrying one to another thread by copying the struct
9//! would carry a set of dangling pointers.
10//!
11//! Capture therefore normalises to the **self-relative** form, in which every
12//! part lives at an offset inside one contiguous blob, and owns that blob.
13
14use std::ffi::c_void;
15use std::fmt;
16use std::io;
17use std::ptr;
18
19use windows_sys::Win32::Foundation::{ERROR_INSUFFICIENT_BUFFER, FALSE, TRUE};
20use windows_sys::Win32::Security::{
21    ACL, GetSecurityDescriptorControl, GetSecurityDescriptorDacl, GetSecurityDescriptorLength,
22    GetSecurityDescriptorSacl, IsValidSecurityDescriptor, MakeSelfRelativeSD, PSECURITY_DESCRIPTOR,
23    SE_SELF_RELATIVE, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR_CONTROL,
24};
25
26use crate::buffer::AlignedBuffer;
27
28/// A self-relative security descriptor must be DWORD-aligned.
29///
30/// Windows states this as a requirement on the buffer a self-relative
31/// descriptor lives in, not as a property of any one field, which is why it is
32/// enforced on the whole blob.
33const SELF_RELATIVE_ALIGNMENT: usize = align_of::<u32>();
34
35/// Why a caller's security attributes could not be captured.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37#[non_exhaustive]
38pub enum SecurityCaptureFailure {
39    /// Windows rejected the descriptor as malformed.
40    ///
41    /// Reported at construction, on the calling thread, rather than left for
42    /// the eventual call to fail with it on a worker.
43    InvalidDescriptor,
44    /// Windows could not report the descriptor's control flags, so whether it
45    /// was absolute or self-relative could not be established.
46    ReadControl,
47    /// Windows could not size the self-relative form of an absolute
48    /// descriptor.
49    SizeSelfRelative,
50    /// Windows could not convert an absolute descriptor to self-relative form.
51    MakeSelfRelative,
52    /// The captured copy did not validate, which would mean the conversion
53    /// produced something Windows will not accept later.
54    InvalidCopy,
55    /// Windows could not report the descriptor's DACL or SACL.
56    ReadAcl,
57}
58
59/// A synchronous failure while capturing a caller's security attributes.
60#[derive(Debug)]
61pub struct SecurityCaptureError {
62    failure: SecurityCaptureFailure,
63    source: io::Error,
64}
65
66impl SecurityCaptureError {
67    fn new(failure: SecurityCaptureFailure, source: io::Error) -> Self {
68        Self { failure, source }
69    }
70
71    fn last_os(failure: SecurityCaptureFailure) -> Self {
72        Self::new(failure, io::Error::last_os_error())
73    }
74
75    /// Why the capture failed.
76    #[must_use]
77    pub fn failure(&self) -> SecurityCaptureFailure {
78        self.failure
79    }
80
81    /// The underlying Win32 error code.
82    #[must_use]
83    pub fn raw_os_error(&self) -> Option<i32> {
84        self.source.raw_os_error()
85    }
86}
87
88impl fmt::Display for SecurityCaptureError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        let stage = match self.failure {
91            SecurityCaptureFailure::InvalidDescriptor => "IsValidSecurityDescriptor",
92            SecurityCaptureFailure::ReadControl => "GetSecurityDescriptorControl",
93            SecurityCaptureFailure::SizeSelfRelative => "MakeSelfRelativeSD (sizing)",
94            SecurityCaptureFailure::MakeSelfRelative => "MakeSelfRelativeSD",
95            SecurityCaptureFailure::InvalidCopy => "IsValidSecurityDescriptor (captured copy)",
96            SecurityCaptureFailure::ReadAcl => "GetSecurityDescriptorDacl",
97        };
98
99        write!(f, "{stage}: {}", self.source)
100    }
101}
102
103impl std::error::Error for SecurityCaptureError {
104    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
105        Some(&self.source)
106    }
107}
108
109/// What a descriptor says about one of its access-control lists.
110///
111/// The three states that look alike and are not are kept apart here, because
112/// collapsing any pair of them silently changes what the resulting object
113/// permits.
114///
115/// # Example
116///
117/// The pair that matters most: a **NULL** DACL grants everyone complete access
118/// and an **empty** one grants nobody anything. They are opposites, so an
119/// `Option<Acl>` that flattened them would not lose detail -- it would invert
120/// the grant.
121///
122/// ```
123/// use windows_namespace_request_sys::AclState;
124///
125/// let absent = AclState::Absent;
126/// let null = AclState::Null;
127/// let empty = AclState::Empty;
128///
129/// assert_ne!(null, empty, "NULL allows all; empty allows none");
130/// assert_ne!(absent, null);
131/// assert_ne!(absent, empty);
132///
133/// // A populated list also reports how many entries it carries.
134/// assert_eq!(AclState::Populated(1), AclState::Populated(1));
135/// assert_ne!(AclState::Populated(1), AclState::Populated(2));
136/// ```
137#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
138pub enum AclState {
139    /// The descriptor carries no list at all. The object takes its default.
140    Absent,
141    /// The descriptor carries a **NULL** list. For a DACL this grants everyone
142    /// complete access -- the opposite of what [`Empty`](Self::Empty) does.
143    Null,
144    /// The descriptor carries a list with no entries. For a DACL this grants
145    /// nobody any access.
146    Empty,
147    /// The descriptor carries a list with this many entries.
148    Populated(u32),
149}
150
151/// An owned, self-relative copy of a caller's security descriptor.
152///
153/// Whatever form the caller supplied, this holds the self-relative form in one
154/// contiguous, DWORD-aligned blob, so it names nothing outside itself and can
155/// be carried to another thread.
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub struct SecurityDescriptor {
158    blob: AlignedBuffer,
159}
160
161impl SecurityDescriptor {
162    /// Captures the descriptor at `descriptor`.
163    ///
164    /// An absolute descriptor is converted to self-relative form; a
165    /// self-relative one is copied as it stands. Either way the result owns
166    /// every byte it refers to.
167    ///
168    /// # Errors
169    ///
170    /// Returns a [`SecurityCaptureError`] when Windows rejects the descriptor
171    /// as malformed, or when the conversion fails.
172    ///
173    /// # Safety
174    ///
175    /// `descriptor` must be non-null and point to a security descriptor that
176    /// stays valid for the duration of this call, including everything an
177    /// absolute descriptor points at.
178    pub unsafe fn capture(descriptor: *const c_void) -> Result<Self, SecurityCaptureError> {
179        let descriptor = descriptor.cast_mut();
180
181        // SAFETY: the caller guarantees a live descriptor.
182        if unsafe { IsValidSecurityDescriptor(descriptor) } == FALSE {
183            return Err(SecurityCaptureError::last_os(
184                SecurityCaptureFailure::InvalidDescriptor,
185            ));
186        }
187
188        let mut control: SECURITY_DESCRIPTOR_CONTROL = 0;
189        let mut revision: u32 = 0;
190        // SAFETY: as above; both out-parameters point to writable storage.
191        if unsafe { GetSecurityDescriptorControl(descriptor, &raw mut control, &raw mut revision) }
192            == FALSE
193        {
194            return Err(SecurityCaptureError::last_os(
195                SecurityCaptureFailure::ReadControl,
196            ));
197        }
198
199        let blob = if control & SE_SELF_RELATIVE == 0 {
200            unsafe { Self::convert_absolute(descriptor) }?
201        } else {
202            unsafe { Self::copy_self_relative(descriptor) }
203        };
204
205        let captured = Self { blob };
206
207        // SAFETY: the blob holds a self-relative descriptor of the length
208        // Windows reported or wrote.
209        if unsafe { IsValidSecurityDescriptor(captured.as_ptr().cast_mut()) } == FALSE {
210            return Err(SecurityCaptureError::last_os(
211                SecurityCaptureFailure::InvalidCopy,
212            ));
213        }
214
215        Ok(captured)
216    }
217
218    /// # Safety
219    ///
220    /// `descriptor` must be a live, valid, self-relative descriptor.
221    unsafe fn copy_self_relative(descriptor: PSECURITY_DESCRIPTOR) -> AlignedBuffer {
222        // SAFETY: for a self-relative descriptor this reports the length of the
223        // whole blob, which is exactly what must be copied.
224        let length = unsafe { GetSecurityDescriptorLength(descriptor) } as usize;
225
226        let mut blob = AlignedBuffer::zeroed(length, SELF_RELATIVE_ALIGNMENT);
227        // SAFETY: the source is valid for `length` bytes by the contract above,
228        // the destination was just allocated with that length, and the two
229        // allocations cannot overlap.
230        unsafe {
231            ptr::copy_nonoverlapping(descriptor.cast::<u8>(), blob.as_mut_ptr(), length);
232        }
233
234        blob
235    }
236
237    /// # Safety
238    ///
239    /// `descriptor` must be a live, valid, absolute descriptor, including
240    /// everything it points at.
241    unsafe fn convert_absolute(
242        descriptor: PSECURITY_DESCRIPTOR,
243    ) -> Result<AlignedBuffer, SecurityCaptureError> {
244        let mut length: u32 = 0;
245        // SAFETY: a null destination with a zero length is the documented way
246        // to ask for the required size; it is expected to fail.
247        let sized = unsafe { MakeSelfRelativeSD(descriptor, ptr::null_mut(), &raw mut length) };
248        if sized != FALSE {
249            // Windows reported success without being given a buffer, which
250            // contradicts its own contract; treat the size as unusable.
251            return Err(SecurityCaptureError::new(
252                SecurityCaptureFailure::SizeSelfRelative,
253                io::Error::from_raw_os_error(
254                    i32::try_from(ERROR_INSUFFICIENT_BUFFER)
255                        .expect("ERROR_INSUFFICIENT_BUFFER fits in i32"),
256                ),
257            ));
258        }
259
260        let error = io::Error::last_os_error();
261        if error.raw_os_error()
262            != Some(
263                i32::try_from(ERROR_INSUFFICIENT_BUFFER)
264                    .expect("ERROR_INSUFFICIENT_BUFFER fits in i32"),
265            )
266        {
267            return Err(SecurityCaptureError::new(
268                SecurityCaptureFailure::SizeSelfRelative,
269                error,
270            ));
271        }
272
273        let mut blob = AlignedBuffer::zeroed(length as usize, SELF_RELATIVE_ALIGNMENT);
274        // SAFETY: blob is writable for exactly the length Windows asked for.
275        let converted = unsafe {
276            MakeSelfRelativeSD(
277                descriptor,
278                blob.as_mut_ptr().cast::<c_void>(),
279                &raw mut length,
280            )
281        };
282        if converted == FALSE {
283            return Err(SecurityCaptureError::last_os(
284                SecurityCaptureFailure::MakeSelfRelative,
285            ));
286        }
287
288        Ok(blob)
289    }
290
291    /// The captured descriptor's address, for handing to a Win32 call.
292    ///
293    /// The pointer borrows from this value and must not outlive it.
294    #[must_use]
295    pub fn as_ptr(&self) -> *const c_void {
296        self.blob.as_ptr().cast::<c_void>()
297    }
298
299    /// The captured descriptor's length in bytes.
300    #[must_use]
301    pub fn len(&self) -> usize {
302        self.blob.len()
303    }
304
305    /// Whether the captured descriptor is empty, which a valid one never is.
306    #[must_use]
307    pub fn is_empty(&self) -> bool {
308        self.blob.is_empty()
309    }
310
311    /// The captured descriptor's bytes.
312    #[must_use]
313    pub fn as_bytes(&self) -> &[u8] {
314        self.blob.as_slice()
315    }
316
317    /// What the descriptor says about its discretionary access-control list.
318    ///
319    /// # Errors
320    ///
321    /// Returns a [`SecurityCaptureError`] if Windows will not report the list,
322    /// which a descriptor that validated at capture should not do.
323    pub fn dacl(&self) -> Result<AclState, SecurityCaptureError> {
324        let mut present = FALSE;
325        let mut acl: *mut ACL = ptr::null_mut();
326        let mut defaulted = FALSE;
327
328        // SAFETY: the blob holds a descriptor that validated at capture, and
329        // all three out-parameters point to writable storage.
330        let read = unsafe {
331            GetSecurityDescriptorDacl(
332                self.as_ptr().cast_mut(),
333                &raw mut present,
334                &raw mut acl,
335                &raw mut defaulted,
336            )
337        };
338
339        Self::acl_state(read, present, acl)
340    }
341
342    /// What the descriptor says about its system access-control list.
343    ///
344    /// # Errors
345    ///
346    /// As [`dacl`](Self::dacl).
347    pub fn sacl(&self) -> Result<AclState, SecurityCaptureError> {
348        let mut present = FALSE;
349        let mut acl: *mut ACL = ptr::null_mut();
350        let mut defaulted = FALSE;
351
352        // SAFETY: as dacl.
353        let read = unsafe {
354            GetSecurityDescriptorSacl(
355                self.as_ptr().cast_mut(),
356                &raw mut present,
357                &raw mut acl,
358                &raw mut defaulted,
359            )
360        };
361
362        Self::acl_state(read, present, acl)
363    }
364
365    fn acl_state(
366        read: i32,
367        present: i32,
368        acl: *const ACL,
369    ) -> Result<AclState, SecurityCaptureError> {
370        if read == FALSE {
371            return Err(SecurityCaptureError::last_os(
372                SecurityCaptureFailure::ReadAcl,
373            ));
374        }
375
376        if present != TRUE {
377            return Ok(AclState::Absent);
378        }
379
380        // SAFETY: Windows either left the pointer null or pointed it at an ACL
381        // inside the descriptor blob this method borrows.
382        let Some(acl) = (unsafe { acl.as_ref() }) else {
383            return Ok(AclState::Null);
384        };
385
386        Ok(match u32::from(acl.AceCount) {
387            0 => AclState::Empty,
388            count => AclState::Populated(count),
389        })
390    }
391}
392
393/// An owned capture of a caller's `lpSecurityAttributes` argument.
394///
395/// Three outcomes that a single nullable pointer runs together are kept apart
396/// by this type and [`AclState`], because they are three different grants:
397///
398/// | Caller passed | Meaning | Represented as |
399/// |---|---|---|
400/// | `NULL` attributes | default security, non-inheritable handle | `None` where a `SecurityAttributes` is expected |
401/// | attributes with a `NULL` descriptor | default security, caller's inheritance choice | [`descriptor`](Self::descriptor) is `None` |
402/// | attributes with a descriptor | the caller's security | [`descriptor`](Self::descriptor) is `Some` |
403///
404/// and within a descriptor, an absent DACL, a NULL DACL, and an empty DACL are
405/// three further distinct grants, reported by [`SecurityDescriptor::dacl`].
406///
407/// # Example
408///
409/// The distinction a single nullable pointer runs together. Passing no
410/// attributes at all and passing attributes that carry no descriptor are
411/// different requests -- the second still states an inheritance choice:
412///
413/// ```
414/// use windows_namespace_request_sys::SecurityAttributes;
415///
416/// // Attributes with no descriptor: default security, but the caller's
417/// // inheritance choice is still carried.
418/// let inheritable = SecurityAttributes::new(None, true);
419/// assert!(inheritable.descriptor().is_none());
420/// assert!(inheritable.inherit_handle());
421///
422/// let raw = inheritable.to_raw();
423/// assert!(raw.lpSecurityDescriptor.is_null());
424/// assert_ne!(raw.bInheritHandle, 0, "the choice survives into the Win32 struct");
425///
426/// // Passing *no* attributes is the third case, and is spelled `None` where a
427/// // `SecurityAttributes` is expected rather than being confused with this.
428/// let none: Option<SecurityAttributes> = None;
429/// assert!(none.is_none());
430/// ```
431#[derive(Clone, Debug, PartialEq, Eq)]
432pub struct SecurityAttributes {
433    descriptor: Option<SecurityDescriptor>,
434    inherit_handle: bool,
435}
436
437impl SecurityAttributes {
438    /// Builds a capture from an already-owned descriptor and an inheritance
439    /// choice.
440    #[must_use]
441    pub fn new(descriptor: Option<SecurityDescriptor>, inherit_handle: bool) -> Self {
442        Self {
443            descriptor,
444            inherit_handle,
445        }
446    }
447
448    /// Captures the `SECURITY_ATTRIBUTES` at `attributes`.
449    ///
450    /// A null `attributes` is not an error: it is the caller declining to
451    /// supply any, which is reported as `Ok(None)` rather than being confused
452    /// with attributes that carry no descriptor.
453    ///
454    /// # Errors
455    ///
456    /// Returns a [`SecurityCaptureError`] when the referenced descriptor cannot
457    /// be captured.
458    ///
459    /// # Safety
460    ///
461    /// `attributes`, if non-null, must point to a `SECURITY_ATTRIBUTES` that
462    /// stays valid for the duration of this call, as must any descriptor it
463    /// names.
464    pub unsafe fn capture(
465        attributes: *const SECURITY_ATTRIBUTES,
466    ) -> Result<Option<Self>, SecurityCaptureError> {
467        // SAFETY: the caller guarantees a live pointer or null.
468        let Some(attributes) = (unsafe { attributes.as_ref() }) else {
469            return Ok(None);
470        };
471
472        let descriptor = if attributes.lpSecurityDescriptor.is_null() {
473            None
474        } else {
475            // SAFETY: non-null, and live for this call by the contract above.
476            Some(unsafe { SecurityDescriptor::capture(attributes.lpSecurityDescriptor) }?)
477        };
478
479        Ok(Some(Self {
480            descriptor,
481            inherit_handle: attributes.bInheritHandle != FALSE,
482        }))
483    }
484
485    /// The captured descriptor, if the caller supplied one.
486    #[must_use]
487    pub fn descriptor(&self) -> Option<&SecurityDescriptor> {
488        self.descriptor.as_ref()
489    }
490
491    /// Whether the caller asked for the resulting handle to be inheritable.
492    #[must_use]
493    pub fn inherit_handle(&self) -> bool {
494        self.inherit_handle
495    }
496
497    /// Rebuilds the `SECURITY_ATTRIBUTES` to pass to a Win32 call.
498    ///
499    /// The result points into this value and must not outlive it.
500    #[must_use]
501    pub fn to_raw(&self) -> SECURITY_ATTRIBUTES {
502        SECURITY_ATTRIBUTES {
503            nLength: u32::try_from(size_of::<SECURITY_ATTRIBUTES>())
504                .expect("SECURITY_ATTRIBUTES is far smaller than u32::MAX"),
505            lpSecurityDescriptor: self
506                .descriptor
507                .as_ref()
508                .map_or(ptr::null_mut(), |descriptor| descriptor.as_ptr().cast_mut()),
509            bInheritHandle: if self.inherit_handle { TRUE } else { FALSE },
510        }
511    }
512}
513
514// Visible to the crate's own cross-module tests, which reuse this module's
515// absolute-descriptor builder rather than standing up a second copy of it.
516#[cfg(test)]
517pub(crate) mod tests;