mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +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::path_mask_allows;
|
||||
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_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::provision_sandbox_users;
|
||||
use sandbox_users::resolve_sandbox_users_group_sid;
|
||||
use sandbox_users::resolve_sid;
|
||||
use sandbox_users::sid_bytes_to_psid;
|
||||
|
||||
#[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::INVALID_HANDLE_VALUE;
|
||||
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::NERR_Success;
|
||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAdd;
|
||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetLocalGroupAddMembers;
|
||||
use windows_sys::Win32::NetworkManagement::NetManagement::NetUserAdd;
|
||||
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::ConvertStringSidToSidW;
|
||||
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::PSECURITY_DESCRIPTOR;
|
||||
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::FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
use codex_windows_sandbox::SANDBOX_USERS_GROUP;
|
||||
use codex_windows_sandbox::SETUP_VERSION;
|
||||
use codex_windows_sandbox::SetupErrorCode;
|
||||
use codex_windows_sandbox::SetupFailure;
|
||||
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_secrets_dir;
|
||||
use codex_windows_sandbox::string_from_sid_bytes;
|
||||
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_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>> {
|
||||
resolve_sid(SANDBOX_USERS_GROUP)
|
||||
@@ -73,7 +61,14 @@ pub fn provision_sandbox_users(
|
||||
online_username: &str,
|
||||
log: &mut dyn Write,
|
||||
) -> 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(
|
||||
log,
|
||||
&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(())
|
||||
}
|
||||
|
||||
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<()> {
|
||||
// If the member is already in the group, NetLocalGroupAddMembers may
|
||||
// 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(())
|
||||
}
|
||||
|
||||
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> {
|
||||
let sid_w = to_wide(OsStr::new(sid_str));
|
||||
let mut psid: *mut c_void = std::ptr::null_mut();
|
||||
|
||||
@@ -19,12 +19,6 @@ use std::io::Read;
|
||||
use std::io::Write;
|
||||
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.
|
||||
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.
|
||||
pub fn write_frame<W: Write>(mut writer: W, msg: &FramedMessage) -> Result<()> {
|
||||
let payload = serde_json::to_vec(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(())
|
||||
pub fn write_frame<W: Write>(writer: W, msg: &FramedMessage) -> Result<()> {
|
||||
crate::framed_io::write_frame(writer, msg)
|
||||
}
|
||||
|
||||
/// Read a length-prefixed JSON frame; returns `Ok(None)` on EOF.
|
||||
pub fn read_frame<R: Read>(mut reader: R) -> Result<Option<FramedMessage>> {
|
||||
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 msg: FramedMessage = serde_json::from_slice(&payload)?;
|
||||
Ok(Some(msg))
|
||||
pub fn read_frame<R: Read>(reader: R) -> Result<Option<FramedMessage>> {
|
||||
crate::framed_io::read_frame(reader)
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
let path = setup_marker_path(codex_home);
|
||||
let marker = match fs::read_to_string(&path) {
|
||||
|
||||
@@ -65,6 +65,8 @@ mod dpapi;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod env;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod framed_io;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod helper_materialization;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod hide_users;
|
||||
@@ -77,6 +79,8 @@ mod path_normalization;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod process;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod provisioning_protocol;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod resolved_permissions;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod token;
|
||||
@@ -197,6 +201,8 @@ pub use identity::require_logon_sandbox_creds;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use identity::sandbox_setup_is_complete;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use identity::sandbox_setup_is_complete_with_settings;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use ipc_framed::ErrorPayload;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use ipc_framed::ErrorStage;
|
||||
@@ -253,6 +259,22 @@ pub use process::read_handle_loop;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::spawn_process_with_pipes;
|
||||
#[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;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use resolved_permissions::WindowsSandboxTokenMode;
|
||||
@@ -338,8 +360,14 @@ pub use windows_impl::run_windows_sandbox_capture_with_filesystem_overrides;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows_impl::run_windows_sandbox_legacy_preflight;
|
||||
#[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;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use winutil::resolve_sid;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use winutil::string_from_sid_bytes;
|
||||
#[cfg(target_os = "windows")]
|
||||
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::HLOCAL;
|
||||
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::ConvertStringSidToSidW;
|
||||
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::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> {
|
||||
let mut v: Vec<u16> = s.as_ref().encode_wide().collect();
|
||||
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_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>> {
|
||||
if let Some(sid_str) = well_known_sid_str(name) {
|
||||
return sid_bytes_from_string(sid_str);
|
||||
|
||||
Reference in new issue
Block a user