0cbdcfdc0e7d2931edaed97a7d4321fafbfe7e30
[feed/packages.git] / net / openssh / patches / 0006-scp-handle-braces.patch
1 From 7a3fa37583d4abf128f7f4c6eb1e7ffc90115eab Mon Sep 17 00:00:00 2001
2 From: "djm@openbsd.org" <djm@openbsd.org>
3 Date: Sun, 10 Feb 2019 11:15:52 +0000
4 Subject: upstream: when checking that filenames sent by the server side
5
6 match what the client requested, be prepared to handle shell-style brace
7 alternations, e.g. "{foo,bar}".
8
9 "looks good to me" millert@ + in snaps for the last week courtesy
10 deraadt@
11
12 OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e
13
14 Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=3d896c157c722bc47adca51a58dca859225b5874
15 Bug-Debian: https://bugs.debian.org/923486
16 Last-Update: 2019-03-01
17
18 Patch-Name: scp-handle-braces.patch
19 ---
20 scp.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
21 1 file changed, 269 insertions(+), 11 deletions(-)
22
23 diff --git a/scp.c b/scp.c
24 index 035037bcc..3888baab0 100644
25 --- a/scp.c
26 +++ b/scp.c
27 @@ -635,6 +635,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
28 return r;
29 }
30
31 +/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
32 +static int
33 +append(char *cp, char ***ap, size_t *np)
34 +{
35 + char **tmp;
36 +
37 + if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
38 + return -1;
39 + tmp[(*np)] = cp;
40 + (*np)++;
41 + *ap = tmp;
42 + return 0;
43 +}
44 +
45 +/*
46 + * Finds the start and end of the first brace pair in the pattern.
47 + * returns 0 on success or -1 for invalid patterns.
48 + */
49 +static int
50 +find_brace(const char *pattern, int *startp, int *endp)
51 +{
52 + int i;
53 + int in_bracket, brace_level;
54 +
55 + *startp = *endp = -1;
56 + in_bracket = brace_level = 0;
57 + for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
58 + switch (pattern[i]) {
59 + case '\\':
60 + /* skip next character */
61 + if (pattern[i + 1] != '\0')
62 + i++;
63 + break;
64 + case '[':
65 + in_bracket = 1;
66 + break;
67 + case ']':
68 + in_bracket = 0;
69 + break;
70 + case '{':
71 + if (in_bracket)
72 + break;
73 + if (pattern[i + 1] == '}') {
74 + /* Protect a single {}, for find(1), like csh */
75 + i++; /* skip */
76 + break;
77 + }
78 + if (*startp == -1)
79 + *startp = i;
80 + brace_level++;
81 + break;
82 + case '}':
83 + if (in_bracket)
84 + break;
85 + if (*startp < 0) {
86 + /* Unbalanced brace */
87 + return -1;
88 + }
89 + if (--brace_level <= 0)
90 + *endp = i;
91 + break;
92 + }
93 + }
94 + /* unbalanced brackets/braces */
95 + if (*endp < 0 && (*startp >= 0 || in_bracket))
96 + return -1;
97 + return 0;
98 +}
99 +
100 +/*
101 + * Assembles and records a successfully-expanded pattern, returns -1 on
102 + * alloc failure.
103 + */
104 +static int
105 +emit_expansion(const char *pattern, int brace_start, int brace_end,
106 + int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
107 +{
108 + char *cp;
109 + int o = 0, tail_len = strlen(pattern + brace_end + 1);
110 +
111 + if ((cp = malloc(brace_start + (sel_end - sel_start) +
112 + tail_len + 1)) == NULL)
113 + return -1;
114 +
115 + /* Pattern before initial brace */
116 + if (brace_start > 0) {
117 + memcpy(cp, pattern, brace_start);
118 + o = brace_start;
119 + }
120 + /* Current braced selection */
121 + if (sel_end - sel_start > 0) {
122 + memcpy(cp + o, pattern + sel_start,
123 + sel_end - sel_start);
124 + o += sel_end - sel_start;
125 + }
126 + /* Remainder of pattern after closing brace */
127 + if (tail_len > 0) {
128 + memcpy(cp + o, pattern + brace_end + 1, tail_len);
129 + o += tail_len;
130 + }
131 + cp[o] = '\0';
132 + if (append(cp, patternsp, npatternsp) != 0) {
133 + free(cp);
134 + return -1;
135 + }
136 + return 0;
137 +}
138 +
139 +/*
140 + * Expand the first encountered brace in pattern, appending the expanded
141 + * patterns it yielded to the *patternsp array.
142 + *
143 + * Returns 0 on success or -1 on allocation failure.
144 + *
145 + * Signals whether expansion was performed via *expanded and whether
146 + * pattern was invalid via *invalid.
147 + */
148 +static int
149 +brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
150 + int *expanded, int *invalid)
151 +{
152 + int i;
153 + int in_bracket, brace_start, brace_end, brace_level;
154 + int sel_start, sel_end;
155 +
156 + *invalid = *expanded = 0;
157 +
158 + if (find_brace(pattern, &brace_start, &brace_end) != 0) {
159 + *invalid = 1;
160 + return 0;
161 + } else if (brace_start == -1)
162 + return 0;
163 +
164 + in_bracket = brace_level = 0;
165 + for (i = sel_start = brace_start + 1; i < brace_end; i++) {
166 + switch (pattern[i]) {
167 + case '{':
168 + if (in_bracket)
169 + break;
170 + brace_level++;
171 + break;
172 + case '}':
173 + if (in_bracket)
174 + break;
175 + brace_level--;
176 + break;
177 + case '[':
178 + in_bracket = 1;
179 + break;
180 + case ']':
181 + in_bracket = 0;
182 + break;
183 + case '\\':
184 + if (i < brace_end - 1)
185 + i++; /* skip */
186 + break;
187 + }
188 + if (pattern[i] == ',' || i == brace_end - 1) {
189 + if (in_bracket || brace_level > 0)
190 + continue;
191 + /* End of a selection, emit an expanded pattern */
192 +
193 + /* Adjust end index for last selection */
194 + sel_end = (i == brace_end - 1) ? brace_end : i;
195 + if (emit_expansion(pattern, brace_start, brace_end,
196 + sel_start, sel_end, patternsp, npatternsp) != 0)
197 + return -1;
198 + /* move on to the next selection */
199 + sel_start = i + 1;
200 + continue;
201 + }
202 + }
203 + if (in_bracket || brace_level > 0) {
204 + *invalid = 1;
205 + return 0;
206 + }
207 + /* success */
208 + *expanded = 1;
209 + return 0;
210 +}
211 +
212 +/* Expand braces from pattern. Returns 0 on success, -1 on failure */
213 +static int
214 +brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
215 +{
216 + char *cp, *cp2, **active = NULL, **done = NULL;
217 + size_t i, nactive = 0, ndone = 0;
218 + int ret = -1, invalid = 0, expanded = 0;
219 +
220 + *patternsp = NULL;
221 + *npatternsp = 0;
222 +
223 + /* Start the worklist with the original pattern */
224 + if ((cp = strdup(pattern)) == NULL)
225 + return -1;
226 + if (append(cp, &active, &nactive) != 0) {
227 + free(cp);
228 + return -1;
229 + }
230 + while (nactive > 0) {
231 + cp = active[nactive - 1];
232 + nactive--;
233 + if (brace_expand_one(cp, &active, &nactive,
234 + &expanded, &invalid) == -1) {
235 + free(cp);
236 + goto fail;
237 + }
238 + if (invalid)
239 + fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
240 + if (expanded) {
241 + /*
242 + * Current entry expanded to new entries on the
243 + * active list; discard the progenitor pattern.
244 + */
245 + free(cp);
246 + continue;
247 + }
248 + /*
249 + * Pattern did not expand; append the finename component to
250 + * the completed list
251 + */
252 + if ((cp2 = strrchr(cp, '/')) != NULL)
253 + *cp2++ = '\0';
254 + else
255 + cp2 = cp;
256 + if (append(xstrdup(cp2), &done, &ndone) != 0) {
257 + free(cp);
258 + goto fail;
259 + }
260 + free(cp);
261 + }
262 + /* success */
263 + *patternsp = done;
264 + *npatternsp = ndone;
265 + done = NULL;
266 + ndone = 0;
267 + ret = 0;
268 + fail:
269 + for (i = 0; i < nactive; i++)
270 + free(active[i]);
271 + free(active);
272 + for (i = 0; i < ndone; i++)
273 + free(done[i]);
274 + free(done);
275 + return ret;
276 +}
277 +
278 void
279 toremote(int argc, char **argv)
280 {
281 @@ -998,7 +1245,8 @@ sink(int argc, char **argv, const char *src)
282 unsigned long long ull;
283 int setimes, targisdir, wrerrno = 0;
284 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
285 - char *src_copy = NULL, *restrict_pattern = NULL;
286 + char **patterns = NULL;
287 + size_t n, npatterns = 0;
288 struct timeval tv[2];
289
290 #define atime tv[0]
291 @@ -1028,16 +1276,13 @@ sink(int argc, char **argv, const char *src)
292 * Prepare to try to restrict incoming filenames to match
293 * the requested destination file glob.
294 */
295 - if ((src_copy = strdup(src)) == NULL)
296 - fatal("strdup failed");
297 - if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
298 - *restrict_pattern++ = '\0';
299 - }
300 + if (brace_expand(src, &patterns, &npatterns) != 0)
301 + fatal("%s: could not expand pattern", __func__);
302 }
303 for (first = 1;; first = 0) {
304 cp = buf;
305 if (atomicio(read, remin, cp, 1) != 1)
306 - return;
307 + goto done;
308 if (*cp++ == '\n')
309 SCREWUP("unexpected <newline>");
310 do {
311 @@ -1063,7 +1308,7 @@ sink(int argc, char **argv, const char *src)
312 }
313 if (buf[0] == 'E') {
314 (void) atomicio(vwrite, remout, "", 1);
315 - return;
316 + goto done;
317 }
318 if (ch == '\n')
319 *--cp = 0;
320 @@ -1138,9 +1383,14 @@ sink(int argc, char **argv, const char *src)
321 run_err("error: unexpected filename: %s", cp);
322 exit(1);
323 }
324 - if (restrict_pattern != NULL &&
325 - fnmatch(restrict_pattern, cp, 0) != 0)
326 - SCREWUP("filename does not match request");
327 + if (npatterns > 0) {
328 + for (n = 0; n < npatterns; n++) {
329 + if (fnmatch(patterns[n], cp, 0) == 0)
330 + break;
331 + }
332 + if (n >= npatterns)
333 + SCREWUP("filename does not match request");
334 + }
335 if (targisdir) {
336 static char *namebuf;
337 static size_t cursize;
338 @@ -1299,7 +1549,15 @@ bad: run_err("%s: %s", np, strerror(errno));
339 break;
340 }
341 }
342 +done:
343 + for (n = 0; n < npatterns; n++)
344 + free(patterns[n]);
345 + free(patterns);
346 + return;
347 screwup:
348 + for (n = 0; n < npatterns; n++)
349 + free(patterns[n]);
350 + free(patterns);
351 run_err("protocol error: %s", why);
352 exit(1);
353 }