Added a way to specify where the chroot should occur in the module's

path, which allows a daemon admin to have chroot protection and still
have files that are outside the transfer area (such as libraries).
This commit is contained in:
Wayne Davison
2008-02-15 18:55:02 -08:00
parent 0b52f94da7
commit 2fe1feea75
3 changed files with 67 additions and 20 deletions

5
NEWS
View File

@@ -44,6 +44,11 @@ Changes since 2.6.9:
translation is not desired. See the daemon's "numeric ids" parameter
for full details.
- A chroot daemon can now indicate which part of its path should affect the
chroot call, and which part should become an inside-chroot path for the
module. This allows you to have outside-the-transfer paths (such as for
libraries) even when you enable chroot protection.
- If a file's data arrived successfully on the receiving side but the
rename of the temporary file to the destination file failed AND the
--remove-source-files (or the deprecated --remove-sent-files) option

View File

@@ -67,7 +67,8 @@ int munge_symlinks = 0;
struct chmod_mode_struct *daemon_chmod_modes;
/* module_dirlen is the length of the module_dir string when in daemon
* mode, not chrooted, and the path is not "/"; otherwise 0. */
* mode and module_dir is not "/"; otherwise 0. (Note that a chroot-
* enabled module can have a non-"/" module_dir these days.) */
char *module_dir = NULL;
unsigned int module_dirlen = 0;
@@ -379,7 +380,7 @@ static int read_arg_from_pipe(int fd, char *buf, int limit)
static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
{
int argc, opt_cnt;
char **argv;
char **argv, *chroot_path = NULL;
char line[BIGPATHBUFLEN];
uid_t uid = (uid_t)-2; /* canonically "nobody" */
gid_t gid = (gid_t)-2;
@@ -483,14 +484,29 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
* to make sure that the module's path is absolute. After this
* check, module_dir will be set to an absolute path. */
module_dir = lp_path(i);
if (use_chroot) {
if ((p = strstr(module_dir, "/./")) != NULL) {
*p = '\0';
p += 2;
} else if ((p = strdup("/")) == NULL)
out_of_memory("rsync_module");
}
strlcpy(line, curr_dir, sizeof line);
if (!push_dir(module_dir, 1))
goto chdir_failed;
if (strcmp(curr_dir, module_dir) != 0)
module_dir = strdup(curr_dir);
if (strcmp(curr_dir, module_dir) != 0
&& (module_dir = strdup(curr_dir)) == NULL)
out_of_memory("rsync_module");
push_dir(line, 1); /* Restore curr_dir. */
if (use_chroot || (module_dirlen = strlen(module_dir)) == 1) {
if (use_chroot) {
chroot_path = module_dir;
module_dir = p; /* p is "/" or our inside-chroot path */
}
module_dirlen = clean_fname(module_dir, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR);
if (module_dirlen == 1) {
module_dirlen = 0;
set_filter_dir("/", 1);
} else
@@ -523,8 +539,17 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
char *modname, *modpath, *hostaddr, *hostname, *username;
int status;
if (!use_chroot)
p = module_dir;
else if (module_dirlen) {
pathjoin(line, sizeof line, chroot_path, module_dir+1);
p = line;
} else
p = chroot_path;
if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0
|| asprintf(&modpath, "RSYNC_MODULE_PATH=%s", module_dir) < 0
|| asprintf(&modpath, "RSYNC_MODULE_PATH=%s", p) < 0
|| asprintf(&hostaddr, "RSYNC_HOST_ADDR=%s", addr) < 0
|| asprintf(&hostname, "RSYNC_HOST_NAME=%s", host) < 0
|| asprintf(&username, "RSYNC_USER_NAME=%s", auth_user) < 0)
@@ -624,13 +649,15 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
* a warning, unless a "require chroot" flag is set,
* in which case we fail.
*/
if (chroot(module_dir)) {
rsyserr(FLOG, errno, "chroot %s failed", module_dir);
if (chroot(chroot_path)) {
rsyserr(FLOG, errno, "chroot %s failed", chroot_path);
io_printf(f_out, "@ERROR: chroot failed\n");
return -1;
}
if (!push_dir("/", 0))
if (!push_dir(module_dir, 0))
goto chdir_failed;
if (module_dirlen)
sanitize_paths = 1;
} else {
if (!push_dir(module_dir, 0)) {
chdir_failed:
@@ -642,7 +669,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
}
if ((munge_symlinks = lp_munge_symlinks(i)) < 0)
munge_symlinks = !use_chroot;
munge_symlinks = !use_chroot || module_dirlen;
if (munge_symlinks) {
STRUCT_STAT st;
if (stat(SYMLINK_PREFIX, &st) == 0 && S_ISDIR(st.st_mode)) {

View File

@@ -135,13 +135,26 @@ holes, but it has the disadvantages of requiring super-user privileges,
of not being able to follow symbolic links that are either absolute or outside
of the new root path, and of complicating the preservation of users and groups
by name (see below).
When "use chroot" is false, rsync will: (1) munge symlinks by
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 "use chroot" is false or the inside-chroot path 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
bf(--backup-dir), bf(--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 chroot.
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).
@@ -182,7 +195,7 @@ dit(bf(munge symlinks)) The "munge symlinks" option tells rsync to modify
all incoming symlinks in a way that makes them unusable but recoverable
(see below). This should help protect your files from user trickery when
your daemon module is writable. The default is disabled when "use chroot"
is on and enabled when "use chroot" is off.
is on and the inside-chroot path is "/", otherwise it is enabled.
If you disable this option on a daemon that is not read-only, there
are tricks that a user can play with uploaded symlinks to access
@@ -194,8 +207,9 @@ The way rsync disables the use of symlinks is to prefix each one with
the string "/rsyncd-munged/". This prevents the links from being used
as long as that directory does not exist. When this option is enabled,
rsync will refuse to run if that path is a directory or a symlink to
a directory. When using the "munge symlinks" option in a chroot area,
you should add this path to the exclude setting for the module so that
a directory. When using the "munge symlinks" option in a chroot area
that has an inside-chroot path of "/", you should add "/rsyncd-munged/"
to the exclude setting for the module so that
a user can't try to create it.
Note: rsync makes no attempt to verify that any pre-existing symlinks in
@@ -206,7 +220,8 @@ every symlink's value. There is a perl script in the support directory
of the source code named "munge-symlinks" that can be used to add or remove
this prefix from your symlinks.
When this option is disabled on a writable module and "use chroot" is off,
When this option is disabled on a writable module and "use chroot" is off
(or the inside-chroot path is not "/"),
incoming symlinks will be modified to drop a leading slash and to remove ".."
path elements that rsync believes will allow a symlink to escape the module's
hierarchy. There are tricky ways to work around this, though, so you had
@@ -635,21 +650,21 @@ A more sophisticated example would be:
verb(
uid = nobody
gid = nobody
use chroot = no
use chroot = yes
max connections = 4
syslog facility = local5
pid file = /var/run/rsyncd.pid
[ftp]
path = /var/ftp/pub
path = /var/ftp/./pub
comment = whole ftp area (approx 6.1 GB)
[sambaftp]
path = /var/ftp/pub/samba
path = /var/ftp/./pub/samba
comment = Samba ftp area (approx 300 MB)
[rsyncftp]
path = /var/ftp/pub/rsync
path = /var/ftp/./pub/rsync
comment = rsync ftp area (approx 6 MB)
[sambawww]