mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
recover stale Windows sandbox credentials (#27944)
## Why The elevated Windows sandbox persists dedicated sandbox account credentials so later commands can launch without reprovisioning. If those persisted credentials drift from the actual Windows account password, `CreateProcessWithLogonW` fails with `ERROR_LOGON_FAILURE` and Codex currently surfaces that as a hard runner launch failure. This change makes that failure self-healing. When Windows specifically rejects the sandbox login, Codex now treats the persisted sandbox credentials as stale, regenerates them through the existing setup path, and retries the runner launch once. ## What Changed - Preserve `CreateProcessWithLogonW` failures as a typed runner logon error so callers can distinguish `ERROR_LOGON_FAILURE` from unrelated launch failures. - Add a sandbox credential refresh helper that deletes the persisted `sandbox_users.json` record and reuses `require_logon_sandbox_creds()` to reprovision credentials through the established setup flow. - Retry elevated runner startup after stale-credential failures in both the legacy elevated capture path and unified exec elevated backend. - Add focused tests for stale logon failure detection and persisted sandbox user file removal. ## Validation - `git diff --check` - `cargo test -p codex-windows-sandbox`
This commit is contained in:
1 parent
fc1fb682a7
commit
c0b36d234a
4 files changed
+201
-18
No files matched your search
@@ -3,12 +3,16 @@ use super::windows_common::make_runner_resizer;
|
||||
use super::windows_common::start_runner_pipe_writer;
|
||||
use super::windows_common::start_runner_stdin_writer;
|
||||
use super::windows_common::start_runner_stdout_reader;
|
||||
use crate::identity::SandboxCreds;
|
||||
use crate::identity::refresh_logon_sandbox_creds;
|
||||
use crate::ipc_framed::EmptyPayload;
|
||||
use crate::ipc_framed::FramedMessage;
|
||||
use crate::ipc_framed::IPC_PROTOCOL_VERSION;
|
||||
use crate::ipc_framed::Message;
|
||||
use crate::ipc_framed::SpawnRequest;
|
||||
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||
use crate::runner_client::RunnerTransport;
|
||||
use crate::runner_client::is_stale_sandbox_creds_error;
|
||||
use crate::runner_client::spawn_runner_transport;
|
||||
use crate::spawn_prep::prepare_elevated_spawn_context_for_permissions;
|
||||
use anyhow::Result;
|
||||
@@ -23,6 +27,26 @@ use tokio::sync::broadcast;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
async fn spawn_runner_transport_task(
|
||||
codex_home: PathBuf,
|
||||
cwd: PathBuf,
|
||||
sandbox_creds: SandboxCreds,
|
||||
logs_base_dir: Option<PathBuf>,
|
||||
spawn_request: SpawnRequest,
|
||||
) -> Result<RunnerTransport> {
|
||||
tokio::task::spawn_blocking(move || -> Result<_> {
|
||||
spawn_runner_transport(
|
||||
&codex_home,
|
||||
&cwd,
|
||||
&sandbox_creds,
|
||||
logs_base_dir.as_deref(),
|
||||
spawn_request,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!("runner handshake task failed: {err}"))?
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||
permission_profile: &PermissionProfile,
|
||||
@@ -55,7 +79,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
|
||||
workspace_roots,
|
||||
)?;
|
||||
let elevated = prepare_elevated_spawn_context_for_permissions(
|
||||
permissions,
|
||||
permissions.clone(),
|
||||
codex_home,
|
||||
cwd,
|
||||
&mut env_map,
|
||||
@@ -83,19 +107,42 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
|
||||
};
|
||||
let codex_home = codex_home.to_path_buf();
|
||||
let cwd = cwd.to_path_buf();
|
||||
let sandbox_creds = elevated.sandbox_creds.clone();
|
||||
let sandbox_creds = elevated.sandbox_creds;
|
||||
let logs_base_dir = elevated.logs_base_dir.clone();
|
||||
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
|
||||
spawn_runner_transport(
|
||||
&codex_home,
|
||||
&cwd,
|
||||
&sandbox_creds,
|
||||
logs_base_dir.as_deref(),
|
||||
spawn_request,
|
||||
)
|
||||
})
|
||||
let transport = match spawn_runner_transport_task(
|
||||
codex_home.clone(),
|
||||
cwd.clone(),
|
||||
sandbox_creds,
|
||||
logs_base_dir.clone(),
|
||||
spawn_request.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!("runner handshake task failed: {err}"))??;
|
||||
{
|
||||
Ok(transport) => transport,
|
||||
Err(err) if is_stale_sandbox_creds_error(&err) => {
|
||||
let sandbox_creds = refresh_logon_sandbox_creds(
|
||||
&permissions,
|
||||
&cwd,
|
||||
&env_map,
|
||||
&codex_home,
|
||||
read_roots_override,
|
||||
read_roots_include_platform_defaults,
|
||||
write_roots_override,
|
||||
&deny_read_paths_override,
|
||||
&deny_write_paths_override,
|
||||
/*proxy_enforced*/ false,
|
||||
)?;
|
||||
spawn_runner_transport_task(
|
||||
codex_home,
|
||||
cwd,
|
||||
sandbox_creds,
|
||||
logs_base_dir,
|
||||
spawn_request,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let (pipe_write, pipe_read) = transport.into_files();
|
||||
|
||||
let (writer_tx, writer_rx) = mpsc::channel::<Vec<u8>>(128);
|
||||
|
||||
Reference in new issue
Block a user