use std::ffi::OsStr;
use std::io;
use std::io::Error;
use std::iter::once;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use winapi::shared::minwindef::{BOOL, FALSE};
use winapi::shared::ntdef::NULL;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::securitybaseapi::AdjustTokenPrivileges;
use winapi::um::winbase::LookupPrivilegeValueW;
use winapi::um::winnt::{
HANDLE, PLUID, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES,
};
struct ProcessToken(HANDLE);
impl ProcessToken {
pub fn open_current(desired_access: u32) -> io::Result<Self> {
Self::open(unsafe { GetCurrentProcess() }, desired_access)
}
pub fn open(process: HANDLE, desired_access: u32) -> io::Result<Self> {
let mut process_token: HANDLE = NULL;
let result =
unsafe { OpenProcessToken(process, desired_access, &mut process_token as *mut HANDLE) };
match result {
0 => Err(Error::last_os_error()),
_ => Ok(ProcessToken(process_token)),
}
}
}
impl Drop for ProcessToken {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
pub fn update_privileges() -> io::Result<()> {
let mut tp = mem::MaybeUninit::<TOKEN_PRIVILEGES>::uninit();
let se_system_environment_privilege: Vec<u16> = OsStr::new("SeSystemEnvironmentPrivilege")
.encode_wide()
.chain(once(0))
.collect();
let (mut tp, result) = unsafe {
(*tp.as_mut_ptr()).PrivilegeCount = 1;
let result = LookupPrivilegeValueW(
null_mut(),
se_system_environment_privilege.as_ptr(),
&mut (*tp.as_mut_ptr()).Privileges[0].Luid as PLUID,
);
(tp.assume_init(), result)
};
if result == 0 {
return Err(Error::last_os_error());
}
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
let process_token = ProcessToken::open_current(TOKEN_ADJUST_PRIVILEGES)?;
let result = unsafe {
AdjustTokenPrivileges(
process_token.0,
FALSE as BOOL,
&mut tp as *mut TOKEN_PRIVILEGES,
0,
null_mut(),
null_mut(),
)
};
match result {
0 => Err(Error::last_os_error()),
_ => Ok(()),
}
}