use super::super::{call_error, User};
use std::{
ffi::c_void,
io,
mem::size_of,
ptr::{addr_of, null_mut},
};
use windows_sys::Win32::{
Foundation::LocalFree,
Security::{
Authorization::{
ConvertSecurityDescriptorToStringSecurityDescriptorW, ConvertSidToStringSidW,
ConvertStringSecurityDescriptorToSecurityDescriptorW, GetSecurityInfo, SDDL_REVISION_1,
SE_KERNEL_OBJECT,
},
GetAce, GetSecurityDescriptorControl, GetSecurityDescriptorDacl, ACCESS_ALLOWED_ACE,
ACE_HEADER, DACL_SECURITY_INFORMATION, PSID, SE_DACL_PROTECTED,
},
System::Threading::GetCurrentProcess,
};
struct Allocation(*mut c_void);
impl Drop for Allocation {
fn drop(&mut self) {
unsafe { LocalFree(self.0) };
}
}
fn allocated_text(pointer: *mut u16) -> String {
let _allocation = Allocation(pointer.cast());
unsafe {
let length = (0..).take_while(|&i| *pointer.add(i) != 0).count();
String::from_utf16(std::slice::from_raw_parts(pointer, length)).unwrap()
}
}
fn sid_string(sid: PSID) -> io::Result<String> {
let mut text = null_mut();
if unsafe { ConvertSidToStringSidW(sid, &mut text) } == 0 {
return Err(call_error(
"ConvertSidToStringSidW(process trustee)",
io::Error::last_os_error(),
));
}
Ok(allocated_text(text))
}
pub(super) fn current_process_sddl() -> io::Result<String> {
let mut raw = null_mut();
let error = unsafe {
GetSecurityInfo(
GetCurrentProcess(),
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
&mut raw,
)
};
if error != 0 {
return Err(call_error(
"GetSecurityInfo(process DACL readback)",
io::Error::from_raw_os_error(error as i32),
));
}
let descriptor = Allocation(raw);
let mut text = null_mut();
if unsafe {
ConvertSecurityDescriptorToStringSecurityDescriptorW(
descriptor.0,
SDDL_REVISION_1,
DACL_SECURITY_INFORMATION,
&mut text,
null_mut(),
)
} == 0
{
return Err(call_error(
"ConvertSecurityDescriptorToStringSecurityDescriptorW(process DACL)",
io::Error::last_os_error(),
));
}
Ok(allocated_text(text))
}
pub(super) fn assert_exact_protected_dacl(sddl: &str) {
let text: Vec<u16> = sddl.encode_utf16().chain(Some(0)).collect();
let mut raw = null_mut();
assert_ne!(
unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
text.as_ptr(),
SDDL_REVISION_1,
&mut raw,
null_mut(),
)
},
0,
"parse child DACL {sddl}: {}",
io::Error::last_os_error()
);
let descriptor = Allocation(raw);
let mut control = 0;
let mut revision = 0;
assert_ne!(
unsafe { GetSecurityDescriptorControl(descriptor.0, &mut control, &mut revision) },
0,
"GetSecurityDescriptorControl: {}",
io::Error::last_os_error()
);
assert_ne!(
control & SE_DACL_PROTECTED,
0,
"process DACL must be protected: {sddl}"
);
let mut present = 0;
let mut defaulted = 0;
let mut dacl = null_mut();
assert_ne!(
unsafe { GetSecurityDescriptorDacl(descriptor.0, &mut present, &mut dacl, &mut defaulted) },
0,
"GetSecurityDescriptorDacl: {}",
io::Error::last_os_error()
);
assert_ne!(present, 0, "process DACL must be present: {sddl}");
assert!(
!dacl.is_null(),
"null process DACL grants unrestricted access: {sddl}"
);
assert_eq!(
unsafe { (*dacl).AceCount },
2,
"exactly two process ACEs: {sddl}"
);
let user = User::current().expect("token user for process DACL assertion");
let mut trustees = Vec::new();
for index in 0..2 {
let mut raw_ace = null_mut();
assert_ne!(
unsafe { GetAce(dacl, index, &mut raw_ace) },
0,
"GetAce(process DACL): {}",
io::Error::last_os_error()
);
let header = unsafe { &*raw_ace.cast::<ACE_HEADER>() };
assert_eq!(header.AceType, 0, "ACCESS_ALLOWED ACE: {sddl}");
assert!(usize::from(header.AceSize) >= size_of::<ACCESS_ALLOWED_ACE>());
assert_eq!(header.AceFlags, 0, "non-inheriting explicit ACE: {sddl}");
let ace = unsafe { &*raw_ace.cast::<ACCESS_ALLOWED_ACE>() };
assert_eq!(
ace.Mask, 0x0010_1001,
"only daemon rights in each ACE: {sddl}"
);
let sid = addr_of!(ace.SidStart).cast_mut().cast();
trustees.push(sid_string(sid).expect("process ACE trustee SID"));
}
trustees.sort();
let mut expected = vec![sid_string(user.sid()).unwrap(), "S-1-3-4".to_owned()];
expected.sort();
assert_eq!(
trustees, expected,
"token user and OW (OWNER RIGHTS) trustees: {sddl}"
);
}