lookup system SIDs instead of hardcoding English strings. (#9552)

The elevated setup does not work on non-English windows installs where
Users/Administrators/etc are in different languages. This PR uses the
well-known SIDs instead, which do not vary based on locale
This commit is contained in:
iceweasel-oai
2026-01-20 13:55:37 -08:00
committed by GitHub
parent b0049ab644
commit 9ea8e3115e
2 changed files with 129 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
use windows_sys::Win32::NetworkManagement::NetManagement::NERR_Success;
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAdd;
@@ -27,7 +28,10 @@ use windows_sys::Win32::NetworkManagement::NetManagement::USER_INFO_1;
use windows_sys::Win32::NetworkManagement::NetManagement::USER_INFO_1003;
use windows_sys::Win32::NetworkManagement::NetManagement::USER_PRIV_USER;
use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;
use windows_sys::Win32::Security::CopySid;
use windows_sys::Win32::Security::GetLengthSid;
use windows_sys::Win32::Security::LookupAccountNameW;
use windows_sys::Win32::Security::LookupAccountSidW;
use windows_sys::Win32::Security::SID_NAME_USE;
use codex_windows_sandbox::dpapi_protect;
@@ -38,6 +42,11 @@ use codex_windows_sandbox::SETUP_VERSION;
pub const SANDBOX_USERS_GROUP: &str = "CodexSandboxUsers";
const SANDBOX_USERS_GROUP_COMMENT: &str = "Codex sandbox internal group (managed)";
const SID_ADMINISTRATORS: &str = "S-1-5-32-544";
const SID_USERS: &str = "S-1-5-32-545";
const SID_AUTHENTICATED_USERS: &str = "S-1-5-11";
const SID_EVERYONE: &str = "S-1-1-0";
const SID_SYSTEM: &str = "S-1-5-18";
pub fn ensure_sandbox_users_group(log: &mut File) -> Result<()> {
ensure_local_group(SANDBOX_USERS_GROUP, SANDBOX_USERS_GROUP_COMMENT, log)
@@ -119,17 +128,24 @@ pub fn ensure_local_user(name: &str, password: &str, log: &mut File) -> Result<(
}
// Ensure the principal is a regular local user account.
let group = to_wide(OsStr::new("Users"));
let member = LOCALGROUP_MEMBERS_INFO_3 {
lgrmi3_domainandname: name_w.as_ptr() as *mut u16,
};
let _ = NetLocalGroupAddMembers(
std::ptr::null(),
group.as_ptr(),
3,
&member as *const _ as *mut u8,
1,
);
if let Ok(group_name) = lookup_account_name_for_sid(SID_USERS) {
let group = to_wide(OsStr::new(&group_name));
let member = LOCALGROUP_MEMBERS_INFO_3 {
lgrmi3_domainandname: name_w.as_ptr() as *mut u16,
};
let _ = NetLocalGroupAddMembers(
std::ptr::null(),
group.as_ptr(),
3,
&member as *const _ as *mut u8,
1,
);
} else {
super::log_line(
log,
"LookupAccountSidW failed for Users SID; skipping Users group membership",
)?;
}
}
Ok(())
}
@@ -184,6 +200,9 @@ pub fn ensure_local_group_member(group_name: &str, member_name: &str) -> Result<
}
pub fn resolve_sid(name: &str) -> Result<Vec<u8>> {
if let Some(sid_str) = well_known_sid_str(name) {
return sid_bytes_from_string(sid_str);
}
let name_w = to_wide(OsStr::new(name));
let mut sid_buffer = vec![0u8; 68];
let mut sid_len: u32 = sid_buffer.len() as u32;
@@ -218,6 +237,104 @@ pub fn resolve_sid(name: &str) -> Result<Vec<u8>> {
}
}
fn well_known_sid_str(name: &str) -> Option<&'static str> {
match name {
"Administrators" => Some(SID_ADMINISTRATORS),
"Users" => Some(SID_USERS),
"Authenticated Users" => Some(SID_AUTHENTICATED_USERS),
"Everyone" => Some(SID_EVERYONE),
"SYSTEM" => Some(SID_SYSTEM),
_ => None,
}
}
fn sid_bytes_from_string(sid_str: &str) -> Result<Vec<u8>> {
let sid_w = to_wide(OsStr::new(sid_str));
let mut psid: *mut c_void = std::ptr::null_mut();
if unsafe { ConvertStringSidToSidW(sid_w.as_ptr(), &mut psid) } == 0 {
return Err(anyhow::anyhow!(
"ConvertStringSidToSidW failed for {sid_str}: {}",
unsafe { GetLastError() }
));
}
let sid_len = unsafe { GetLengthSid(psid) };
if sid_len == 0 {
unsafe {
LocalFree(psid as _);
}
return Err(anyhow::anyhow!("GetLengthSid failed for {sid_str}"));
}
let mut out = vec![0u8; sid_len as usize];
let ok = unsafe { CopySid(sid_len, out.as_mut_ptr() as *mut c_void, psid) };
unsafe {
LocalFree(psid as _);
}
if ok == 0 {
return Err(anyhow::anyhow!("CopySid failed for {sid_str}"));
}
Ok(out)
}
fn lookup_account_name_for_sid(sid_str: &str) -> Result<String> {
let sid_w = to_wide(OsStr::new(sid_str));
let mut psid: *mut c_void = std::ptr::null_mut();
if unsafe { ConvertStringSidToSidW(sid_w.as_ptr(), &mut psid) } == 0 {
return Err(anyhow::anyhow!(
"ConvertStringSidToSidW failed for {sid_str}: {}",
unsafe { GetLastError() }
));
}
let mut name_len: u32 = 0;
let mut domain_len: u32 = 0;
let mut use_type: SID_NAME_USE = 0;
let ok = unsafe {
LookupAccountSidW(
std::ptr::null(),
psid,
std::ptr::null_mut(),
&mut name_len,
std::ptr::null_mut(),
&mut domain_len,
&mut use_type,
)
};
if ok == 0 {
let err = unsafe { GetLastError() };
if err != ERROR_INSUFFICIENT_BUFFER {
unsafe {
LocalFree(psid as _);
}
return Err(anyhow::anyhow!(
"LookupAccountSidW preflight failed for {sid_str}: {err}"
));
}
}
let mut name_buf: Vec<u16> = vec![0u16; name_len as usize];
let mut domain_buf: Vec<u16> = vec![0u16; domain_len as usize];
let ok = unsafe {
LookupAccountSidW(
std::ptr::null(),
psid,
name_buf.as_mut_ptr(),
&mut name_len,
domain_buf.as_mut_ptr(),
&mut domain_len,
&mut use_type,
)
};
unsafe {
LocalFree(psid as _);
}
if ok == 0 {
return Err(anyhow::anyhow!(
"LookupAccountSidW failed for {sid_str}: {}",
unsafe { GetLastError() }
));
}
let name = String::from_utf16_lossy(&name_buf);
Ok(name.trim_end_matches('\0').to_string())
}
pub fn sid_bytes_to_psid(sid: &[u8]) -> Result<*mut c_void> {
let sid_str = string_from_sid_bytes(sid).map_err(anyhow::Error::msg)?;
let sid_w = to_wide(OsStr::new(&sid_str));

View File

@@ -26,7 +26,7 @@ use windows_sys::Win32::Security::CheckTokenMembership;
use windows_sys::Win32::Security::FreeSid;
use windows_sys::Win32::Security::SECURITY_NT_AUTHORITY;
pub const SETUP_VERSION: u32 = 3;
pub const SETUP_VERSION: u32 = 4;
pub const OFFLINE_USERNAME: &str = "CodexSandboxOffline";
pub const ONLINE_USERNAME: &str = "CodexSandboxOnline";
const SECURITY_BUILTIN_DOMAIN_RID: u32 = 0x0000_0020;