Skip to content

Daemon: fail closed on symlinked COVEN_HOME and non-socket socket path (AUTH.md hardening gap) #142

@maplesyzzurp

Description

@maplesyzzurp

Summary

docs/AUTH.md → "Current hardening gap" lists the fail-closed checks the Rust daemon should perform before creating, binding, or removing daemon state. Two are still unimplemented in crates/coven-cli/src/daemon.rs:

  • A COVEN_HOME that resolves through a symlink is not rejected. ensure_private_coven_home calls create_dir_all + set_permissions(0700) and will follow a symlinked home, chmod-ing and populating the link target.
  • An existing socket path that is a symlink or non-socket file is not rejected. bind_api_socket does if socket_path.exists() { remove_file(socket_path) } — it blindly removes whatever sits at coven.sock before binding, with no symlink/socket-type guard.

The permission hardening from the same list (0700 home, 0600 socket/status) and the body-cap / loopback-TCP work already landed — this is only the residual symlink/type guards. The COVEN_HOME ownership (euid) check is intentionally out of scope here since it needs a libc::geteuid dependency.

Why it matters

With a 0700 home these are defense-in-depth rather than a same-user break, but they are exactly the conditions AUTH.md says the daemon should "fail closed" on before broad distribution: a planted symlink at the home or socket path could redirect daemon state (socket, status, SQLite ledger) outside the trusted directory, and the unconditional remove_file will delete an unrelated regular file that happens to occupy the socket path.

Proposed fix (implemented, std-only)

  • ensure_private_coven_home: symlink_metadata the home; bail if it is a symlink.
  • bind_api_socket: symlink_metadata the socket path; bail on a symlink or any non-socket file; only remove_file a genuine stale socket; treat NotFound as fresh.

No new dependencies, no unsafe, Unix-gated. Three new #[cfg(unix)] tests cover a symlinked home, a symlinked socket path (and assert the link target is not deleted), and a non-socket file at the socket path.

Verification (Linux): cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace --locked all green (696 unit + 4 smoke; the 3 new tests pass, no regressions).

Branch

A branch is ready to PR when the contribution window reopens (July 2026, per CONTRIBUTING):
https://github.com/maplesyzzurp/coven/tree/fix/daemon-socket-hardening (commit 8c6dabb)

Filing as an issue rather than a PR per the current freeze notice.

Refs: docs/AUTH.md "Current hardening gap"; crates/coven-cli/src/daemon.rs (ensure_private_coven_home, bind_api_socket).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions