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
6 match what the client requested, be prepared to handle shell-style brace
7 alternations, e.g. "{foo,bar}".
9 "looks good to me" millert@ + in snaps for the last week courtesy
12 OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e
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
18 Patch-Name: scp-handle-braces.patch
20 scp.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
21 1 file changed, 269 insertions(+), 11 deletions(-)
23 diff --git a/scp.c b/scp.c
24 index 035037bcc..3888baab0 100644
27 @@ -635,6 +635,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
31 +/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
33 +append(char *cp, char ***ap, size_t *np)
37 + if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
46 + * Finds the start and end of the first brace pair in the pattern.
47 + * returns 0 on success or -1 for invalid patterns.
50 +find_brace(const char *pattern, int *startp, int *endp)
53 + int in_bracket, brace_level;
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]) {
60 + /* skip next character */
61 + if (pattern[i + 1] != '\0')
73 + if (pattern[i + 1] == '}') {
74 + /* Protect a single {}, for find(1), like csh */
86 + /* Unbalanced brace */
89 + if (--brace_level <= 0)
94 + /* unbalanced brackets/braces */
95 + if (*endp < 0 && (*startp >= 0 || in_bracket))
101 + * Assembles and records a successfully-expanded pattern, returns -1 on
105 +emit_expansion(const char *pattern, int brace_start, int brace_end,
106 + int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
109 + int o = 0, tail_len = strlen(pattern + brace_end + 1);
111 + if ((cp = malloc(brace_start + (sel_end - sel_start) +
112 + tail_len + 1)) == NULL)
115 + /* Pattern before initial brace */
116 + if (brace_start > 0) {
117 + memcpy(cp, pattern, brace_start);
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;
126 + /* Remainder of pattern after closing brace */
127 + if (tail_len > 0) {
128 + memcpy(cp + o, pattern + brace_end + 1, tail_len);
132 + if (append(cp, patternsp, npatternsp) != 0) {
140 + * Expand the first encountered brace in pattern, appending the expanded
141 + * patterns it yielded to the *patternsp array.
143 + * Returns 0 on success or -1 on allocation failure.
145 + * Signals whether expansion was performed via *expanded and whether
146 + * pattern was invalid via *invalid.
149 +brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
150 + int *expanded, int *invalid)
153 + int in_bracket, brace_start, brace_end, brace_level;
154 + int sel_start, sel_end;
156 + *invalid = *expanded = 0;
158 + if (find_brace(pattern, &brace_start, &brace_end) != 0) {
161 + } else if (brace_start == -1)
164 + in_bracket = brace_level = 0;
165 + for (i = sel_start = brace_start + 1; i < brace_end; i++) {
166 + switch (pattern[i]) {
184 + if (i < brace_end - 1)
188 + if (pattern[i] == ',' || i == brace_end - 1) {
189 + if (in_bracket || brace_level > 0)
191 + /* End of a selection, emit an expanded pattern */
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)
198 + /* move on to the next selection */
203 + if (in_bracket || brace_level > 0) {
212 +/* Expand braces from pattern. Returns 0 on success, -1 on failure */
214 +brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
216 + char *cp, *cp2, **active = NULL, **done = NULL;
217 + size_t i, nactive = 0, ndone = 0;
218 + int ret = -1, invalid = 0, expanded = 0;
223 + /* Start the worklist with the original pattern */
224 + if ((cp = strdup(pattern)) == NULL)
226 + if (append(cp, &active, &nactive) != 0) {
230 + while (nactive > 0) {
231 + cp = active[nactive - 1];
233 + if (brace_expand_one(cp, &active, &nactive,
234 + &expanded, &invalid) == -1) {
239 + fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
242 + * Current entry expanded to new entries on the
243 + * active list; discard the progenitor pattern.
249 + * Pattern did not expand; append the finename component to
250 + * the completed list
252 + if ((cp2 = strrchr(cp, '/')) != NULL)
256 + if (append(xstrdup(cp2), &done, &ndone) != 0) {
264 + *npatternsp = ndone;
269 + for (i = 0; i < nactive; i++)
272 + for (i = 0; i < ndone; i++)
279 toremote(int argc, char **argv)
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];
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.
295 - if ((src_copy = strdup(src)) == NULL)
296 - fatal("strdup failed");
297 - if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
298 - *restrict_pattern++ = '\0';
300 + if (brace_expand(src, &patterns, &npatterns) != 0)
301 + fatal("%s: could not expand pattern", __func__);
303 for (first = 1;; first = 0) {
305 if (atomicio(read, remin, cp, 1) != 1)
309 SCREWUP("unexpected <newline>");
311 @@ -1063,7 +1308,7 @@ sink(int argc, char **argv, const char *src)
314 (void) atomicio(vwrite, remout, "", 1);
320 @@ -1138,9 +1383,14 @@ sink(int argc, char **argv, const char *src)
321 run_err("error: unexpected filename: %s", cp);
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)
332 + if (n >= npatterns)
333 + SCREWUP("filename does not match request");
336 static char *namebuf;
337 static size_t cursize;
338 @@ -1299,7 +1549,15 @@ bad: run_err("%s: %s", np, strerror(errno));
343 + for (n = 0; n < npatterns; n++)
348 + for (n = 0; n < npatterns; n++)
351 run_err("protocol error: %s", why);