Skip to content

Agent ready-file O_NOFOLLOW + workspace consolidation + threat-model refresh#46

Merged
jgowdy-godaddy merged 5 commits intomainfrom
docs/design-cleanup
Apr 17, 2026
Merged

Agent ready-file O_NOFOLLOW + workspace consolidation + threat-model refresh#46
jgowdy-godaddy merged 5 commits intomainfrom
docs/design-cleanup

Conversation

@jgowdy-godaddy
Copy link
Copy Markdown
Contributor

Summary

  • signal_ready on the daemonize handshake now opens its \$TMPDIR/sshenc-agent-ready-<pid>-<nanos>.tmp file with create_new(true).custom_flags(libc::O_NOFOLLOW).mode(0o600). Pre-planted symlinks at the target path are refused with ELOOP; existing files fail with EEXIST. Either way the write cannot be redirected to an attacker-chosen target.
  • Workspace consolidationCargo.toml path deps now point at ../libenclaveapp/crates/... instead of the (now-deleted) top-level ../crates/. libenclaveapp is the single source of truth.
  • THREAT_MODEL refresh — Ready-File, Windows Named-Pipe DACL, and Metadata .meta sections rewritten to reflect currently-in-tree mitigations.

Depends on

Test plan

  • cargo check --workspace clean.
  • cargo fmt --all -- --check clean.
  • cargo clippy --workspace --all-targets -- -D warnings clean.
  • cargo test --workspace — 383 tests passing, 0 failing.
  • New signal_ready_refuses_preplanted_symlink test asserts both the signal_ready error AND that the decoy target file was NOT created.

jgowdy added 5 commits April 17, 2026 04:04
- Correct crate count (9 -> 10) and add sshenc-tpm-bridge to the crate table.
- Expand the sshenc-cli subcommand list (config, openssh, uninstall, identity,
  default, ssh, completions) to match main.rs.
- Replace the stale "socat + npiperelay" WSL bridge description with the
  current JSON-RPC bridge (sshenc-tpm-bridge) on both the platform backends
  and platform support tables.
- Document the Windows named pipe DACL and the Unix socket/directory
  permissions.
- Document that the Linux software fallback is wrapped by an OS keyring key
  via enclaveapp-keyring, not raw P-256 on disk.
- Add sshenc-tpm-bridge to the binaries list.
signal_ready now opens the ready file with
OpenOptions::new().write(true).create_new(true).custom_flags(libc::O_NOFOLLOW).mode(0o600)
on Unix. If the path already exists the open fails with EEXIST; if it's
a symlink the open fails with ELOOP. Either way the write never lands
on an attacker-chosen target, and the error surfaces to the parent
process via the daemonize handshake instead of being silently followed.

mode(0o600) at open time is load-bearing on umask-permissive systems;
the explicit set_permissions(0o600) after write is belt-and-suspenders.

The parent (daemonize in main.rs) is responsible for removing the
ready-file path before and after spawning the child, so signal_ready
always starts from a clean slate and can safely demand create_new
semantics without a separate unlink.

New test signal_ready_refuses_preplanted_symlink pre-plants a symlink
at the ready path and asserts that:
  1. signal_ready returns an error (ELOOP / EEXIST / File exists),
  2. the symlink's target file is NOT created — so the attack is blocked
     rather than silently followed.
Consolidates on libenclaveapp's own crates/ as the single source of
truth. The former ../crates/ location (top-level /enclaveapps/crates/)
is gone — it was a divergent copy that periodically drifted from
libenclaveapp/crates/. sshenc builds against the canonical tree now.
- 'Ready-File Symlink in $TMPDIR' rewritten around the new O_NOFOLLOW
  + create_new hardening; residual risk reduced from 'theoretically
  exploitable' to 'none for the symlink-redirect vector.'
- 'Windows Named-Pipe Hijack' notes the explicit DACL now granting
  full control only to creator-owner + SYSTEM.
- 'Metadata File Tamper (.meta)' notes the HMAC sidecar now written
  by the Linux keyring backend via enclaveapp-keyring::meta_hmac_key
  — .meta tamper without keyring access is now caught with a hard
  meta_hmac_verify error.
@jgowdy-godaddy jgowdy-godaddy merged commit 74084ea into main Apr 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants