feat(d5-daemon-auth): password auth, --password-file, --early-input

This commit is contained in:
2026-09-10 12:50:56 +02:00
parent 958eddf414
commit dd5ae60459
18 changed files with 1675 additions and 46 deletions
+62
View File
@@ -3,6 +3,7 @@
#include "chmod.h"
#include "compression.h"
#include "config.h"
#include "credentials.h"
#include "delta.h"
#include "file.h"
#include "file_list.h"
@@ -584,6 +585,11 @@ static const OptionEntry OPTION_TABLE[] = {
{"--old-d", NULL, OPT_FLAG, offsetof(Config, dirs)},
{"--relative", "-R", OPT_FLAG, offsetof(Config, relative)},
{"--mkpath", NULL, OPT_FLAG, offsetof(Config, mkpath)},
/* --password-file: client-only path to a `user:password` secret file used
* to authenticate a daemon (host::module/path) destination. Stored as a
* path; main() reads it (after the destination form is known) and derives
* the wire credentials. Never crosses the wire. */
{"--password-file", NULL, OPT_STRING, offsetof(Config, password_file)},
{"--delete-before", NULL, OPT_FLAG, offsetof(Config, delete_before)},
{"--delete-during", "--del", OPT_FLAG, offsetof(Config, delete_during)},
{"--delete-delay", NULL, OPT_FLAG, offsetof(Config, delete_delay)},
@@ -1440,6 +1446,55 @@ static int read_patterns_from_file(const char* filepath, char*** patterns, int*
}
#ifndef FASTSYNC_TEST_BUILD
/* Daemon auth (Wave B): read --password-file and derive the wire credentials
* (username + SHA-256 hex digest of the password). Runs once the destination
* form is known: the credentials only make sense for a daemon
* (host::module/path) destination, so a --password-file without one is a hard
* error here rather than a silently-ignored flag. The literal password is
* hashed immediately and wiped from memory; only the digest (and username) are
* kept on the Config for config_send. Returns 0 on success, -1 on error (the
* reason is logged; neither the password nor its digest is ever logged). */
static int load_daemon_credentials(Config* config) {
if (!config->password_file)
return 0;
if (!config->module || config->module[0] == '\0') {
log_message(LOG_LEVEL_ERROR,
"--password-file requires a daemon destination (host::module/path)");
return -1;
}
char err[512];
char* user = NULL;
char* password = NULL;
if (credentials_read_secret_file(config->password_file, &user, &password, err, sizeof(err)) !=
0) {
log_message(LOG_LEVEL_ERROR, "%s", err);
return -1;
}
char hash[CREDENTIAL_HASH_HEX_LEN + 1];
if (!credentials_hash_password(password, hash)) {
log_message(LOG_LEVEL_ERROR, "failed to hash the password from '%s'", config->password_file);
credentials_burn(password, strlen(password));
free(password);
free(user);
return -1;
}
credentials_burn(password, strlen(password));
free(password);
free(config->auth_user);
free(config->auth_password_hash);
config->auth_user = user;
config->auth_password_hash = str_dup(hash);
if (!config->auth_password_hash) {
log_message(LOG_LEVEL_ERROR, "memory allocation failed reading '%s'", config->password_file);
free(config->auth_user);
config->auth_user = NULL;
return -1;
}
log_info_message(LOG_INFO_MISC, "Loaded daemon credentials for user '%s'", config->auth_user);
return 0;
}
int main(int argc, char* argv[]) {
/* The server may close a connection mid-stream (e.g. when it rejects an
oversized delta). Ignore SIGPIPE so that a broken TCP connection
@@ -1519,6 +1574,13 @@ int main(int argc, char* argv[]) {
goto cleanup;
}
/* Daemon auth: read --password-file (if any) into the wire credentials now
* that the destination's module is known. */
if (load_daemon_credentials(config) != 0) {
exit_code = 1;
goto cleanup;
}
if (!validate_config(config)) {
exit_code = 1;
goto cleanup;
+4
View File
@@ -170,6 +170,10 @@ void print_usage(void) {
printf(" --save-to-disk Write received files to disk\n");
printf(" --server-host <ip> Server IP address (default: 127.0.0.1)\n");
printf(" --server-port <n> Server port (default: 8080)\n");
printf(" --password-file <f> Authenticate a host::module/path daemon destination.\n");
printf(" The file's first user:password line supplies the\n");
printf(" username and password (only a SHA-256 digest of the\n");
printf(" password is sent; keep the file mode 0600)\n");
printf(" --bwlimit <KB/s> Bandwidth limit in kilobytes per second\n");
printf(" --tls Enable TLS encryption\n");
printf(" --cert <path> TLS certificate file (PEM)\n");