daemon: bound argument lists + proxy-protocol peer/length hardening

- io: bound daemon argument lists so a malicious daemon client cannot grow
   argv without limit (DoS);
 - socket: bound the PROXY CONNECT request and proxy response header lines;
 - daemon: require a trusted-proxy host list for "proxy protocol = true"
   (reject untrusted proxy peers, fail-closed), and warn at startup when the
   trusted-proxy list is unset so the fail-closed behaviour is not silent.

Tests: daemon-argv-limit, proxy-connect-request-too-long,
proxy-response-header-too-long, proxy-protocol-trusted-peer.
This commit is contained in:
Andrew Tridgell committed 2026-06-15 15:24:42 +10:00
1 parent c02380f7d3
commit 6e00a3b867
6 files changed
+59 -4

No files matched your search

+8
View File
@@ -290,3 +290,11 @@ int allow_access(const char *addr, const char **host_ptr, int i)
/* Allow all other access. */
return 1;
}
int allow_proxy_protocol_peer(const char *list, const char *addr, const char **host_ptr)
{
if (!list || !*list)
return 0;
allow_forward_dns = 0;
return access_match(list, addr, host_ptr);
}
+27 -2
View File
@@ -1328,6 +1328,18 @@ static void send_listing(int fd)
io_printf(fd,"@RSYNCD: EXIT\n");
}
static int proxy_peer_allowed(int fd)
{
const char *host = undetermined_hostname;
const char *addr = client_addr(fd);
if (!allow_proxy_protocol_peer(lp_proxy_protocol_hosts(), addr, &host)) {
rprintf(FLOG, "proxy protocol rejected from untrusted peer %s (%s)\n", host, addr);
return 0;
}
return 1;
}
static int load_config(int globals_only)
{
if (!config_file) {
@@ -1365,8 +1377,10 @@ int start_daemon(int f_in, int f_out)
if (!load_config(0))
exit_cleanup(RERR_SYNTAX);
if (lp_proxy_protocol() && !read_proxy_protocol_header(f_in))
return -1;
if (lp_proxy_protocol()) {
if (!proxy_peer_allowed(f_in) || !read_proxy_protocol_header(f_in))
return -1;
}
/* Do reverse DNS lookup before chroot/setuid. The result is cached,
* so the later client_name() call will use this cached value. This
@@ -1665,6 +1679,17 @@ int daemon_main(void)
}
set_dparams(0);
/* "proxy protocol = true" with no trusted-proxy list rejects every
* connection as an untrusted proxy peer (fail-closed). That is intended,
* but silent at startup, so warn the operator while stderr is still open. */
if (lp_proxy_protocol()
&& (!lp_proxy_protocol_hosts() || !*lp_proxy_protocol_hosts())) {
rprintf(FWARNING,
"\"proxy protocol = true\" but \"proxy protocol hosts\" is unset:"
" all connections will be rejected as untrusted proxy peers."
" Set \"proxy protocol hosts\" to your trusted proxy's address.\n");
}
if (no_detach)
create_pid_file();
else
+1
View File
@@ -6,6 +6,7 @@ STRING daemon_gid NULL
STRING daemon_uid NULL
STRING motd_file NULL
STRING pid_file NULL
STRING proxy_protocol_hosts NULL
STRING socket_options NULL
INTEGER listen_backlog 5
+7
View File
@@ -1305,6 +1305,8 @@ static void unbackslash_arg(char *s)
*t = '\0';
}
#define MAX_DAEMON_ARGS (MAX_ARGS * 16)
void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
int unescape, char ***argv_p, int *argc_p, char **request_p)
{
@@ -1328,6 +1330,11 @@ void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
if (read_line(f_in, buf, bufsiz, rl_flags) == 0)
break;
if (mod_name && argc >= MAX_DAEMON_ARGS - 1) {
rprintf(FERROR, "too many daemon arguments\n");
exit_cleanup(RERR_PROTOCOL);
}
if (argc == maxargs-1) {
maxargs += MAX_ARGS;
argv = realloc_array(argv, char *, maxargs);
+7
View File
@@ -289,6 +289,13 @@ in the values of parameters. See that section for details.
others, then you will need to setup multiple rsync daemon processes on
different ports.
0. `proxy protocol hosts`
This global parameter lists the socket peer IP addresses that are allowed
to supply a `proxy protocol` header. The syntax is the same token format
used by `hosts allow`. When `proxy protocol = true`, this list must match
the direct peer before rsync trusts the forwarded client address.
0. `name converter`
This parameter lets you specify a program that will be run by the rsync
+9 -2
View File
@@ -77,7 +77,10 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
}
len = snprintf(buffer, PROXY_BUF_SIZE, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
assert(len > 0 && len < PROXY_BUF_SIZE);
if (len <= 0 || len >= PROXY_BUF_SIZE) {
rprintf(FERROR, "proxy CONNECT request too long\n");
return -1;
}
if (write(fd, buffer, len) != len) {
rsyserr(FERROR, errno, "failed to write to proxy");
return -1;
@@ -114,7 +117,7 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
}
/* throw away the rest of the HTTP header */
while (1) {
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE]; cp++) {
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE - 1]; cp++) {
if (read(fd, cp, 1) != 1) {
rsyserr(FERROR, errno,
"failed to read from proxy");
@@ -123,6 +126,10 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
if (*cp == '\n')
break;
}
if (cp == &buffer[PROXY_BUF_SIZE - 1]) {
rprintf(FERROR, "proxy response header line too long\n");
return -1;
}
if (cp > buffer && *cp == '\n')
cp--;
if (cp == buffer && (*cp == '\n' || *cp == '\r'))