summaryrefslogtreecommitdiff
path: root/scp.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-02-10 11:15:52 +0000
committerDamien Miller <djm@mindrot.org>2019-02-10 22:24:24 +1100
commit3d896c157c722bc47adca51a58dca859225b5874 (patch)
tree6218b65b9afa6a629ed3fd68a2599698779e7101 /scp.c
parent318e4f8548a4f5c0c913f61e27d4fc21ffb1eaae (diff)
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
Diffstat (limited to 'scp.c')
-rw-r--r--scp.c282
1 files changed, 270 insertions, 12 deletions
diff --git a/scp.c b/scp.c
index 96fc246cd..80bc0e8b1 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: scp.c,v 1.203 2019/01/27 07:14:11 jmc Exp $ */ 1/* $OpenBSD: scp.c,v 1.204 2019/02/10 11:15:52 djm Exp $ */
2/* 2/*
3 * scp - secure remote copy. This is basically patched BSD rcp which 3 * scp - secure remote copy. This is basically patched BSD rcp which
4 * uses ssh to do the data transfer (instead of using rcmd). 4 * uses ssh to do the data transfer (instead of using rcmd).
@@ -630,6 +630,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
630 return r; 630 return r;
631} 631}
632 632
633/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
634static int
635append(char *cp, char ***ap, size_t *np)
636{
637 char **tmp;
638
639 if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
640 return -1;
641 tmp[(*np)] = cp;
642 (*np)++;
643 *ap = tmp;
644 return 0;
645}
646
647/*
648 * Finds the start and end of the first brace pair in the pattern.
649 * returns 0 on success or -1 for invalid patterns.
650 */
651static int
652find_brace(const char *pattern, int *startp, int *endp)
653{
654 int i;
655 int in_bracket, brace_level;
656
657 *startp = *endp = -1;
658 in_bracket = brace_level = 0;
659 for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
660 switch (pattern[i]) {
661 case '\\':
662 /* skip next character */
663 if (pattern[i + 1] != '\0')
664 i++;
665 break;
666 case '[':
667 in_bracket = 1;
668 break;
669 case ']':
670 in_bracket = 0;
671 break;
672 case '{':
673 if (in_bracket)
674 break;
675 if (pattern[i + 1] == '}') {
676 /* Protect a single {}, for find(1), like csh */
677 i++; /* skip */
678 break;
679 }
680 if (*startp == -1)
681 *startp = i;
682 brace_level++;
683 break;
684 case '}':
685 if (in_bracket)
686 break;
687 if (*startp < 0) {
688 /* Unbalanced brace */
689 return -1;
690 }
691 if (--brace_level <= 0)
692 *endp = i;
693 break;
694 }
695 }
696 /* unbalanced brackets/braces */
697 if (*endp < 0 && (*startp >= 0 || in_bracket))
698 return -1;
699 return 0;
700}
701
702/*
703 * Assembles and records a successfully-expanded pattern, returns -1 on
704 * alloc failure.
705 */
706static int
707emit_expansion(const char *pattern, int brace_start, int brace_end,
708 int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
709{
710 char *cp;
711 int o = 0, tail_len = strlen(pattern + brace_end + 1);
712
713 if ((cp = malloc(brace_start + (sel_end - sel_start) +
714 tail_len + 1)) == NULL)
715 return -1;
716
717 /* Pattern before initial brace */
718 if (brace_start > 0) {
719 memcpy(cp, pattern, brace_start);
720 o = brace_start;
721 }
722 /* Current braced selection */
723 if (sel_end - sel_start > 0) {
724 memcpy(cp + o, pattern + sel_start,
725 sel_end - sel_start);
726 o += sel_end - sel_start;
727 }
728 /* Remainder of pattern after closing brace */
729 if (tail_len > 0) {
730 memcpy(cp + o, pattern + brace_end + 1, tail_len);
731 o += tail_len;
732 }
733 cp[o] = '\0';
734 if (append(cp, patternsp, npatternsp) != 0) {
735 free(cp);
736 return -1;
737 }
738 return 0;
739}
740
741/*
742 * Expand the first encountered brace in pattern, appending the expanded
743 * patterns it yielded to the *patternsp array.
744 *
745 * Returns 0 on success or -1 on allocation failure.
746 *
747 * Signals whether expansion was performed via *expanded and whether
748 * pattern was invalid via *invalid.
749 */
750static int
751brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
752 int *expanded, int *invalid)
753{
754 int i;
755 int in_bracket, brace_start, brace_end, brace_level;
756 int sel_start, sel_end;
757
758 *invalid = *expanded = 0;
759
760 if (find_brace(pattern, &brace_start, &brace_end) != 0) {
761 *invalid = 1;
762 return 0;
763 } else if (brace_start == -1)
764 return 0;
765
766 in_bracket = brace_level = 0;
767 for (i = sel_start = brace_start + 1; i < brace_end; i++) {
768 switch (pattern[i]) {
769 case '{':
770 if (in_bracket)
771 break;
772 brace_level++;
773 break;
774 case '}':
775 if (in_bracket)
776 break;
777 brace_level--;
778 break;
779 case '[':
780 in_bracket = 1;
781 break;
782 case ']':
783 in_bracket = 0;
784 break;
785 case '\\':
786 if (i < brace_end - 1)
787 i++; /* skip */
788 break;
789 }
790 if (pattern[i] == ',' || i == brace_end - 1) {
791 if (in_bracket || brace_level > 0)
792 continue;
793 /* End of a selection, emit an expanded pattern */
794
795 /* Adjust end index for last selection */
796 sel_end = (i == brace_end - 1) ? brace_end : i;
797 if (emit_expansion(pattern, brace_start, brace_end,
798 sel_start, sel_end, patternsp, npatternsp) != 0)
799 return -1;
800 /* move on to the next selection */
801 sel_start = i + 1;
802 continue;
803 }
804 }
805 if (in_bracket || brace_level > 0) {
806 *invalid = 1;
807 return 0;
808 }
809 /* success */
810 *expanded = 1;
811 return 0;
812}
813
814/* Expand braces from pattern. Returns 0 on success, -1 on failure */
815static int
816brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
817{
818 char *cp, *cp2, **active = NULL, **done = NULL;
819 size_t i, nactive = 0, ndone = 0;
820 int ret = -1, invalid = 0, expanded = 0;
821
822 *patternsp = NULL;
823 *npatternsp = 0;
824
825 /* Start the worklist with the original pattern */
826 if ((cp = strdup(pattern)) == NULL)
827 return -1;
828 if (append(cp, &active, &nactive) != 0) {
829 free(cp);
830 return -1;
831 }
832 while (nactive > 0) {
833 cp = active[nactive - 1];
834 nactive--;
835 if (brace_expand_one(cp, &active, &nactive,
836 &expanded, &invalid) == -1) {
837 free(cp);
838 goto fail;
839 }
840 if (invalid)
841 fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
842 if (expanded) {
843 /*
844 * Current entry expanded to new entries on the
845 * active list; discard the progenitor pattern.
846 */
847 free(cp);
848 continue;
849 }
850 /*
851 * Pattern did not expand; append the finename component to
852 * the completed list
853 */
854 if ((cp2 = strrchr(cp, '/')) != NULL)
855 *cp2++ = '\0';
856 else
857 cp2 = cp;
858 if (append(xstrdup(cp2), &done, &ndone) != 0) {
859 free(cp);
860 goto fail;
861 }
862 free(cp);
863 }
864 /* success */
865 *patternsp = done;
866 *npatternsp = ndone;
867 done = NULL;
868 ndone = 0;
869 ret = 0;
870 fail:
871 for (i = 0; i < nactive; i++)
872 free(active[i]);
873 free(active);
874 for (i = 0; i < ndone; i++)
875 free(done[i]);
876 free(done);
877 return ret;
878}
879
633void 880void
634toremote(int argc, char **argv) 881toremote(int argc, char **argv)
635{ 882{
@@ -993,7 +1240,8 @@ sink(int argc, char **argv, const char *src)
993 unsigned long long ull; 1240 unsigned long long ull;
994 int setimes, targisdir, wrerrno = 0; 1241 int setimes, targisdir, wrerrno = 0;
995 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 1242 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
996 char *src_copy = NULL, *restrict_pattern = NULL; 1243 char **patterns = NULL;
1244 size_t n, npatterns = 0;
997 struct timeval tv[2]; 1245 struct timeval tv[2];
998 1246
999#define atime tv[0] 1247#define atime tv[0]
@@ -1023,16 +1271,13 @@ sink(int argc, char **argv, const char *src)
1023 * Prepare to try to restrict incoming filenames to match 1271 * Prepare to try to restrict incoming filenames to match
1024 * the requested destination file glob. 1272 * the requested destination file glob.
1025 */ 1273 */
1026 if ((src_copy = strdup(src)) == NULL) 1274 if (brace_expand(src, &patterns, &npatterns) != 0)
1027 fatal("strdup failed"); 1275 fatal("%s: could not expand pattern", __func__);
1028 if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
1029 *restrict_pattern++ = '\0';
1030 }
1031 } 1276 }
1032 for (first = 1;; first = 0) { 1277 for (first = 1;; first = 0) {
1033 cp = buf; 1278 cp = buf;
1034 if (atomicio(read, remin, cp, 1) != 1) 1279 if (atomicio(read, remin, cp, 1) != 1)
1035 return; 1280 goto done;
1036 if (*cp++ == '\n') 1281 if (*cp++ == '\n')
1037 SCREWUP("unexpected <newline>"); 1282 SCREWUP("unexpected <newline>");
1038 do { 1283 do {
@@ -1058,7 +1303,7 @@ sink(int argc, char **argv, const char *src)
1058 } 1303 }
1059 if (buf[0] == 'E') { 1304 if (buf[0] == 'E') {
1060 (void) atomicio(vwrite, remout, "", 1); 1305 (void) atomicio(vwrite, remout, "", 1);
1061 return; 1306 goto done;
1062 } 1307 }
1063 if (ch == '\n') 1308 if (ch == '\n')
1064 *--cp = 0; 1309 *--cp = 0;
@@ -1133,9 +1378,14 @@ sink(int argc, char **argv, const char *src)
1133 run_err("error: unexpected filename: %s", cp); 1378 run_err("error: unexpected filename: %s", cp);
1134 exit(1); 1379 exit(1);
1135 } 1380 }
1136 if (restrict_pattern != NULL && 1381 if (npatterns > 0) {
1137 fnmatch(restrict_pattern, cp, 0) != 0) 1382 for (n = 0; n < npatterns; n++) {
1138 SCREWUP("filename does not match request"); 1383 if (fnmatch(patterns[n], cp, 0) == 0)
1384 break;
1385 }
1386 if (n >= npatterns)
1387 SCREWUP("filename does not match request");
1388 }
1139 if (targisdir) { 1389 if (targisdir) {
1140 static char *namebuf; 1390 static char *namebuf;
1141 static size_t cursize; 1391 static size_t cursize;
@@ -1294,7 +1544,15 @@ bad: run_err("%s: %s", np, strerror(errno));
1294 break; 1544 break;
1295 } 1545 }
1296 } 1546 }
1547done:
1548 for (n = 0; n < npatterns; n++)
1549 free(patterns[n]);
1550 free(patterns);
1551 return;
1297screwup: 1552screwup:
1553 for (n = 0; n < npatterns; n++)
1554 free(patterns[n]);
1555 free(patterns);
1298 run_err("protocol error: %s", why); 1556 run_err("protocol error: %s", why);
1299 exit(1); 1557 exit(1);
1300} 1558}