Files
codex/codex-rs/windows-sandbox-rs/src/acl.rs
T
iceweasel-oaiandGitHub 3f92ad4190 add deny ACEs for world writable dirs (#7022)
Our Restricted Token contains 3 SIDs (Logon, Everyone, {WorkspaceWrite
Capability || ReadOnly Capability})

because it must include Everyone, that left us vulnerable to directories
that allow writes to Everyone. Even though those directories do not have
ACEs that enable our capability SIDs to write to them, they could still
be written to even in ReadOnly mode, or even in WorkspaceWrite mode if
they are outside of a writable root.

A solution to this is to explicitly add *Deny* ACEs to these
directories, always for the ReadOnly Capability SID, and for the
WorkspaceWrite SID if the directory is outside of a workspace root.

Under a restricted token, Windows always checks Deny ACEs before Allow
ACEs so even though our restricted token would allow a write to these
directories due to the Everyone SID, it fails first because of the Deny
ACE on the capability SID
2025-11-20 14:50:33 -08:00

397 lines
14 KiB
Rust

use crate::winutil::to_wide;
use anyhow::anyhow;
use anyhow::Result;
use std::ffi::c_void;
use std::path::Path;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
use windows_sys::Win32::Foundation::HLOCAL;
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::Security::AclSizeInformation;
use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;
use windows_sys::Win32::Security::Authorization::GetSecurityInfo;
use windows_sys::Win32::Security::Authorization::SetEntriesInAclW;
use windows_sys::Win32::Security::Authorization::SetNamedSecurityInfoW;
use windows_sys::Win32::Security::Authorization::SetSecurityInfo;
use windows_sys::Win32::Security::Authorization::EXPLICIT_ACCESS_W;
use windows_sys::Win32::Security::Authorization::TRUSTEE_IS_SID;
use windows_sys::Win32::Security::Authorization::TRUSTEE_IS_UNKNOWN;
use windows_sys::Win32::Security::Authorization::TRUSTEE_W;
use windows_sys::Win32::Security::EqualSid;
use windows_sys::Win32::Security::GetAce;
use windows_sys::Win32::Security::GetAclInformation;
use windows_sys::Win32::Security::ACCESS_ALLOWED_ACE;
use windows_sys::Win32::Security::ACE_HEADER;
use windows_sys::Win32::Security::ACL;
use windows_sys::Win32::Security::ACL_SIZE_INFORMATION;
use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION;
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_NORMAL;
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_EXECUTE;
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_READ;
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE;
use windows_sys::Win32::Storage::FileSystem::FILE_APPEND_DATA;
use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_ATTRIBUTES;
use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_DATA;
use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_EA;
use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_READ;
use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_WRITE;
use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING;
const SE_KERNEL_OBJECT: u32 = 6;
const INHERIT_ONLY_ACE: u8 = 0x08;
const GENERIC_WRITE_MASK: u32 = 0x4000_0000;
const DENY_ACCESS: i32 = 3;
pub unsafe fn dacl_has_write_allow_for_sid(p_dacl: *mut ACL, psid: *mut c_void) -> bool {
if p_dacl.is_null() {
return false;
}
let mut info: ACL_SIZE_INFORMATION = std::mem::zeroed();
let ok = 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;
}
let count = info.AceCount as usize;
for i in 0..count {
let mut p_ace: *mut c_void = std::ptr::null_mut();
if GetAce(p_dacl as *const ACL, i as u32, &mut p_ace) == 0 {
continue;
}
let hdr = &*(p_ace as *const ACE_HEADER);
if hdr.AceType != 0 {
continue; // ACCESS_ALLOWED_ACE_TYPE
}
// Ignore ACEs that are inherit-only (do not apply to the current object)
if (hdr.AceFlags & INHERIT_ONLY_ACE) != 0 {
continue;
}
let ace = &*(p_ace as *const ACCESS_ALLOWED_ACE);
let mask = ace.Mask;
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;
let eq = EqualSid(sid_ptr, psid);
if eq != 0 && (mask & FILE_GENERIC_WRITE) != 0 {
return true;
}
}
false
}
pub unsafe fn dacl_has_write_deny_for_sid(p_dacl: *mut ACL, psid: *mut c_void) -> bool {
if p_dacl.is_null() {
return false;
}
let mut info: ACL_SIZE_INFORMATION = std::mem::zeroed();
let ok = 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;
}
let deny_write_mask = FILE_GENERIC_WRITE
| FILE_WRITE_DATA
| FILE_APPEND_DATA
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
| GENERIC_WRITE_MASK;
for i in 0..info.AceCount {
let mut p_ace: *mut c_void = std::ptr::null_mut();
if GetAce(p_dacl as *const ACL, i, &mut p_ace) == 0 {
continue;
}
let hdr = &*(p_ace as *const ACE_HEADER);
if hdr.AceType != 1 {
continue; // ACCESS_DENIED_ACE_TYPE
}
if (hdr.AceFlags & INHERIT_ONLY_ACE) != 0 {
continue;
}
let ace = &*(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 EqualSid(sid_ptr, psid) != 0 && (ace.Mask & deny_write_mask) != 0 {
return true;
}
}
false
}
// Compute effective rights for a trustee SID against a DACL and decide if write is effectively allowed.
// This accounts for deny ACEs and ordering; falls back to a conservative per-ACE scan if the API fails.
#[allow(dead_code)]
pub unsafe fn dacl_effective_allows_write(p_dacl: *mut ACL, psid: *mut c_void) -> bool {
if p_dacl.is_null() {
return false;
}
use windows_sys::Win32::Security::Authorization::GetEffectiveRightsFromAclW;
use windows_sys::Win32::Security::Authorization::TRUSTEE_IS_SID;
use windows_sys::Win32::Security::Authorization::TRUSTEE_IS_UNKNOWN;
use windows_sys::Win32::Security::Authorization::TRUSTEE_W;
let trustee = TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
};
let mut access: u32 = 0;
let ok = GetEffectiveRightsFromAclW(p_dacl, &trustee, &mut access);
if ok != 0 {
// Check for generic or specific write bits
let write_bits = FILE_GENERIC_WRITE
| windows_sys::Win32::Storage::FileSystem::FILE_WRITE_DATA
| windows_sys::Win32::Storage::FileSystem::FILE_APPEND_DATA
| windows_sys::Win32::Storage::FileSystem::FILE_WRITE_EA
| windows_sys::Win32::Storage::FileSystem::FILE_WRITE_ATTRIBUTES;
return (access & write_bits) != 0;
}
// Fallback: simple allow ACE scan (already ignores inherit-only)
dacl_has_write_allow_for_sid(p_dacl, psid)
}
pub unsafe fn add_allow_ace(path: &Path, psid: *mut c_void) -> Result<bool> {
let mut p_sd: *mut c_void = std::ptr::null_mut();
let mut p_dacl: *mut ACL = std::ptr::null_mut();
let code = 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 {
return Err(anyhow!("GetNamedSecurityInfoW failed: {}", code));
}
let mut added = false;
if !dacl_has_write_allow_for_sid(p_dacl, psid) {
let trustee = TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
};
let mut explicit: EXPLICIT_ACCESS_W = std::mem::zeroed();
explicit.grfAccessPermissions =
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE;
explicit.grfAccessMode = 2; // SET_ACCESS
explicit.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
explicit.Trustee = trustee;
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl);
if code2 == ERROR_SUCCESS {
let code3 = 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 {
added = true;
}
if !p_new_dacl.is_null() {
LocalFree(p_new_dacl as HLOCAL);
}
}
}
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
Ok(added)
}
pub unsafe fn add_deny_write_ace(path: &Path, psid: *mut c_void) -> Result<bool> {
let mut p_sd: *mut c_void = std::ptr::null_mut();
let mut p_dacl: *mut ACL = std::ptr::null_mut();
let code = 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 {
return Err(anyhow!("GetNamedSecurityInfoW failed: {}", code));
}
let mut added = false;
if !dacl_has_write_deny_for_sid(p_dacl, psid) {
let trustee = TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
};
let mut explicit: EXPLICIT_ACCESS_W = std::mem::zeroed();
explicit.grfAccessPermissions = FILE_GENERIC_WRITE
| FILE_WRITE_DATA
| FILE_APPEND_DATA
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
| GENERIC_WRITE_MASK;
explicit.grfAccessMode = DENY_ACCESS;
explicit.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
explicit.Trustee = trustee;
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl);
if code2 == ERROR_SUCCESS {
let code3 = 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 {
added = true;
}
if !p_new_dacl.is_null() {
LocalFree(p_new_dacl as HLOCAL);
}
}
}
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
Ok(added)
}
pub unsafe fn revoke_ace(path: &Path, psid: *mut c_void) {
let mut p_sd: *mut c_void = std::ptr::null_mut();
let mut p_dacl: *mut ACL = std::ptr::null_mut();
let code = 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 {
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
return;
}
let trustee = TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
};
let mut explicit: EXPLICIT_ACCESS_W = std::mem::zeroed();
explicit.grfAccessPermissions = 0;
explicit.grfAccessMode = 4; // REVOKE_ACCESS
explicit.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
explicit.Trustee = trustee;
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl);
if code2 == ERROR_SUCCESS {
let _ = 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 !p_new_dacl.is_null() {
LocalFree(p_new_dacl as HLOCAL);
}
}
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
}
pub unsafe fn allow_null_device(psid: *mut c_void) {
let desired = 0x00020000 | 0x00040000; // READ_CONTROL | WRITE_DAC
let h = 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 = 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 {
let trustee = TRUSTEE_W {
pMultipleTrustee: std::ptr::null_mut(),
MultipleTrusteeOperation: 0,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_UNKNOWN,
ptstrName: psid as *mut u16,
};
let mut explicit: EXPLICIT_ACCESS_W = std::mem::zeroed();
explicit.grfAccessPermissions =
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE;
explicit.grfAccessMode = 2; // SET_ACCESS
explicit.grfInheritance = 0;
explicit.Trustee = trustee;
let mut p_new_dacl: *mut ACL = std::ptr::null_mut();
let code2 = SetEntriesInAclW(1, &explicit, p_dacl, &mut p_new_dacl);
if code2 == ERROR_SUCCESS {
let _ = 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() {
LocalFree(p_new_dacl as HLOCAL);
}
}
}
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
CloseHandle(h);
}
const CONTAINER_INHERIT_ACE: u32 = 0x2;
const OBJECT_INHERIT_ACE: u32 = 0x1;