Skip to main content

win_authorization_sys/
security.rs

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