Added a way for supplementary groups to be specified in the rsyncd.conf

file.  Also made explicitly-set uid/gid values no longer ignored by a
daemon that was not run by a super-user.
This commit is contained in:
Wayne Davison committed 2009-02-19 23:08:48 -08:00
1 parent df7ec1cf42
commit 7f367bb1b4
6 files changed
+134 -47

No files matched your search

+4
View File
@@ -38,6 +38,10 @@ Changes since 3.0.4:
- Added the "reverse lookup" parameter to the rsync daemon config file to
allow reverse-DNS lookups to be disabled.
- Added a way for supplementary groups to be specified in the rsyncd.conf
file. Also made explicitly-set uid/gid values no longer ignored by a
daemon that was not run by a super-user.
EXTRAS:
- Added an "instant-rsyncd" script to the support directory, which makes
+112 -36
View File
@@ -62,6 +62,8 @@ extern char *iconv_opt;
extern iconv_t ic_send, ic_recv;
#endif
#define MAX_GID_LIST 32
char *auth_user;
int read_only = 0;
int module_id = -1;
@@ -81,6 +83,9 @@ static int rl_nulls = 0;
static struct sigaction sigact;
#endif
static gid_t gid_list[MAX_GID_LIST];
static int gid_count = 0;
/* Used when "reverse lookup" is off. */
const char undetermined_hostname[] = "UNDETERMINED";
@@ -408,13 +413,75 @@ static int path_failure(int f_out, const char *dir, BOOL was_chdir)
return -1;
}
static int add_a_group(int f_out, const char *gname)
{
gid_t gid;
if (!name_to_gid(gname, &gid)) {
if (!isDigit(gname)) {
rprintf(FLOG, "Invalid gid %s\n", gname);
io_printf(f_out, "@ERROR: invalid gid %s\n", gname);
return -1;
}
gid = atol(gname);
}
if (gid_count == MAX_GID_LIST) {
rprintf(FLOG, "Too many groups specified via gid parameter.\n");
io_printf(f_out, "@ERROR: too many groups\n");
return -1;
}
gid_list[gid_count++] = gid;
return 0;
}
static struct passwd *want_all_groups(int f_out, uid_t uid)
{
#if defined HAVE_GETGROUPLIST || defined HAVE_INITGROUPS
struct passwd *pw;
if ((pw = getpwuid(uid)) == NULL) {
rsyserr(FLOG, errno, "getpwuid failed");
io_printf(f_out, "@ERROR: getpwuid failed\n");
return NULL;
}
# ifdef HAVE_GETGROUPLIST
/* Get all the process's groups, with the pw_gid group first. */
gid_count = MAX_GID_LIST;
if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list, &gid_count) < 0) {
rsyserr(FLOG, errno, "getgrouplist failed");
io_printf(f_out, "@ERROR: getgrouplist failed\n");
return NULL;
}
/* Paranoia: is the default group not first in the list? */
if (gid_list[0] != pw->pw_gid) {
int j;
for (j = 0; j < gid_count; j++) {
if (gid_list[j] == pw->pw_gid) {
gid_list[j] = gid_list[0];
gid_list[0] = pw->pw_gid;
break;
}
}
}
# else
/* Start with the default group and initgroups() will add the reset. */
gid_count = 1;
gid_list[0] = pw->pw_gid;
# endif
return pw;
#else
rprintf(FLOG, "This rsync does not support a gid of \"*\"\n");
io_printf(f_out, "@ERROR: invalid gid setting.\n");
return NULL;
#endif
}
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;
char line[BIGPATHBUFLEN];
uid_t uid = (uid_t)-2; /* canonically "nobody" */
gid_t gid = (gid_t)-2;
struct passwd *pw = NULL;
uid_t uid;
int set_uid;
char *p, *err_msg = NULL;
char *name = lp_name(i);
int use_chroot = lp_use_chroot(i);
@@ -486,37 +553,47 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
logfile_format_has_o_or_i = 1;
am_root = (MY_UID() == 0);
uid = MY_UID();
am_root = (uid == 0);
if (am_root) {
p = lp_uid(i);
p = *lp_uid(i) ? lp_uid(i) : am_root ? NOBODY_USER : NULL;
if (p) {
if (!name_to_uid(p, &uid)) {
if (!isDigit(p)) {
rprintf(FLOG, "Invalid uid %s\n", p);
io_printf(f_out, "@ERROR: invalid uid %s\n", p);
return -1;
}
uid = atoi(p);
uid = atol(p);
}
set_uid = 1;
} else
set_uid = 0;
p = lp_gid(i);
if (!name_to_gid(p, &gid)) {
if (!isDigit(p)) {
rprintf(FLOG, "Invalid gid %s\n", p);
io_printf(f_out, "@ERROR: invalid gid %s\n", p);
p = *lp_gid(i) ? strtok(lp_gid(i), ", ") : NULL;
if (p) {
/* The "*" gid must be the first item in the list. */
if (strcmp(p, "*") == 0) {
if ((pw = want_all_groups(f_out, uid)) == NULL)
return -1;
} else if (add_a_group(f_out, p) < 0)
return -1;
while ((p = strtok(NULL, ", ")) != NULL) {
#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
if (pw) {
rprintf(FLOG, "This rsync cannot add groups after \"*\".\n");
io_printf(f_out, "@ERROR: invalid gid setting.\n");
return -1;
}
gid = atoi(p);
#endif
if (add_a_group(f_out, p) < 0)
return -1;
}
} else if (am_root) {
if (add_a_group(f_out, NOBODY_GROUP) < 0)
return -1;
}
/* TODO: If we're not root, but the configuration requests
* that we change to some uid other than the current one, then
* log a warning. */
/* TODO: Perhaps take a list of gids, and make them into the
* supplementary groups. */
module_dir = lp_path(i);
if (use_chroot) {
if ((p = strstr(module_dir, "/./")) != NULL) {
@@ -705,34 +782,33 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
}
}
if (am_root) {
/* XXXX: You could argue that if the daemon is started
* by a non-root user and they explicitly specify a
* gid, then we should try to change to that gid --
* this could be possible if it's already in their
* supplementary groups. */
/* TODO: Perhaps we need to document that if rsyncd is
* started by somebody other than root it will inherit
* all their supplementary groups. */
if (setgid(gid)) {
rsyserr(FLOG, errno, "setgid %d failed", (int)gid);
if (gid_count) {
if (setgid(gid_list[0])) {
rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_list[0]);
io_printf(f_out, "@ERROR: setgid failed\n");
return -1;
}
#ifdef HAVE_SETGROUPS
/* Get rid of any supplementary groups this process
* might have inheristed. */
if (setgroups(1, &gid)) {
/* Set the group(s) we want to be active. */
if (setgroups(gid_count, gid_list)) {
rsyserr(FLOG, errno, "setgroups failed");
io_printf(f_out, "@ERROR: setgroups failed\n");
return -1;
}
#endif
#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
/* pw is set if the user wants all the user's groups. */
if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
rsyserr(FLOG, errno, "initgroups failed");
io_printf(f_out, "@ERROR: initgroups failed\n");
return -1;
}
#endif
}
if (set_uid) {
if (setuid(uid)) {
rsyserr(FLOG, errno, "setuid %d failed", (int)uid);
rsyserr(FLOG, errno, "setuid %ld failed", (long)uid);
io_printf(f_out, "@ERROR: setuid failed\n");
return -1;
}
+2 -1
View File
@@ -552,7 +552,8 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
strerror putenv iconv_open locale_charset nl_langinfo getxattr \
extattr_get_link sigaction sigprocmask setattrlist)
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
initgroups)
dnl cygwin iconv.h defines iconv_open as libiconv_open
if test x"$ac_cv_func_iconv_open" != x"yes"; then
+2 -2
View File
@@ -185,7 +185,7 @@ static const all_vars Defaults = {
/* exclude; */ NULL,
/* exclude_from; */ NULL,
/* filter; */ NULL,
/* gid; */ NOBODY_GROUP,
/* gid; */ NULL,
/* hosts_allow; */ NULL,
/* hosts_deny; */ NULL,
/* include; */ NULL,
@@ -202,7 +202,7 @@ static const all_vars Defaults = {
/* refuse_options; */ NULL,
/* secrets_file; */ NULL,
/* temp_dir; */ NULL,
/* uid; */ NOBODY_USER,
/* uid; */ NULL,
/* max_connections; */ 0,
/* max_verbosity; */ 1,
+12 -6
View File
@@ -316,13 +316,19 @@ 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
was run as root. In combination with the "gid" parameter this determines what
file permissions are available. The default is uid -2, which is normally
the user "nobody".
file permissions are available. The default when run by a super-user is to
switch to the system's "nobody" user. The default for a non-super-user is to
not try to change the user. See also the "gid" parameter.
dit(bf(gid)) This parameter specifies the group name or group ID that
file transfers to and from that module should take place as when the daemon
was run as root. This complements the "uid" parameter. The default is gid -2,
which is normally the group "nobody".
dit(bf(gid)) This parameter specifies one or more group names/IDs that will be
used when accessing the module. The first one will be the default group, and
any extra ones be set as supplemental groups. You may also specify a "*" as
the first gid in the list, which will be replaced by all the normal groups for
the transfer's user (see "uid"). The default when run by a super-user is to
switch to your OS's "nobody" (or perhaps "nogroup") group with no other
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(fake super)) Setting "fake super = yes" for a module causes the
daemon side to behave as if the bf(--fake-user) command-line option had
+2 -2
View File
@@ -269,8 +269,8 @@ log format = %i %h [%a] %m (%u) %l %f%L
transfer logging = yes
exclude = ? foobar.baz
max verbosity = 4
uid = 0
gid = 0
#uid = 0
#gid = 0
[test-from]
path = $fromdir