Skip to content

Security: broker UNIX socket does not authenticate the connecting peer #92

@jmcte

Description

@jmcte

Summary

The Swift broker (BrokerServer.run() in native-app/Sources/NativeAppLib/BrokerCore.swift) accepts connections on ~/.apw/native-app/broker.sock and immediately dispatches login / fill / status / doctor requests. It never verifies the identity of the connecting peer (no getpeereid() / LOCAL_PEERCRED). The only access control is the filesystem: the socket is chmod 0600 inside a 0700 directory.

The Rust client mirrors this — socket_path_safe_to_connect() in rust/src/native_app.rs checks the socket's file type and that its mode is exactly 0600, but does not verify the socket is owned by the current euid.

Why it matters

For a credential broker, the value proposition over reading the keychain directly is mediation. Relying only on 0600 perms means the trust boundary is "any process running as the same user." That boundary is widening with the newly added automation surfaces (AppleScript request login/request fill, AppIntents/Shortcuts, and the direct-exec fallback), all of which can reach the broker without the operator's CLI in the loop.

Credential return is still user-mediated (the AuthenticationServices OS picker in production, or the approval prompt in APW_DEMO), so this is defense-in-depth rather than a silent-exfiltration bug. But the request channel itself is unauthenticated.

Recommendation

  • In the Swift accept() loop, call getpeereid() on the accepted fd and reject connections whose euid != the broker's euid.
  • In socket_path_safe_to_connect(), also assert metadata.uid() == geteuid() (mirroring the ownership check already used in validate_external_provider_path).
  • Consider request audit logging (requesting PID/path) to the broker log so credential requests are attributable.

References

  • native-app/Sources/NativeAppLib/BrokerCore.swift (run(), bindListeningSocket())
  • rust/src/native_app.rs (socket_path_safe_to_connect)
  • Note: SECURITY.md lists cross-user access as out of scope; this is a same-user defense-in-depth hardening for a mediating broker.

Severity: Medium (defense-in-depth)

Filed by an automated deep security review.

Metadata

Metadata

Assignees

No one assigned

    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