[SCM] The rsync repository. - branch master updated

Rsync CVS commit messages rsync-cvs at lists.samba.org
Sun Mar 29 20:21:47 UTC 2020


The branch, master has been updated
       via  08650cb1 Add a --copy-as=USER[:GROUP] option
       via  24c28cd7 Match the latest git "clean" text.
      from  c0c6a97c Try to fix the iconv crash in bug 11338.

https://git.samba.org/?p=rsync.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 08650cb14cd2b07fdebea8ec79cf78e7bfc473a6
Author: Wayne Davison <wayned at samba.org>
Date:   Sun Mar 29 13:01:13 2020 -0700

    Add a --copy-as=USER[:GROUP] option
    
    This can be used by a root-run rsync to try to make reading or writing
    files safer in a situation where you can't run the whole rsync command
    as a non-root user.

commit 24c28cd715b30ac9e0b669ddadc895e340ed0e9c
Author: Wayne Davison <wayned at samba.org>
Date:   Tue Mar 19 09:35:59 2019 -0700

    Match the latest git "clean" text.

-----------------------------------------------------------------------

Summary of changes:
 main.c                  | 78 +++++++++++++++++++++++++++++++++++++++++++++++++
 options.c               |  3 ++
 packaging/nightly-rsync |  2 +-
 rsync.yo                | 77 ++++++++++++++++++++++++++++++++----------------
 4 files changed, 134 insertions(+), 26 deletions(-)


Changeset truncated at 500 lines:

diff --git a/main.c b/main.c
index 6a6ac559..f7905494 100644
--- a/main.c
+++ b/main.c
@@ -89,6 +89,7 @@ extern char *shell_cmd;
 extern char *batch_name;
 extern char *password_file;
 extern char *backup_dir;
+extern char *copy_as;
 extern char curr_dir[MAXPATHLEN];
 extern char backup_dir_buf[MAXPATHLEN];
 extern char *basis_dir[MAX_BASIS_DIRS+1];
@@ -231,6 +232,74 @@ void read_del_stats(int f)
 	stats.deleted_files += stats.deleted_specials = read_varint(f);
 }
 
+static void become_copy_as_user()
+{
+	char *gname;
+	uid_t uid;
+	gid_t gid;
+
+	if (!copy_as)
+		return;
+
+	if (DEBUG_GTE(CMD, 2))
+		rprintf(FINFO, "[%s] copy_as=%s\n", who_am_i(), copy_as);
+
+	if ((gname = strchr(copy_as, ':')) != NULL)
+		*gname++ = '\0';
+
+	if (!user_to_uid(copy_as, &uid, True)) {
+		rprintf(FERROR, "Invalid copy-as user: %s\n", copy_as);
+		exit_cleanup(RERR_SYNTAX);
+	}
+
+	if (gname) {
+		if (!group_to_gid(gname, &gid, True)) {
+			rprintf(FERROR, "Invalid copy-as group: %s\n", gname);
+			exit_cleanup(RERR_SYNTAX);
+		}
+	} else {
+		struct passwd *pw;
+		if ((pw = getpwuid(uid)) == NULL) {
+			rsyserr(FERROR, errno, "getpwuid failed");
+			exit_cleanup(RERR_SYNTAX);
+		}
+		gid = pw->pw_gid;
+	}
+
+	if (setgid(gid) < 0) {
+		rsyserr(FERROR, errno, "setgid failed");
+		exit_cleanup(RERR_SYNTAX);
+	}
+#ifdef HAVE_SETGROUPS
+	if (setgroups(1, &gid)) {
+		rsyserr(FERROR, errno, "setgroups failed");
+		exit_cleanup(RERR_SYNTAX);
+	}
+#endif
+#ifdef HAVE_INITGROUPS
+	if (!gname && initgroups(copy_as, gid) < 0) {
+		rsyserr(FERROR, errno, "initgroups failed");
+		exit_cleanup(RERR_SYNTAX);
+	}
+#endif
+
+	if (setuid(uid) < 0
+#ifdef HAVE_SETEUID
+	 || seteuid(uid) < 0
+#endif
+	) {
+		rsyserr(FERROR, errno, "setuid failed");
+		exit_cleanup(RERR_SYNTAX);
+	}
+
+	our_uid = MY_UID();
+	our_gid = MY_GID();
+	am_root = (our_uid == 0);
+
+	if (gname)
+		gname[-1] = ':';
+}
+
 /* This function gets called from all 3 processes.  We want the client side
  * to actually output the text, but the sender is the only process that has
  * all the stats we need.  So, if we're a client sender, we do the report.
@@ -824,6 +893,8 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
 		exit_cleanup(RERR_SYNTAX);
 	}
 
+	become_copy_as_user();
+
 	dir = argv[0];
 	if (!relative_paths) {
 		if (!change_dir(dir, CD_NORMAL)) {
@@ -1027,6 +1098,8 @@ static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
 		return;
 	}
 
+	become_copy_as_user();
+
 	if (argc > 0) {
 		char *dir = argv[0];
 		argc--;
@@ -1186,6 +1259,9 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
 
 		if (write_batch && !am_server)
 			start_write_batch(f_out);
+
+		become_copy_as_user();
+
 		flist = send_file_list(f_out, argc, argv);
 		if (DEBUG_GTE(FLIST, 3))
 			rprintf(FINFO,"file list sent\n");
@@ -1219,6 +1295,8 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
 			io_start_buffering_out(f_out);
 	}
 
+	become_copy_as_user();
+
 	send_filter_list(read_batch ? -1 : f_out);
 
 	if (filesfrom_fd >= 0) {
diff --git a/options.c b/options.c
index e5b0cb68..a9b07184 100644
--- a/options.c
+++ b/options.c
@@ -126,6 +126,7 @@ int inplace = 0;
 int delay_updates = 0;
 long block_size = 0; /* "long" because popt can't set an int32. */
 char *skip_compress = NULL;
