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