From matt at mattmccutchen.net Mon Jan 12 21:30:25 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Tue, 13 Jan 2009 00:30:25 -0500 Subject: [Rsync-patches] [PATCH] Properly handle the simultaneous arrival of connections on different sockets: In-Reply-To: <1231824061.3048.14.camel@mattlaptop2.local> References: <1231824061.3048.14.camel@mattlaptop2.local> Message-ID: <1231824622.3048.19.camel@mattlaptop2.local> http://lists.samba.org/archive/rsync/2009-January/022458.html --- socket.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/socket.c b/socket.c index a3fc9ae..5df3a50 100644 --- a/socket.c +++ b/socket.c @@ -578,7 +578,7 @@ void start_accept_loop(int port, int (*fn)(int, int)) fds = deffds; #endif - if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1) + if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1) continue; for (i = 0, fd = -1; sp[i] >= 0; i++) { -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Mon Jan 12 22:50:43 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Tue, 13 Jan 2009 01:50:43 -0500 Subject: [Rsync-patches] [PATCH] Fix group preservation when neither --usermap nor --groupmap is passed. Message-ID: <1231829443.3048.74.camel@mattlaptop2.local> recv_add_uid was guessing whether it was processing users or groups by checking "idmap == uidmap", but this is wrong if neither --usermap nor --groupmap is passed, meaning that uidmap and gidmap are both NULL. This caused the chgrp test to fail. Make recv_add_uid check "idlist_ptr == &uidlist" instead. --- uidlist.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uidlist.c b/uidlist.c index 7162dfa..7e8cbd7 100644 --- a/uidlist.c +++ b/uidlist.c @@ -179,7 +179,7 @@ static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idm if (node) id2 = node->id2; else if (*name && id) { - if (idmap == uidmap) { + if (idlist_ptr == &uidlist) { uid_t uid; id2 = name_to_uid(name, &uid) ? uid : id; } else { @@ -189,12 +189,12 @@ static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idm } else id2 = id; - flag = idmap == gidmap && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0; + flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0; node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag); if (DEBUG_GTE(OWN, 2)) { rprintf(FINFO, "%sid %u(%s) maps to %u\n", - idmap == uidmap ? "u" : "g", + idlist_ptr == &uidlist ? "u" : "g", (unsigned)id, name, (unsigned)id2); } -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Tue Jan 13 22:12:23 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 01:12:23 -0500 Subject: [Rsync-patches] [PATCH 1/3] access_match: Don't mutate the passed hostname. Message-ID: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> This resulted in the following bogus-looking log output: 2009/01/14 01:08:25 [15314] connect from UNKNOWN (192.168.1.46) 2009/01/14 01:08:26 [15319] rsync on module/ from user at unknown (192.168.1.46) --- access.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/access.c b/access.c index 772ec27..32ab216 100644 --- a/access.c +++ b/access.c @@ -214,13 +214,16 @@ static int access_match(char *list, char *addr, char *host) { char *tok; char *list2 = strdup(list); + char *host2; if (!list2) out_of_memory("access_match"); - strlower(list2); - if (host) - strlower(host); + if (host) { + host2 = strdup(host); + strlower(host2); + } else + host2 = NULL; for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) { if (match_hostname(host, tok) || match_address(addr, tok)) { -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Tue Jan 13 22:16:30 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 01:16:30 -0500 Subject: [Rsync-patches] [PATCH 2/3] Add const to start_daemon's "addr" and "host" vars and in access.c. In-Reply-To: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> References: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> Message-ID: <3b5a07bc39bba51bc23d2e4f37a28c73a8c2aa00.1231916483.git.matt@mattmccutchen.net> --- access.c | 11 ++++++----- clientserver.c | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/access.c b/access.c index 32ab216..7b01e10 100644 --- a/access.c +++ b/access.c @@ -20,14 +20,14 @@ #include "rsync.h" -static int match_hostname(char *host, char *tok) +static int match_hostname(const char *host, const char *tok) { if (!host || !*host) return 0; return wildmatch(tok, host); } -static int match_binary(char *b1, char *b2, char *mask, int addrlen) +static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen) { int i; @@ -56,7 +56,7 @@ static void make_mask(char *mask, int plen, int addrlen) return; } -static int match_address(char *addr, char *tok) +static int match_address(const char *addr, const char *tok) { char *p; struct addrinfo hints, *resa, *rest; @@ -210,7 +210,7 @@ static int match_address(char *addr, char *tok) return ret; } -static int access_match(char *list, char *addr, char *host) +static int access_match(const char *list, const char *addr, const char *host) { char *tok; char *list2 = strdup(list); @@ -236,7 +236,8 @@ static int access_match(char *list, char *addr, char *host) return 0; } -int allow_access(char *addr, char *host, char *allow_list, char *deny_list) +int allow_access(const char *addr, const char *host, + const char *allow_list, const char *deny_list) { if (allow_list && !*allow_list) allow_list = NULL; diff --git a/clientserver.c b/clientserver.c index 09fd315..6fc3bbe 100644 --- a/clientserver.c +++ b/clientserver.c @@ -405,7 +405,7 @@ static int path_failure(int f_out, const char *dir, BOOL was_chdir) return -1; } -static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) +static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host) { int argc; char **argv, **orig_argv, **orig_early_argv, *module_chdir; @@ -914,7 +914,7 @@ static int load_config(int globals_only) int start_daemon(int f_in, int f_out) { char line[1024]; - char *addr, *host; + const char *addr, *host; int i; io_set_sock_fds(f_in, f_out); -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Tue Jan 13 22:57:25 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 01:57:25 -0500 Subject: [Rsync-patches] [PATCH 3/3] Daemon: Don't reverse-lookup the client's hostname if "hosts allow" and "hosts deny" are both unset. Update rsyncd.conf(5) accordingly. In-Reply-To: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> References: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> Message-ID: --- clientserver.c | 11 ++++++++++- rsyncd.conf.yo | 11 ++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/clientserver.c b/clientserver.c index 6fc3bbe..5f8ed48 100644 --- a/clientserver.c +++ b/clientserver.c @@ -405,6 +405,11 @@ static int path_failure(int f_out, const char *dir, BOOL was_chdir) return -1; } +/* We only reverse-lookup the client's hostname if at least one of + * "hosts allow" or "hosts deny" is specified. */ +const char *undetermined_hostname = "UNDETERMINED"; +#define SHOULD_LOOKUP_HOSTNAME(i) (*lp_hosts_allow(i) || *lp_hosts_deny(i)) + static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host) { int argc; @@ -427,6 +432,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char iconv_opt = NULL; #endif + if (host == undetermined_hostname && SHOULD_LOOKUP_HOSTNAME(i)) + host = client_name(f_in); + if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", name, host, addr); @@ -927,7 +935,8 @@ int start_daemon(int f_in, int f_out) exit_cleanup(RERR_SYNTAX); addr = client_addr(f_in); - host = client_name(f_in); + host = SHOULD_LOOKUP_HOSTNAME(-1) ? client_name(f_in) + : undetermined_hostname; rprintf(FLOG, "connect from %s (%s)\n", host, addr); if (!am_server) { diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo index 06768e9..df3f46d 100644 --- a/rsyncd.conf.yo +++ b/rsyncd.conf.yo @@ -305,9 +305,12 @@ will be possible if file permissions on the daemon side allow them. The default is for this parameter to be disabled. dit(bf(list)) This parameter determines if this module should be -listed when the client asks for a listing of available modules. By -setting this to false you can create hidden modules. The default is -for modules to be listable. +listed when the client asks for a listing of available modules. In addition, +if this is false, rsync will pretend the module does not exist when a client +blocked by "hosts allow" or "hosts deny" attempts to access it; but realize +that if these two parameters are unset globally, the client may still be able +to probe the module's existence by watching for the reverse lookup caused by +the module-specific parameters. The default is for modules to be listable. dit(bf(uid)) This parameter specifies the user name or user ID that file transfers to and from that module should take place as when the daemon @@ -473,6 +476,8 @@ that the host is rejected. If the host does not match either the connect. The default is no "hosts allow" parameter, which means all hosts can connect. +If neither "hosts allow" nor "hosts deny" is specified (globally or for the +requested module), the reverse lookup is not performed. dit(bf(hosts deny)) This parameter allows you to specify a list of patterns that are matched against a connecting clients -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Wed Jan 14 01:41:12 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 04:41:12 -0500 Subject: [Rsync-patches] [PATCH 3/3] Daemon: Don't reverse-lookup [...] In-Reply-To: References: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> Message-ID: <1231926072.3003.48.camel@localhost> Oops, this is not as simple as I thought! For the %h escape, log_formatted calls client_{name,addr} with fd 0 (which isn't the socket), counting on the calls in start_daemon to have forced the initialization of the client name and address. With my patch, this is no longer the case when "hosts allow" and "hosts deny" are unspecified. But by a wild stroke of luck, the right answer is obtained: am_server is on by the time any transfer logging is done, so client_name re-parses the IP address string cached by client_addr. I don't think it's acceptable to rely on this working. Two possible approaches for a real fix: - Still do the reverse lookup on the first call to log_formatted, but redesign clientname.c to save the fd. Laziest, but may result in an unexpected delay when logging the first file transfer. - Detect at the beginning of rsync_module that the log format contains %h, and do the reverse lookup then. Needs an ad-hoc check in rsync_module, but it is not necessary to change clientname.c (though it could still use a redesign!). Wayne, which one do you prefer? For now I've moved the patch series from master to wip/reverse-lookup in my repository. -- Matt From matt at mattmccutchen.net Wed Jan 14 05:34:21 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 08:34:21 -0500 Subject: [Rsync-patches] [PATCH 3/3] Daemon: Don't reverse-lookup [...] In-Reply-To: <1231926072.3003.48.camel@localhost> References: <5428b7dc6331bffba9327589fb22eabda0770653.1231916483.git.matt@mattmccutchen.net> <1231926072.3003.48.camel@localhost> Message-ID: <1231940061.3094.2.camel@localhost> On Wed, 2009-01-14 at 04:41 -0500, Matt McCutchen wrote: > Oops, this is not as simple as I thought! For the %h escape, > log_formatted calls client_{name,addr} with fd 0 (which isn't the > socket), counting on the calls in start_daemon to have forced the > initialization of the client name and address. With my patch, this is > no longer the case when "hosts allow" and "hosts deny" are unspecified. > [...] Better idea: add a "reverse lookup" parameter. That will let daemon admins disable reverse lookups but still use a "hosts allow" based on IP addresses, if they wish. I'm not sure why I was resisting this at first. -- Matt From matt at mattmccutchen.net Wed Jan 14 06:37:13 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Wed, 14 Jan 2009 09:37:13 -0500 Subject: [Rsync-patches] [PATCH] Add "reverse lookup" daemon parameter to control whether reverse lookups are done. In-Reply-To: <1231940061.3094.2.camel@localhost> References: <1231940061.3094.2.camel@localhost> Message-ID: <1231943833.3094.3.camel@localhost> --- clientserver.c | 11 ++++++++++- loadparm.c | 4 ++++ log.c | 8 +++++++- rsyncd.conf.yo | 27 ++++++++++++++++++++++----- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/clientserver.c b/clientserver.c index 6fc3bbe..b45d8aa 100644 --- a/clientserver.c +++ b/clientserver.c @@ -81,6 +81,9 @@ static int rl_nulls = 0; static struct sigaction sigact; #endif +/* Used when "reverse lookup" is off. */ +const char undetermined_hostname[] = "UNDETERMINED"; + /** * Run a client connected to an rsyncd. The alternative to this * function for remote-shell connections is do_cmd(). @@ -427,6 +430,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char iconv_opt = NULL; #endif + /* If reverse lookup is disabled globally but enabled for this module, + * we need to do it now before the access check. */ + if (host == undetermined_hostname && lp_reverse_lookup(i)) + host = client_name(f_in); + if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", name, host, addr); @@ -927,7 +935,8 @@ int start_daemon(int f_in, int f_out) exit_cleanup(RERR_SYNTAX); addr = client_addr(f_in); - host = client_name(f_in); + host = lp_reverse_lookup(-1) ? client_name(f_in) + : undetermined_hostname; rprintf(FLOG, "connect from %s (%s)\n", host, addr); if (!am_server) { diff --git a/loadparm.c b/loadparm.c index f5e1f16..b3197a8 100644 --- a/loadparm.c +++ b/loadparm.c @@ -149,6 +149,7 @@ typedef struct { BOOL munge_symlinks; BOOL numeric_ids; BOOL read_only; + BOOL reverse_lookup; BOOL strict_modes; BOOL transfer_logging; BOOL use_chroot; @@ -203,6 +204,7 @@ static section sDefault = { /* munge_symlinks; */ (BOOL)-1, /* numeric_ids; */ (BOOL)-1, /* read_only; */ True, + /* reverse_lookup; */ True, /* strict_modes; */ True, /* transfer_logging; */ False, /* use_chroot; */ True, @@ -327,6 +329,7 @@ static struct parm_struct parm_table[] = #endif {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0}, {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0}, + {"reverse lookup", P_BOOL, P_LOCAL, &sDefault.reverse_lookup, NULL,0}, {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0}, {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0}, {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0}, @@ -417,6 +420,7 @@ FN_LOCAL_BOOL(lp_list, list) FN_LOCAL_BOOL(lp_munge_symlinks, munge_symlinks) FN_LOCAL_BOOL(lp_numeric_ids, numeric_ids) FN_LOCAL_BOOL(lp_read_only, read_only) +FN_LOCAL_BOOL(lp_reverse_lookup, reverse_lookup) FN_LOCAL_BOOL(lp_strict_modes, strict_modes) FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging) FN_LOCAL_BOOL(lp_use_chroot, use_chroot) diff --git a/log.c b/log.c index 551cb06..3a7d956 100644 --- a/log.c +++ b/log.c @@ -60,6 +60,7 @@ extern char curr_dir[MAXPATHLEN]; extern char *full_module_path; extern unsigned int module_dirlen; extern char sender_file_sum[MAX_DIGEST_LEN]; +extern const char undetermined_hostname[]; static int log_initialised; static int logfile_was_closed; @@ -504,9 +505,14 @@ static void log_formatted(enum logcode code, const char *format, const char *op, n = NULL; switch (*p) { + /* For %h and %a, it doesn't matter what fd we pass to + * client_{name,addr} because rsync_module will already have + * forced the answer to be cached (assuming, of course, for %h + * that lp_reverse_lookup(module_id) is true). */ case 'h': if (am_daemon) - n = client_name(0); + n = lp_reverse_lookup(module_id) + ? client_name(0) : undetermined_hostname; break; case 'a': if (am_daemon) diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo index 06768e9..992b7ce 100644 --- a/rsyncd.conf.yo +++ b/rsyncd.conf.yo @@ -304,10 +304,14 @@ attempted downloads will fail. If "write only" is false then downloads will be possible if file permissions on the daemon side allow them. The default is for this parameter to be disabled. -dit(bf(list)) This parameter determines if this module should be -listed when the client asks for a listing of available modules. By -setting this to false you can create hidden modules. The default is -for modules to be listable. +dit(bf(list)) This parameter determines whether this module is +listed when the client asks for a listing of available modules. In addition, +if this is false, the daemon will pretend the module does not exist +when a client denied by "hosts allow" or "hosts deny" attempts to access it. +Realize that if "reverse lookup" is disabled globally but enabled for the +module, the resulting reverse lookup to a potentially client-controlled DNS +server may still reveal to the client that it hit an existing module. +The default is for modules to be listable. dit(bf(uid)) This parameter specifies the user name or user ID that file transfers to and from that module should take place as when the daemon @@ -450,7 +454,8 @@ quote(itemization( addresses which match the masked IP address will be allowed in. it() a hostname. The hostname as determined by a reverse lookup will be matched (case insensitive) against the pattern. Only an exact - match is allowed in. + match is allowed in. This only works if "reverse lookup" is enabled + (the default). it() a hostname pattern using wildcards. These are matched using the same rules as normal unix filename matching. If the pattern matches then the client is allowed in. @@ -481,6 +486,18 @@ rejected. See the "hosts allow" parameter for more information. The default is no "hosts deny" parameter, which means all hosts can connect. +dit(bf(reverse lookup)) Controls whether the daemon performs a reverse lookup +on the client's IP address to determine its hostname, which is used for +"hosts allow"/"hosts deny" checks and the "%h" log escape. This is enabled by +default, but you may wish to disable it to save time if you know the lookup will +not return a useful result, in which case the daemon will use the name +"UNDETERMINED" instead. + +If this parameter is enabled globally (even by default), rsync performs the +lookup as soon as a client connects, so disabling it for a module will not +avoid the lookup. Thus, you probably want to disable it globally and then +enable it for modules that need the information. + dit(bf(ignore errors)) This parameter tells rsyncd to ignore I/O errors on the daemon when deciding whether to run the delete phase of the transfer. Normally rsync skips the bf(--delete) step if any -- 1.6.1.86.g57f37 From matt at mattmccutchen.net Fri Jan 30 22:14:17 2009 From: matt at mattmccutchen.net (Matt McCutchen) Date: Sat, 31 Jan 2009 01:14:17 -0500 Subject: [Rsync-patches] [PATCH] Give a meaningful error message when we fail to write to a batch file. In-Reply-To: <1232836803.10089.2.camel@mattlaptop2.local> References: <1232836803.10089.2.camel@mattlaptop2.local> Message-ID: <1233382457.24062.20.camel@mattlaptop2.local> --- io.c | 26 ++++++++++++++++++++------ 1 files changed, 20 insertions(+), 6 deletions(-) diff --git a/io.c b/io.c index 5f70e81..6a89c8f 100644 --- a/io.c +++ b/io.c @@ -1423,6 +1423,22 @@ static void sleep_for_bwlimit(int bytes_written) total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024); } +static const char *what_fd_is(int fd) +{ + static char buf[20]; + + if (fd == sock_f_out) + return "socket"; + else if (fd == msg_fd_out) + return "message fd"; + else if (fd == batch_fd) + return "batch file"; + else { + snprintf(buf, sizeof buf, "fd %d", fd); + return buf; + } +} + /* Write len bytes to the file descriptor fd, looping as necessary to get * the job done and also (in certain circumstances) reading any data on * msg_fd_in to avoid deadlock. @@ -1501,8 +1517,8 @@ static void writefd_unbuffered(int fd, const char *buf, size_t len) if (am_server && fd == msg_fd_out) exit_cleanup(RERR_STREAMIO); rsyserr(FERROR, errno, - "writefd_unbuffered failed to write %ld bytes [%s]", - (long)len, who_am_i()); + "writefd_unbuffered failed to write %ld bytes to %s [%s]", + (long)len, what_fd_is(fd), who_am_i()); /* If the other side is sending us error messages, try * to grab any messages they sent before they died. */ while (!am_server && fd == sock_f_out && io_multiplexing_in) { @@ -1560,10 +1576,8 @@ static void writefd(int fd, const char *buf, size_t len) if (fd == sock_f_out) stats.total_written += len; - if (fd == write_batch_monitor_out) { - if ((size_t)write(batch_fd, buf, len) != len) - exit_cleanup(RERR_FILEIO); - } + if (fd == write_batch_monitor_out) + writefd_unbuffered(batch_fd, buf, len); if (!iobuf_out || fd != iobuf_f_out) { writefd_unbuffered(fd, buf, len); -- 1.6.1.220.gaabfe