Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
f1d6258eaa Tighten Windows sandbox restricted SIDs 2026-04-19 05:20:30 -07:00

View File

@@ -1,18 +1,18 @@
use crate::winutil::to_wide;
use anyhow::anyhow;
use anyhow::Result;
use anyhow::anyhow;
use std::ffi::c_void;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Foundation::HLOCAL;
use windows_sys::Win32::Foundation::LUID;
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Security::AdjustTokenPrivileges;
use windows_sys::Win32::Security::Authorization::SetEntriesInAclW;
use windows_sys::Win32::Security::Authorization::EXPLICIT_ACCESS_W;
use windows_sys::Win32::Security::Authorization::GRANT_ACCESS;
use windows_sys::Win32::Security::Authorization::SetEntriesInAclW;
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;
@@ -24,8 +24,6 @@ use windows_sys::Win32::Security::GetTokenInformation;
use windows_sys::Win32::Security::LookupPrivilegeValueW;
use windows_sys::Win32::Security::SetTokenInformation;
use windows_sys::Win32::Security::TokenDefaultDacl;
use windows_sys::Win32::Security::TokenGroups;
use windows_sys::Win32::Security::ACL;
use windows_sys::Win32::Security::SID_AND_ATTRIBUTES;
use windows_sys::Win32::Security::TOKEN_ADJUST_DEFAULT;
@@ -35,6 +33,8 @@ use windows_sys::Win32::Security::TOKEN_ASSIGN_PRIMARY;
use windows_sys::Win32::Security::TOKEN_DUPLICATE;
use windows_sys::Win32::Security::TOKEN_PRIVILEGES;
use windows_sys::Win32::Security::TOKEN_QUERY;
use windows_sys::Win32::Security::TokenDefaultDacl;
use windows_sys::Win32::Security::TokenGroups;
use windows_sys::Win32::System::Threading::GetCurrentProcess;
const DISABLE_MAX_PRIVILEGE: u32 = 0x01;
@@ -135,11 +135,7 @@ pub unsafe fn convert_string_sid_to_sid(s: &str) -> Option<*mut c_void> {
}
let mut psid: *mut c_void = std::ptr::null_mut();
let ok = unsafe { ConvertStringSidToSidW(to_wide(s).as_ptr(), &mut psid) };
if ok != 0 {
Some(psid)
} else {
None
}
if ok != 0 { Some(psid) } else { None }
}
/// # Safety
@@ -327,6 +323,34 @@ pub unsafe fn create_readonly_token_with_caps_from(
create_token_with_caps_from(base_token, psid_capabilities)
}
fn build_restricted_sid_entries(
psid_capabilities: &[*mut c_void],
psid_logon: *mut c_void,
) -> Vec<SID_AND_ATTRIBUTES> {
let mut entries: Vec<SID_AND_ATTRIBUTES> =
vec![unsafe { std::mem::zeroed() }; psid_capabilities.len() + 1];
for (i, psid) in psid_capabilities.iter().enumerate() {
entries[i].Sid = *psid;
entries[i].Attributes = 0;
}
let logon_idx = psid_capabilities.len();
entries[logon_idx].Sid = psid_logon;
entries[logon_idx].Attributes = 0;
entries
}
fn build_default_dacl_sids(
psid_capabilities: &[*mut c_void],
psid_logon: *mut c_void,
psid_everyone: *mut c_void,
) -> Vec<*mut c_void> {
let mut dacl_sids: Vec<*mut c_void> = Vec::with_capacity(psid_capabilities.len() + 2);
dacl_sids.push(psid_logon);
dacl_sids.push(psid_everyone);
dacl_sids.extend_from_slice(psid_capabilities);
dacl_sids
}
unsafe fn create_token_with_caps_from(
base_token: HANDLE,
psid_capabilities: &[*mut c_void],
@@ -339,18 +363,10 @@ unsafe fn create_token_with_caps_from(
let mut everyone = world_sid()?;
let psid_everyone = everyone.as_mut_ptr() as *mut c_void;
// Exact order: Capabilities..., Logon, Everyone
let mut entries: Vec<SID_AND_ATTRIBUTES> =
vec![std::mem::zeroed(); psid_capabilities.len() + 2];
for (i, psid) in psid_capabilities.iter().enumerate() {
entries[i].Sid = *psid;
entries[i].Attributes = 0;
}
let logon_idx = psid_capabilities.len();
entries[logon_idx].Sid = psid_logon;
entries[logon_idx].Attributes = 0;
entries[logon_idx + 1].Sid = psid_everyone;
entries[logon_idx + 1].Attributes = 0;
// Restrict writes to paths that explicitly grant one of our capability SIDs or
// the sandbox logon SID. Including Everyone here would reopen writes on broadly
// writable locations outside the configured workspace roots.
let mut entries = build_restricted_sid_entries(psid_capabilities, psid_logon);
let mut new_token: HANDLE = 0;
let flags = DISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED;
@@ -369,12 +385,46 @@ unsafe fn create_token_with_caps_from(
return Err(anyhow!("CreateRestrictedToken failed: {}", GetLastError()));
}
let mut dacl_sids: Vec<*mut c_void> = Vec::with_capacity(psid_capabilities.len() + 2);
dacl_sids.push(psid_logon);
dacl_sids.push(psid_everyone);
dacl_sids.extend_from_slice(psid_capabilities);
let dacl_sids = build_default_dacl_sids(psid_capabilities, psid_logon, psid_everyone);
set_default_dacl(new_token, &dacl_sids)?;
enable_single_privilege(new_token, "SeChangeNotifyPrivilege")?;
Ok(new_token)
}
#[cfg(test)]
mod tests {
use super::build_default_dacl_sids;
use super::build_restricted_sid_entries;
use std::ffi::c_void;
use std::ptr::null_mut;
fn fake_ptr(value: usize) -> *mut c_void {
value as *mut c_void
}
#[test]
fn restricted_sids_exclude_everyone() {
let caps = [fake_ptr(0x10), fake_ptr(0x20)];
let logon = fake_ptr(0x30);
let everyone = fake_ptr(0x40);
let entries = build_restricted_sid_entries(&caps, logon);
let restricted: Vec<*mut c_void> = entries.iter().map(|entry| entry.Sid).collect();
assert_eq!(restricted, vec![caps[0], caps[1], logon]);
assert!(!restricted.contains(&everyone));
}
#[test]
fn default_dacl_keeps_everyone_for_ipc_compatibility() {
let caps = [fake_ptr(0x10), fake_ptr(0x20)];
let logon = fake_ptr(0x30);
let everyone = fake_ptr(0x40);
let dacl_sids = build_default_dacl_sids(&caps, logon, everyone);
assert_eq!(dacl_sids, vec![logon, everyone, caps[0], caps[1]]);
assert_ne!(dacl_sids[0], null_mut());
}
}