Codex Handbook
windows-sandbox-rs/src/elevated/runner_pipe.rs 135 lines
//! Named pipe helpers for the elevated Windows sandbox runner.//!//! This module generates paired pipe names, creates server‑side pipes with//! sandbox-user-scoped ACLs, and waits for the runner to connect. It is//! **elevated-path only** and is//! used by the parent to establish the IPC channel for both unified_exec sessions//! and elevated capture. The legacy restricted‑token path spawns the child directly//! and does not use these helpers.use crate::helper_materialization::HelperExecutable;use crate::helper_materialization::resolve_helper_for_launch;use crate::winutil::resolve_sid;use crate::winutil::string_from_sid_bytes;use crate::winutil::to_wide;use rand::Rng;use rand::SeedableRng;use rand::rngs::SmallRng;use std::io;use std::path::Path;use std::path::PathBuf;use std::ptr;use windows_sys::Win32::Foundation::GetLastError;use windows_sys::Win32::Foundation::HANDLE;use windows_sys::Win32::Foundation::HLOCAL;use windows_sys::Win32::Foundation::LocalFree;use windows_sys::Win32::Security::Authorization::ConvertStringSecurityDescriptorToSecurityDescriptorW;use windows_sys::Win32::Security::PSECURITY_DESCRIPTOR;use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;use windows_sys::Win32::System::Pipes::ConnectNamedPipe;use windows_sys::Win32::System::Pipes::CreateNamedPipeW;use windows_sys::Win32::System::Pipes::GetNamedPipeClientProcessId;use windows_sys::Win32::System::Pipes::PIPE_READMODE_BYTE;use windows_sys::Win32::System::Pipes::PIPE_TYPE_BYTE;use windows_sys::Win32::System::Pipes::PIPE_WAIT;/// PIPE_ACCESS_INBOUND (win32 constant), not exposed in windows-sys 0.52.pub const PIPE_ACCESS_INBOUND: u32 = 0x0000_0001;/// PIPE_ACCESS_OUTBOUND (win32 constant), not exposed in windows-sys 0.52.pub const PIPE_ACCESS_OUTBOUND: u32 = 0x0000_0002;/// Resolves the elevated command runner path, preferring the copied helper under/// `.sandbox-bin` and falling back to the legacy sibling lookup when needed.pub fn find_runner_exe(codex_home: &Path, log_dir: Option<&Path>) -> PathBuf {    resolve_helper_for_launch(HelperExecutable::CommandRunner, codex_home, log_dir)}/// Generates a unique named-pipe path used to communicate with the runner process.pub fn pipe_pair() -> (String, String) {    let mut rng = SmallRng::from_entropy();    let nonce: u128 = rng.r#gen();    let base = format!(r"\\.\pipe\codex-runner-{nonce:x}");    (format!("{base}-in"), format!("{base}-out"))}/// Creates a named pipe whose DACL only allows the sandbox user to connect.pub fn create_named_pipe(name: &str, access: u32, sandbox_username: &str) -> io::Result<HANDLE> {    let sandbox_sid = resolve_sid(sandbox_username)        .map_err(|err| io::Error::new(io::ErrorKind::PermissionDenied, err.to_string()))?;    let sandbox_sid = string_from_sid_bytes(&sandbox_sid)        .map_err(|err| io::Error::new(io::ErrorKind::PermissionDenied, err))?;    let sddl = to_wide(format!("D:(A;;GA;;;{sandbox_sid})"));    let mut sd: PSECURITY_DESCRIPTOR = ptr::null_mut();    let ok = unsafe {        ConvertStringSecurityDescriptorToSecurityDescriptorW(            sddl.as_ptr(),            1, // SDDL_REVISION_1            &mut sd,            ptr::null_mut(),        )    };    if ok == 0 {        return Err(io::Error::from_raw_os_error(unsafe {            GetLastError() as i32        }));    }    let mut sa = SECURITY_ATTRIBUTES {        nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,        lpSecurityDescriptor: sd,        bInheritHandle: 0,    };    let wide = to_wide(name);    let h = unsafe {        CreateNamedPipeW(            wide.as_ptr(),            access,            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,            1,            65536,            65536,            0,            &mut sa as *mut SECURITY_ATTRIBUTES,        )    };    unsafe {        LocalFree(sd as HLOCAL);    }    if h == 0 || h == windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE {        return Err(io::Error::from_raw_os_error(unsafe {            GetLastError() as i32        }));    }    Ok(h)}/// Waits for the runner to connect to a parent-created server pipe.////// This is parent-side only: the runner opens the pipe with `CreateFileW`, while the/// parent calls `ConnectNamedPipe`, tolerates the already-connected case, and/// verifies that the connected client is the runner process we just spawned.pub fn connect_pipe(h: HANDLE, expected_runner_pid: u32) -> io::Result<()> {    let ok = unsafe { ConnectNamedPipe(h, ptr::null_mut()) };    if ok == 0 {        let err = unsafe { GetLastError() };        const ERROR_PIPE_CONNECTED: u32 = 535;        if err != ERROR_PIPE_CONNECTED {            return Err(io::Error::from_raw_os_error(err as i32));        }    }    let mut client_pid = 0;    let ok = unsafe { GetNamedPipeClientProcessId(h, &mut client_pid) };    if ok == 0 {        return Err(io::Error::from_raw_os_error(unsafe {            GetLastError() as i32        }));    }    if client_pid != expected_runner_pid {        return Err(io::Error::new(            io::ErrorKind::PermissionDenied,            format!(                "named pipe client pid {client_pid} did not match runner pid {expected_runner_pid}"            ),        ));    }    Ok(())}