fix(a7-3/s1): require TLS or local transport for daemon auth
Daemon modules that declare 'auth users' no longer accept credentials over a remote plaintext connection: server_module_gate refuses at the config gate, before any SCRAM challenge is sent, unless the connection is verified TLS with a client certificate matching --client-cn, or a local/SSH transport (loopback TCP peer or the --stdio pipe). --allow-unauthenticated does not relax this. The TLS client-CN comparison now uses credentials_secure_equal (S2). Clients sending --password-file to a non-loopback daemon must use --tls; validate_config rejects the plaintext case before any network I/O. Adds utils_sockaddr_is_loopback / utils_fd_peer_is_local / utils_host_is_loopback helpers with unit tests, a client validation unit test, and integration tests for the client-side plaintext rejection and the wrong-CN gate refusal.
This commit is contained in:
+17
-5
@@ -173,7 +173,7 @@ static bool tls_client_identity_allowed(SSL* ssl) {
|
||||
size_t required_length = strlen(required_client_cn);
|
||||
bool allowed = length >= 0 && (size_t)length == required_length &&
|
||||
required_length < sizeof(common_name) &&
|
||||
memcmp(common_name, required_client_cn, required_length) == 0;
|
||||
credentials_secure_equal(common_name, required_client_cn, required_length);
|
||||
X509_free(certificate);
|
||||
return allowed;
|
||||
}
|
||||
@@ -382,11 +382,23 @@ static const char* server_module_gate(const Config* config, void* context) {
|
||||
return "requested daemon module requires authentication and no credential "
|
||||
"store is configured";
|
||||
}
|
||||
if (gate_ctx && !gate_ctx->ssl) {
|
||||
log_message(LOG_LEVEL_WARNING,
|
||||
"daemon module '%s' is authenticating over a plaintext connection (no --tls); "
|
||||
"the credential exchange is not encrypted",
|
||||
/* Transport policy (A7-3/S1): an auth-required module only accepts
|
||||
* credentials over an encrypted, verified TLS connection whose client
|
||||
* certificate matches --client-cn, or over a local/SSH transport (a
|
||||
* loopback TCP peer, or the --stdio pipe). A remote plaintext peer is
|
||||
* refused HERE, before the challenge is sent, so an unverified client never
|
||||
* receives a nonce. --allow-unauthenticated is intentionally NOT consulted:
|
||||
* that flag relaxes the standalone plaintext gate, never this one. */
|
||||
bool tls_ok = gate_ctx && gate_ctx->ssl && SSL_get_verify_result(gate_ctx->ssl) == X509_V_OK &&
|
||||
tls_client_identity_allowed(gate_ctx->ssl);
|
||||
bool local_ok = gate_ctx && gate_ctx->fd >= 0 && utils_fd_peer_is_local(gate_ctx->fd);
|
||||
if (!tls_ok && !local_ok) {
|
||||
log_message(LOG_LEVEL_ERROR,
|
||||
"daemon module '%s' requires authentication over an encrypted, verified TLS "
|
||||
"connection (or a local/SSH transport); refusing",
|
||||
config->module);
|
||||
return "daemon module requires authentication over an encrypted, verified TLS "
|
||||
"connection";
|
||||
}
|
||||
if (!gate_ctx || gate_ctx->fd < 0) {
|
||||
log_message(LOG_LEVEL_ERROR, "daemon module '%s': no auth transport available",
|
||||
|
||||
Reference in New Issue
Block a user