Author: emaste
Date: Wed Mar 27 14:35:38 2019
New Revision: 345576
URL: https://svnweb.freebsd.org/changeset/base/345576

Log:
  Merge r345574 from vendor-crypto:
  
  upstream: when checking that filenames sent by the server side
  
  match what the client requested, be prepared to handle shell-style brace
  alternations, e.g. "{foo,bar}".
  
  "looks good to me" millert@ + in snaps for the last week courtesy
  deraadt@
  
  OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e
  
  Discussed with: des
  Obtained from:        OpenSSH-portable 
3d896c157c722bc47adca51a58dca859225b5874

Modified:
  head/crypto/openssh/scp.c
Directory Properties:
  head/crypto/openssh/   (props changed)

Modified: head/crypto/openssh/scp.c
==============================================================================
--- head/crypto/openssh/scp.c   Wed Mar 27 14:15:35 2019        (r345575)
+++ head/crypto/openssh/scp.c   Wed Mar 27 14:35:38 2019        (r345576)
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.203 2019/01/27 07:14:11 jmc Exp $ */
+/* $OpenBSD: scp.c,v 1.204 2019/02/10 11:15:52 djm Exp $ */
 /*
  * scp - secure remote copy.  This is basically patched BSD rcp which
  * uses ssh to do the data transfer (instead of using rcmd).
@@ -626,6 +626,253 @@ parse_scp_uri(const char *uri, char **userp, char **ho
        return r;
 }
 
+/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
+static int
+append(char *cp, char ***ap, size_t *np)
+{
+       char **tmp;
+
+       if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
+               return -1;
+       tmp[(*np)] = cp;
+       (*np)++;
+       *ap = tmp;
+       return 0;
+}
+
+/*
+ * Finds the start and end of the first brace pair in the pattern.
+ * returns 0 on success or -1 for invalid patterns.
+ */
+static int
+find_brace(const char *pattern, int *startp, int *endp)
+{
+       int i;
+       int in_bracket, brace_level;
+
+       *startp = *endp = -1;
+       in_bracket = brace_level = 0;
+       for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
+               switch (pattern[i]) {
+               case '\\':
+                       /* skip next character */
+                       if (pattern[i + 1] != '\0')
+                               i++;
+                       break;
+               case '[':
+                       in_bracket = 1;
+                       break;
+               case ']':
+                       in_bracket = 0;
+                       break;
+               case '{':
+                       if (in_bracket)
+                               break;
+                       if (pattern[i + 1] == '}') {
+                               /* Protect a single {}, for find(1), like csh */
+                               i++; /* skip */
+                               break;
+                       }
+                       if (*startp == -1)
+                               *startp = i;
+                       brace_level++;
+                       break;
+               case '}':
+                       if (in_bracket)
+                               break;
+                       if (*startp < 0) {
+                               /* Unbalanced brace */
+                               return -1;
+                       }
+                       if (--brace_level <= 0)
+                               *endp = i;
+                       break;
+               }
+       }
+       /* unbalanced brackets/braces */
+       if (*endp < 0 && (*startp >= 0 || in_bracket))
+               return -1;
+       return 0;
+}
+
+/*
+ * Assembles and records a successfully-expanded pattern, returns -1 on
+ * alloc failure.
+ */
+static int
+emit_expansion(const char *pattern, int brace_start, int brace_end,
+    int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
+{
+       char *cp;
+       int o = 0, tail_len = strlen(pattern + brace_end + 1);
+
+       if ((cp = malloc(brace_start + (sel_end - sel_start) +
+           tail_len + 1)) == NULL)
+               return -1;
+
+       /* Pattern before initial brace */
+       if (brace_start > 0) {
+               memcpy(cp, pattern, brace_start);
+               o = brace_start;
+       }
+       /* Current braced selection */
+       if (sel_end - sel_start > 0) {
+               memcpy(cp + o, pattern + sel_start,
+                   sel_end - sel_start);
+               o += sel_end - sel_start;
+       }
+       /* Remainder of pattern after closing brace */
+       if (tail_len > 0) {
+               memcpy(cp + o, pattern + brace_end + 1, tail_len);
+               o += tail_len;
+       }
+       cp[o] = '\0';
+       if (append(cp, patternsp, npatternsp) != 0) {
+               free(cp);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Expand the first encountered brace in pattern, appending the expanded
+ * patterns it yielded to the *patternsp array.
+ *
+ * Returns 0 on success or -1 on allocation failure.
+ *
+ * Signals whether expansion was performed via *expanded and whether
+ * pattern was invalid via *invalid.
+ */
+static int
+brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
+    int *expanded, int *invalid)
+{
+       int i;
+       int in_bracket, brace_start, brace_end, brace_level;
+       int sel_start, sel_end;
+
+       *invalid = *expanded = 0;
+
+       if (find_brace(pattern, &brace_start, &brace_end) != 0) {
+               *invalid = 1;
+               return 0;
+       } else if (brace_start == -1)
+               return 0;
+
+       in_bracket = brace_level = 0;
+       for (i = sel_start = brace_start + 1; i < brace_end; i++) {
+               switch (pattern[i]) {
+               case '{':
+                       if (in_bracket)
+                               break;
+                       brace_level++;
+                       break;
+               case '}':
+                       if (in_bracket)
+                               break;
+                       brace_level--;
+                       break;
+               case '[':
+                       in_bracket = 1;
+                       break;
+               case ']':
+                       in_bracket = 0;
+                       break;
+               case '\\':
+                       if (i < brace_end - 1)
+                               i++; /* skip */
+                       break;
+               }
+               if (pattern[i] == ',' || i == brace_end - 1) {
+                       if (in_bracket || brace_level > 0)
+                               continue;
+                       /* End of a selection, emit an expanded pattern */
+
+                       /* Adjust end index for last selection */
+                       sel_end = (i == brace_end - 1) ? brace_end : i;
+                       if (emit_expansion(pattern, brace_start, brace_end,
+                           sel_start, sel_end, patternsp, npatternsp) != 0)
+                               return -1;
+                       /* move on to the next selection */
+                       sel_start = i + 1;
+                       continue;
+               }
+       }
+       if (in_bracket || brace_level > 0) {
+               *invalid = 1;
+               return 0;
+       }
+       /* success */
+       *expanded = 1;
+       return 0;
+}
+
+/* Expand braces from pattern. Returns 0 on success, -1 on failure */
+static int
+brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
+{
+       char *cp, *cp2, **active = NULL, **done = NULL;
+       size_t i, nactive = 0, ndone = 0;
+       int ret = -1, invalid = 0, expanded = 0;
+
+       *patternsp = NULL;
+       *npatternsp = 0;
+
+       /* Start the worklist with the original pattern */
+       if ((cp = strdup(pattern)) == NULL)
+               return -1;
+       if (append(cp, &active, &nactive) != 0) {
+               free(cp);
+               return -1;
+       }
+       while (nactive > 0) {
+               cp = active[nactive - 1];
+               nactive--;
+               if (brace_expand_one(cp, &active, &nactive,
+                   &expanded, &invalid) == -1) {
+                       free(cp);
+                       goto fail;
+               }
+               if (invalid)
+                       fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
+               if (expanded) {
+                       /*
+                        * Current entry expanded to new entries on the
+                        * active list; discard the progenitor pattern.
+                        */
+                       free(cp);
+                       continue;
+               }
+               /*
+                * Pattern did not expand; append the finename component to
+                * the completed list
+                */
+               if ((cp2 = strrchr(cp, '/')) != NULL)
+                       *cp2++ = '\0';
+               else
+                       cp2 = cp;
+               if (append(xstrdup(cp2), &done, &ndone) != 0) {
+                       free(cp);
+                       goto fail;
+               }
+               free(cp);
+       }
+       /* success */
+       *patternsp = done;
+       *npatternsp = ndone;
+       done = NULL;
+       ndone = 0;
+       ret = 0;
+ fail:
+       for (i = 0; i < nactive; i++)
+               free(active[i]);
+       free(active);
+       for (i = 0; i < ndone; i++)
+               free(done[i]);
+       free(done);
+       return ret;
+}
+
 void
 toremote(int argc, char **argv)
 {
@@ -989,7 +1236,8 @@ sink(int argc, char **argv, const char *src)
        unsigned long long ull;
        int setimes, targisdir, wrerrno = 0;
        char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
-       char *src_copy = NULL, *restrict_pattern = NULL;
+       char **patterns = NULL;
+       size_t n, npatterns = 0;
        struct timeval tv[2];
 
 #define        atime   tv[0]
@@ -1019,16 +1267,13 @@ sink(int argc, char **argv, const char *src)
                 * Prepare to try to restrict incoming filenames to match
                 * the requested destination file glob.
                 */
-               if ((src_copy = strdup(src)) == NULL)
-                       fatal("strdup failed");
-               if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
-                       *restrict_pattern++ = '\0';
-               }
+               if (brace_expand(src, &patterns, &npatterns) != 0)
+                       fatal("%s: could not expand pattern", __func__);
        }
        for (first = 1;; first = 0) {
                cp = buf;
                if (atomicio(read, remin, cp, 1) != 1)
-                       return;
+                       goto done;
                if (*cp++ == '\n')
                        SCREWUP("unexpected <newline>");
                do {
@@ -1054,7 +1299,7 @@ sink(int argc, char **argv, const char *src)
                }
                if (buf[0] == 'E') {
                        (void) atomicio(vwrite, remout, "", 1);
-                       return;
+                       goto done;
                }
                if (ch == '\n')
                        *--cp = 0;
@@ -1129,9 +1374,14 @@ sink(int argc, char **argv, const char *src)
                        run_err("error: unexpected filename: %s", cp);
                        exit(1);
                }
-               if (restrict_pattern != NULL &&
-                   fnmatch(restrict_pattern, cp, 0) != 0)
-                       SCREWUP("filename does not match request");
+               if (npatterns > 0) {
+                       for (n = 0; n < npatterns; n++) {
+                               if (fnmatch(patterns[n], cp, 0) == 0)
+                                       break;
+                       }
+                       if (n >= npatterns)
+                               SCREWUP("filename does not match request");
+               }
                if (targisdir) {
                        static char *namebuf;
                        static size_t cursize;
@@ -1290,7 +1540,15 @@ bad:                     run_err("%s: %s", np, 
strerror(errno));
                        break;
                }
        }
+done:
+       for (n = 0; n < npatterns; n++)
+               free(patterns[n]);
+       free(patterns);
+       return;
 screwup:
+       for (n = 0; n < npatterns; n++)
+               free(patterns[n]);
+       free(patterns);
        run_err("protocol error: %s", why);
        exit(1);
 }


_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to