fix(d5-daemon-auth): address auth review findings (Wave B)
- test_credentials.c: NUL-terminate the overlong-line stack buffer before make_tmp_file's strlen() (was a stack-buffer-overflow READ under ASan); still exercises the overlong-rejection path. - Add redacted protocol string variants (protocol_send_str_redacted / receive + fd send_str_redacted/receive_str_redacted) and use them for the daemon auth username/digest so --verbose / LOG_DEBUG_ALL never logs a replayable credential while other protocol strings keep their debug trace. - credentials_verify/gate: replace byte-wise-short-circuiting strcmp with a fixed-length constant-time username compare (closes user-enumeration oracle); update doc comment to match. - read_secret_file: preserve password exact bytes (only strip trailing CR/LF) and burn the stack line buffer; document the whitespace behavior. - test_server_cli.c: note the parser zero-inits opts on failure. - Add debug-level daemon test asserting the digest never appears under --verbose. PROTOCOL_VERSION stays 2.15.0.
This commit is contained in:
@@ -75,7 +75,11 @@ bool credentials_hash_password(const char* password, char* out_hex);
|
||||
* `user:password` (the literal password). *user_out and *password_out are
|
||||
* freshly allocated on success (password is plaintext -- the caller hashes it
|
||||
* and then burns/frees it); both are NULL on error. Returns 0 on success, -1
|
||||
* on failure (err filled: the path is named, never the credential itself). */
|
||||
* on failure (err filled: the path is named, never the credential itself).
|
||||
* Only the line's trailing CR/LF are stripped: the password's bytes are
|
||||
* otherwise preserved exactly, so a password with leading/trailing whitespace
|
||||
* (after the ':') is kept usable. The username is trimmed of surrounding
|
||||
* space/tabs. */
|
||||
int credentials_read_secret_file(const char* path, char** user_out, char** password_out, char* err,
|
||||
size_t err_size);
|
||||
|
||||
@@ -92,7 +96,9 @@ void credentials_burn(char* secret, size_t len);
|
||||
* the store holds an entry for `user` whose stored digest equals the presented
|
||||
* one. A NULL store, NULL user/digest, unknown user and wrong digest all
|
||||
* return false. The digest comparison runs over a fixed dummy whenever the
|
||||
* user is absent, so "unknown user" and "wrong password" take the same time
|
||||
* user is absent, and the username lookup is a single constant-time
|
||||
* full-length compare (no byte-wise early exit), so neither "unknown user" vs
|
||||
* "wrong password" nor a username prefix match can be distinguished by timing
|
||||
* (no user-enumeration oracle in the comparison path). */
|
||||
bool credentials_verify(const CredentialStore* store, const char* user,
|
||||
const char* presented_hash_hex);
|
||||
@@ -103,7 +109,9 @@ bool credentials_verify(const CredentialStore* store, const char* user,
|
||||
* list AND to verify against the store. Returns false (fail closed) when the
|
||||
* store is NULL, when no credential was presented, when the user is not on the
|
||||
* module's list, or when verification fails. This is the single decision the
|
||||
* server_module_gate seam applies to an auth-required module. */
|
||||
* server_module_gate seam applies to an auth-required module. Like
|
||||
* credentials_verify, username matches here use a constant-time full-length
|
||||
* compare rather than a byte-wise-short-circuiting strcmp. */
|
||||
bool credentials_gate_allows(const CredentialStore* store, const char* const* module_users,
|
||||
int module_user_count, const char* presented_user,
|
||||
const char* presented_hash_hex);
|
||||
|
||||
Reference in New Issue
Block a user