Tuesday, August 02, 2022

Re: net/rsync: CVE-2022-29154 fix

On 2022/08/02 10:10, T.J. Townsend wrote:
> https://www.openwall.com/lists/oss-security/2022/08/02/1
> https://github.com/WayneD/rsync/commit/b7231c7d02.patch

https://lists.samba.org/archive/rsync/2022-August/032838.html :-

"I'd really appreciate it if people would give this release some
extensive testing and see if the improved security results in any false
alerts. I did a bunch of testing and found a few options that caused a
problem (and were then fixed), but there may be more"

> Index: Makefile
> ===================================================================
> RCS file: /cvs/ports/net/rsync/Makefile,v
> retrieving revision 1.93
> diff -u -p -r1.93 Makefile
> --- Makefile 23 May 2022 00:24:58 -0000 1.93
> +++ Makefile 2 Aug 2022 14:09:58 -0000
> @@ -1,6 +1,7 @@
> COMMENT = mirroring/synchronization over low bandwidth links
>
> DISTNAME = rsync-3.2.4
> +REVISION = 0
> CATEGORIES = net
> HOMEPAGE = https://rsync.samba.org/
>
> Index: patches/patch-exclude_c
> ===================================================================
> RCS file: patches/patch-exclude_c
> diff -N patches/patch-exclude_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-exclude_c 2 Aug 2022 14:09:58 -0000
> @@ -0,0 +1,182 @@
> +CVE-2022-29154
> +
> +Index: exclude.c
> +--- exclude.c.orig
> ++++ exclude.c
> +@@ -27,16 +27,22 @@ extern int am_server;
> + extern int am_sender;
> + extern int eol_nulls;
> + extern int io_error;
> ++extern int xfer_dirs;
> ++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 list_only;
> + extern int module_id;
> +
> ++extern char *filesfrom_host;
> + extern char curr_dir[MAXPATHLEN];
> + extern unsigned int curr_dir_len;
> + extern unsigned int module_dirlen;
> +@@ -44,8 +50,10 @@ extern unsigned int module_dirlen;
> + filter_rule_list filter_list = { .debug_type = "" };
> + filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
> + 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_filter = 0;
> +
> + /* Need room enough for ":MODS " prefix plus some room to grow. */
> + #define MAX_RULE_PREFIX (16)
> +@@ -292,6 +300,125 @@ static void add_rule(filter_rule_list *listp, const ch
> + }
> + }
> +
> ++/* 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)
> ++{
> ++ filter_rule *rule;
> ++ int arg_len, saw_wild = 0, backslash_cnt = 0;
> ++ int slash_cnt = 1; /* We know we're adding a leading slash. */
> ++ const char *cp;
> ++ char *p;
> ++ if (old_style_args || list_only || filesfrom_host != NULL)
> ++ return;
> ++ if (relative_paths) {
> ++ cp = strstr(arg, "/./");
> ++ if (cp)
> ++ arg = cp+3;
> ++ } else {
> ++ if ((cp = strrchr(arg, '/')) != NULL)
> ++ arg = cp + 1;
> ++ }
> ++ arg_len = strlen(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) {
> ++ arg_len++;
> ++ cp++;
> ++ }
> ++ saw_wild = 1;
> ++ }
> ++ arg_len++; /* Leave room for the prefixed slash */
> ++ rule = new0(filter_rule);
> ++ if (!implied_filter_list.head)
> ++ implied_filter_list.head = implied_filter_list.tail = rule;
> ++ else {
> ++ rule->next = implied_filter_list.head;
> ++ implied_filter_list.head = rule;
> ++ }
> ++ rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
> ++ p = rule->pattern = new_array(char, arg_len + 1);
> ++ *p++ = '/';
> ++ cp = arg;
> ++ while (*cp) {
> ++ switch (*cp) {
> ++ case '\\':
> ++ backslash_cnt++;
> ++ if (saw_wild)
> ++ *p++ = '\\';
> ++ *p++ = *cp++;
> ++ break;
> ++ case '/':
> ++ if (p[-1] == '/') /* This is safe because of the initial slash. */
> ++ break;
> ++ if (relative_paths) {
> ++ filter_rule const *ent;
> ++ int found = 0;
> ++ *p = '\0';
> ++ for (ent = implied_filter_list.head; ent; ent = ent->next) {
> ++ if (ent != rule && strcmp(ent->pattern, rule->pattern) == 0)
> ++ found = 1;
> ++ }
> ++ if (!found) {
> ++ filter_rule *R_rule = new0(filter_rule);
> ++ R_rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
> ++ R_rule->pattern = strdup(rule->pattern);
> ++ R_rule->u.slash_cnt = slash_cnt;
> ++ R_rule->next = implied_filter_list.head;
> ++ implied_filter_list.head = R_rule;
> ++ }
> ++ }
> ++ slash_cnt++;
> ++ *p++ = *cp++;
> ++ break;
> ++ default:
> ++ *p++ = *cp++;
> ++ break;
> ++ }
> ++ }
> ++ *p = '\0';
> ++ rule->u.slash_cnt = slash_cnt;
> ++ arg = (const char *)rule->pattern;
> ++ }
> ++
> ++ if (recurse || xfer_dirs) {
> ++ /* Now create a rule with an added "/" & "**" or "*" at the end */
> ++ rule = new0(filter_rule);
> ++ if (recurse)
> ++ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD | FILTRULE_WILD2;
> ++ else
> ++ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
> ++ /* A +4 in the len leaves enough room for / * * \0 or / * \0 \0 */
> ++ 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) {
> ++ if (*cp == '\\')
> ++ *p++ = '\\';
> ++ *p++ = *cp++;
> ++ }
> ++ } else {
> ++ p = rule->pattern = new_array(char, arg_len + 3 + 1);
> ++ if (arg_len) {
> ++ memcpy(p, arg, arg_len);
> ++ p += arg_len;
> ++ }
> ++ }
> ++ if (p[-1] != '/')
> ++ *p++ = '/';
> ++ *p++ = '*';
> ++ if (recurse)
> ++ *p++ = '*';
> ++ *p = '\0';
> ++ rule->u.slash_cnt = slash_cnt + 1;
> ++ rule->next = implied_filter_list.head;
> ++ implied_filter_list.head = rule;
> ++ }
> ++}
> ++
> + /* This frees any non-inherited items, leaving just inherited items on the list. */
> + static void pop_filter_list(filter_rule_list *listp)
> + {
> +@@ -718,7 +845,7 @@ static void report_filter_result(enum logcode code, ch
> + : name_flags & NAME_IS_DIR ? "directory"
> + : "file";
> + rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
> +- w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)],
> ++ w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)],
> + t, name, ent->pattern,
> + ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
> + }
> +@@ -890,6 +1017,7 @@ static filter_rule *parse_rule_tok(const char **rulest
> + }
> + switch (ch) {
> + case ':':
> ++ trust_sender_filter = 1;
> + rule->rflags |= FILTRULE_PERDIR_MERGE
> + | FILTRULE_FINISH_SETUP;
> + /* FALL THROUGH */
> Index: patches/patch-flist_c
> ===================================================================
> RCS file: patches/patch-flist_c
> diff -N patches/patch-flist_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-flist_c 2 Aug 2022 14:09:58 -0000
> @@ -0,0 +1,43 @@
> +CVE-2022-29154
> +
> +Index: flist.c
> +--- flist.c.orig
> ++++ flist.c
> +@@ -73,6 +73,7 @@ extern int need_unsorted_flist;
> + extern int sender_symlink_iconv;
> + extern int output_needs_newline;
> + extern int sender_keeps_checksum;
> ++extern int trust_sender_filter;
> + extern int unsort_ndx;
> + extern uid_t our_uid;
> + extern struct stats stats;
> +@@ -83,8 +84,7 @@ extern char curr_dir[MAXPATHLEN];
> +
> + extern struct chmod_mode_struct *chmod_modes;
> +
> +-extern filter_rule_list filter_list;
> +-extern filter_rule_list daemon_filter_list;
> ++extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list;
> +
> + #ifdef ICONV_OPTION
> + extern int filesfrom_convert;
> +@@ -984,6 +984,19 @@ static struct file_struct *recv_file_entry(int f, stru
> + if (file_length < 0) {
> + rprintf(FERROR, "Offset underflow: file-length is negative\n");
> + exit_cleanup(RERR_UNSUPPORTED);
> ++ }
> ++
> ++ if (*thisname != '.' || thisname[1] != '\0') {
> ++ int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
> ++ if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
> ++ && filter_list.head && check_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
> ++ rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
> ++ exit_cleanup(RERR_PROTOCOL);
> ++ }
> ++ if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
> ++ rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
> ++ exit_cleanup(RERR_PROTOCOL);
> ++ }
> + }
> +
> + if (inc_recurse && S_ISDIR(mode)) {
> Index: patches/patch-io_c
> ===================================================================
> RCS file: patches/patch-io_c
> diff -N patches/patch-io_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-io_c 2 Aug 2022 14:09:58 -0000
> @@ -0,0 +1,26 @@
> +CVE-2022-29154
> +
> +Index: io.c
> +--- io.c.orig
> ++++ io.c
> +@@ -419,6 +419,7 @@ static void forward_filesfrom_data(void)
> + while (s != eob) {
> + if (*s++ == '\0') {
> + ff_xb.len = s - sob - 1;
> ++ add_implied_include(sob);
> + 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'. */
> +@@ -450,9 +451,12 @@ static void forward_filesfrom_data(void)
> + char *f = ff_xb.buf + ff_xb.pos;
> + char *t = ff_xb.buf;
> + char *eob = f + len;
> ++ char *cur = t;
> + /* Eliminate any multi-'\0' runs. */
> + while (f != eob) {
> + if (!(*t++ = *f++)) {
> ++ add_implied_include(cur);
> ++ cur = t;
> + while (f != eob && *f == '\0')
> + f++;
> + }
> Index: patches/patch-main_c
> ===================================================================
> RCS file: patches/patch-main_c
> diff -N patches/patch-main_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-main_c 2 Aug 2022 14:09:58 -0000
> @@ -0,0 +1,47 @@
> +CVE-2022-29154
> +
> +Index: main.c
> +--- main.c.orig
> ++++ main.c
> +@@ -89,6 +89,7 @@ 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;
> +@@ -104,7 +105,7 @@ extern char curr_dir[MAXPATHLEN];
> + extern char backup_dir_buf[MAXPATHLEN];
> + extern char *basis_dir[MAX_BASIS_DIRS+1];
> + extern struct file_list *first_flist;
> +-extern filter_rule_list daemon_filter_list;
> ++extern filter_rule_list daemon_filter_list, implied_filter_list;
> +
> + uid_t our_uid;
> + gid_t our_gid;
> +@@ -635,6 +636,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *us
> + #ifdef ICONV_CONST
> + setup_iconv();
> +

No comments:

Post a Comment