win_authorization_sys/security/query.rs
1// Copyright (c) Mike Grier.
2
3//! The security descriptor of an open handle: `GetSecurityInfo`.
4//!
5//! Windows allocates the descriptor it returns with `LocalAlloc`. It is copied into this crate's own
6//! owned [`SecurityDescriptor`] and the allocation freed, so nothing the caller holds points at it.
7
8use std::os::windows::io::{AsRawHandle, BorrowedHandle};
9use std::ptr;
10
11use win_error_codes::Win32Error;
12use windows_sys::Win32::Foundation::{ERROR_INVALID_DATA, ERROR_INVALID_PARAMETER, ERROR_SUCCESS};
13use windows_sys::Win32::Security::Authorization::{
14 GetSecurityInfo, SE_DS_OBJECT, SE_DS_OBJECT_ALL, SE_FILE_OBJECT, SE_KERNEL_OBJECT, SE_LMSHARE,
15 SE_OBJECT_TYPE, SE_PRINTER, SE_PROVIDER_DEFINED_OBJECT, SE_REGISTRY_KEY,
16 SE_REGISTRY_WOW64_32KEY, SE_REGISTRY_WOW64_64KEY, SE_SERVICE, SE_UNKNOWN_OBJECT_TYPE,
17 SE_WINDOW_OBJECT, SE_WMIGUID_OBJECT,
18};
19use windows_sys::Win32::Security::PSECURITY_DESCRIPTOR;
20
21use super::sddl::LocalAllocation;
22use super::{
23 SecurityCaptureError, SecurityCaptureFailure, SecurityDescriptor, SecurityInformation,
24};
25
26#[cfg(test)]
27mod tests;
28
29/// The kind of object a handle names, which `GetSecurityInfo` needs to know to find its security.
30///
31/// A newtype over `SE_OBJECT_TYPE`, because Windows defines more kinds than are named here and this
32/// crate removes none of them. [`get_security_info`] passes only the kinds
33/// [`reads_any_handle`](Self::reads_any_handle) names; [`get_security_info_unchecked`] takes every
34/// kind, from a caller who knows the kind of the object the handle names.
35///
36/// # Example
37///
38/// ```
39/// use win_authorization_sys::ObjectType;
40///
41/// assert_ne!(ObjectType::FILE, ObjectType::KERNEL_OBJECT);
42/// assert_eq!(ObjectType::from_raw(ObjectType::FILE.as_raw()), ObjectType::FILE);
43/// ```
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
45pub struct ObjectType(SE_OBJECT_TYPE);
46
47impl ObjectType {
48 /// `SE_UNKNOWN_OBJECT_TYPE`.
49 pub const UNKNOWN: Self = Self(SE_UNKNOWN_OBJECT_TYPE);
50 /// `SE_FILE_OBJECT`: a file or directory, or a named pipe or other object a file handle names.
51 pub const FILE: Self = Self(SE_FILE_OBJECT);
52 /// `SE_SERVICE`: a Windows service.
53 pub const SERVICE: Self = Self(SE_SERVICE);
54 /// `SE_PRINTER`: a printer.
55 pub const PRINTER: Self = Self(SE_PRINTER);
56 /// `SE_REGISTRY_KEY`: a registry key.
57 pub const REGISTRY_KEY: Self = Self(SE_REGISTRY_KEY);
58 /// `SE_LMSHARE`: a network share.
59 pub const LMSHARE: Self = Self(SE_LMSHARE);
60 /// `SE_KERNEL_OBJECT`: a kernel object that has no kind of its own here, such as an event, a
61 /// mutex, a semaphore, a process or a thread.
62 pub const KERNEL_OBJECT: Self = Self(SE_KERNEL_OBJECT);
63 /// `SE_WINDOW_OBJECT`: a window station or desktop.
64 pub const WINDOW_OBJECT: Self = Self(SE_WINDOW_OBJECT);
65 /// `SE_DS_OBJECT`: a directory service object.
66 pub const DS_OBJECT: Self = Self(SE_DS_OBJECT);
67 /// `SE_DS_OBJECT_ALL`: a directory service object and its properties.
68 pub const DS_OBJECT_ALL: Self = Self(SE_DS_OBJECT_ALL);
69 /// `SE_PROVIDER_DEFINED_OBJECT`: an object of a provider's own kind.
70 pub const PROVIDER_DEFINED: Self = Self(SE_PROVIDER_DEFINED_OBJECT);
71 /// `SE_WMIGUID_OBJECT`: a WMI object.
72 pub const WMIGUID: Self = Self(SE_WMIGUID_OBJECT);
73 /// `SE_REGISTRY_WOW64_32KEY`: a registry key in the 32-bit view.
74 pub const REGISTRY_WOW64_32KEY: Self = Self(SE_REGISTRY_WOW64_32KEY);
75 /// `SE_REGISTRY_WOW64_64KEY`: a registry key in the 64-bit view.
76 pub const REGISTRY_WOW64_64KEY: Self = Self(SE_REGISTRY_WOW64_64KEY);
77
78 /// A kind from its Win32 value.
79 #[must_use]
80 pub const fn from_raw(raw: SE_OBJECT_TYPE) -> Self {
81 Self(raw)
82 }
83
84 /// The kind's Win32 value.
85 #[must_use]
86 pub const fn as_raw(self) -> SE_OBJECT_TYPE {
87 self.0
88 }
89
90 /// Whether [`get_security_info`] passes this kind with a handle of any kind: the kinds under
91 /// which Windows was measured reading the security of the handle it is given, whatever the
92 /// handle names, and `UNKNOWN`, which it refuses with its own code. Every other kind is for
93 /// [`get_security_info_unchecked`].
94 #[must_use]
95 pub const fn reads_any_handle(self) -> bool {
96 matches!(
97 self.0,
98 SE_FILE_OBJECT | SE_KERNEL_OBJECT | SE_WINDOW_OBJECT | SE_UNKNOWN_OBJECT_TYPE
99 )
100 }
101}
102
103/// The parts `information` selects of the security descriptor of the object `handle` names, as an
104/// owned [`SecurityDescriptor`]: `GetSecurityInfo`, for the kinds that read any handle.
105///
106/// `GetSecurityInfo` reads `ObjectType` as the kind of the object the handle names, and a
107/// [`BorrowedHandle`] says nothing about that kind. So this safe entry passes only the kinds
108/// [`ObjectType::reads_any_handle`] names -- file, kernel object, window object, and `UNKNOWN` --
109/// under which Windows was measured reading whatever handle it is given. Any other kind is
110/// refused before the call, as this crate's own narrowing; a caller who knows the handle names an
111/// object of that kind uses [`get_security_info_unchecked`].
112///
113/// What is read is what the handle was opened with the right to read: `READ_CONTROL` for the owner,
114/// the group and the DACL, and `ACCESS_SYSTEM_SECURITY` for the SACL, which Windows grants only to a
115/// caller holding the privilege for it. A part the handle cannot read is Windows' refusal, with its
116/// code, and not an absent part: nothing here retries with less. Windows reads a handle's security
117/// whichever of the file, kernel-object and window kinds is named (measured), so the kind does not
118/// make a handle of another kind an error, and an object that has no descriptor reads as one with
119/// no parts. A part that was not selected is
120/// absent from the result, and one that was and the object does not have reads as an absent list
121/// ([`AclState::Absent`](crate::AclState::Absent)).
122///
123/// # Example
124///
125/// The DACL a file was created with, read back through an open handle:
126///
127/// ```
128/// use std::fs::File;
129/// use std::os::windows::io::AsHandle;
130///
131/// use win_authorization_sys::{get_security_info, AclState, ObjectType, SecurityInformation};
132///
133/// let path = std::env::temp_dir().join(format!("wa-doc-{}.tmp", std::process::id()));
134/// let file = File::create(&path)?;
135///
136/// let descriptor = get_security_info(file.as_handle(), ObjectType::FILE, SecurityInformation::DACL)?;
137/// assert!(matches!(descriptor.dacl()?, AclState::Populated(_)));
138/// # drop(file);
139/// # std::fs::remove_file(&path)?;
140/// # Ok::<(), Box<dyn std::error::Error>>(())
141/// ```
142///
143/// # Errors
144///
145/// [`SecurityCaptureFailure::UnsupportedObjectType`] for a kind
146/// [`ObjectType::reads_any_handle`] does not name, before any call is made, with
147/// `ERROR_INVALID_PARAMETER`, which this crate supplies. [`SecurityCaptureFailure::ReadSecurity`]
148/// with Windows' own code, unaltered, when it refuses the query: `ERROR_ACCESS_DENIED` for a handle
149/// opened without the right to read what was selected, `ERROR_INVALID_PARAMETER` for
150/// [`ObjectType::UNKNOWN`]. Otherwise the failures of capturing the descriptor Windows returned,
151/// including [`SecurityCaptureFailure::Allocate`] when the memory for the copy cannot be allocated.
152pub fn get_security_info(
153 handle: BorrowedHandle<'_>,
154 object_type: ObjectType,
155 information: SecurityInformation,
156) -> Result<SecurityDescriptor, SecurityCaptureError> {
157 if !object_type.reads_any_handle() {
158 return Err(SecurityCaptureError::new(
159 SecurityCaptureFailure::UnsupportedObjectType,
160 Win32Error::from_code(ERROR_INVALID_PARAMETER).to_io_error(),
161 ));
162 }
163 // SAFETY: the kind is one Windows was measured reading any open handle under, which is what
164 // the check above established.
165 unsafe { get_security_info_unchecked(handle, object_type, information) }
166}
167
168/// [`get_security_info`] for any kind, including those it does not pass: `GetSecurityInfo` with the
169/// kind as given.
170///
171/// # Errors
172///
173/// As [`get_security_info`], except that no kind is refused before the call.
174///
175/// # Safety
176///
177/// `object_type` must be the kind of the object `handle` names. The `GetSecurityInfo` page (read
178/// 2026-10-19) gives `handle` as "a handle to the object" and `ObjectType` as the value "that
179/// indicates the type of object", and says nothing of a kind that does not match; Windows does not
180/// always refuse one, and `LMSHARE` with a file handle was measured writing through a null pointer.
181/// The kinds [`ObjectType::reads_any_handle`] names have no such requirement.
182pub unsafe fn get_security_info_unchecked(
183 handle: BorrowedHandle<'_>,
184 object_type: ObjectType,
185 information: SecurityInformation,
186) -> Result<SecurityDescriptor, SecurityCaptureError> {
187 let mut descriptor: PSECURITY_DESCRIPTOR = ptr::null_mut();
188 // SAFETY: the handle is open for the borrow and names an object of the kind given, by this
189 // function's contract; the four parts that would be returned by pointer are not asked for, and
190 // `descriptor` is a writable pointer for the allocation Windows makes.
191 let status = unsafe {
192 GetSecurityInfo(
193 handle.as_raw_handle(),
194 object_type.as_raw(),
195 information.bits(),
196 ptr::null_mut(),
197 ptr::null_mut(),
198 ptr::null_mut(),
199 ptr::null_mut(),
200 &raw mut descriptor,
201 )
202 };
203 // Freed on every path below, including the capture's failure.
204 let allocation = LocalAllocation(descriptor);
205
206 if status != ERROR_SUCCESS {
207 return Err(SecurityCaptureError::new(
208 SecurityCaptureFailure::ReadSecurity,
209 Win32Error::from_code(status).to_io_error(),
210 ));
211 }
212 // Windows documents a descriptor on success, so this is not reached; it stands between a
213 // violation of that and the dereference the capture would make of it, and no test can reach it.
214 if allocation.0.is_null() {
215 return Err(SecurityCaptureError::new(
216 SecurityCaptureFailure::ReadSecurity,
217 Win32Error::from_code(ERROR_INVALID_DATA).to_io_error(),
218 ));
219 }
220
221 // SAFETY: Windows returned a valid self-relative descriptor in memory that stays allocated until
222 // `allocation` is dropped, after the capture has copied out of it.
223 unsafe { SecurityDescriptor::capture(allocation.0) }
224}