Files
codex/codex-rs/windows-sandbox-rs/src/setup_launch.rs
T
johnl-oai 7e45bdb5fd Enable authenticated Windows sandbox provisioning (#42351)
## Why

The Windows sandbox service had provisioning policy and client authentication in place, but its IPC listener was still disabled.

## What changed

- Serve framed provisioning requests over a local named pipe, authenticate packaged clients, validate requests and machine policy, and report provisioning outcomes through bounded responses and Windows event logging.
- Return `unavailable` for configuration parse failures so clients can fall back to the elevated setup helper.
- Keep validated directory handles alive in the setup helper so path protections survive an unexpected service exit.
- Make connection recovery and shutdown wakeups tolerate clients that disconnect before the listener accepts them.

## Testing

Added Windows tests for response framing, configuration-error classification, pipe security and reconnect behavior, shutdown wakeups, and retained helper handles.

GitOrigin-RevId: 68e9d546dcada88ff162ea4f98c0f4d748706b34
2026-09-02 20:27:05 +00:00

76 lines
2.6 KiB
Rust

//! Starts provisioning helpers only after their retained directory handles are installed.
//! The child owns its duplicates until exit, independently of the service lifetime.
use std::io;
use std::os::windows::io::AsRawHandle;
use std::os::windows::io::BorrowedHandle;
use std::os::windows::process::CommandExt;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use windows_sys::Win32::Foundation::DUPLICATE_SAME_ACCESS;
use windows_sys::Win32::Foundation::DuplicateHandle;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Foundation::NTSTATUS;
use windows_sys::Win32::Foundation::RtlNtStatusToDosError;
use windows_sys::Win32::System::Threading::CREATE_NO_WINDOW;
use windows_sys::Win32::System::Threading::CREATE_SUSPENDED;
use windows_sys::Win32::System::Threading::GetCurrentProcess;
#[link(name = "ntdll")]
unsafe extern "system" {
fn NtResumeProcess(process_handle: HANDLE) -> NTSTATUS;
}
pub(super) fn spawn_with_retained_handles(
command: &mut Command,
retained_handles: &[BorrowedHandle<'_>],
) -> io::Result<Child> {
let mut child = command
.creation_flags(CREATE_NO_WINDOW | CREATE_SUSPENDED)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
let initialized = (|| {
for handle in retained_handles {
let mut child_handle = 0;
if unsafe {
DuplicateHandle(
GetCurrentProcess(),
handle.as_raw_handle() as HANDLE,
child.as_raw_handle() as HANDLE,
&mut child_handle,
/*dwdesiredaccess*/ 0,
/*binherithandle*/ 0,
DUPLICATE_SAME_ACCESS,
)
} == 0
{
return Err(io::Error::last_os_error());
}
// This value belongs to the child's handle table. Do not close it here
// or make it inheritable by unrelated processes or helper descendants.
}
let status = unsafe { NtResumeProcess(child.as_raw_handle() as HANDLE) };
if status < 0 {
return Err(io::Error::from_raw_os_error(
unsafe { RtlNtStatusToDosError(status) } as i32,
));
}
Ok(())
})();
if let Err(error) = initialized {
// A partially initialized helper must never run after its parent drops
// the directory protections. Reap it, including any installed duplicates.
child.kill()?;
child.wait()?;
return Err(error);
}
Ok(child)
}
#[cfg(test)]
#[path = "setup_launch_tests.rs"]
mod tests;