From 460f6b990a89a13060b57e1e52f72346bf020679 Mon Sep 17 00:00:00 2001 From: David Dykstra Date: Mon, 1 Mar 1999 19:24:39 +0000 Subject: [PATCH] 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. --- configure.in | 2 +- rsync.c | 64 ++++++++++++++++++++++++++++++++++++++++------------ rsync.yo | 14 ++++++------ uidlist.c | 10 +++++--- 4 files changed, 65 insertions(+), 25 deletions(-) diff --git a/configure.in b/configure.in index 11d6faec..be39200e 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/rsync.c b/rsync.c index 3f79f504..ceabfdac 100644 --- a/rsync.c +++ b/rsync.c @@ -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 diff --git a/rsync.yo b/rsync.yo index 9f27cf97..30bdca6c 100644 --- a/rsync.yo +++ b/rsync.yo @@ -1,5 +1,5 @@ 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 diff --git a/uidlist.c b/uidlist.c index eff8749b..e4b61b88 100644 --- a/uidlist.c +++ b/uidlist.c @@ -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;icount;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) { -- 2.34.1