mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Add a Windows sandbox provisioning protocol (#42334)
## What changed - Define a versioned, length-prefixed JSON request/response protocol for the machine-wide Windows sandbox provisioning service, including its named pipe and network setup parameters. - Add a readiness check that compares the recorded proxy ports and local-binding policy with the requested provisioning settings. - Share framed JSON I/O, sandbox-group creation, and SID resolution between the existing elevated setup path and provisioning clients. GitOrigin-RevId: d6250542d06f7646ffeb92150472e7d7fa1e5e66
This commit is contained in:
1 parent
dc0dc4f15d
commit
301a7c5e01
8 files changed
+199
-156
No files matched your search
@@ -22,6 +22,7 @@ use codex_windows_sandbox::log_note;
|
|||||||
use codex_windows_sandbox::log_writer;
|
use codex_windows_sandbox::log_writer;
|
||||||
use codex_windows_sandbox::path_mask_allows;
|
use codex_windows_sandbox::path_mask_allows;
|
||||||
use codex_windows_sandbox::path_write_aces_need_refresh;
|
use codex_windows_sandbox::path_write_aces_need_refresh;
|
||||||
|
use codex_windows_sandbox::resolve_sid;
|
||||||
use codex_windows_sandbox::sandbox_bin_dir;
|
use codex_windows_sandbox::sandbox_bin_dir;
|
||||||
use codex_windows_sandbox::sandbox_dir;
|
use codex_windows_sandbox::sandbox_dir;
|
||||||
use codex_windows_sandbox::sandbox_secrets_dir;
|
use codex_windows_sandbox::sandbox_secrets_dir;
|
||||||
@@ -80,7 +81,6 @@ use sandbox_users::commit_setup_marker;
|
|||||||
use sandbox_users::prepare_setup_marker;
|
use sandbox_users::prepare_setup_marker;
|
||||||
use sandbox_users::provision_sandbox_users;
|
use sandbox_users::provision_sandbox_users;
|
||||||
use sandbox_users::resolve_sandbox_users_group_sid;
|
use sandbox_users::resolve_sandbox_users_group_sid;
|
||||||
use sandbox_users::resolve_sid;
|
|
||||||
use sandbox_users::sid_bytes_to_psid;
|
use sandbox_users::sid_bytes_to_psid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
|||||||
@@ -16,10 +16,8 @@ use windows_sys::Win32::Foundation::GENERIC_WRITE;
|
|||||||
use windows_sys::Win32::Foundation::GetLastError;
|
use windows_sys::Win32::Foundation::GetLastError;
|
||||||
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
|
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
|
||||||
use windows_sys::Win32::Foundation::LocalFree;
|
use windows_sys::Win32::Foundation::LocalFree;
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::LOCALGROUP_INFO_1;
|
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::LOCALGROUP_MEMBERS_INFO_3;
|
use windows_sys::Win32::NetworkManagement::NetManagement::LOCALGROUP_MEMBERS_INFO_3;
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::NERR_Success;
|
use windows_sys::Win32::NetworkManagement::NetManagement::NERR_Success;
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAdd;
|
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAddMembers;
|
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAddMembers;
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetUserAdd;
|
use windows_sys::Win32::NetworkManagement::NetManagement::NetUserAdd;
|
||||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetUserSetInfo;
|
use windows_sys::Win32::NetworkManagement::NetManagement::NetUserSetInfo;
|
||||||
@@ -31,9 +29,6 @@ use windows_sys::Win32::NetworkManagement::NetManagement::USER_PRIV_USER;
|
|||||||
use windows_sys::Win32::Security::Authorization::ConvertStringSecurityDescriptorToSecurityDescriptorW;
|
use windows_sys::Win32::Security::Authorization::ConvertStringSecurityDescriptorToSecurityDescriptorW;
|
||||||
use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;
|
use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;
|
||||||
use windows_sys::Win32::Security::Authorization::SDDL_REVISION_1;
|
use windows_sys::Win32::Security::Authorization::SDDL_REVISION_1;
|
||||||
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::LookupAccountSidW;
|
||||||
use windows_sys::Win32::Security::PSECURITY_DESCRIPTOR;
|
use windows_sys::Win32::Security::PSECURITY_DESCRIPTOR;
|
||||||
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
|
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
|
||||||
@@ -42,26 +37,19 @@ use windows_sys::Win32::Storage::FileSystem::CREATE_NEW;
|
|||||||
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
|
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
|
||||||
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_NORMAL;
|
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_NORMAL;
|
||||||
|
|
||||||
|
use codex_windows_sandbox::SANDBOX_USERS_GROUP;
|
||||||
use codex_windows_sandbox::SETUP_VERSION;
|
use codex_windows_sandbox::SETUP_VERSION;
|
||||||
use codex_windows_sandbox::SetupErrorCode;
|
use codex_windows_sandbox::SetupErrorCode;
|
||||||
use codex_windows_sandbox::SetupFailure;
|
use codex_windows_sandbox::SetupFailure;
|
||||||
use codex_windows_sandbox::dpapi_protect;
|
use codex_windows_sandbox::dpapi_protect;
|
||||||
|
use codex_windows_sandbox::ensure_sandbox_users_group;
|
||||||
|
use codex_windows_sandbox::resolve_sid;
|
||||||
use codex_windows_sandbox::sandbox_dir;
|
use codex_windows_sandbox::sandbox_dir;
|
||||||
use codex_windows_sandbox::sandbox_secrets_dir;
|
use codex_windows_sandbox::sandbox_secrets_dir;
|
||||||
use codex_windows_sandbox::string_from_sid_bytes;
|
use codex_windows_sandbox::string_from_sid_bytes;
|
||||||
use codex_windows_sandbox::to_wide;
|
use codex_windows_sandbox::to_wide;
|
||||||
|
|
||||||
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_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 dyn Write) -> Result<()> {
|
|
||||||
ensure_local_group(SANDBOX_USERS_GROUP, SANDBOX_USERS_GROUP_COMMENT, log)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_sandbox_users_group_sid() -> Result<Vec<u8>> {
|
pub fn resolve_sandbox_users_group_sid() -> Result<Vec<u8>> {
|
||||||
resolve_sid(SANDBOX_USERS_GROUP)
|
resolve_sid(SANDBOX_USERS_GROUP)
|
||||||
@@ -73,7 +61,14 @@ pub fn provision_sandbox_users(
|
|||||||
online_username: &str,
|
online_username: &str,
|
||||||
log: &mut dyn Write,
|
log: &mut dyn Write,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
ensure_sandbox_users_group(log)?;
|
if let Err(err) = ensure_sandbox_users_group() {
|
||||||
|
let message = format!("failed to create local group {SANDBOX_USERS_GROUP}: {err}");
|
||||||
|
super::log_line(log, &message)?;
|
||||||
|
return Err(anyhow::Error::new(SetupFailure::new(
|
||||||
|
SetupErrorCode::HelperUsersGroupCreateFailed,
|
||||||
|
message,
|
||||||
|
)));
|
||||||
|
}
|
||||||
super::log_line(
|
super::log_line(
|
||||||
log,
|
log,
|
||||||
&format!("ensuring sandbox users offline={offline_username} online={online_username}"),
|
&format!("ensuring sandbox users offline={offline_username} online={online_username}"),
|
||||||
@@ -162,38 +157,6 @@ pub fn ensure_local_user(name: &str, password: &str, log: &mut dyn Write) -> Res
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_local_group(name: &str, comment: &str, log: &mut dyn Write) -> Result<()> {
|
|
||||||
const ERROR_ALIAS_EXISTS: u32 = 1379;
|
|
||||||
const NERR_GROUP_EXISTS: u32 = 2223;
|
|
||||||
|
|
||||||
let name_w = to_wide(OsStr::new(name));
|
|
||||||
let comment_w = to_wide(OsStr::new(comment));
|
|
||||||
unsafe {
|
|
||||||
let info = LOCALGROUP_INFO_1 {
|
|
||||||
lgrpi1_name: name_w.as_ptr() as *mut u16,
|
|
||||||
lgrpi1_comment: comment_w.as_ptr() as *mut u16,
|
|
||||||
};
|
|
||||||
let mut parm_err: u32 = 0;
|
|
||||||
let status = NetLocalGroupAdd(
|
|
||||||
std::ptr::null(),
|
|
||||||
1,
|
|
||||||
&info as *const _ as *mut u8,
|
|
||||||
&mut parm_err as *mut _,
|
|
||||||
);
|
|
||||||
if status != NERR_Success && status != ERROR_ALIAS_EXISTS && status != NERR_GROUP_EXISTS {
|
|
||||||
super::log_line(
|
|
||||||
log,
|
|
||||||
&format!("NetLocalGroupAdd failed for {name} code {status} parm_err={parm_err}"),
|
|
||||||
)?;
|
|
||||||
return Err(anyhow::Error::new(SetupFailure::new(
|
|
||||||
SetupErrorCode::HelperUsersGroupCreateFailed,
|
|
||||||
format!("failed to create local group {name}, code {status}"),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ensure_local_group_member(group_name: &str, member_name: &str) -> Result<()> {
|
pub fn ensure_local_group_member(group_name: &str, member_name: &str) -> Result<()> {
|
||||||
// If the member is already in the group, NetLocalGroupAddMembers may
|
// If the member is already in the group, NetLocalGroupAddMembers may
|
||||||
// return an error code. We don't care.
|
// return an error code. We don't care.
|
||||||
@@ -214,82 +177,6 @@ pub fn ensure_local_group_member(group_name: &str, member_name: &str) -> Result<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
let mut domain: Vec<u16> = Vec::new();
|
|
||||||
let mut domain_len: u32 = 0;
|
|
||||||
let mut use_type: SID_NAME_USE = 0;
|
|
||||||
loop {
|
|
||||||
let ok = unsafe {
|
|
||||||
LookupAccountNameW(
|
|
||||||
std::ptr::null(),
|
|
||||||
name_w.as_ptr(),
|
|
||||||
sid_buffer.as_mut_ptr() as *mut c_void,
|
|
||||||
&mut sid_len,
|
|
||||||
domain.as_mut_ptr(),
|
|
||||||
&mut domain_len,
|
|
||||||
&mut use_type,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ok != 0 {
|
|
||||||
sid_buffer.truncate(sid_len as usize);
|
|
||||||
return Ok(sid_buffer);
|
|
||||||
}
|
|
||||||
let err = unsafe { GetLastError() };
|
|
||||||
if err == ERROR_INSUFFICIENT_BUFFER {
|
|
||||||
sid_buffer.resize(sid_len as usize, 0);
|
|
||||||
domain.resize(domain_len as usize, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"LookupAccountNameW failed for {name}: {err}"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
fn lookup_account_name_for_sid(sid_str: &str) -> Result<String> {
|
||||||
let sid_w = to_wide(OsStr::new(sid_str));
|
let sid_w = to_wide(OsStr::new(sid_str));
|
||||||
let mut psid: *mut c_void = std::ptr::null_mut();
|
let mut psid: *mut c_void = std::ptr::null_mut();
|
||||||
|
|||||||
@@ -19,12 +19,6 @@ use std::io::Read;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Safety cap for a single framed message payload.
|
|
||||||
///
|
|
||||||
/// This is not a protocol requirement; it simply bounds memory use and rejects
|
|
||||||
/// obviously invalid frames.
|
|
||||||
const MAX_FRAME_LEN: usize = 8 * 1024 * 1024;
|
|
||||||
|
|
||||||
/// Protocol version shared by the parent process and elevated command runner.
|
/// Protocol version shared by the parent process and elevated command runner.
|
||||||
pub const IPC_PROTOCOL_VERSION: u8 = 6;
|
pub const IPC_PROTOCOL_VERSION: u8 = 6;
|
||||||
|
|
||||||
@@ -151,34 +145,13 @@ pub fn decode_bytes(data: &str) -> Result<Vec<u8>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write a length-prefixed JSON frame.
|
/// Write a length-prefixed JSON frame.
|
||||||
pub fn write_frame<W: Write>(mut writer: W, msg: &FramedMessage) -> Result<()> {
|
pub fn write_frame<W: Write>(writer: W, msg: &FramedMessage) -> Result<()> {
|
||||||
let payload = serde_json::to_vec(msg)?;
|
crate::framed_io::write_frame(writer, msg)
|
||||||
if payload.len() > MAX_FRAME_LEN {
|
|
||||||
anyhow::bail!("frame too large: {}", payload.len());
|
|
||||||
}
|
|
||||||
let len = payload.len() as u32;
|
|
||||||
writer.write_all(&len.to_le_bytes())?;
|
|
||||||
writer.write_all(&payload)?;
|
|
||||||
writer.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a length-prefixed JSON frame; returns `Ok(None)` on EOF.
|
/// Read a length-prefixed JSON frame; returns `Ok(None)` on EOF.
|
||||||
pub fn read_frame<R: Read>(mut reader: R) -> Result<Option<FramedMessage>> {
|
pub fn read_frame<R: Read>(reader: R) -> Result<Option<FramedMessage>> {
|
||||||
let mut len_buf = [0u8; 4];
|
crate::framed_io::read_frame(reader)
|
||||||
match reader.read_exact(&mut len_buf) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
}
|
|
||||||
let len = u32::from_le_bytes(len_buf) as usize;
|
|
||||||
if len > MAX_FRAME_LEN {
|
|
||||||
anyhow::bail!("frame too large: {len}");
|
|
||||||
}
|
|
||||||
let mut payload = vec![0u8; len];
|
|
||||||
reader.read_exact(&mut payload)?;
|
|
||||||
let msg: FramedMessage = serde_json::from_slice(&payload)?;
|
|
||||||
Ok(Some(msg))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//! Shared length-prefixed JSON framing for independent Windows sandbox IPC protocols.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
/// Bound the memory used by an individual untrusted IPC frame.
|
||||||
|
const MAX_FRAME_LEN: usize = 8 * 1024 * 1024;
|
||||||
|
|
||||||
|
pub(crate) fn write_frame<W: Write, T: Serialize>(mut writer: W, message: &T) -> Result<()> {
|
||||||
|
let payload = serde_json::to_vec(message)?;
|
||||||
|
if payload.len() > MAX_FRAME_LEN {
|
||||||
|
anyhow::bail!("frame too large: {}", payload.len());
|
||||||
|
}
|
||||||
|
let len = payload.len() as u32;
|
||||||
|
writer.write_all(&len.to_le_bytes())?;
|
||||||
|
writer.write_all(&payload)?;
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_frame<R: Read, T: DeserializeOwned>(mut reader: R) -> Result<Option<T>> {
|
||||||
|
let mut len_buf = [0u8; 4];
|
||||||
|
match reader.read_exact(&mut len_buf) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
}
|
||||||
|
let len = u32::from_le_bytes(len_buf) as usize;
|
||||||
|
if len > MAX_FRAME_LEN {
|
||||||
|
anyhow::bail!("frame too large: {len}");
|
||||||
|
}
|
||||||
|
let mut payload = vec![0u8; len];
|
||||||
|
reader.read_exact(&mut payload)?;
|
||||||
|
let message = serde_json::from_slice(&payload)?;
|
||||||
|
Ok(Some(message))
|
||||||
|
}
|
||||||
@@ -47,6 +47,24 @@ pub fn sandbox_setup_is_complete(codex_home: &Path) -> bool {
|
|||||||
matches!(load_users(codex_home), Ok(Some(users)) if users.version_matches())
|
matches!(load_users(codex_home), Ok(Some(users)) if users.version_matches())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when setup artifacts and provisioned network settings match.
|
||||||
|
pub fn sandbox_setup_is_complete_with_settings(
|
||||||
|
codex_home: &Path,
|
||||||
|
settings: &crate::WindowsSandboxProvisioningSettings,
|
||||||
|
) -> bool {
|
||||||
|
let Ok(Some(mut marker)) = load_marker(codex_home) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
marker.proxy_ports.sort_unstable();
|
||||||
|
let mut proxy_ports = settings.proxy_ports.clone();
|
||||||
|
proxy_ports.sort_unstable();
|
||||||
|
marker.version_matches()
|
||||||
|
&& marker.proxy_ports == proxy_ports
|
||||||
|
&& marker.allow_local_binding == settings.allow_local_binding
|
||||||
|
&& matches!(load_users(codex_home), Ok(Some(users)) if users.version_matches())
|
||||||
|
}
|
||||||
|
|
||||||
fn load_marker(codex_home: &Path) -> Result<Option<SetupMarker>> {
|
fn load_marker(codex_home: &Path) -> Result<Option<SetupMarker>> {
|
||||||
let path = setup_marker_path(codex_home);
|
let path = setup_marker_path(codex_home);
|
||||||
let marker = match fs::read_to_string(&path) {
|
let marker = match fs::read_to_string(&path) {
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ mod dpapi;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod env;
|
mod env;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
mod framed_io;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
mod helper_materialization;
|
mod helper_materialization;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod hide_users;
|
mod hide_users;
|
||||||
@@ -77,6 +79,8 @@ mod path_normalization;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod process;
|
mod process;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
mod provisioning_protocol;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
mod resolved_permissions;
|
mod resolved_permissions;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod token;
|
mod token;
|
||||||
@@ -197,6 +201,8 @@ pub use identity::require_logon_sandbox_creds;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use identity::sandbox_setup_is_complete;
|
pub use identity::sandbox_setup_is_complete;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use identity::sandbox_setup_is_complete_with_settings;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use ipc_framed::ErrorPayload;
|
pub use ipc_framed::ErrorPayload;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use ipc_framed::ErrorStage;
|
pub use ipc_framed::ErrorStage;
|
||||||
@@ -253,6 +259,22 @@ pub use process::read_handle_loop;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use process::spawn_process_with_pipes;
|
pub use process::spawn_process_with_pipes;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::FramedProvisioningMessage;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::PROVISIONING_PROTOCOL_VERSION;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::ProvisioningMessage;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::SANDBOX_PROVISIONING_PIPE_NAME;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::SandboxProvisioningRequest;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::SandboxProvisioningResponse;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::read_provisioning_frame;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use provisioning_protocol::write_provisioning_frame;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use resolved_permissions::ResolvedWindowsSandboxPermissions;
|
pub use resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use resolved_permissions::WindowsSandboxTokenMode;
|
pub use resolved_permissions::WindowsSandboxTokenMode;
|
||||||
@@ -338,8 +360,14 @@ pub use windows_impl::run_windows_sandbox_capture_with_filesystem_overrides;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use windows_impl::run_windows_sandbox_legacy_preflight;
|
pub use windows_impl::run_windows_sandbox_legacy_preflight;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use winutil::SANDBOX_USERS_GROUP;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use winutil::ensure_sandbox_users_group;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use winutil::quote_windows_arg;
|
pub use winutil::quote_windows_arg;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use winutil::resolve_sid;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use winutil::string_from_sid_bytes;
|
pub use winutil::string_from_sid_bytes;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use winutil::to_wide;
|
pub use winutil::to_wide;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//! Dedicated protocol exchanged with the Windows sandbox provisioning service.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
/// Protocol version shared by provisioning clients and the Windows service.
|
||||||
|
pub const PROVISIONING_PROTOCOL_VERSION: u8 = 1;
|
||||||
|
|
||||||
|
/// Named pipe used by the machine-wide Windows sandbox provisioning service.
|
||||||
|
pub const SANDBOX_PROVISIONING_PIPE_NAME: &str = r"\\.\pipe\OpenAI.CodexSandbox";
|
||||||
|
|
||||||
|
/// Versioned provisioning-service message carried in a length-prefixed JSON frame.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct FramedProvisioningMessage {
|
||||||
|
pub version: u8,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub message: ProvisioningMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request and response messages exchanged with the provisioning service.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub enum ProvisioningMessage {
|
||||||
|
ProvisionSandboxRequest {
|
||||||
|
payload: SandboxProvisioningRequest,
|
||||||
|
},
|
||||||
|
ProvisionSandboxResponse {
|
||||||
|
payload: SandboxProvisioningResponse,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sandbox setup parameters sent to the provisioning service.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct SandboxProvisioningRequest {
|
||||||
|
pub codex_home: String,
|
||||||
|
pub http_port: Option<u16>,
|
||||||
|
pub socks_port: Option<u16>,
|
||||||
|
pub allow_local_binding: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result returned by the sandbox provisioning service.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "status", rename_all = "snake_case")]
|
||||||
|
pub enum SandboxProvisioningResponse {
|
||||||
|
Ok,
|
||||||
|
Error { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a length-prefixed provisioning-service message.
|
||||||
|
pub fn write_provisioning_frame<W: Write>(
|
||||||
|
writer: W,
|
||||||
|
message: &FramedProvisioningMessage,
|
||||||
|
) -> Result<()> {
|
||||||
|
crate::framed_io::write_frame(writer, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a length-prefixed provisioning-service message, returning `None` on EOF.
|
||||||
|
pub fn read_provisioning_frame<R: Read>(reader: R) -> Result<Option<FramedProvisioningMessage>> {
|
||||||
|
crate::framed_io::read_frame(reader)
|
||||||
|
}
|
||||||
@@ -5,6 +5,9 @@ use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
|
|||||||
use windows_sys::Win32::Foundation::GetLastError;
|
use windows_sys::Win32::Foundation::GetLastError;
|
||||||
use windows_sys::Win32::Foundation::HLOCAL;
|
use windows_sys::Win32::Foundation::HLOCAL;
|
||||||
use windows_sys::Win32::Foundation::LocalFree;
|
use windows_sys::Win32::Foundation::LocalFree;
|
||||||
|
use windows_sys::Win32::NetworkManagement::NetManagement::LOCALGROUP_INFO_1;
|
||||||
|
use windows_sys::Win32::NetworkManagement::NetManagement::NERR_Success;
|
||||||
|
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAdd;
|
||||||
use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
|
use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
|
||||||
use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;
|
use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;
|
||||||
use windows_sys::Win32::Security::CopySid;
|
use windows_sys::Win32::Security::CopySid;
|
||||||
@@ -16,6 +19,9 @@ use windows_sys::Win32::System::Diagnostics::Debug::FORMAT_MESSAGE_FROM_SYSTEM;
|
|||||||
use windows_sys::Win32::System::Diagnostics::Debug::FORMAT_MESSAGE_IGNORE_INSERTS;
|
use windows_sys::Win32::System::Diagnostics::Debug::FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
use windows_sys::Win32::System::Diagnostics::Debug::FormatMessageW;
|
use windows_sys::Win32::System::Diagnostics::Debug::FormatMessageW;
|
||||||
|
|
||||||
|
pub const SANDBOX_USERS_GROUP: &str = "CodexSandboxUsers";
|
||||||
|
const SANDBOX_USERS_GROUP_COMMENT: &str = "Codex sandbox internal group (managed)";
|
||||||
|
|
||||||
pub fn to_wide<S: AsRef<OsStr>>(s: S) -> Vec<u16> {
|
pub fn to_wide<S: AsRef<OsStr>>(s: S) -> Vec<u16> {
|
||||||
let mut v: Vec<u16> = s.as_ref().encode_wide().collect();
|
let mut v: Vec<u16> = s.as_ref().encode_wide().collect();
|
||||||
v.push(0);
|
v.push(0);
|
||||||
@@ -129,6 +135,34 @@ const SID_AUTHENTICATED_USERS: &str = "S-1-5-11";
|
|||||||
const SID_EVERYONE: &str = "S-1-1-0";
|
const SID_EVERYONE: &str = "S-1-1-0";
|
||||||
const SID_SYSTEM: &str = "S-1-5-18";
|
const SID_SYSTEM: &str = "S-1-5-18";
|
||||||
|
|
||||||
|
pub fn ensure_sandbox_users_group() -> Result<Vec<u8>> {
|
||||||
|
const ERROR_ALIAS_EXISTS: u32 = 1379;
|
||||||
|
const NERR_GROUP_EXISTS: u32 = 2223;
|
||||||
|
|
||||||
|
let name = to_wide(SANDBOX_USERS_GROUP);
|
||||||
|
let comment = to_wide(SANDBOX_USERS_GROUP_COMMENT);
|
||||||
|
let info = LOCALGROUP_INFO_1 {
|
||||||
|
lgrpi1_name: name.as_ptr() as *mut u16,
|
||||||
|
lgrpi1_comment: comment.as_ptr() as *mut u16,
|
||||||
|
};
|
||||||
|
let mut parameter_error = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
NetLocalGroupAdd(
|
||||||
|
std::ptr::null(),
|
||||||
|
1,
|
||||||
|
(&raw const info).cast(),
|
||||||
|
&raw mut parameter_error,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if status != NERR_Success && status != ERROR_ALIAS_EXISTS && status != NERR_GROUP_EXISTS {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"NetLocalGroupAdd failed for {SANDBOX_USERS_GROUP} code {status} parm_err={parameter_error}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_sid(SANDBOX_USERS_GROUP)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_sid(name: &str) -> Result<Vec<u8>> {
|
pub fn resolve_sid(name: &str) -> Result<Vec<u8>> {
|
||||||
if let Some(sid_str) = well_known_sid_str(name) {
|
if let Some(sid_str) = well_known_sid_str(name) {
|
||||||
return sid_bytes_from_string(sid_str);
|
return sid_bytes_from_string(sid_str);
|
||||||
|
|||||||
Reference in new issue
Block a user