]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/rsync-CVE-2022-29154.patch
libseccomp: Bump package version
[ipfire-2.x.git] / src / patches / rsync-CVE-2022-29154.patch
1 commit b7231c7d02cfb65d291af74ff66e7d8c507ee871
2 Author: Wayne Davison <wayne@opencoder.net>
3 Date: Sun Jul 31 16:55:34 2022 -0700
4
5 Some extra file-list safety checks.
6
7 diff --git a/exclude.c b/exclude.c
8 index 39073a0c..b670c8ba 100644
9 --- a/exclude.c
10 +++ b/exclude.c
11 @@ -27,16 +27,22 @@ extern int am_server;
12 extern int am_sender;
13 extern int eol_nulls;
14 extern int io_error;
15 +extern int xfer_dirs;
16 +extern int recurse;
17 extern int local_server;
18 extern int prune_empty_dirs;
19 extern int ignore_perishable;
20 +extern int old_style_args;
21 +extern int relative_paths;
22 extern int delete_mode;
23 extern int delete_excluded;
24 extern int cvs_exclude;
25 extern int sanitize_paths;
26 extern int protocol_version;
27 +extern int list_only;
28 extern int module_id;
29
30 +extern char *filesfrom_host;
31 extern char curr_dir[MAXPATHLEN];
32 extern unsigned int curr_dir_len;
33 extern unsigned int module_dirlen;
34 @@ -44,8 +50,10 @@ extern unsigned int module_dirlen;
35 filter_rule_list filter_list = { .debug_type = "" };
36 filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
37 filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
38 +filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
39
40 int saw_xattr_filter = 0;
41 +int trust_sender_filter = 0;
42
43 /* Need room enough for ":MODS " prefix plus some room to grow. */
44 #define MAX_RULE_PREFIX (16)
45 @@ -292,6 +300,125 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
46 }
47 }
48
49 +/* Each arg the client sends to the remote sender turns into an implied include
50 + * that the receiver uses to validate the file list from the sender. */
51 +void add_implied_include(const char *arg)
52 +{
53 + filter_rule *rule;
54 + int arg_len, saw_wild = 0, backslash_cnt = 0;
55 + int slash_cnt = 1; /* We know we're adding a leading slash. */
56 + const char *cp;
57 + char *p;
58 + if (old_style_args || list_only || filesfrom_host != NULL)
59 + return;
60 + if (relative_paths) {
61 + cp = strstr(arg, "/./");
62 + if (cp)
63 + arg = cp+3;
64 + } else {
65 + if ((cp = strrchr(arg, '/')) != NULL)
66 + arg = cp + 1;
67 + }
68 + arg_len = strlen(arg);
69 + if (arg_len) {
70 + if (strpbrk(arg, "*[?")) {
71 + /* We need to add room to escape backslashes if wildcard chars are present. */
72 + cp = arg;
73 + while ((cp = strchr(cp, '\\')) != NULL) {
74 + arg_len++;
75 + cp++;
76 + }
77 + saw_wild = 1;
78 + }
79 + arg_len++; /* Leave room for the prefixed slash */
80 + rule = new0(filter_rule);
81 + if (!implied_filter_list.head)
82 + implied_filter_list.head = implied_filter_list.tail = rule;
83 + else {
84 + rule->next = implied_filter_list.head;
85 + implied_filter_list.head = rule;
86 + }
87 + rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
88 + p = rule->pattern = new_array(char, arg_len + 1);
89 + *p++ = '/';
90 + cp = arg;
91 + while (*cp) {
92 + switch (*cp) {
93 + case '\\':
94 + backslash_cnt++;
95 + if (saw_wild)
96 + *p++ = '\\';
97 + *p++ = *cp++;
98 + break;
99 + case '/':
100 + if (p[-1] == '/') /* This is safe because of the initial slash. */
101 + break;
102 + if (relative_paths) {
103 + filter_rule const *ent;
104 + int found = 0;
105 + *p = '\0';
106 + for (ent = implied_filter_list.head; ent; ent = ent->next) {
107 + if (ent != rule && strcmp(ent->pattern, rule->pattern) == 0)
108 + found = 1;
109 + }
110 + if (!found) {
111 + filter_rule *R_rule = new0(filter_rule);
112 + R_rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
113 + R_rule->pattern = strdup(rule->pattern);
114 + R_rule->u.slash_cnt = slash_cnt;
115 + R_rule->next = implied_filter_list.head;
116 + implied_filter_list.head = R_rule;
117 + }
118 + }
119 + slash_cnt++;
120 + *p++ = *cp++;
121 + break;
122 + default:
123 + *p++ = *cp++;
124 + break;
125 + }
126 + }
127 + *p = '\0';
128 + rule->u.slash_cnt = slash_cnt;
129 + arg = (const char *)rule->pattern;
130 + }
131 +
132 + if (recurse || xfer_dirs) {
133 + /* Now create a rule with an added "/" & "**" or "*" at the end */
134 + rule = new0(filter_rule);
135 + if (recurse)
136 + rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD | FILTRULE_WILD2;
137 + else
138 + rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
139 + /* A +4 in the len leaves enough room for / * * \0 or / * \0 \0 */
140 + if (!saw_wild && backslash_cnt) {
141 + /* We are appending a wildcard, so now the backslashes need to be escaped. */
142 + p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
143 + cp = arg;
144 + while (*cp) {
145 + if (*cp == '\\')
146 + *p++ = '\\';
147 + *p++ = *cp++;
148 + }
149 + } else {
150 + p = rule->pattern = new_array(char, arg_len + 3 + 1);
151 + if (arg_len) {
152 + memcpy(p, arg, arg_len);
153 + p += arg_len;
154 + }
155 + }
156 + if (p[-1] != '/')
157 + *p++ = '/';
158 + *p++ = '*';
159 + if (recurse)
160 + *p++ = '*';
161 + *p = '\0';
162 + rule->u.slash_cnt = slash_cnt + 1;
163 + rule->next = implied_filter_list.head;
164 + implied_filter_list.head = rule;
165 + }
166 +}
167 +
168 /* This frees any non-inherited items, leaving just inherited items on the list. */
169 static void pop_filter_list(filter_rule_list *listp)
170 {
171 @@ -718,7 +845,7 @@ static void report_filter_result(enum logcode code, char const *name,
172 : name_flags & NAME_IS_DIR ? "directory"
173 : "file";
174 rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
175 - w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)],
176 + w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)],
177 t, name, ent->pattern,
178 ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
179 }
180 @@ -890,6 +1017,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
181 }
182 switch (ch) {
183 case ':':
184 + trust_sender_filter = 1;
185 rule->rflags |= FILTRULE_PERDIR_MERGE
186 | FILTRULE_FINISH_SETUP;
187 /* FALL THROUGH */
188 diff --git a/flist.c b/flist.c
189 index 1ba306bc..0e6bf782 100644
190 --- a/flist.c
191 +++ b/flist.c
192 @@ -73,6 +73,7 @@ extern int need_unsorted_flist;
193 extern int sender_symlink_iconv;
194 extern int output_needs_newline;
195 extern int sender_keeps_checksum;
196 +extern int trust_sender_filter;
197 extern int unsort_ndx;
198 extern uid_t our_uid;
199 extern struct stats stats;
200 @@ -83,8 +84,7 @@ extern char curr_dir[MAXPATHLEN];
201
202 extern struct chmod_mode_struct *chmod_modes;
203
204 -extern filter_rule_list filter_list;
205 -extern filter_rule_list daemon_filter_list;
206 +extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list;
207
208 #ifdef ICONV_OPTION
209 extern int filesfrom_convert;
210 @@ -986,6 +986,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
211 exit_cleanup(RERR_UNSUPPORTED);
212 }
213
214 + if (*thisname != '.' || thisname[1] != '\0') {
215 + int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
216 + if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
217 + && filter_list.head && check_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
218 + rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
219 + exit_cleanup(RERR_PROTOCOL);
220 + }
221 + if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
222 + rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
223 + exit_cleanup(RERR_PROTOCOL);
224 + }
225 + }
226 +
227 if (inc_recurse && S_ISDIR(mode)) {
228 if (one_file_system) {
229 /* Room to save the dir's device for -x */
230 diff --git a/io.c b/io.c
231 index cf94cee7..a6e3ed30 100644
232 --- a/io.c
233 +++ b/io.c
234 @@ -419,6 +419,7 @@ static void forward_filesfrom_data(void)
235 while (s != eob) {
236 if (*s++ == '\0') {
237 ff_xb.len = s - sob - 1;
238 + add_implied_include(sob);
239 if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
240 exit_cleanup(RERR_PROTOCOL); /* impossible? */
241 write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
242 @@ -450,9 +451,12 @@ static void forward_filesfrom_data(void)
243 char *f = ff_xb.buf + ff_xb.pos;
244 char *t = ff_xb.buf;
245 char *eob = f + len;
246 + char *cur = t;
247 /* Eliminate any multi-'\0' runs. */
248 while (f != eob) {
249 if (!(*t++ = *f++)) {
250 + add_implied_include(cur);
251 + cur = t;
252 while (f != eob && *f == '\0')
253 f++;
254 }
255 diff --git a/main.c b/main.c
256 index 58920a2d..5a7fbdd7 100644
257 --- a/main.c
258 +++ b/main.c
259 @@ -89,6 +89,7 @@ extern int backup_dir_len;
260 extern int basis_dir_cnt;
261 extern int default_af_hint;
262 extern int stdout_format_has_i;
263 +extern int trust_sender_filter;
264 extern struct stats stats;
265 extern char *stdout_format;
266 extern char *logfile_format;
267 @@ -104,7 +105,7 @@ extern char curr_dir[MAXPATHLEN];
268 extern char backup_dir_buf[MAXPATHLEN];
269 extern char *basis_dir[MAX_BASIS_DIRS+1];
270 extern struct file_list *first_flist;
271 -extern filter_rule_list daemon_filter_list;
272 +extern filter_rule_list daemon_filter_list, implied_filter_list;
273
274 uid_t our_uid;
275 gid_t our_gid;
276 @@ -635,6 +636,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
277 #ifdef ICONV_CONST
278 setup_iconv();
279 #endif
280 + trust_sender_filter = 1;
281 } else if (local_server) {
282 /* If the user didn't request --[no-]whole-file, force
283 * it on, but only if we're not batch processing. */
284 @@ -1500,6 +1502,8 @@ static int start_client(int argc, char *argv[])
285 char *dummy_host;
286 int dummy_port = rsync_port;
287 int i;
288 + if (filesfrom_fd < 0)
289 + add_implied_include(remote_argv[0]);
290 /* For remote source, any extra source args must have either
291 * the same hostname or an empty hostname. */
292 for (i = 1; i < remote_argc; i++) {
293 @@ -1523,6 +1527,7 @@ static int start_client(int argc, char *argv[])
294 if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
295 arg = ".";
296 remote_argv[i] = arg;
297 + add_implied_include(arg);
298 }
299 }
300
301 diff --git a/receiver.c b/receiver.c
302 index b3a69da0..93cf8efd 100644
303 --- a/receiver.c
304 +++ b/receiver.c
305 @@ -593,10 +593,13 @@ int recv_files(int f_in, int f_out, char *local_name)
306 if (DEBUG_GTE(RECV, 1))
307 rprintf(FINFO, "recv_files(%s)\n", fname);
308
309 - if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')
310 - && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
311 - rprintf(FERROR, "attempt to hack rsync failed.\n");
312 - exit_cleanup(RERR_PROTOCOL);
313 + if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) {
314 + int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE;
315 + if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) {
316 + rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n",
317 + fname);
318 + exit_cleanup(RERR_PROTOCOL);
319 + }
320 }
321
322 #ifdef SUPPORT_XATTRS