mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
use machine scope instead of user scope for dpapi. (#9713)
This fixes a bug where the elevated sandbox setup encrypts sandbox user passwords as an admin user, but normal command execution attempts to decrypt them as a different user. Machine scope allows all users to encyrpt/decrypt this PR also moves the encrypted file to a different location .codex/.sandbox-secrets which the sandbox users cannot read.
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
use windows_sys::Win32::Foundation::HLOCAL;
|
||||
use windows_sys::Win32::Foundation::LocalFree;
|
||||
use windows_sys::Win32::Foundation::HLOCAL;
|
||||
use windows_sys::Win32::Security::Cryptography::CryptProtectData;
|
||||
use windows_sys::Win32::Security::Cryptography::CryptUnprotectData;
|
||||
use windows_sys::Win32::Security::Cryptography::CRYPT_INTEGER_BLOB;
|
||||
use windows_sys::Win32::Security::Cryptography::CRYPTPROTECT_LOCAL_MACHINE;
|
||||
use windows_sys::Win32::Security::Cryptography::CRYPTPROTECT_UI_FORBIDDEN;
|
||||
use windows_sys::Win32::Security::Cryptography::CRYPT_INTEGER_BLOB;
|
||||
|
||||
fn make_blob(data: &[u8]) -> CRYPT_INTEGER_BLOB {
|
||||
CRYPT_INTEGER_BLOB {
|
||||
@@ -29,12 +30,15 @@ pub fn protect(data: &[u8]) -> Result<Vec<u8>> {
|
||||
std::ptr::null(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
CRYPTPROTECT_UI_FORBIDDEN,
|
||||
// Use machine scope so elevated and non-elevated processes can decrypt.
|
||||
CRYPTPROTECT_UI_FORBIDDEN | CRYPTPROTECT_LOCAL_MACHINE,
|
||||
&mut out_blob,
|
||||
)
|
||||
};
|
||||
if ok == 0 {
|
||||
return Err(anyhow!("CryptProtectData failed: {}", unsafe { GetLastError() }));
|
||||
return Err(anyhow!("CryptProtectData failed: {}", unsafe {
|
||||
GetLastError()
|
||||
}));
|
||||
}
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts(out_blob.pbData, out_blob.cbData as usize) }.to_vec();
|
||||
@@ -60,15 +64,15 @@ pub fn unprotect(blob: &[u8]) -> Result<Vec<u8>> {
|
||||
std::ptr::null(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
CRYPTPROTECT_UI_FORBIDDEN,
|
||||
// Use machine scope so elevated and non-elevated processes can decrypt.
|
||||
CRYPTPROTECT_UI_FORBIDDEN | CRYPTPROTECT_LOCAL_MACHINE,
|
||||
&mut out_blob,
|
||||
)
|
||||
};
|
||||
if ok == 0 {
|
||||
return Err(anyhow!(
|
||||
"CryptUnprotectData failed: {}",
|
||||
unsafe { GetLastError() }
|
||||
));
|
||||
return Err(anyhow!("CryptUnprotectData failed: {}", unsafe {
|
||||
GetLastError()
|
||||
}));
|
||||
}
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts(out_blob.pbData, out_blob.cbData as usize) }.to_vec();
|
||||
|
||||
@@ -63,6 +63,8 @@ pub use setup::run_setup_refresh;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use setup::sandbox_dir;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use setup::sandbox_secrets_dir;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use setup::SETUP_VERSION;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use token::convert_string_sid_to_sid;
|
||||
|
||||
@@ -36,6 +36,7 @@ use windows_sys::Win32::Security::SID_NAME_USE;
|
||||
|
||||
use codex_windows_sandbox::dpapi_protect;
|
||||
use codex_windows_sandbox::sandbox_dir;
|
||||
use codex_windows_sandbox::sandbox_secrets_dir;
|
||||
use codex_windows_sandbox::string_from_sid_bytes;
|
||||
use codex_windows_sandbox::to_wide;
|
||||
use codex_windows_sandbox::SETUP_VERSION;
|
||||
@@ -394,6 +395,8 @@ fn write_secrets(
|
||||
) -> Result<()> {
|
||||
let sandbox_dir = sandbox_dir(codex_home);
|
||||
std::fs::create_dir_all(&sandbox_dir)?;
|
||||
let secrets_dir = sandbox_secrets_dir(codex_home);
|
||||
std::fs::create_dir_all(&secrets_dir)?;
|
||||
let offline_blob = dpapi_protect(offline_pwd.as_bytes())?;
|
||||
let online_blob = dpapi_protect(online_pwd.as_bytes())?;
|
||||
let users = SandboxUsersFile {
|
||||
@@ -415,7 +418,7 @@ fn write_secrets(
|
||||
read_roots: Vec::new(),
|
||||
write_roots: Vec::new(),
|
||||
};
|
||||
let users_path = sandbox_dir.join("sandbox_users.json");
|
||||
let users_path = secrets_dir.join("sandbox_users.json");
|
||||
let marker_path = sandbox_dir.join("setup_marker.json");
|
||||
std::fs::write(users_path, serde_json::to_vec_pretty(&users)?)?;
|
||||
std::fs::write(marker_path, serde_json::to_vec_pretty(&marker)?)?;
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_windows_sandbox::load_or_create_cap_sids;
|
||||
use codex_windows_sandbox::log_note;
|
||||
use codex_windows_sandbox::path_mask_allows;
|
||||
use codex_windows_sandbox::sandbox_dir;
|
||||
use codex_windows_sandbox::sandbox_secrets_dir;
|
||||
use codex_windows_sandbox::string_from_sid_bytes;
|
||||
use codex_windows_sandbox::to_wide;
|
||||
use codex_windows_sandbox::LOG_FILE_NAME;
|
||||
@@ -52,6 +53,8 @@ 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;
|
||||
|
||||
const DENY_ACCESS: i32 = 3;
|
||||
|
||||
mod read_acl_mutex;
|
||||
mod sandbox_users;
|
||||
use read_acl_mutex::acquire_read_acl_mutex;
|
||||
@@ -225,6 +228,7 @@ fn lock_sandbox_dir(
|
||||
dir: &Path,
|
||||
real_user: &str,
|
||||
sandbox_group_sid: &[u8],
|
||||
sandbox_group_access_mode: i32,
|
||||
_log: &mut File,
|
||||
) -> Result<()> {
|
||||
std::fs::create_dir_all(dir)?;
|
||||
@@ -232,27 +236,31 @@ fn lock_sandbox_dir(
|
||||
let admins_sid = resolve_sid("Administrators")?;
|
||||
let real_sid = resolve_sid(real_user)?;
|
||||
let entries = [
|
||||
(
|
||||
sandbox_group_sid.to_vec(),
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
|
||||
sandbox_group_access_mode,
|
||||
),
|
||||
(
|
||||
system_sid,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
|
||||
GRANT_ACCESS,
|
||||
),
|
||||
(
|
||||
admins_sid,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
|
||||
GRANT_ACCESS,
|
||||
),
|
||||
(
|
||||
real_sid,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
|
||||
),
|
||||
(
|
||||
sandbox_group_sid.to_vec(),
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
|
||||
GRANT_ACCESS,
|
||||
),
|
||||
];
|
||||
unsafe {
|
||||
let mut eas: Vec<EXPLICIT_ACCESS_W> = Vec::new();
|
||||
let mut sids: Vec<*mut c_void> = Vec::new();
|
||||
for (sid_bytes, mask) in entries.iter().map(|(s, m)| (s, *m)) {
|
||||
for (sid_bytes, mask, access_mode) in entries.iter().map(|(s, m, a)| (s, *m, *a)) {
|
||||
let sid_str = string_from_sid_bytes(sid_bytes).map_err(anyhow::Error::msg)?;
|
||||
let sid_w = to_wide(OsStr::new(&sid_str));
|
||||
let mut psid: *mut c_void = std::ptr::null_mut();
|
||||
@@ -265,7 +273,7 @@ fn lock_sandbox_dir(
|
||||
sids.push(psid);
|
||||
eas.push(EXPLICIT_ACCESS_W {
|
||||
grfAccessPermissions: mask,
|
||||
grfAccessMode: GRANT_ACCESS,
|
||||
grfAccessMode: access_mode,
|
||||
grfInheritance: OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
|
||||
Trustee: TRUSTEE_W {
|
||||
pMultipleTrustee: std::ptr::null_mut(),
|
||||
@@ -605,8 +613,20 @@ fn run_setup_full(payload: &Payload, log: &mut File, sbx_dir: &Path) -> Result<(
|
||||
&sandbox_dir(&payload.codex_home),
|
||||
&payload.real_user,
|
||||
&sandbox_group_sid,
|
||||
GRANT_ACCESS,
|
||||
log,
|
||||
)?;
|
||||
lock_sandbox_dir(
|
||||
&sandbox_secrets_dir(&payload.codex_home),
|
||||
&payload.real_user,
|
||||
&sandbox_group_sid,
|
||||
DENY_ACCESS,
|
||||
log,
|
||||
)?;
|
||||
let legacy_users = sandbox_dir(&payload.codex_home).join("sandbox_users.json");
|
||||
if legacy_users.exists() {
|
||||
let _ = std::fs::remove_file(&legacy_users);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
if !sandbox_group_psid.is_null() {
|
||||
|
||||
@@ -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 = 4;
|
||||
pub const SETUP_VERSION: u32 = 5;
|
||||
pub const OFFLINE_USERNAME: &str = "CodexSandboxOffline";
|
||||
pub const ONLINE_USERNAME: &str = "CodexSandboxOnline";
|
||||
const SECURITY_BUILTIN_DOMAIN_RID: u32 = 0x0000_0020;
|
||||
@@ -36,12 +36,16 @@ pub fn sandbox_dir(codex_home: &Path) -> PathBuf {
|
||||
codex_home.join(".sandbox")
|
||||
}
|
||||
|
||||
pub fn sandbox_secrets_dir(codex_home: &Path) -> PathBuf {
|
||||
codex_home.join(".sandbox-secrets")
|
||||
}
|
||||
|
||||
pub fn setup_marker_path(codex_home: &Path) -> PathBuf {
|
||||
sandbox_dir(codex_home).join("setup_marker.json")
|
||||
}
|
||||
|
||||
pub fn sandbox_users_path(codex_home: &Path) -> PathBuf {
|
||||
sandbox_dir(codex_home).join("sandbox_users.json")
|
||||
sandbox_secrets_dir(codex_home).join("sandbox_users.json")
|
||||
}
|
||||
|
||||
pub fn run_setup_refresh(
|
||||
@@ -430,10 +434,16 @@ fn filter_sensitive_write_roots(mut roots: Vec<PathBuf>, codex_home: &Path) -> V
|
||||
let codex_home_key = crate::audit::normalize_path_key(codex_home);
|
||||
let sbx_dir_key = crate::audit::normalize_path_key(&sandbox_dir(codex_home));
|
||||
let sbx_dir_prefix = format!("{}/", sbx_dir_key.trim_end_matches('/'));
|
||||
let secrets_dir_key = crate::audit::normalize_path_key(&sandbox_secrets_dir(codex_home));
|
||||
let secrets_dir_prefix = format!("{}/", secrets_dir_key.trim_end_matches('/'));
|
||||
|
||||
roots.retain(|root| {
|
||||
let key = crate::audit::normalize_path_key(root);
|
||||
key != codex_home_key && key != sbx_dir_key && !key.starts_with(&sbx_dir_prefix)
|
||||
key != codex_home_key
|
||||
&& key != sbx_dir_key
|
||||
&& !key.starts_with(&sbx_dir_prefix)
|
||||
&& key != secrets_dir_key
|
||||
&& !key.starts_with(&secrets_dir_prefix)
|
||||
});
|
||||
roots
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user