use std::io;
use std::mem;
use std::ptr;
use windows_sys::Win32::Foundation::{
ERROR_INSUFFICIENT_BUFFER, GENERIC_READ, GENERIC_WRITE, GetLastError, HANDLE, HLOCAL, LocalFree,
};
use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
use windows_sys::Win32::Security::{
ACCESS_ALLOWED_ACE, ACL, ACL_REVISION, AddAccessAllowedAce, CopySid, GetLengthSid,
GetTokenInformation, InitializeAcl, InitializeSecurityDescriptor, IsValidSid, PSID,
SE_DACL_PROTECTED, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR, SetSecurityDescriptorControl,
SetSecurityDescriptorDacl, SetSecurityDescriptorOwner, TOKEN_QUERY, TOKEN_USER, TokenUser,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
use super::Handle;
pub(super) const PIPE_RIGHTS: u32 = GENERIC_READ | GENERIC_WRITE;
const SECURITY_DESCRIPTOR_REVISION: u32 = 1;
pub(super) struct Sid {
words: Vec<u32>,
}
impl Sid {
pub(super) fn current_user() -> io::Result<Self> {
let token = process_token()?;
let mut needed = 0u32;
let sized =
unsafe { GetTokenInformation(token.raw(), TokenUser, ptr::null_mut(), 0, &mut needed) };
if sized == 0 {
let err = unsafe { GetLastError() };
if err != ERROR_INSUFFICIENT_BUFFER {
return Err(os_error(err));
}
}
let len = needed as usize;
if len < mem::size_of::<TOKEN_USER>() {
return Err(io::Error::other(
"the process token reported no room for its user",
));
}
let mut info = vec![0u64; len.div_ceil(mem::size_of::<u64>())];
let filled = unsafe {
GetTokenInformation(
token.raw(),
TokenUser,
info.as_mut_ptr().cast(),
needed,
&mut needed,
)
};
if filled == 0 {
return Err(io::Error::last_os_error());
}
let sid = unsafe { (*info.as_ptr().cast::<TOKEN_USER>()).User.Sid };
if unsafe { IsValidSid(sid) } == 0 {
return Err(io::Error::other(
"the process token holds no valid user SID",
));
}
Self::copy_of(sid)
}
pub(super) fn copy_of(sid: PSID) -> io::Result<Self> {
let len = unsafe { GetLengthSid(sid) };
let mut words = vec![0u32; (len as usize).div_ceil(mem::size_of::<u32>())];
if unsafe { CopySid(len, words.as_mut_ptr().cast(), sid) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self { words })
}
pub(super) fn as_psid(&self) -> PSID {
self.words.as_ptr().cast_mut().cast()
}
pub(super) fn to_string_sid(&self) -> io::Result<String> {
let mut wide = ptr::null_mut();
if unsafe { ConvertSidToStringSidW(self.as_psid(), &mut wide) } == 0 {
return Err(io::Error::last_os_error());
}
let string = LocalMemory(wide.cast());
let units = unsafe {
let mut len = 0;
while *wide.add(len) != 0 {
len += 1;
}
std::slice::from_raw_parts(wide, len)
};
let sid = String::from_utf16(units)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "a SID string is not UTF-16"));
drop(string);
sid
}
}
pub(super) struct PipeSecurity<'sid> {
descriptor: Box<SECURITY_DESCRIPTOR>,
_acl: Vec<u32>,
_owner: &'sid Sid,
}
impl<'sid> PipeSecurity<'sid> {
pub(super) fn owned_by(owner: &'sid Sid) -> io::Result<Self> {
let sid = owner.as_psid();
let sid_len = unsafe { GetLengthSid(sid) } as usize;
let acl_len = (mem::size_of::<ACL>() + mem::size_of::<ACCESS_ALLOWED_ACE>()
- mem::size_of::<u32>()
+ sid_len)
.next_multiple_of(mem::size_of::<u32>());
let acl_len_u32 = u32::try_from(acl_len)
.map_err(|_| io::Error::other("a one-entry ACL does not fit its own length field"))?;
let mut acl = vec![0u32; acl_len / mem::size_of::<u32>()];
let acl_ptr: *mut ACL = acl.as_mut_ptr().cast();
if unsafe { InitializeAcl(acl_ptr, acl_len_u32, ACL_REVISION) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { AddAccessAllowedAce(acl_ptr, ACL_REVISION, PIPE_RIGHTS, sid) } == 0 {
return Err(io::Error::last_os_error());
}
let mut descriptor: Box<SECURITY_DESCRIPTOR> = Box::new(unsafe { mem::zeroed() });
let sd = (&raw mut *descriptor).cast();
if unsafe { InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetSecurityDescriptorOwner(sd, sid, 0) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetSecurityDescriptorDacl(sd, 1, acl_ptr, 0) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetSecurityDescriptorControl(sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self {
descriptor,
_acl: acl,
_owner: owner,
})
}
pub(super) fn attributes(&self) -> SECURITY_ATTRIBUTES {
SECURITY_ATTRIBUTES {
nLength: mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: (&raw const *self.descriptor).cast_mut().cast(),
bInheritHandle: 0,
}
}
}
pub(super) struct LocalMemory(pub(super) HLOCAL);
impl Drop for LocalMemory {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { LocalFree(self.0) };
}
}
}
fn process_token() -> io::Result<Handle> {
let mut token: HANDLE = ptr::null_mut();
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(Handle(token))
}
fn os_error(code: u32) -> io::Error {
io::Error::from_raw_os_error(code as i32)
}