[SCM] The rsync repository. - branch master updated

Rsync CVS commit messages rsync-cvs at lists.samba.org
Tue Aug 9 18:47:22 UTC 2022


The branch, master has been updated
       via  cff8f044 Add `--trust-sender` option.
       via  db8034f1 Escape leading tilde char when "~" or with -R.
       via  c86763dc Fix handling of daemon module names in file-list verification; convert some while loops to for loops.
      from  5ce575b1 Preparing for release of 3.2.5pre2

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


- Log -----------------------------------------------------------------
commit cff8f044776c5143a5b270969d4bb0f1fea8b017
Author: Wayne Davison <wayne at opencoder.net>
Date:   Tue Aug 9 11:45:56 2022 -0700

    Add `--trust-sender` option.

commit db8034f12ecda624ce9f2adf3aa47ef40a12626c
Author: Wayne Davison <wayne at opencoder.net>
Date:   Tue Aug 9 11:42:14 2022 -0700

    Escape leading tilde char when "~" or with -R.

commit c86763dc382fc6ed6c4c4c461243f51acfc3f226
Author: Wayne Davison <wayne at opencoder.net>
Date:   Tue Aug 9 11:37:37 2022 -0700

    Fix handling of daemon module names in file-list verification; convert some while loops to for loops.

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

Summary of changes:
 NEWS.md    |  8 +++++++
 exclude.c  | 34 ++++++++++++---------------
 io.c       |  4 ++--
 main.c     |  6 ++---
 options.c  | 16 +++++++++++++
 rsync.1.md | 78 ++++++++++++++++++++++++++++++++++++++++++++------------------
 6 files changed, 99 insertions(+), 47 deletions(-)


Changeset truncated at 500 lines:

diff --git a/NEWS.md b/NEWS.md
index 0c212da9..5394b1a9 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -17,6 +17,9 @@
 
 ### BUG FIXES:
 
+- Fixed the handling of filenames specified with backslash-quoted wildcards
+  when the default remote-arg-escaping is enabled.
+
 - Fixed the configure check for signed char that was causing a host that
   defaults to unsigned characters to generate bogus rolling checksums. This
   made rsync send mostly literal data for a copy instead of finding matching
@@ -26,6 +29,11 @@
 - Lots of manpage improvements, including an attempt to better describe how
   include/exclude filters work.
 
