Files
qiming/qimingclaw/crates/windows-sandbox-helper/src/acl.rs

260 lines
8.0 KiB
Rust

//! DACL manipulation (add/deny ACEs).
use crate::winutil::to_wide;
use anyhow::Result;
use std::ffi::c_void;
use std::path::Path;
use windows_sys::Win32::Foundation::{
CloseHandle, LocalFree, ERROR_SUCCESS, HLOCAL, INVALID_HANDLE_VALUE,
};
use windows_sys::Win32::Security::{
AclSizeInformation, EqualSid, GetAce, GetAclInformation,
Authorization::{
GetNamedSecurityInfoW, GetSecurityInfo, SetEntriesInAclW, SetNamedSecurityInfoW,
SetSecurityInfo, EXPLICIT_ACCESS_W, TRUSTEE_IS_SID, TRUSTEE_IS_UNKNOWN, TRUSTEE_W,
},
ACL, ACL_SIZE_INFORMATION, ACCESS_ALLOWED_ACE, ACE_HEADER, DACL_SECURITY_INFORMATION,
};
use windows_sys::Win32::Storage::FileSystem::{
CreateFileW, FILE_ATTRIBUTE_NORMAL, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ,
FILE_GENERIC_WRITE, FILE_APPEND_DATA, FILE_WRITE_ATTRIBUTES, FILE_WRITE_DATA,
FILE_WRITE_EA, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
};
const SE_KERNEL_OBJECT: u32 = 6;
const INHERIT_ONLY_ACE: u8 = 0x08;
const GENERIC_WRITE_MASK: u32 = 0x4000_0000;
const CONTAINER_INHERIT_ACE: u32 = 0x2;
const OBJECT_INHERIT_ACE: u32 = 0x1;
// ACE access modes
const GRANT_ACCESS: i32 = 2;
const DENY_ACCESS: i32 = 3;
const REVOKE_ACCESS: i32 = 4;
const DENY_WRITE_MASK: u32 = FILE_GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA
| FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE_MASK;
/// Check if a DACL contains an ACE of the given type matching the SID with the specified mask.
fn dacl_has_ace_for_sid(
p_dacl: *mut ACL,
psid: *mut c_void,
ace_type: u8, // 0 = ACCESS_ALLOWED, 1 = ACCESS_DENIED
mask_check: u32,
) -> bool {
if p_dacl.is_null() {
return false;
}
let mut info: ACL_SIZE_INFORMATION = unsafe { std::mem::zeroed() };
let ok = unsafe {
GetAclInformation(
p_dacl as *const ACL,
&mut info as *mut _ as *mut c_void,
std::mem::size_of::<ACL_SIZE_INFORMATION>() as u32,
AclSizeInformation,
)
};
if ok == 0 {
return false;
}
for i in 0..info.AceCount {
let mut p_ace: *mut c_void = std::ptr::null_mut();
if unsafe { GetAce(p_dacl as *const ACL, i, &mut p_ace) } == 0 {
continue;
}
let hdr = unsafe { &*(p_ace as *const ACE_HEADER) };
if hdr.AceType != ace_type {
continue;
}
if (hdr.AceFlags & INHERIT_ONLY_ACE) != 0 {
continue;
}
let ace = unsafe { &*(p_ace as *const ACCESS_ALLOWED_ACE) };
let base = p_ace as usize;
let sid_ptr = (base + std::mem::size_of::<ACE_HEADER>() + std::mem::size_of::<u32>()) as *mut c_void;
if unsafe { EqualSid(sid_ptr, psid) } != 0 && (ace.Mask & mask_check) != 0 {
return true;
}
}
false
}
fn trustee(psid: *mut c_void) -> TRUSTEE_W {
TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
}
}
fn get_dacl(path: &Path) -> Result<(*mut c_void, *mut ACL)> {
let mut p_sd: *mut c_void = std::ptr::null_mut();
let mut p_dacl: *mut ACL = std::ptr::null_mut();
let code = unsafe {
GetNamedSecurityInfoW(
to_wide(path).as_ptr(),
1,
DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut p_dacl,
std::ptr::null_mut(),
&mut p_sd,
)
};
if code != ERROR_SUCCESS as u32 {
return Err(anyhow::anyhow!("GetNamedSecurityInfoW failed: {}", code));
}
Ok((p_sd, p_dacl))
}
fn free_sd(p_sd: *mut c_void) {
if !p_sd.is_null() {
unsafe { LocalFree(p_sd as HLOCAL) };
}
}
/// Internal: apply an EXPLICIT_ACCESS_W entry to a path's DACL.
unsafe fn set_ace_on_path(
path: &Path,
psid: *mut c_void,
access_mode: i32,
permissions: u32,
skip_if_present: Option<(u8, u32)>, // (ace_type, mask) to check before adding
) -> Result<bool> {
let (p_sd, p_dacl) = get_dacl(path)?;
if let Some((ace_type, mask)) = skip_if_present {
if dacl_has_ace_for_sid(p_dacl, psid, ace_type, mask) {
free_sd(p_sd);
return Ok(false);
}
}
let mut explicit: EXPLICIT_ACCESS_W = std::mem::zeroed();
explicit.grfAccessPermissions = permissions;
explicit.grfAccessMode = access_mode;
explicit.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
explicit.Trustee = trustee(psid);
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = unsafe { SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl) };
let mut added = false;
if code2 == ERROR_SUCCESS as u32 {
let code3 = unsafe {
SetNamedSecurityInfoW(
to_wide(path).as_ptr() as *mut u16,
1,
DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
p_new_dacl,
std::ptr::null_mut(),
)
};
if code3 == ERROR_SUCCESS as u32 {
added = true;
}
if !p_new_dacl.is_null() {
unsafe { LocalFree(p_new_dacl as HLOCAL) };
}
}
free_sd(p_sd);
Ok(added)
}
pub unsafe fn add_allow_ace(path: &Path, psid: *mut c_void) -> Result<bool> {
set_ace_on_path(
path,
psid,
GRANT_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
Some((0, FILE_GENERIC_WRITE)), // skip if ALLOW ACE with write exists
)
}
pub unsafe fn add_deny_write_ace(path: &Path, psid: *mut c_void) -> Result<bool> {
set_ace_on_path(
path,
psid,
DENY_ACCESS,
DENY_WRITE_MASK,
Some((1, DENY_WRITE_MASK)), // skip if DENY ACE with write mask exists
)
}
pub unsafe fn revoke_ace(path: &Path, psid: *mut c_void) {
let _ = set_ace_on_path(
path,
psid,
REVOKE_ACCESS,
0,
None, // always revoke
);
}
pub unsafe fn allow_null_device(psid: *mut c_void) {
let desired = 0x00020000 | 0x00040000;
let h = unsafe {
CreateFileW(
to_wide(r"\\.\NUL").as_ptr(),
desired,
FILE_SHARE_READ | FILE_SHARE_WRITE,
std::ptr::null_mut(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0,
)
};
if h == 0 || h == INVALID_HANDLE_VALUE {
return;
}
let mut p_sd: *mut c_void = std::ptr::null_mut();
let mut p_dacl: *mut ACL = std::ptr::null_mut();
let code = unsafe {
GetSecurityInfo(
h,
SE_KERNEL_OBJECT as i32,
DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut p_dacl,
std::ptr::null_mut(),
&mut p_sd,
)
};
if code == ERROR_SUCCESS as u32 {
let mut explicit: EXPLICIT_ACCESS_W = unsafe { std::mem::zeroed() };
explicit.grfAccessPermissions = FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE;
explicit.grfAccessMode = GRANT_ACCESS;
explicit.grfInheritance = 0;
explicit.Trustee = trustee(psid);
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = unsafe { SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl) };
if code2 == ERROR_SUCCESS as u32 {
let _ = unsafe {
SetSecurityInfo(
h,
SE_KERNEL_OBJECT as i32,
DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
p_new_dacl,
std::ptr::null_mut(),
)
};
if !p_new_dacl.is_null() {
unsafe { LocalFree(p_new_dacl as HLOCAL) };
}
}
}
if !p_sd.is_null() {
unsafe { LocalFree(p_sd as HLOCAL) };
}
unsafe { CloseHandle(h) };
}