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