blob: 5ebd4ddcca10c5bffaae51d2ad5b6d933663bc17 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use crate::windows::{checked, owned};
use std::{
io,
os::windows::io::{AsRawHandle, BorrowedHandle, OwnedHandle},
ptr,
};
use windows_sys::Win32::{
Foundation::{ERROR_NO_TOKEN, ERROR_NOT_ALL_ASSIGNED, GetLastError, LUID},
Security::*,
System::Threading::{
GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken, SetThreadToken,
},
};
/// The authenticated caller's effective filesystem identity. The elevated
/// helper keeps OS provisioning privileges only outside these synchronous scopes.
pub struct Caller(OwnedHandle);
impl Caller {
pub(super) fn capture(process: BorrowedHandle<'_>) -> io::Result<Self> {
let open = || {
let mut token = ptr::null_mut();
checked(unsafe {
OpenProcessToken(
process.as_raw_handle(),
TOKEN_QUERY | TOKEN_DUPLICATE,
&mut token,
)
})?;
owned(token).map(Self)
};
match open() {
Err(error) if error.kind() == io::ErrorKind::PermissionDenied => {
// Alternate-credential UAC: query only this already authenticated
// process, then immediately restore the helper's privilege state.
let _privilege = DebugPrivilege::acquire()?;
open()
}
result => result,
}
}
pub fn run<T>(&self, action: impl FnOnce() -> io::Result<T>) -> io::Result<T> {
let mut previous = ptr::null_mut();
let previous = if unsafe {
OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
1,
&mut previous,
)
} != 0
{
Some(owned(previous)?)
} else {
let error = io::Error::last_os_error();
if error.raw_os_error() != Some(ERROR_NO_TOKEN as i32) {
return Err(error);
}
None
};
checked(unsafe { ImpersonateLoggedOnUser(self.0.as_raw_handle()) })?;
struct Restore(Option<OwnedHandle>);
impl Drop for Restore {
fn drop(&mut self) {
let result = match &self.0 {
Some(token) => unsafe { SetThreadToken(ptr::null(), token.as_raw_handle()) },
None => unsafe { RevertToSelf() },
};
if result == 0 {
std::process::abort();
}
}
}
let _restore = Restore(previous);
action()
}
}
struct DebugPrivilege {
token: OwnedHandle,
previous: TOKEN_PRIVILEGES,
}
impl DebugPrivilege {
fn acquire() -> io::Result<Self> {
let mut token = ptr::null_mut();
checked(unsafe {
OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&mut token,
)
})?;
let token = owned(token)?;
let name: Vec<_> = "SeDebugPrivilege".encode_utf16().chain(Some(0)).collect();
let mut luid = LUID::default();
checked(unsafe { LookupPrivilegeValueW(ptr::null(), name.as_ptr(), &mut luid) })?;
let desired = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
let mut previous = TOKEN_PRIVILEGES::default();
let mut size = 0;
checked(unsafe {
AdjustTokenPrivileges(
token.as_raw_handle(),
0,
&desired,
size_of::<TOKEN_PRIVILEGES>() as u32,
&mut previous,
&mut size,
)
})?;
let result = Self { token, previous };
if unsafe { GetLastError() } == ERROR_NOT_ALL_ASSIGNED {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"setup helper cannot access its authenticated caller token",
));
}
Ok(result)
}
}
impl Drop for DebugPrivilege {
fn drop(&mut self) {
if unsafe {
AdjustTokenPrivileges(
self.token.as_raw_handle(),
0,
&self.previous,
0,
ptr::null_mut(),
ptr::null_mut(),
)
} == 0
{
std::process::abort();
}
}
}