返回 CodeWhale
windows_identity.rs
根目录 / crates / config / src / windows_identity.rs
1 //! Existing Windows current-user token/SID custody, shared by owned storage and IPC.
2
3 use anyhow::{Context, Result, bail};
4 use std::os::windows::io::FromRawHandle as _;
5
6 #[cfg(windows)]
7 pub struct CurrentWindowsUser {
8 _token: std::os::windows::io::OwnedHandle,
9 token_info: Vec<usize>,
10 }
11
12 #[cfg(windows)]
13 impl CurrentWindowsUser {
14 pub fn open() -> Result<Self> {
15 // SAFETY: Windows supplies a process pseudo-handle; no ownership transfer.
16 Self::from_process(unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() })
17 }
18
19 fn from_process(process: windows_sys::Win32::Foundation::HANDLE) -> Result<Self> {
20 use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE};
21 use windows_sys::Win32::Security::{
22 GetTokenInformation, TOKEN_QUERY, TOKEN_USER, TokenUser,
23 };
24 use windows_sys::Win32::System::Threading::OpenProcessToken;
25
26 let mut token: HANDLE = std::ptr::null_mut();
27 // SAFETY: the pseudo-process handle is valid and `token` is writable.
28 if unsafe { OpenProcessToken(process, TOKEN_QUERY, &mut token) } == 0 {
29 return Err(std::io::Error::last_os_error())
30 .context("opening current Windows user token");
31 }
32 let mut needed = 0;
33 // SAFETY: a null buffer/zero length asks for the required size.
34 let _ =
35 unsafe { GetTokenInformation(token, TokenUser, std::ptr::null_mut(), 0, &mut needed) };
36 if needed < std::mem::size_of::<TOKEN_USER>() as u32 || needed > 65536 {
37 let error = std::io::Error::from_raw_os_error(unsafe { GetLastError() } as i32);
38 // SAFETY: the token is owned on this error path.
39 unsafe { CloseHandle(token) };
40 return Err(error).context("sizing current Windows user token information");
41 }
42 let words = (needed as usize).div_ceil(std::mem::size_of::<usize>());
43 let mut token_info = vec![0usize; words];
44 // SAFETY: the aligned buffer contains at least `needed` writable bytes.
45 if unsafe {
46 GetTokenInformation(
47 token,
48 TokenUser,
49 token_info.as_mut_ptr().cast(),
50 needed,
51 &mut needed,
52 )
53 } == 0
54 {
55 let error = std::io::Error::last_os_error();
56 // SAFETY: the token is owned on this error path.
57 unsafe { CloseHandle(token) };
58 return Err(error).context("reading current Windows user token information");
59 }
60 let user = unsafe { &*token_info.as_ptr().cast::<TOKEN_USER>() };
61 if user.User.Sid.is_null() {
62 // SAFETY: the token is owned on this error path.
63 unsafe { CloseHandle(token) };
64 bail!("current Windows user token has no SID");
65 }
66 // SAFETY: the token was opened above and is transferred exactly once.
67 let token = unsafe { std::os::windows::io::OwnedHandle::from_raw_handle(token) };
68 Ok(Self {
69 _token: token,
70 token_info,
71 })
72 }
73
74 pub fn sid_string(&self) -> Result<String> {
75 use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
76 let mut text = std::ptr::null_mut();
77 // SAFETY: the validated SID remains held and the output is writable.
78 if unsafe { ConvertSidToStringSidW(self.sid(), &mut text) } == 0 {
79 return Err(std::io::Error::last_os_error()).context("encoding current Windows SID");
80 }
81 let _text = WindowsLocalAllocation(text.cast());
82 anyhow::ensure!(!text.is_null(), "Windows SID text unavailable");
83 let mut units = Vec::new();
84 for index in 0..192 {
85 let unit = unsafe { *text.add(index) };
86 if unit == 0 {
87 return String::from_utf16(&units).map_err(Into::into);
88 }
89 units.push(unit);
90 }
91 bail!("Windows SID text exceeds its fixed bound")
92 }
93
94 pub fn sid(&self) -> windows_sys::Win32::Security::PSID {
95 use windows_sys::Win32::Security::TOKEN_USER;
96 // SAFETY: the aligned token buffer remains owned by `self`.
97 unsafe { (*self.token_info.as_ptr().cast::<TOKEN_USER>()).User.Sid }
98 }
99 }
100
101 #[cfg(windows)]
102 pub(crate) struct WindowsLocalAllocation(pub(crate) *mut core::ffi::c_void);
103
104 #[cfg(windows)]
105 impl Drop for WindowsLocalAllocation {
106 fn drop(&mut self) {
107 if !self.0.is_null() {
108 // SAFETY: Windows allocated this block for a LocalFree caller.
109 unsafe { windows_sys::Win32::Foundation::LocalFree(self.0) };
110 }
111 }
112 }
113
114 /// The actual kernel-selected peer process and token, held across control admission.
115 /// A display PID or a same-user SID is never sufficient to select an owner.
116 pub struct WindowsPeerProcess {
117 process: std::os::windows::io::OwnedHandle,
118 user: CurrentWindowsUser,
119 pid: u32,
120 start: String,
121 }
122
123 impl WindowsPeerProcess {
124 pub fn open_current_user(pid: u32) -> Result<Self> {
125 use std::os::windows::io::AsRawHandle as _;
126 use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE;
127 use windows_sys::Win32::System::Threading::{
128 GetProcessId, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION,
129 };
130 anyhow::ensure!(pid > 0, "invalid Windows peer PID");
131 let process =
132 unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, 0, pid) };
133 anyhow::ensure!(!process.is_null(), "Windows peer process unavailable");
134 let process = unsafe { std::os::windows::io::OwnedHandle::from_raw_handle(process) };
135 anyhow::ensure!(
136 unsafe { GetProcessId(process.as_raw_handle()) } == pid,
137 "Windows kernel peer PID changed"
138 );
139 let user = CurrentWindowsUser::from_process(process.as_raw_handle())?;
140 let start = process_creation(process.as_raw_handle())?;
141 let value = Self {
142 process,
143 user,
144 pid,
145 start,
146 };
147 value.check_current_user()?;
148 Ok(value)
149 }
150 pub fn pid(&self) -> u32 {
151 self.pid
152 }
153 pub fn start(&self) -> &str {
154 &self.start
155 }
156 pub fn principal(&self) -> Result<String> {
157 self.user.sid_string()
158 }
159 pub fn check_current_user(&self) -> Result<()> {
160 use std::os::windows::io::AsRawHandle as _;
161 use windows_sys::Win32::Security::EqualSid;
162 use windows_sys::Win32::System::Threading::GetExitCodeProcess;
163 let mut status = 0;
164 anyhow::ensure!(
165 unsafe { GetExitCodeProcess(self.process.as_raw_handle(), &mut status) } != 0
166 && status == 259,
167 "Windows peer process has exited"
168 );
169 let current = CurrentWindowsUser::open()?;
170 let peer = CurrentWindowsUser::from_process(self.process.as_raw_handle())?;
171 anyhow::ensure!(
172 unsafe { EqualSid(current.sid(), peer.sid()) } != 0
173 && unsafe { EqualSid(self.user.sid(), peer.sid()) } != 0,
174 "Windows peer principal changed"
175 );
176 anyhow::ensure!(
177 process_creation(self.process.as_raw_handle())? == self.start,
178 "Windows peer generation changed"
179 );
180 Ok(())
181 }
182 }
183
184 fn process_creation(process: windows_sys::Win32::Foundation::HANDLE) -> Result<String> {
185 use windows_sys::Win32::Foundation::FILETIME;
186 use windows_sys::Win32::System::Threading::GetProcessTimes;
187 let (mut creation, mut exit, mut kernel, mut user) = (
188 FILETIME {
189 dwLowDateTime: 0,
190 dwHighDateTime: 0,
191 },
192 FILETIME {
193 dwLowDateTime: 0,
194 dwHighDateTime: 0,
195 },
196 FILETIME {
197 dwLowDateTime: 0,
198 dwHighDateTime: 0,
199 },
200 FILETIME {
201 dwLowDateTime: 0,
202 dwHighDateTime: 0,
203 },
204 );
205 anyhow::ensure!(
206 unsafe { GetProcessTimes(process, &mut creation, &mut exit, &mut kernel, &mut user) } != 0,
207 "Windows process creation identity unavailable"
208 );
209 Ok(format!(
210 "windows:{}:{}",
211 creation.dwHighDateTime, creation.dwLowDateTime
212 ))
213 }
214
215 /// One current-user ACL builder, immediately adopted by private storage and
216 /// named-pipe creation. Both commit the same protected owner/DACL policy.
217 pub struct OwnerOnlyAcl {
218 user: CurrentWindowsUser,
219 acl: WindowsLocalAllocation,
220 }
221 impl OwnerOnlyAcl {
222 pub fn new(access: u32, inherit_to_children: bool) -> Result<Self> {
223 use windows_sys::Win32::Security::Authorization::{
224 EXPLICIT_ACCESS_W, SET_ACCESS, SetEntriesInAclW, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
225 TRUSTEE_W,
226 };
227 use windows_sys::Win32::Security::{NO_INHERITANCE, SUB_CONTAINERS_AND_OBJECTS_INHERIT};
228 let user = CurrentWindowsUser::open()?;
229 let entry = EXPLICIT_ACCESS_W {
230 grfAccessPermissions: access,
231 grfAccessMode: SET_ACCESS,
232 grfInheritance: if inherit_to_children {
233 SUB_CONTAINERS_AND_OBJECTS_INHERIT
234 } else {
235 NO_INHERITANCE
236 },
237 Trustee: TRUSTEE_W {
238 pMultipleTrustee: std::ptr::null_mut(),
239 MultipleTrusteeOperation: 0,
240 TrusteeForm: TRUSTEE_IS_SID,
241 TrusteeType: TRUSTEE_IS_USER,
242 ptstrName: user.sid().cast(),
243 },
244 };
245 let mut acl = std::ptr::null_mut();
246 let status = unsafe { SetEntriesInAclW(1, &entry, std::ptr::null(), &mut acl) };
247 anyhow::ensure!(
248 status == 0,
249 "building current-user-only ACL failed ({status})"
250 );
251 Ok(Self {
252 user,
253 acl: WindowsLocalAllocation(acl.cast()),
254 })
255 }
256 pub(crate) fn user_sid(&self) -> windows_sys::Win32::Security::PSID {
257 self.user.sid()
258 }
259 pub(crate) fn acl(&self) -> *mut windows_sys::Win32::Security::ACL {
260 self.acl.0.cast()
261 }
262 pub fn with_security_attributes<T>(
263 &self,
264 create: impl FnOnce(*mut core::ffi::c_void) -> Result<T>,
265 ) -> Result<T> {
266 use windows_sys::Win32::Security::{
267 InitializeSecurityDescriptor, SE_DACL_PROTECTED, SECURITY_ATTRIBUTES,
268 SECURITY_DESCRIPTOR, SetSecurityDescriptorControl, SetSecurityDescriptorDacl,
269 SetSecurityDescriptorOwner,
270 };
271 let mut descriptor = SECURITY_DESCRIPTOR::default();
272 let pointer = (&mut descriptor as *mut SECURITY_DESCRIPTOR).cast();
273 anyhow::ensure!(
274 unsafe { InitializeSecurityDescriptor(pointer, 1) } != 0,
275 "initializing owner security descriptor"
276 );
277 anyhow::ensure!(
278 unsafe { SetSecurityDescriptorOwner(pointer, self.user.sid(), 0) } != 0,
279 "setting owner SID"
280 );
281 anyhow::ensure!(
282 unsafe { SetSecurityDescriptorDacl(pointer, 1, self.acl(), 0) } != 0,
283 "setting owner-only DACL"
284 );
285 anyhow::ensure!(
286 unsafe { SetSecurityDescriptorControl(pointer, SE_DACL_PROTECTED, SE_DACL_PROTECTED) }
287 != 0,
288 "protecting owner-only DACL"
289 );
290 let mut attributes = SECURITY_ATTRIBUTES {
291 nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
292 lpSecurityDescriptor: pointer,
293 bInheritHandle: 0,
294 };
295 create((&mut attributes as *mut SECURITY_ATTRIBUTES).cast())
296 }
297 }
298
298 lines RUST