diff --git a/access.c b/access.c index b924e0a3..48b28e67 100644 --- a/access.c +++ b/access.c @@ -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); +} diff --git a/clientserver.c b/clientserver.c index a845aae7..3344daed 100644 --- a/clientserver.c +++ b/clientserver.c @@ -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 diff --git a/daemon-parm.txt b/daemon-parm.txt index 69034173..5c98ccc1 100644 --- a/daemon-parm.txt +++ b/daemon-parm.txt @@ -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 diff --git a/io.c b/io.c index cc8d682e..66b5eb39 100644 --- a/io.c +++ b/io.c @@ -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); diff --git a/rsyncd.conf.5.md b/rsyncd.conf.5.md index 2f257659..6c864042 100644 --- a/rsyncd.conf.5.md +++ b/rsyncd.conf.5.md @@ -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 diff --git a/socket.c b/socket.c index 6a8f6f4a..d100c2ca 100644 --- a/socket.c +++ b/socket.c @@ -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'))