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 <wa...@opencoder.net> Date: Tue Aug 9 11:45:56 2022 -0700 Add `--trust-sender` option. commit db8034f12ecda624ce9f2adf3aa47ef40a12626c Author: Wayne Davison <wa...@opencoder.net> Date: Tue Aug 9 11:42:14 2022 -0700 Escape leading tilde char when "~" or with -R. commit c86763dc382fc6ed6c4c4c461243f51acfc3f226 Author: Wayne Davison <wa...@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. _______________________________________________ rsync-cvs mailing list rsync-cvs@lists.samba.org https://lists.samba.org/mailman/listinfo/rsync-cvs