Commit 460f6b99 authored by David Dykstra's avatar David Dykstra

Prevent the -g option from preserving groups that a non-root receiver

does not belong to, in these two ways:
    1. If a group mapping doesn't exist for a group name, do not preserve
	it for a non-root receiver.  This is especially evident with the
	sender is a daemon using chroot because then no mappings are
	available.
    2. Before setting the group on a file make sure that it is in the list
	of groups returned by getgroups().  The same thing is done by chgrp
	on systems that support bsd-style chown/chgrp, and this enforces
	that it happens the same way on all systems.  Overhead is very
	little, especially since most systems don't allow more then 16
	groups per user.
parent 896bd482
......@@ -52,7 +52,7 @@ AC_FUNC_UTIME_NULL
AC_CHECK_FUNCS(waitpid wait4 getcwd strdup strerror chown chmod mknod)
AC_CHECK_FUNCS(fchmod fstat strchr readlink link utime utimes strftime)
AC_CHECK_FUNCS(memmove getopt_long lchown vsnprintf snprintf setsid glob strpbrk)
AC_CHECK_FUNCS(strlcat strlcpy)
AC_CHECK_FUNCS(strlcat strlcpy getgroups)
echo $ac_n "checking for working fnmatch... $ac_c"
AC_TRY_RUN([#include <fnmatch.h>
......
......@@ -113,12 +113,46 @@ int delete_file(char *fname)
return 0;
}
static int is_in_group(gid_t gid)
{
#ifdef HAVE_GETGROUPS
static gid_t last_in = (gid_t) -2, last_out;
static int ngroups = -2;
static gid_t *gidset;
int n;
if (gid == last_in)
return last_out;
if (ngroups < -1) {
/* treat failure (-1) as if not member of any group */
ngroups = getgroups(0, 0);
if (ngroups > 0) {
gidset = (gid_t *) malloc(ngroups * sizeof(gid_t));
ngroups = getgroups(ngroups, gidset);
}
}
last_in = gid;
last_out = 0;
for (n = 0; n < ngroups; n++) {
if (gidset[n] == gid) {
last_out = 1;
break;
}
}
return last_out;
#else
return 0;
#endif
}
int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st,
int report)
{
int updated = 0;
STRUCT_STAT st2;
int change_uid, change_gid;
extern int am_daemon;
if (dry_run) return 0;
......@@ -145,22 +179,24 @@ int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st,
}
}
if ((am_root || !am_daemon) &&
((am_root && preserve_uid && st->st_uid != file->uid) ||
(preserve_gid && st->st_gid != file->gid))) {
change_uid = am_root && preserve_uid && st->st_uid != file->uid;
change_gid = !am_daemon && preserve_gid && file->gid != -1 \
&& st->st_gid != file->gid;
if (change_gid && !am_root) {
/* enforce bsd-style group semantics: non-root can only
change to groups that the user is a member of */
change_gid = is_in_group(file->gid);
}
if (change_uid || change_gid) {
if (do_lchown(fname,
(am_root&&preserve_uid)?file->uid:st->st_uid,
preserve_gid?file->gid:st->st_gid) != 0) {
if (preserve_uid && st->st_uid != file->uid)
updated = 1;
if (verbose>1 || preserve_uid) {
rprintf(FERROR,"chown %s : %s\n",
fname,strerror(errno));
return 0;
}
} else {
updated = 1;
change_uid?file->uid:st->st_uid,
change_gid?file->gid:st->st_gid) != 0) {
/* shouldn't have attempted to change uid or gid
unless have the privilege */
rprintf(FERROR,"chown %s : %s\n", fname,strerror(errno));
return 0;
}
updated = 1;
}
#ifdef HAVE_CHMOD
......
mailto(rsync-bugs@samba.org)
manpage(rsync)(1)(22 Feb 1999)()()
manpage(rsync)(1)(1 Mar 1999)()()
manpagename(rsync)(faster, flexible replacement for rcp)
manpagesynopsis()
......@@ -319,11 +319,11 @@ explicitly checked on the receiver and any files of the same name
which already exist and have the same checksum and size on the
receiver are skipped. This option can be quite slow.
dit(bf(-a, --archive)) This is equivalent to -rlptDg. It is a quick way
dit(bf(-a, --archive)) This is equivalent to -rlptg. It is a quick way
of saying you want recursion and want to preserve everything.
Note: if the user launching rsync is root then the -o option (preserve
uid) is also implied.
Note: if the user launching rsync is root then the -o (preserve
uid) and -D (preserve devices) options are also implied.
dit(bf(-r, --recursive)) This tells rsync to copy directories recursively.
......@@ -400,9 +400,9 @@ the --numeric-ids option is implied because the source system cannot get
access to the usernames.
dit(bf(-g, --group)) This option causes rsync to update the remote group
of the file to be the same as the local group. Note that if the source
system is a daemon using chroot, the --numeric-ids option is implied because
the source system cannot get access to the group names.
of the file to be the same as the local group. If the receving system is
not running as the super-user, only groups that the receiver is a member of
will be preserved (by group name, not group id number).
dit(bf(-D, --devices)) This option causes rsync to transfer character and
block device information to the remote system to recreate these
......
......@@ -28,6 +28,7 @@
extern int preserve_uid;
extern int preserve_gid;
extern int numeric_ids;
extern int am_root;
struct idlist {
struct idlist *next;
......@@ -122,7 +123,10 @@ static gid_t match_gid(gid_t gid)
list = list->next;
}
last_out = gid;
if (am_root)
last_out = gid;
else
last_out = -1;
return last_out;
}
......@@ -276,12 +280,12 @@ void recv_uid_list(int f, struct file_list *flist)
}
}
if (!uidlist && !gidlist) return;
if (!(am_root && preserve_uid) && !preserve_gid) return;
/* now convert the uid/gid of all files in the list to the mapped
uid/gid */
for (i=0;i<flist->count;i++) {
if (preserve_uid && flist->files[i]->uid != 0) {
if (am_root && preserve_uid && flist->files[i]->uid != 0) {
flist->files[i]->uid = match_uid(flist->files[i]->uid);
}
if (preserve_gid && flist->files[i]->gid != 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment