From 881addc9e14a584a461929801968d9824281fab2 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 4 Sep 2017 14:20:16 -0700 Subject: [PATCH] Add "daemon chroot|uid|gid" parameters. This allows the daemon to run chrooted as any uid+gid you like (prior to the transfer possibly changing the chroot and/or the uid+gid further). Based on the patch in #12817. --- clientserver.c | 38 ++++++++++++++++++++++++++++++++++++-- loadparm.c | 12 ++++++++++++ rsyncd.conf.yo | 23 ++++++++++++++++++++--- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/clientserver.c b/clientserver.c index 91aee270..7c79e90a 100644 --- a/clientserver.c +++ b/clientserver.c @@ -426,7 +426,7 @@ static int read_arg_from_pipe(int fd, char *buf, int limit) static int path_failure(int f_out, const char *dir, BOOL was_chdir) { if (was_chdir) - rsyserr(FLOG, errno, "chdir %s failed\n", dir); + rsyserr(FLOG, errno, "chdir %s failed", dir); else rprintf(FLOG, "normalize_path(%s) failed\n", dir); io_printf(f_out, "@ERROR: chdir failed\n"); @@ -794,7 +794,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) + if (module_dirlen || (!use_chroot && !*lp_daemon_chroot())) sanitize_paths = 1; if ((munge_symlinks = lp_munge_symlinks(i)) < 0) @@ -1039,6 +1039,7 @@ int start_daemon(int f_in, int f_out) { char line[1024]; const char *addr, *host; + char *p; int i; io_set_sock_fds(f_in, f_out); @@ -1050,6 +1051,39 @@ int start_daemon(int f_in, int f_out) if (!load_config(0)) exit_cleanup(RERR_SYNTAX); + 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); + return -1; + } + } + p = lp_daemon_gid(); + if (*p) { + gid_t gid; + if (!group_to_gid(p, &gid, True)) { + rprintf(FLOG, "Invalid daemon gid: %s\n", p); + return -1; + } + if (setgid(gid) < 0) { + rsyserr(FLOG, errno, "Unable to set group to daemon gid %ld", (long)gid); + return -1; + } + } + p = lp_daemon_uid(); + if (*p) { + uid_t uid; + if (!user_to_uid(p, &uid, True)) { + rprintf(FLOG, "Invalid daemon uid: %s\n", p); + return -1; + } + if (setuid(uid) < 0) { + rsyserr(FLOG, errno, "Unable to set user to daemon uid %ld", (long)uid); + return -1; + } + } + addr = client_addr(f_in); host = lp_reverse_lookup(-1) ? client_name(f_in) : undetermined_hostname; rprintf(FLOG, "connect from %s (%s)\n", host, addr); diff --git a/loadparm.c b/loadparm.c index 21703efe..a531106c 100644 --- a/loadparm.c +++ b/loadparm.c @@ -93,6 +93,9 @@ struct parm_struct { /* This structure describes global (ie., server-wide) parameters. */ typedef struct { char *bind_address; + char *daemon_chroot; + char *daemon_gid; + char *daemon_uid; char *motd_file; char *pid_file; char *socket_options; @@ -173,6 +176,9 @@ static const all_vars Defaults = { /* ==== global_vars ==== */ { /* bind_address; */ NULL, + /* daemon_chroot; */ NULL, + /* daemon_gid; */ NULL, + /* daemon_uid; */ NULL, /* motd_file; */ NULL, /* pid_file; */ NULL, /* socket_options; */ NULL, @@ -315,6 +321,9 @@ static struct enum_list enum_facilities[] = { static struct parm_struct parm_table[] = { {"address", P_STRING, P_GLOBAL,&Vars.g.bind_address, NULL,0}, + {"daemon chroot", P_STRING, P_GLOBAL,&Vars.g.daemon_chroot, NULL,0}, + {"daemon gid", P_STRING, P_GLOBAL,&Vars.g.daemon_gid, NULL,0}, + {"daemon uid", P_STRING, P_GLOBAL,&Vars.g.daemon_uid, NULL,0}, {"listen backlog", P_INTEGER,P_GLOBAL,&Vars.g.listen_backlog, NULL,0}, {"motd file", P_STRING, P_GLOBAL,&Vars.g.motd_file, NULL,0}, {"pid file", P_STRING, P_GLOBAL,&Vars.g.pid_file, NULL,0}, @@ -447,6 +456,9 @@ static char *expand_vars(char *str) int fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;} FN_GLOBAL_STRING(lp_bind_address, &Vars.g.bind_address) +FN_GLOBAL_STRING(lp_daemon_chroot, &Vars.g.daemon_chroot) +FN_GLOBAL_STRING(lp_daemon_gid, &Vars.g.daemon_gid) +FN_GLOBAL_STRING(lp_daemon_uid, &Vars.g.daemon_uid) FN_GLOBAL_STRING(lp_motd_file, &Vars.g.motd_file) FN_GLOBAL_STRING(lp_pid_file, &Vars.g.pid_file) FN_GLOBAL_STRING(lp_socket_options, &Vars.g.socket_options) diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo index b6fc2b58..9a6a1e44 100644 --- a/rsyncd.conf.yo +++ b/rsyncd.conf.yo @@ -186,8 +186,8 @@ transfer. For example, specifying "/var/rsync/./module1" will chroot to the 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 +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 @@ -212,6 +212,14 @@ Note also that you are free to setup custom user/group information in the chroot area that is different from your normal system. For example, you could abbreviate the list of users and groups. +dit(bf(daemon chroot)) This parameter specifies a path to which the daemon will +chroot before beginning communication with clients. Module paths (and any "use +chroot" settings) will then be related to this one. This lets you choose if you +want the whole daemon to be chrooted (with this setting), just the transfers to +be chrooted (with "use chroot"), or both. Keep in mind that the "daemon chroot" +area may need various OS/lib/etc files installed to allow the daemon to function. +By default the daemon runs without any chrooting. + dit(bf(numeric ids)) Enabling this parameter disables the mapping of users and groups by name for the current daemon module. This prevents the daemon from trying to load any user/group-related files or libraries. @@ -234,7 +242,8 @@ all symlinks in the same way as the (non-daemon-affecting) bf(--munge-links) command-line option (using a method described 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 the inside-chroot path is "/", otherwise it is enabled. +is on with an inside-chroot path of "/", OR if "daemon chroot" is on, +otherwise it is enabled. If you disable this parameter on a daemon that is not read-only, there are tricks that a user can play with uploaded symlinks to access @@ -392,6 +401,14 @@ supplementary groups. The default for a non-super-user is to not change any group attributes (and indeed, your OS may not allow a non-super-user to try to change their group settings). +dit(bf(daemon uid)) This parameter specifies a uid under which the daemon will +run. The daemon usually runs as user root, and when this is left unset the user +is left unchanged. See also the "uid" parameter. + +dit(bf(daemon gid)) This parameter specifies a gid under which the daemon will +run. The daemon usually runs as group root, and when this is left unset, the +group is left unchanged. See also the "gid" parameter. + dit(bf(fake super)) Setting "fake super = yes" for a module causes the daemon side to behave as if the bf(--fake-super) command-line option had been specified. This allows the full attributes of a file to be stored -- 2.34.1