#![cfg(windows)]
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use std::ptr;
use windows_sys::Win32::Foundation::{
CloseHandle, GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS, GENERIC_ALL,
HANDLE, HLOCAL, INVALID_HANDLE_VALUE,
};
use windows_sys::Win32::Security::Authorization::{
SetEntriesInAclW, SetNamedSecurityInfoW, EXPLICIT_ACCESS_W, GRANT_ACCESS, NO_MULTIPLE_TRUSTEE,
SE_FILE_OBJECT, TRUSTEE_IS_SID, TRUSTEE_IS_USER, TRUSTEE_W,
};
use windows_sys::Win32::Security::{
GetTokenInformation, TokenUser, ACL, DACL_SECURITY_INFORMATION, NO_INHERITANCE,
PROTECTED_DACL_SECURITY_INFORMATION, TOKEN_QUERY, TOKEN_USER,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
use crate::error::Error;
pub fn set_owner_only_dacl(path: &Path) -> Result<(), Error> {
let owner_sid = OwnerSid::current()?;
let explicit = EXPLICIT_ACCESS_W {
grfAccessPermissions: GENERIC_ALL,
grfAccessMode: GRANT_ACCESS,
grfInheritance: NO_INHERITANCE,
Trustee: TRUSTEE_W {
pMultipleTrustee: ptr::null_mut(),
MultipleTrusteeOperation: NO_MULTIPLE_TRUSTEE,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_USER,
ptstrName: owner_sid.as_ptr() as *mut u16,
},
};
let mut new_acl: *mut ACL = ptr::null_mut();
let rc = unsafe { SetEntriesInAclW(1, &explicit, ptr::null_mut(), &mut new_acl) };
if rc != ERROR_SUCCESS || new_acl.is_null() {
return Err(Error::StorageIo(std::io::Error::other(format!(
"SetEntriesInAclW failed: rc={rc}"
))));
}
let _acl_guard = LocalFreeOnDrop(new_acl.cast());
let wide: Vec<u16> = path
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let info = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
let rc = unsafe {
SetNamedSecurityInfoW(
wide.as_ptr(),
SE_FILE_OBJECT,
info,
ptr::null_mut(), ptr::null_mut(), new_acl, ptr::null_mut(), )
};
if rc != ERROR_SUCCESS {
return Err(Error::StorageIo(std::io::Error::other(format!(
"SetNamedSecurityInfoW({}) failed: rc={rc}",
path.display()
))));
}
Ok(())
}
struct OwnerSid {
buffer: Vec<u8>,
}
impl OwnerSid {
fn current() -> Result<Self, Error> {
let mut token: HANDLE = INVALID_HANDLE_VALUE;
let process = unsafe { GetCurrentProcess() };
if unsafe { OpenProcessToken(process, TOKEN_QUERY, &mut token) } == 0 {
return Err(Error::StorageIo(last_os_error("OpenProcessToken")));
}
let _token_guard = HandleGuard(token);
let mut needed: u32 = 0;
let probe =
unsafe { GetTokenInformation(token, TokenUser, ptr::null_mut(), 0, &mut needed) };
if probe == 0 {
let err = unsafe { GetLastError() };
if err != ERROR_INSUFFICIENT_BUFFER {
return Err(Error::StorageIo(std::io::Error::other(format!(
"GetTokenInformation probe failed: rc={err}"
))));
}
}
let mut buffer = vec![0u8; needed as usize];
if unsafe {
GetTokenInformation(
token,
TokenUser,
buffer.as_mut_ptr().cast(),
needed,
&mut needed,
)
} == 0
{
return Err(Error::StorageIo(last_os_error("GetTokenInformation")));
}
Ok(Self { buffer })
}
fn as_ptr(&self) -> *const std::ffi::c_void {
#[allow(clippy::cast_ptr_alignment)]
let token_user = self.buffer.as_ptr().cast::<TOKEN_USER>();
unsafe { (*token_user).User.Sid.cast() }
}
}
struct HandleGuard(HANDLE);
impl Drop for HandleGuard {
fn drop(&mut self) {
if self.0 != INVALID_HANDLE_VALUE {
unsafe {
let _ = CloseHandle(self.0);
}
}
}
}
struct LocalFreeOnDrop(HLOCAL);
impl Drop for LocalFreeOnDrop {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
LocalFree(self.0);
}
}
}
}
fn last_os_error(label: &str) -> std::io::Error {
let err = unsafe { GetLastError() };
std::io::Error::other(format!("{label} failed: rc={err}"))
}