+char *copy_as = NULL;
 item_list dparam_list = EMPTY_ITEM_LIST;
 
 /** Network address family. **/
@@ -777,6 +778,7 @@ void usage(enum logcode F)
   rprintf(F,"     --files-from=FILE       read list of source-file names from FILE\n");
   rprintf(F," -0, --from0                 all *-from/filter files are delimited by 0s\n");
   rprintf(F," -s, --protect-args          no space-splitting; only wildcard special-chars\n");
+  rprintf(F,"     --copy-as=USER[:GROUP]  specify user & optional group for the copy\n");
   rprintf(F,"     --address=ADDRESS       bind address for outgoing socket to daemon\n");
   rprintf(F,"     --port=PORT             specify double-colon alternate port number\n");
   rprintf(F,"     --sockopts=OPTIONS      specify custom TCP options\n");
@@ -1030,6 +1032,7 @@ static struct poptOption long_options[] = {
   {"no-8-bit-output",  0,  POPT_ARG_VAL,    &allow_8bit_chars, 0, 0, 0 },
   {"no-8",             0,  POPT_ARG_VAL,    &allow_8bit_chars, 0, 0, 0 },
   {"qsort",            0,  POPT_ARG_NONE,   &use_qsort, 0, 0, 0 },
+  {"copy-as",          0,  POPT_ARG_STRING, &copy_as, 0, 0, 0 },
   {"address",          0,  POPT_ARG_STRING, &bind_address, 0, 0, 0 },
   {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
   {"sockopts",         0,  POPT_ARG_STRING, &sockopts, 0, 0, 0 },
diff --git a/packaging/nightly-rsync b/packaging/nightly-rsync
index 23451ca7..74891baa 100755
--- a/packaging/nightly-rsync
+++ b/packaging/nightly-rsync
@@ -40,7 +40,7 @@ if ($make_tar) {
     open(IN, '-|', 'git status') or die $!;
     my $status = join('', <IN>);
     close IN;
-    die "The checkout is not clean:\n", $status unless $status =~ /\nnothing to commit.+working directory clean/;
+    die "The checkout is not clean:\n", $status unless $status =~ /\nnothing to commit.+working (directory|tree) clean/;
     die "The checkout is not on the master branch.\n" unless $status =~ /^(?:# )?On branch master\n/;
     system "make $gen_target" and die "make $gen_target failed!\n";
 
diff --git a/rsync.yo b/rsync.yo
index 207d487e..1950349c 100644
--- a/rsync.yo
+++ b/rsync.yo
@@ -440,6 +440,7 @@ to the detailed description below for a complete description.  verb(
      --files-from=FILE       read list of source-file names from FILE
  -0, --from0                 all *from/filter files are delimited by 0s
  -s, --protect-args          no space-splitting; wildcard chars only
+     --copy-as=USER[:GROUP]  specify user & optional group for the copy
      --address=ADDRESS       bind address for outgoing socket to daemon
      --port=PORT             specify double-colon alternate port number
      --sockopts=OPTIONS      specify custom TCP options
@@ -624,8 +625,8 @@ resolution (allowing times to differ from the original by up to 1 second).
 If you want all your transfers to default to comparing nanoseconds, you can
 create a ~/.popt file and put these lines in it:
 
-quote(tt(   rsync alias -a -a at -1))
-quote(tt(   rsync alias -t -t at -1))
+verb(    rsync alias -a -a at -1)
+verb(    rsync alias -t -t at -1)
 
 With that as the default, you'd need to specify bf(--modify-window=0) (aka
 bf(- at 0)) to override it and ignore nanoseconds, e.g. if you're copying between
@@ -713,12 +714,12 @@ just the last parts of the filenames. This is particularly useful when
 you want to send several different directories at the same time. For
 example, if you used this command:
 
-quote(tt(   rsync -av /foo/bar/baz.c remote:/tmp/))
+verb(    rsync -av /foo/bar/baz.c remote:/tmp/)
 
 ... this would create a file named baz.c in /tmp/ on the remote
 machine. If instead you used
 
-quote(tt(   rsync -avR /foo/bar/baz.c remote:/tmp/))
+verb(    rsync -avR /foo/bar/baz.c remote:/tmp/)
 
 then a file named /tmp/foo/bar/baz.c would be created on the remote
 machine, preserving its full path.  These extra path elements are called
@@ -739,24 +740,22 @@ implied directories for each path you specify.  With a modern rsync on the
 sending side (beginning with 2.6.7), you can insert a dot and a slash into
 the source path, like this:
 
-quote(tt(   rsync -avR /foo/./bar/baz.c remote:/tmp/))
+verb(    rsync -avR /foo/./bar/baz.c remote:/tmp/)
 
 That would create /tmp/bar/baz.c on the remote machine.  (Note that the
 dot must be followed by a slash, so "/foo/." would not be abbreviated.)
 For older rsync versions, you would need to use a chdir to limit the
 source path.  For example, when pushing files:
 
-quote(tt(   (cd /foo; rsync -avR bar/baz.c remote:/tmp/) ))
+verb(    (cd /foo; rsync -avR bar/baz.c remote:/tmp/) )
 
 (Note that the parens put the two commands into a sub-shell, so that the
 "cd" command doesn't remain in effect for future commands.)
 If you're pulling files from an older rsync, use this idiom (but only
 for a non-daemon transfer):
 
-quote(
-tt(   rsync -avR --rsync-path="cd /foo; rsync" \ )nl()
-tt(       remote:bar/baz.c /tmp/)
-)
+verb(  rsync -avR --rsync-path="cd /foo; rsync" \ )
+verb(       remote:bar/baz.c /tmp/)
 
 dit(bf(--no-implied-dirs)) This option affects the default behavior of the
 bf(--relative) option.  When it is specified, the attributes of the implied
@@ -1089,11 +1088,11 @@ behavior easier to type, you could define a popt alias for it, such as
 putting this line in the file ~/.popt (the following defines the bf(-Z) option,
 and includes --no-g to use the default group of the destination dir):
 
-quote(tt(   rsync alias -Z --no-p --no-g --chmod=ugo=rwX))
+verb(    rsync alias -Z --no-p --no-g --chmod=ugo=rwX)
 
 You could then use this new option in a command such as this one:
 
-quote(tt(   rsync -avZ src/ dest/))
+verb(    rsync -avZ src/ dest/)
 
 (Caveat: make sure that bf(-a) does not follow bf(-Z), or it will re-enable
 the two "--no-*" options mentioned above.)
@@ -1277,7 +1276,7 @@ The bf(--fake-super) option only affects the side where the option is used.
 To affect the remote side of a remote-shell connection, use the
 bf(--remote-option) (bf(-M)) option:
 
-quote(tt(  rsync -av -M--fake-super /src/ host:/dest/))
+verb(    rsync -av -M--fake-super /src/ host:/dest/)
 
 For a local copy, this option affects both the source and the destination.
 If you wish a local copy to enable this option just for the destination
@@ -1592,10 +1591,8 @@ inside a single-quoted string gives you a single-quote; likewise for
 double-quotes (though you need to pay attention to which quotes your
 shell is parsing and which quotes rsync is parsing).  Some examples:
 
-quote(
-tt(    -e 'ssh -p 2234')nl()
-tt(    -e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"')nl()
-)
+verb(    -e 'ssh -p 2234')
+verb(    -e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"')
 
 (Note that ssh users can alternately customize site-specific connect
 options in their .ssh/config file.)
@@ -1616,20 +1613,20 @@ communicate.
 One tricky example is to set a different default directory on the remote
 machine for use with the bf(--relative) option.  For instance:
 
-quote(tt(    rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/))
+verb(    rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/)
 
 dit(bf(-M, --remote-option=OPTION)) This option is used for more advanced
 situations where you want certain effects to be limited to one side of the
 transfer only.  For instance, if you want to pass bf(--log-file=FILE) and
 bf(--fake-super) to the remote system, specify it like this:
 
-quote(tt(    rsync -av -M --log-file=foo -M--fake-super src/ dest/))
+verb(    rsync -av -M --log-file=foo -M--fake-super src/ dest/)
 
 If you want to have an option affect only the local side of a transfer when
 it normally affects both sides, send its negation to the remote side.  Like
 this:
 
-quote(tt(    rsync -av -x -M--no-x src/ dest/))
+verb(    rsync -av -x -M--no-x src/ dest/)
 
 Be cautious using this, as it is possible to toggle an option that will cause
 rsync to have a different idea about what data to expect next over the socket,
@@ -1696,14 +1693,14 @@ See the FILTER RULES section for detailed information on this option.
 dit(bf(-F)) The bf(-F) option is a shorthand for adding two bf(--filter) rules to
 your command.  The first time it is used is a shorthand for this rule:
 
-quote(tt(   --filter='dir-merge /.rsync-filter'))
+verb(    --filter='dir-merge /.rsync-filter')
 
 This tells rsync to look for per-directory .rsync-filter files that have
 been sprinkled through the hierarchy and use their rules to filter the
 files in the transfer.  If bf(-F) is repeated, it is a shorthand for this
 rule:
 
-quote(tt(   --filter='exclude .rsync-filter'))
+verb(    --filter='exclude .rsync-filter')
 
 This filters out the .rsync-filter files themselves from the transfer.
 
@@ -1757,7 +1754,7 @@ source dir -- any leading slashes are removed and no ".." references are
 allowed to go higher than the source dir.  For example, take this
 command:
 
-quote(tt(   rsync -a --files-from=/tmp/foo /usr remote:/backup))
+verb(  rsync -a --files-from=/tmp/foo /usr remote:/backup)
 
 If /tmp/foo contains the string "bin" (or even "/bin"), the /usr/bin
 directory will be created as /backup/bin on the remote host.  If it
@@ -1778,7 +1775,7 @@ instead of the local host if you specify a "host:" in front of the file
 specify just a prefix of ":" to mean "use the remote end of the
 transfer".  For example:
 
-quote(tt(   rsync -a --files-from=:/path/file-list src:/ /tmp/copy))
+verb(  rsync -a --files-from=:/path/file-list src:/ /tmp/copy)
 
 This would copy all the files specified in the /path/file-list file that
 was located on the remote "src" host.
@@ -1829,6 +1826,36 @@ default (with is overridden by both the environment and the command-line).
 This option will eventually become a new default setting at some
 as-yet-undetermined point in the future.
 
+dit(bf(--copy-as=USER[:GROUP])) This option instructs rsync to use the USER and
+(if specified after a colon) the GROUP for the copy operations. This only works
+if the user that is running rsync has the ability to change users. If the group
+is not specified then the user's default groups are used.
+
+The option only affects one side of the transfer unless the transfer is local,
+in which case it affects both sides. Use the bf(--remote-option) to affect the
+remote side, such as bf(-M--copy-as=joe). For a local transfer, see the "lsh"
+support file provides a local-shell helper script that can be used to allow a
+"localhost:" host-spec to be specified without needing to setup any remote
+shells (allowing you to specify remote options that affect the side of the
+transfer that is using the host-spec, and local options for the other side).
+
+This option can help to reduce the risk of an rsync being run as root into or
+out of a directory that might have live changes happening to it and you want to
+make sure that root-level read or write actions of system files are not
+possible. While you could alternatively run all of rsync as the specified user,
+sometimes you need the root-level host-access credentials to be used, so this
+allows rsync to drop root for the copying part of the operation after the
+remote-shell or daemon connection is established.
+
+For example, the following rsync writes the local files as user "joe":
+
+verb(   sudo rsync -aiv --copy-as=joe host1:backups/joe/ /home/joe/)
+
+This makes all files owned by user "joe", limits the groups to those that are
+available to that user, and makes it impossible for the joe user to do a timed
+exploit of the path to induce a change to a file that the joe use has no
+permissions to change.
+
 dit(bf(-T, --temp-dir=DIR)) This option instructs rsync to use DIR as a
 scratch directory when creating temporary copies of the files transferred
 on the receiving side.  The default behavior is to create each temporary
@@ -1924,7 +1951,7 @@ The files must be identical in all preserved attributes (e.g. permissions,
 possibly ownership) in order for the files to be linked together.
 An example:
 
-quote(tt(  rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/))
+verb(  rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/)
 
 If file's aren't linking, double-check their attributes.  Also check if some
 attributes are getting forced outside of rsync's control, such a mount option


-- 
The rsync repository.



More information about the rsync-cvs mailing list