diff options
-rw-r--r-- | scp.c | 282 |
1 files changed, 270 insertions, 12 deletions
@@ -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 */ | ||
634 | static int | ||
635 | append(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 | */ | ||
651 | static int | ||
652 | find_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 | */ | ||
706 | static int | ||
707 | emit_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 | */ | ||
750 | static int | ||
751 | brace_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 */ | ||
815 | static int | ||
816 | brace_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 | |||
633 | void | 880 | void |
634 | toremote(int argc, char **argv) | 881 | toremote(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 | } |
1547 | done: | ||
1548 | for (n = 0; n < npatterns; n++) | ||
1549 | free(patterns[n]); | ||
1550 | free(patterns); | ||
1551 | return; | ||
1297 | screwup: | 1552 | screwup: |
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 | } |