Skip to main content

dolang_winterop/security/
win32_security.rs

1//! Windows security-descriptor primitives shared across VFS extensions.
2//!
3//! Only [`with_security_privilege`] lives here — everything else needed to
4//! get/set a security descriptor (native self-relative byte-form
5//! conversion via [`super::SecDesc::from_bytes_with_mask`]/
6//! [`super::SecDesc::to_bytes`], and the actual Win32 API call) is either
7//! already public or specific enough to each object type (file handles vs.
8//! registry keys use different APIs entirely) that it doesn't belong here.
9
10use std::io;
11
12#[cfg(windows)]
13use std::{
14    os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle},
15    ptr,
16};
17
18#[cfg(windows)]
19use windows_sys::Win32::{
20    Foundation::{ERROR_NOT_ALL_ASSIGNED, GetLastError, SetLastError},
21    Security::{
22        AdjustTokenPrivileges, DuplicateTokenEx, LUID_AND_ATTRIBUTES, LookupPrivilegeValueW,
23        RevertToSelf, SE_PRIVILEGE_ENABLED, SE_SECURITY_NAME, SecurityImpersonation,
24        TOKEN_ADJUST_PRIVILEGES, TOKEN_DUPLICATE, TOKEN_IMPERSONATE, TOKEN_PRIVILEGES, TOKEN_QUERY,
25        TokenImpersonation,
26    },
27    System::Threading::{GetCurrentProcess, OpenProcessToken, SetThreadToken},
28};
29
30/// Runs `f` with `SeSecurityPrivilege` enabled on the current thread,
31/// reverting to the process token afterward.
32///
33/// Callers decide whether the operation requires the privilege. In particular,
34/// it is required while opening a handle for `ACCESS_SYSTEM_SECURITY` and while
35/// setting a SACL, but not while querying a SACL through a handle that already
36/// has `ACCESS_SYSTEM_SECURITY` access.
37#[cfg_attr(docsrs, doc(cfg(windows)))]
38pub fn with_security_privilege<T, E: From<io::Error>>(
39    f: impl FnOnce() -> Result<T, E>,
40) -> Result<T, E> {
41    #[cfg(windows)]
42    {
43        struct RevertGuard;
44        impl Drop for RevertGuard {
45            fn drop(&mut self) {
46                unsafe {
47                    RevertToSelf();
48                }
49            }
50        }
51
52        let mut process_token = ptr::null_mut();
53        if unsafe {
54            OpenProcessToken(
55                GetCurrentProcess(),
56                TOKEN_DUPLICATE | TOKEN_QUERY,
57                &mut process_token,
58            )
59        } == 0
60        {
61            return Err(io::Error::last_os_error().into());
62        }
63        let process_token = unsafe { OwnedHandle::from_raw_handle(process_token) };
64
65        let mut token = ptr::null_mut();
66        if unsafe {
67            DuplicateTokenEx(
68                process_token.as_raw_handle(),
69                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE,
70                ptr::null(),
71                SecurityImpersonation,
72                TokenImpersonation,
73                &mut token,
74            )
75        } == 0
76        {
77            return Err(io::Error::last_os_error().into());
78        }
79        let token = unsafe { OwnedHandle::from_raw_handle(token) };
80
81        let mut luid = Default::default();
82        if unsafe { LookupPrivilegeValueW(ptr::null(), SE_SECURITY_NAME, &mut luid) } == 0 {
83            return Err(io::Error::last_os_error().into());
84        }
85        let privileges = TOKEN_PRIVILEGES {
86            PrivilegeCount: 1,
87            Privileges: [LUID_AND_ATTRIBUTES {
88                Luid: luid,
89                Attributes: SE_PRIVILEGE_ENABLED,
90            }],
91        };
92        unsafe { SetLastError(0) };
93        if unsafe {
94            AdjustTokenPrivileges(
95                token.as_raw_handle(),
96                0,
97                &privileges,
98                0,
99                ptr::null_mut(),
100                ptr::null_mut(),
101            )
102        } == 0
103        {
104            return Err(io::Error::last_os_error().into());
105        }
106        if unsafe { GetLastError() } == ERROR_NOT_ALL_ASSIGNED {
107            return Err(io::Error::new(
108                io::ErrorKind::PermissionDenied,
109                "SeSecurityPrivilege is not available",
110            )
111            .into());
112        }
113        if unsafe { SetThreadToken(ptr::null(), token.as_raw_handle()) } == 0 {
114            return Err(io::Error::last_os_error().into());
115        }
116        let _guard = RevertGuard;
117        f()
118    }
119    #[cfg(all(docsrs, not(windows)))]
120    {
121        let _ = f;
122        unreachable!()
123    }
124}