mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-16 07:10:39 -04:00
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:
1 parent
c02380f7d3
commit
6e00a3b867
6 files changed
+59
-4
No files matched your search
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'))
|
||||
|
||||
Reference in new issue
Block a user