chore: initialize qiming workspace repository
This commit is contained in:
259
qimingclaw/crates/windows-sandbox-helper/src/acl.rs
Normal file
259
qimingclaw/crates/windows-sandbox-helper/src/acl.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
//! 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) };
|
||||
}
|
||||
Reference in New Issue
Block a user