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];
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.
exit_cleanup(RERR_SYNTAX);
}
+ become_copy_as_user();
+
dir = argv[0];
if (!relative_paths) {
if (!change_dir(dir, CD_NORMAL)) {
return;
}
+ become_copy_as_user();
+
if (argc > 0) {
char *dir = argv[0];
argc--;
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");
io_start_buffering_out(f_out);
}
+ become_copy_as_user();
+
send_filter_list(read_batch ? -1 : f_out);
if (filesfrom_fd >= 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. **/
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");
{"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, ©_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 },
--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
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@-1))
-quote(tt( rsync alias -t -t@-1))
+verb( rsync alias -a -a@-1)
+verb( rsync alias -t -t@-1)
With that as the default, you'd need to specify bf(--modify-window=0) (aka
bf(-@0)) to override it and ignore nanoseconds, e.g. if you're copying between
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
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
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.)
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
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.)
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,
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.
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
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.
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
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