use std::{ffi::c_void, io, mem::size_of, ptr};
use windows_sys::Win32::{
Foundation::{CloseHandle, HANDLE, HLOCAL, LocalFree},
Security::{
Authorization::{
ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW,
SDDL_REVISION_1,
},
GetTokenInformation, SECURITY_ATTRIBUTES, TOKEN_QUERY, TOKEN_USER, TokenUser,
},
System::Threading::{GetCurrentProcess, OpenProcessToken},
};
pub struct CurrentUserOnly {
descriptor: *mut c_void,
attributes: SECURITY_ATTRIBUTES,
}
impl CurrentUserOnly {
pub fn new() -> io::Result<Self> {
let sid = current_user_sid()?;
let sddl = encode_wide(&format!("D:P(A;;FA;;;{sid})"));
let mut descriptor: *mut c_void = ptr::null_mut();
let converted = unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.as_ptr(),
SDDL_REVISION_1,
&mut descriptor,
ptr::null_mut(),
)
};
if converted == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self {
descriptor,
attributes: SECURITY_ATTRIBUTES {
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: descriptor,
bInheritHandle: 0,
},
})
}
pub fn as_raw(&mut self) -> *mut c_void {
ptr::addr_of_mut!(self.attributes).cast()
}
}
unsafe impl Send for CurrentUserOnly {}
impl Drop for CurrentUserOnly {
fn drop(&mut self) {
unsafe { LocalFree(self.descriptor as HLOCAL) };
}
}
struct OwnedToken(HANDLE);
impl Drop for OwnedToken {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
fn current_user_sid() -> io::Result<String> {
let mut raw_token: HANDLE = 0;
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut raw_token) } == 0 {
return Err(io::Error::last_os_error());
}
let token = OwnedToken(raw_token);
let mut needed = 0_u32;
unsafe { GetTokenInformation(token.0, TokenUser, ptr::null_mut(), 0, &mut needed) };
if needed == 0 {
return Err(io::Error::last_os_error());
}
let mut buffer = vec![0_u64; (needed as usize).div_ceil(size_of::<u64>()).max(1)];
let queried = unsafe {
GetTokenInformation(
token.0,
TokenUser,
buffer.as_mut_ptr().cast(),
needed,
&mut needed,
)
};
if queried == 0 {
return Err(io::Error::last_os_error());
}
let user = unsafe { &*(buffer.as_ptr() as *const TOKEN_USER) };
let mut raw_sid = ptr::null_mut();
if unsafe { ConvertSidToStringSidW(user.User.Sid, &mut raw_sid) } == 0 {
return Err(io::Error::last_os_error());
}
let sid = unsafe { decode_wide(raw_sid) };
unsafe { LocalFree(raw_sid as HLOCAL) };
Ok(sid)
}
fn encode_wide(value: &str) -> Vec<u16> {
value.encode_utf16().chain(std::iter::once(0)).collect()
}
unsafe fn decode_wide(raw: *mut u16) -> String {
let mut length = 0;
while unsafe { *raw.add(length) } != 0 {
length += 1;
}
String::from_utf16_lossy(unsafe { std::slice::from_raw_parts(raw, length) })
}
#[cfg(test)]
fn render_dacl(descriptor: *mut c_void) -> io::Result<String> {
use windows_sys::Win32::Security::{
Authorization::ConvertSecurityDescriptorToStringSecurityDescriptorW,
DACL_SECURITY_INFORMATION,
};
let mut raw = ptr::null_mut();
let converted = unsafe {
ConvertSecurityDescriptorToStringSecurityDescriptorW(
descriptor,
SDDL_REVISION_1,
DACL_SECURITY_INFORMATION,
&mut raw,
ptr::null_mut(),
)
};
if converted == 0 {
return Err(io::Error::last_os_error());
}
let sddl = unsafe { decode_wide(raw) };
unsafe { LocalFree(raw as HLOCAL) };
Ok(sddl)
}
#[cfg(test)]
impl CurrentUserOnly {
pub fn dacl(&self) -> io::Result<String> {
render_dacl(self.descriptor)
}
}
#[cfg(test)]
pub fn dacl_of(handle: HANDLE) -> io::Result<String> {
use windows_sys::Win32::Security::{
Authorization::{GetSecurityInfo, SE_KERNEL_OBJECT},
DACL_SECURITY_INFORMATION,
};
let mut descriptor = ptr::null_mut();
let status = unsafe {
GetSecurityInfo(
handle,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
&mut descriptor,
)
};
if status != 0 {
return Err(io::Error::from_raw_os_error(status as i32));
}
let rendered = render_dacl(descriptor);
unsafe { LocalFree(descriptor as HLOCAL) };
rendered
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolves_the_owning_account_as_a_sid() {
let sid = current_user_sid().unwrap();
assert!(sid.starts_with("S-1-"), "{sid}");
assert!(sid.split('-').count() >= 4, "{sid}");
}
#[test]
fn builds_security_attributes_from_that_sid() {
let mut security = CurrentUserOnly::new().unwrap();
assert!(!security.descriptor.is_null());
assert!(!security.as_raw().is_null());
assert_eq!(
security.attributes.nLength as usize,
size_of::<SECURITY_ATTRIBUTES>()
);
assert_eq!(security.attributes.bInheritHandle, 0);
}
}