fix(a7-3/s1): fail closed on non-loopback peers; require plaintext opt-in before challenge
utils_fd_peer_is_local now returns true only when getpeername SUCCEEDS and the peer address classifies as loopback. A non-socket descriptor (pipe/socketpair) or any getpeername error is NOT local, so the daemon auth gate fails closed instead of treating an untestable --stdio pipe as trusted (daemon auth modules are --daemon-only and the stdio path never loads a daemon config). server_module_gate now requires --allow-unauthenticated for the loopback plaintext auth path: a plaintext loopback connection without the operator opt-in is refused at the config gate BEFORE server_auth_handshake, so no SCRAM challenge is sent. Remote peers still require verified TLS regardless of the flag; the handler keeps its defense-in-depth checks. Docs state the exact policy (verified TLS with matching --client-cn, or operator-opted-in loopback plaintext), drop the SSH/stdio auth-transport claim (they are daemon-only), and add the loopback trust-boundary relay caveat and the CN-only (no SAN) residual. Adds a unit-test negative for pipe/socketpair and an integration test where a relay observes no challenge when the flag is absent.
This commit is contained in:
+7
-5
@@ -571,17 +571,19 @@ bool utils_sockaddr_is_loopback(const struct sockaddr* addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* True when the fd's peer is a local channel: a loopback TCP peer, or a
|
||||
non-socket descriptor (the --stdio SSH transport is a pipe, so a failed
|
||||
getpeername with ENOTSOCK counts as local). Any other socket peer is not
|
||||
local. */
|
||||
/* True when the fd's peer is provably a loopback TCP peer: getpeername must
|
||||
succeed AND the returned address must classify as loopback. Everything else
|
||||
is NOT local, including a non-socket descriptor (pipe/socketpair): a failed
|
||||
getpeername (ENOTSOCK, ENOTCONN, ...) fails closed. The daemon auth gate
|
||||
must not treat "I cannot tell" as "trusted", and daemon auth modules are
|
||||
daemon-only anyway (the --stdio path never loads a daemon config). */
|
||||
bool utils_fd_peer_is_local(int fd) {
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct sockaddr_storage peer;
|
||||
socklen_t length = sizeof(peer);
|
||||
if (getpeername(fd, (struct sockaddr*)&peer, &length) != 0)
|
||||
return errno == ENOTSOCK;
|
||||
return false;
|
||||
return utils_sockaddr_is_loopback((const struct sockaddr*)&peer);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user