From 97e02bf21a119971e258550bdcd3e56096bdd7b2 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 19 Sep 2022 22:37:29 -0700 Subject: [PATCH] Some "use chroot" improvements. - The sanitize_paths variable was set too often. It only needs to be set when the "inner" path is not "/". This change avoids sanitizing & munging things for a path=/ module just because chroot is off. - The default for "use chroot" is now "unset" instead of "true". When unset it checks if chrooting works, and if not, it proceeds with a sanitized copy instead of totally failing to work. This makes it easier to setup a non-root rsync daemon, for instance. It will have no effect on a typical Linux root-run daemon where the default will continue to use chroot (because chrooting works). A config file can explicitly set "use chroot = true | false" to force the choice. - Try to improve the "use chroot" manpage. --- NEWS.md | 13 +++++++++++ clientserver.c | 40 ++++++++++++++++++-------------- daemon-parm.txt | 2 +- rsyncd.conf.5.md | 70 +++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 84 insertions(+), 41 deletions(-) diff --git a/NEWS.md b/NEWS.md index d40b7fa2..63191360 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ - When rsync gets an unpack error on an ACL, mention the filename. +- Avoid oversetting sanitize_paths when a daemon is serving "/" (even if + "use chroot" is false). + ### ENHANCEMENTS: - Added negotiated daemon-auth support that allows a stronger checksum digest @@ -32,6 +35,11 @@ converted. Newer rsync versions will provide more complete info than older versions. +- The [`use chroot`](#rsyncd.conf) daemon parameter now defaults to "unset" so + that rsync can test if chrooting works and decide to proceed with a sanitized + copy if chroot is not supported (e.g., for a non-root daemon). Explicitly + setting it to true or false (on or off) behaves the same way as before. + ### PACKAGING RELATED: - The checksum code now uses openssl's EVP methods, which gets rid of various @@ -49,6 +57,11 @@ configured path in the OPENSSL_CONF environment variable (when the variable is not already set). This will enable openssl's MD4 code for rsync to use. +- The packager may wish to include an explicit "use chroot = true" in the top + section of the /etc/rsyncd.conf file if the daemon is being installed to run + as the root user (though rsync should behave the same even with the value + unset, a little extra paranoia doesn't hurt). + ------------------------------------------------------------------------------ # NEWS for rsync 3.2.6 (9 Sep 2022) diff --git a/clientserver.c b/clientserver.c index 9ad7eaf7..67983f77 100644 --- a/clientserver.c +++ b/clientserver.c @@ -701,7 +701,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char int set_uid; char *p, *err_msg = NULL; char *name = lp_name(i); - int use_chroot = lp_use_chroot(i); + int use_chroot = lp_use_chroot(i); /* might be 1 (yes), 0 (no), or -1 (unset) */ int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1; int save_munge_symlinks; pid_t pre_exec_pid = 0; @@ -826,6 +826,20 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char io_printf(f_out, "@ERROR: no path setting.\n"); return -1; } + if (use_chroot < 0) { + if (strstr(module_dir, "/./") != NULL) + use_chroot = 1; /* The module is expecting a chroot inner & outer path. */ + else if (chroot("/") < 0) { + rprintf(FLOG, "chroot test failed: %s. " + "Switching 'use chroot' from unset to no.\n", + strerror(errno)); + use_chroot = 0; + } else { + if (chdir("/") < 0) + rsyserr(FLOG, errno, "chdir(\"/\") failed"); + use_chroot = 1; + } + } if (use_chroot) { if ((p = strstr(module_dir, "/./")) != NULL) { *p = '\0'; /* Temporary... */ @@ -962,20 +976,8 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char } if (use_chroot) { - /* - * XXX: The 'use chroot' flag is a fairly reliable - * source of confusion, because it fails under two - * important circumstances: running as non-root, - * running on Win32 (or possibly others). On the - * other hand, if you are running as root, then it - * might be better to always use chroot. - * - * So, perhaps if we can't chroot we should just issue - * a warning, unless a "require chroot" flag is set, - * in which case we fail. - */ if (chroot(module_chdir)) { - rsyserr(FLOG, errno, "chroot %s failed", module_chdir); + rsyserr(FLOG, errno, "chroot(\"%s\") failed", module_chdir); io_printf(f_out, "@ERROR: chroot failed\n"); return -1; } @@ -984,7 +986,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char if (!change_dir(module_chdir, CD_NORMAL)) return path_failure(f_out, module_chdir, True); - if (module_dirlen || (!use_chroot && !*lp_daemon_chroot())) + if (module_dirlen) sanitize_paths = 1; if ((munge_symlinks = lp_munge_symlinks(module_id)) < 0) @@ -1299,8 +1301,12 @@ int start_daemon(int f_in, int f_out) p = lp_daemon_chroot(); if (*p) { log_init(0); /* Make use we've initialized syslog before chrooting. */ - if (chroot(p) < 0 || chdir("/") < 0) { - rsyserr(FLOG, errno, "daemon chroot %s failed", p); + if (chroot(p) < 0) { + rsyserr(FLOG, errno, "daemon chroot(\"%s\") failed", p); + return -1; + } + if (chdir("/") < 0) { + rsyserr(FLOG, errno, "daemon chdir(\"/\") failed"); return -1; } } diff --git a/daemon-parm.txt b/daemon-parm.txt index 3b438b02..69034173 100644 --- a/daemon-parm.txt +++ b/daemon-parm.txt @@ -60,9 +60,9 @@ BOOL read_only True BOOL reverse_lookup True BOOL strict_modes True BOOL transfer_logging False -BOOL use_chroot True BOOL write_only False BOOL3 munge_symlinks Unset BOOL3 numeric_ids Unset BOOL3 open_noatime Unset +BOOL3 use_chroot Unset diff --git a/rsyncd.conf.5.md b/rsyncd.conf.5.md index 400ad107..abb6c578 100644 --- a/rsyncd.conf.5.md +++ b/rsyncd.conf.5.md @@ -164,6 +164,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details. available in this module. You must specify this parameter for each module in `rsyncd.conf`. + If the value contains a "/./" element then the path will be divided at that + point into a chroot dir and an inner-chroot subdir. If [`use chroot`](#) + is set to false, though, the extraneous dot dir is just cleaned out of the + path. An example of this idiom is: + + > path = /var/rsync/./module1 + + This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot + path to "/module1". + You may base the path's value off of an environment variable by surrounding the variable name with percent signs. You can even reference a variable that is set by rsync when the user connects. For example, this would use @@ -187,29 +197,43 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details. path, and of complicating the preservation of users and groups by name (see below). - As an additional safety feature, you can specify a dot-dir in the module's - "[path](#)" to indicate the point where the chroot should occur. This allows - rsync to run in a chroot with a non-"/" path for the top of the transfer - hierarchy. Doing this guards against unintended library loading (since - those absolute paths will not be inside the transfer hierarchy unless you - have used an unwise pathname), and lets you setup libraries for the chroot - that are outside of the transfer. For example, specifying - "/var/rsync/./module1" will chroot to the "/var/rsync" directory and set - the inside-chroot path to "/module1". If you had omitted the dot-dir, the - chroot would have used the whole path, and the inside-chroot path would - have been "/". - - When both "use chroot" and "[daemon chroot](#)" are false, OR the inside-chroot - path of "use chroot" is not "/", rsync will: (1) munge symlinks by default - for security reasons (see "[munge symlinks](#)" for a way to turn this off, but - only if you trust your users), (2) substitute leading slashes in absolute - paths with the module's path (so that options such as `--backup-dir`, - `--compare-dest`, etc. interpret an absolute path as rooted in the module's - "[path](#)" dir), and (3) trim ".." path elements from args if rsync believes - they would escape the module hierarchy. The default for "use chroot" is - true, and is the safer choice (especially if the module is not read-only). - - When this parameter is enabled *and* the "[name converter](#)" parameter is + If `use chroot` is not set, it defaults to trying to enable a chroot but + allows the daemon to continue (after logging a warning) if it fails. The + one exception to this is when a module's [`path`](#) has a "/./" chroot + divider in it -- this causes an unset value to be treated as true for that + module. + + Prior to rsync 3.2.7, the default value was "true". The new default makes + it easier to setup an rsync daemon as a non-root user or to run a daemon on + a system where chroot fails. Explicitly setting the value to true in the + rsyncd.conf file will always require the chroot to succeed. + + It is also possible to specify a dot-dir in the module's "[path](#)" to + indicate that you want to chdir to the earlier part of the path and then + serve files from inside the latter part of the path (with default + sanitizing and symlink munging). This can be useful if you need some + library dirs inside the chroot (typically for uid & gid lookups) but don't + want to put the lib dir into the top of the served path (even though they + can be hidden with an [`exclude`](#) directive). However, a better choice + for a modern rsync setup is to use a [`name converter`](#)" and try to + avoid inner lib dirs altogether. See also the [`daemon chroot`](#) + parameter, which causes rsync to chroot into its own chroot area before + doing any path-related chrooting. + + If the daemon is serving the "/" dir (either directly or due to being + chrooted to the module's path), rsync does not do any extra path sanitizing + or (default) munging. When it has to limit access to a particular subdir + (either due to chroot being disabled or having an inside-chroot path set), + rsync will munge symlinks (by default) and sanitize paths. Those that + dislike munged symlinks (and really, really trust their users to not break + out of the subdir) can disable the symlink munging via the "[munge + symlinks](#)" parameter. Sanitizing paths trims ".." path elements from + args that rsync believes would escape the module hierarchy, and also + substitutes leading slashes in absolute paths with the module's path (so + that options such as `--backup-dir` & `--compare-dest` interpret an + absolute path as rooted in the module's "[path](#)" dir). + + When a chroot is in effect *and* the "[name converter](#)" parameter is *not* set, the "[numeric ids](#)" parameter will default to being enabled (disabling name lookups). This means that if you manually setup name-lookup libraries in your chroot (instead of using a name converter) -- 2.11.4.GIT