+### ENHANCEMENTS:
+
+- The [`--trust-sender`](rsync.1#opt) option was added as a way to bypass the
+  extra file-list safety checking (should that be required).
+
 ### PACKAGING RELATED:
 
 - The build date that goes into the manpages is now based on the developer's
diff --git a/exclude.c b/exclude.c
index ba5ca5a3..da25661b 100644
--- a/exclude.c
+++ b/exclude.c
@@ -33,18 +33,15 @@ extern int recurse;
 extern int local_server;
 extern int prune_empty_dirs;
 extern int ignore_perishable;
-extern int old_style_args;
 extern int relative_paths;
 extern int delete_mode;
 extern int delete_excluded;
 extern int cvs_exclude;
 extern int sanitize_paths;
 extern int protocol_version;
-extern int read_batch;
-extern int list_only;
+extern int trust_sender_args;
 extern int module_id;
 
-extern char *filesfrom_host;
 extern char curr_dir[MAXPATHLEN];
 extern unsigned int curr_dir_len;
 extern unsigned int module_dirlen;
@@ -55,6 +52,7 @@ filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
 filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
 
 int saw_xattr_filter = 0;
+int trust_sender_args = 0;
 int trust_sender_filter = 0;
 
 /* Need room enough for ":MODS " prefix plus some room to grow. */
@@ -316,13 +314,11 @@ static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg
 	if (arg_len < 0)
 		arg_len = strlen(arg);
 
-	cp = arg;
-	while (*cp) {
+	for (cp = arg; *cp; cp++) {
 		if (*cp == '\\' && cp[1]) {
 			cp++;
 		} else if (*cp == '[')
 			cnt++;
-		cp++;
 	}
 	if (!cnt)
 		return;
@@ -331,8 +327,7 @@ static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg
 	rule->rflags = based_on->rflags;
 	rule->u.slash_cnt = based_on->u.slash_cnt;
 	p = rule->pattern = new_array(char, arg_len + cnt + 1);
-	cp = arg;
-	while (*cp) {
+	for (cp = arg; *cp; ) {
 		if (*cp == '\\' && cp[1]) {
 			*p++ = *cp++;
 		} else if (*cp == '[')
@@ -373,14 +368,14 @@ void free_implied_include_partial_string()
 
 /* Each arg the client sends to the remote sender turns into an implied include
  * that the receiver uses to validate the file list from the sender. */
-void add_implied_include(const char *arg)
+void add_implied_include(const char *arg, int skip_daemon_module)
 {
 	filter_rule *rule;
 	int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
 	int slash_cnt = 1; /* We know we're adding a leading slash. */
 	const char *cp;
 	char *p;
-	if (am_server || old_style_args || list_only || read_batch || filesfrom_host != NULL)
+	if (trust_sender_args)
 		return;
 	if (partial_string_len) {
 		arg_len = strlen(arg);
@@ -390,6 +385,12 @@ void add_implied_include(const char *arg)
 		partial_string_len = 0;
 		arg = partial_string_buf;
 	}
+	if (skip_daemon_module) {
+		if ((cp = strchr(arg, '/')) != NULL)
+			arg = cp + 1;
+		else
+			arg = "";
+	}
 	if (relative_paths) {
 		if ((cp = strstr(arg, "/./")) != NULL)
 			arg = cp + 3;
@@ -402,11 +403,8 @@ void add_implied_include(const char *arg)
 	if (arg_len) {
 		if (strpbrk(arg, "*[?")) {
 			/* We need to add room to escape backslashes if wildcard chars are present. */
-			cp = arg;
-			while ((cp = strchr(cp, '\\')) != NULL) {
+			for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
 				arg_len++;
-				cp++;
-			}
 			saw_wild = 1;
 		}
 		arg_len++; /* Leave room for the prefixed slash */
@@ -420,8 +418,7 @@ void add_implied_include(const char *arg)
 		rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
 		p = rule->pattern = new_array(char, arg_len + 1);
 		*p++ = '/';
-		cp = arg;
-		while (*cp) {
+		for (cp = arg; *cp; ) {
 			switch (*cp) {
 			  case '\\':
 				if (cp[1] == ']')
@@ -498,8 +495,7 @@ void add_implied_include(const char *arg)
 		if (!saw_wild && backslash_cnt) {
 			/* We are appending a wildcard, so now the backslashes need to be escaped. */
 			p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
-			cp = arg;
-			while (*cp) {
+			for (cp = arg; *cp; ) {
 				if (*cp == '\\')
 					*p++ = '\\';
 				*p++ = *cp++;
diff --git a/io.c b/io.c
index 7111878a..3f605d74 100644
--- a/io.c
+++ b/io.c
@@ -420,7 +420,7 @@ static void forward_filesfrom_data(void)
 		while (s != eob) {
 			if (*s++ == '\0') {
 				ff_xb.len = s - sob - 1;
-				add_implied_include(sob);
+				add_implied_include(sob, 0);
 				if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
 					exit_cleanup(RERR_PROTOCOL); /* impossible? */
 				write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
@@ -457,7 +457,7 @@ static void forward_filesfrom_data(void)
 		/* Eliminate any multi-'\0' runs. */
 		while (f != eob) {
 			if (!(*t++ = *f++)) {
-				add_implied_include(cur);
+				add_implied_include(cur, 0);
 				cur = t;
 				while (f != eob && *f == '\0')
 					f++;
diff --git a/main.c b/main.c
index fa263d27..9ebfbea7 100644
--- a/main.c
+++ b/main.c
@@ -89,7 +89,6 @@ extern int backup_dir_len;
 extern int basis_dir_cnt;
 extern int default_af_hint;
 extern int stdout_format_has_i;
-extern int trust_sender_filter;
 extern struct stats stats;
 extern char *stdout_format;
 extern char *logfile_format;
@@ -636,7 +635,6 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
 #ifdef ICONV_CONST
 		setup_iconv();
 #endif
-		trust_sender_filter = 1;
 	} else if (local_server) {
 		/* If the user didn't request --[no-]whole-file, force
 		 * it on, but only if we're not batch processing. */
@@ -1504,7 +1502,7 @@ static int start_client(int argc, char *argv[])
 		int dummy_port = rsync_port;
 		int i;
 		if (filesfrom_fd < 0)
-			add_implied_include(remote_argv[0]);
+			add_implied_include(remote_argv[0], daemon_connection);
 		/* For remote source, any extra source args must have either
 		 * the same hostname or an empty hostname. */
 		for (i = 1; i < remote_argc; i++) {
@@ -1528,7 +1526,7 @@ static int start_client(int argc, char *argv[])
 			if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
 				arg = ".";
 			remote_argv[i] = arg;
-			add_implied_include(arg);
+			add_implied_include(arg, daemon_connection);
 		}
 	}
 
diff --git a/options.c b/options.c
index a60ff515..4feeb7e0 100644
--- a/options.c
+++ b/options.c
@@ -27,6 +27,8 @@
 extern int module_id;
 extern int local_server;
 extern int sanitize_paths;
+extern int trust_sender_args;
+extern int trust_sender_filter;
 extern unsigned int module_dirlen;
 extern filter_rule_list filter_list;
 extern filter_rule_list daemon_filter_list;
@@ -64,6 +66,7 @@ int preserve_atimes = 0;
 int preserve_crtimes = 0;
 int omit_dir_times = 0;
 int omit_link_times = 0;
+int trust_sender = 0;
 int update_only = 0;
 int open_noatime = 0;
 int cvs_exclude = 0;
@@ -788,6 +791,7 @@ static struct poptOption long_options[] = {
   {"protect-args",    's', POPT_ARG_VAL,    &protect_args, 1, 0, 0},
   {"no-protect-args",  0,  POPT_ARG_VAL,    &protect_args, 0, 0, 0},
   {"no-s",             0,  POPT_ARG_VAL,    &protect_args, 0, 0, 0},
+  {"trust-sender",     0,  POPT_ARG_VAL,    &trust_sender, 1, 0, 0},
   {"numeric-ids",      0,  POPT_ARG_VAL,    &numeric_ids, 1, 0, 0 },
   {"no-numeric-ids",   0,  POPT_ARG_VAL,    &numeric_ids, 0, 0, 0 },
   {"usermap",          0,  POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
@@ -2465,6 +2469,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
 		}
 	}
 
+	if (trust_sender || am_server || read_batch)
+		trust_sender_args = trust_sender_filter = 1;
+	else if (old_style_args || filesfrom_host != NULL)
+		trust_sender_args = 1;
+
 	am_starting_up = 0;
 
 	return 1;
@@ -2492,12 +2501,17 @@ char *safe_arg(const char *opt, const char *arg)
 	BOOL is_filename_arg = !opt;
 	char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS;
 	BOOL escape_leading_dash = is_filename_arg && *arg == '-';
+	BOOL escape_leading_tilde = 0;
 	int len1 = opt && *opt ? strlen(opt) + 1 : 0;
 	int len2 = strlen(arg);
 	int extras = escape_leading_dash ? 2 : 0;
 	char *ret;
 	if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
 		const char *f;
+		if (!trust_sender_args && *arg == '~' && (relative_paths || !strchr(arg, '/'))) {
+			extras++;
+			escape_leading_tilde = 1;
+		}
 		for (f = arg; *f; f++) {
 			if (strchr(escapes, *f))
 				extras++;
@@ -2520,6 +2534,8 @@ char *safe_arg(const char *opt, const char *arg)
 	else {
 		const char *f = arg;
 		char *t = ret + len1;
+		if (escape_leading_tilde)
+			*t++ = '\\';
 		while (*f) {
                         if (*f == '\\') {
 				if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
diff --git a/rsync.1.md b/rsync.1.md
index 0c27df4c..72675594 100644
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -193,6 +193,8 @@ Dedicate a "host1-files" dir to the remote content:
 
 >     rsync -aiv host1:dir1 ~/host1-files
 
+See the [`--trust-sender`](#opt) option for additional details.
+
 ## ADVANCED USAGE
 
 The syntax for requesting multiple files from a remote host is done by
@@ -463,6 +465,7 @@ has its own detailed description later in this manpage.
 --from0, -0              all *-from/filter files are delimited by 0s
 --old-args               disable the modern arg-protection idiom
 --protect-args, -s       no space-splitting; wildcard chars only
+--trust-sender           trust the remote sender's file list
 --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
@@ -536,7 +539,8 @@ option has a short variant).
 The parameter may need to be quoted in some manner for it to survive the
 shell's command-line parsing.  Also keep in mind that a leading tilde (`~`) in
 a pathname is substituted by your shell, so make sure that you separate the
-option name from the pathname using a space if you want the shell to expand it.
+option name from the pathname using a space if you want the local shell to
+expand it.
 
 [comment]: # (Some markup below uses a literal non-breakable space when a backtick string)
 [comment]: # (needs to contain a space since markdown strips spaces from the start/end)
@@ -1908,8 +1912,8 @@ option name from the pathname using a space if you want the shell to expand it.
     A rule can still apply to both sides even with this option specified if the
     rule is given both the sender & receiver modifer letters (e.g., `-f'-sr
     foo'`).  Receiver-side protect/risk rules can also be explicitly specified
-    to limit the deletions.  This is saves you from having to edit a bunch of
-    `-f'- foo'` rules into `-f'-s foo'` or `-f'H foo'` rules (not to mention
+    to limit the deletions.  This saves you from having to edit a bunch of
+    `-f'- foo'` rules into `-f'-s foo'` (aka `-f'H foo'`) rules (not to mention
     the corresponding includes).
 
     See the [FILTER RULES](#) section for more information.  See
@@ -2408,6 +2412,38 @@ option name from the pathname using a space if you want the shell to expand it.
     Note that this option is incompatible with the use of the restricted rsync
     script (`rrsync`) since it hides options from the script's inspection.
 
+0.  `--trust-sender`
+
+    Disable the extra validation of the file list from a remote sender (this
+    safety feature was added in 3.2.5).  This should only be done if you trust
+    the sender to not try to do something malicious, which should be the case
+    if they're running a stock rsync.
+
+    Normally when pulling files from a remote rsync, the client runs 2 extra
+    validation checks:
+
+    - Verify that additional arg items didn't get added at the top of the
+      transfer.
+    - Verify that none of the items in the file list should have been excluded.
+
+    Note that various options can turn off one or both of these checks if the
+    option interferes with the validation.  For instance:
+
+    - Using a per-directory filter file reads filter rules that only the server
+      knows about, so the filter checking is disabled.
+    - Using the [`--old-args`](#opt) option allows the sender to manipulate the
+      requested args, so the arg checking is disabled.
+    - Reading the files-from list from the server side means that the client
+      doesn't know the arg list, so the arg checking is disabled.
+    - Using [`--read-batch`](#opt) disables both checks since the batch file's
+      contents will have been verified when it was created.
+
+    This option may help an under-powered client server if the extra pattern
+    matching is slowing things down on a huge transfer.  It can also be used
+    to work around a bug in the verification logic, possibly after using the
+    [`--list-only`](#opt) option combined with [`--trust-sender`](#opt) to look
+    over the full file list.
+
 0.  `--copy-as=USER[:GROUP]`
 
     This option instructs rsync to use the USER and (if specified after a
@@ -3444,8 +3480,8 @@ option name from the pathname using a space if you want the shell to expand it.
        include the destination.
 
     CAUTION: keep in mind that a source arg with a wild-card is expanded by the
-    shell into multiple args, so it is never safe to try to list such an arg
-    without using this option. For example:
+    shell into multiple args, so it is never safe to try to specify a single
+    wild-card arg to try to infer this option. A safe example is:
 
     >     rsync -av --list-only foo* dest/
 
@@ -3790,7 +3826,7 @@ different ways.
 We will first cover the basics of how include & exclude rules affect what files
 are transferred, ignoring any deletion side-effects.  Filter rules mainly
 affect the contents of directories that rsync is "recursing" into, but they can
-also affect a top-level item in the transfer that were specified as a argument.
+also affect a top-level item in the transfer that was specified as a argument.
 
 The default for any unmatched file/dir is for it to be included in the
 transfer, which puts the file/dir into the sender's file list.  The use of an
@@ -3919,7 +3955,7 @@ You have your choice of using either short or long RULE names, as described
 below.  If you use a short-named rule, the ',' separating the RULE from the
 MODIFIERS is optional.  The PATTERN or FILENAME that follows (when present)
 must come after either a single space or an underscore (\_). Any additional
-spaces and/or undeerscore are considered to be a part of the pattern name.
+spaces and/or underscores are considered to be a part of the pattern name.
 Here are the available rule prefixes:
 
 0.  `exclude, '-'` specifies an exclude pattern that (by default) is both a
@@ -3929,10 +3965,8 @@ Here are the available rule prefixes:
 0.  `merge, '.'` specifies a merge-file on the client side to read for more
     rules.
 0.  `dir-merge, ':'` specifies a per-directory merge-file.  Using this kind of
-    filter rule requires that you trust the sending side's filter checking, and
-    thus it disables the receiver's verification of the file-list names against
-    the filter rules (since only the sender can know for sure if it obeyed all
-    the filter rules when some are per-dir merged from the sender's files).
+    filter rule requires that you trust the sending side's filter checking, so
+    it has the side-effect mentioned under the [`--trust-sender`](#opt) option.
 0.  `hide, 'H'` specifies a pattern for hiding files from the transfer.
     Equivalent to a sender-only exclude, so `-f'H foo'` could also be specified
     as `-f'-s foo'`.
@@ -3969,15 +4003,15 @@ The matching rules for the pattern argument take several forms:
 - If a pattern contains a `/` (not counting a trailing slash) or a "`**`"
   (which can match a slash), then the pattern is matched against the full
   pathname, including any leading directories within the transfer.  If the
-  pattern doesn't contain a `/` or a "`**`", then it is matched only against
-  the final component of the filename or pathname. For example, `foo` means
-  that the final path component must be "foo" while `foo/bar` would match the
-  last 2 elements of the path (as long as both elements are within the
-  transfer).
+  pattern doesn't contain a (non-trailing) `/` or a "`**`", then it is matched
+  only against the final component of the filename or pathname. For example,
+  `foo` means that the final path component must be "foo" while `foo/bar` would
+  match the last 2 elements of the path (as long as both elements are within
+  the transfer).
 - A pattern that ends with a `/` only matches a directory, not a regular file,
   symlink, or device.
 - A pattern that starts with a `/` is anchored to the start of the transfer
-  path instead of the end.  For example, `/foo` or `/foo/bar` match only
+  path instead of the end.  For example, `/foo/**` or `/foo/bar/**` match only
   leading elements in the path.  If the rule is read from a per-directory
   filter file, the transfer path being matched will begin at the level of the
   filter file instead of the top of the transfer.  See the section on
@@ -4010,11 +4044,11 @@ Here are some examples of exclude/include matching:
 - Option `-f'- /foo'` would exclude a file (or directory) named foo in the
   transfer-root directory
 - Option `-f'- foo/'` would exclude any directory named foo
-- Option `-f'- /foo/*/bar'` would exclude any file/dir named bar which is at
-  two levels below a directory named foo, which must be at the root of the
-  transfer
-- Option `-f'- /foo/**/bar'` would exclude any file/dir named bar two or more
-  levels below a directory named foo, which must be at the root of the transfer
+- Option `-f'- foo/*/bar'` would exclude any file/dir named bar which is at two
+  levels below a directory named foo (if foo is in the transfer)
+- Option `-f'- /foo/**/bar'` would exclude any file/dir named bar that was two
+  or more levels below the top-level directory named foo (exclude /foo/bar in a
+  separate rule, if desired)
 - Options `-f'+ */' -f'+ *.c' -f'- *'` would include all directories and .c
   source files but nothing else
 - Options `-f'+ foo/' -f'+ foo/bar.c' -f'- *'` would include only the foo


-- 
The rsync repository.



More information about the rsync-cvs mailing list