diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-01-26 22:41:28 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-01-27 09:42:39 +1100 |
commit | 391ffc4b9d31fa1f4ad566499fef9176ff8a07dc (patch) | |
tree | 58ebbdac03a5cfe199f0edc3eedb36af756cd6d9 /scp.c | |
parent | c2c18a39683db382a15b438632afab3f551d50ce (diff) |
upstream: check in scp client that filenames sent during
remote->local directory copies satisfy the wildcard specified by the user.
This checking provides some protection against a malicious server
sending unexpected filenames, but it comes at a risk of rejecting wanted
files due to differences between client and server wildcard expansion rules.
For this reason, this also adds a new -T flag to disable the check.
reported by Harry Sintonen
fix approach suggested by markus@;
has been in snaps for ~1wk courtesy deraadt@
OpenBSD-Commit-ID: 00f44b50d2be8e321973f3c6d014260f8f7a8eda
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 39 |
1 files changed, 30 insertions, 9 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: scp.c,v 1.201 2019/01/24 16:52:17 dtucker Exp $ */ | 1 | /* $OpenBSD: scp.c,v 1.202 2019/01/26 22:41:28 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). |
@@ -94,6 +94,7 @@ | |||
94 | #include <dirent.h> | 94 | #include <dirent.h> |
95 | #include <errno.h> | 95 | #include <errno.h> |
96 | #include <fcntl.h> | 96 | #include <fcntl.h> |
97 | #include <fnmatch.h> | ||
97 | #include <limits.h> | 98 | #include <limits.h> |
98 | #include <locale.h> | 99 | #include <locale.h> |
99 | #include <pwd.h> | 100 | #include <pwd.h> |
@@ -375,14 +376,14 @@ void verifydir(char *); | |||
375 | struct passwd *pwd; | 376 | struct passwd *pwd; |
376 | uid_t userid; | 377 | uid_t userid; |
377 | int errs, remin, remout; | 378 | int errs, remin, remout; |
378 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | 379 | int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; |
379 | 380 | ||
380 | #define CMDNEEDS 64 | 381 | #define CMDNEEDS 64 |
381 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | 382 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ |
382 | 383 | ||
383 | int response(void); | 384 | int response(void); |
384 | void rsource(char *, struct stat *); | 385 | void rsource(char *, struct stat *); |
385 | void sink(int, char *[]); | 386 | void sink(int, char *[], const char *); |
386 | void source(int, char *[]); | 387 | void source(int, char *[]); |
387 | void tolocal(int, char *[]); | 388 | void tolocal(int, char *[]); |
388 | void toremote(int, char *[]); | 389 | void toremote(int, char *[]); |
@@ -423,8 +424,9 @@ main(int argc, char **argv) | |||
423 | addargs(&args, "-oRemoteCommand=none"); | 424 | addargs(&args, "-oRemoteCommand=none"); |
424 | addargs(&args, "-oRequestTTY=no"); | 425 | addargs(&args, "-oRequestTTY=no"); |
425 | 426 | ||
426 | fflag = tflag = 0; | 427 | fflag = Tflag = tflag = 0; |
427 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:J:")) != -1) | 428 | while ((ch = getopt(argc, argv, |
429 | "dfl:prtTvBCc:i:P:q12346S:o:F:J:")) != -1) { | ||
428 | switch (ch) { | 430 | switch (ch) { |
429 | /* User-visible flags. */ | 431 | /* User-visible flags. */ |
430 | case '1': | 432 | case '1': |
@@ -504,9 +506,13 @@ main(int argc, char **argv) | |||
504 | setmode(0, O_BINARY); | 506 | setmode(0, O_BINARY); |
505 | #endif | 507 | #endif |
506 | break; | 508 | break; |
509 | case 'T': | ||
510 | Tflag = 1; | ||
511 | break; | ||
507 | default: | 512 | default: |
508 | usage(); | 513 | usage(); |
509 | } | 514 | } |
515 | } | ||
510 | argc -= optind; | 516 | argc -= optind; |
511 | argv += optind; | 517 | argv += optind; |
512 | 518 | ||
@@ -537,7 +543,7 @@ main(int argc, char **argv) | |||
537 | } | 543 | } |
538 | if (tflag) { | 544 | if (tflag) { |
539 | /* Receive data. */ | 545 | /* Receive data. */ |
540 | sink(argc, argv); | 546 | sink(argc, argv, NULL); |
541 | exit(errs != 0); | 547 | exit(errs != 0); |
542 | } | 548 | } |
543 | if (argc < 2) | 549 | if (argc < 2) |
@@ -795,7 +801,7 @@ tolocal(int argc, char **argv) | |||
795 | continue; | 801 | continue; |
796 | } | 802 | } |
797 | free(bp); | 803 | free(bp); |
798 | sink(1, argv + argc - 1); | 804 | sink(1, argv + argc - 1, src); |
799 | (void) close(remin); | 805 | (void) close(remin); |
800 | remin = remout = -1; | 806 | remin = remout = -1; |
801 | } | 807 | } |
@@ -971,7 +977,7 @@ rsource(char *name, struct stat *statp) | |||
971 | (sizeof(type) != 4 && sizeof(type) != 8)) | 977 | (sizeof(type) != 4 && sizeof(type) != 8)) |
972 | 978 | ||
973 | void | 979 | void |
974 | sink(int argc, char **argv) | 980 | sink(int argc, char **argv, const char *src) |
975 | { | 981 | { |
976 | static BUF buffer; | 982 | static BUF buffer; |
977 | struct stat stb; | 983 | struct stat stb; |
@@ -987,6 +993,7 @@ sink(int argc, char **argv) | |||
987 | unsigned long long ull; | 993 | unsigned long long ull; |
988 | int setimes, targisdir, wrerrno = 0; | 994 | int setimes, targisdir, wrerrno = 0; |
989 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; | 995 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; |
996 | char *src_copy = NULL, *restrict_pattern = NULL; | ||
990 | struct timeval tv[2]; | 997 | struct timeval tv[2]; |
991 | 998 | ||
992 | #define atime tv[0] | 999 | #define atime tv[0] |
@@ -1011,6 +1018,17 @@ sink(int argc, char **argv) | |||
1011 | (void) atomicio(vwrite, remout, "", 1); | 1018 | (void) atomicio(vwrite, remout, "", 1); |
1012 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | 1019 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) |
1013 | targisdir = 1; | 1020 | targisdir = 1; |
1021 | if (src != NULL && !iamrecursive && !Tflag) { | ||
1022 | /* | ||
1023 | * Prepare to try to restrict incoming filenames to match | ||
1024 | * the requested destination file glob. | ||
1025 | */ | ||
1026 | if ((src_copy = strdup(src)) == NULL) | ||
1027 | fatal("strdup failed"); | ||
1028 | if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) { | ||
1029 | *restrict_pattern++ = '\0'; | ||
1030 | } | ||
1031 | } | ||
1014 | for (first = 1;; first = 0) { | 1032 | for (first = 1;; first = 0) { |
1015 | cp = buf; | 1033 | cp = buf; |
1016 | if (atomicio(read, remin, cp, 1) != 1) | 1034 | if (atomicio(read, remin, cp, 1) != 1) |
@@ -1115,6 +1133,9 @@ sink(int argc, char **argv) | |||
1115 | run_err("error: unexpected filename: %s", cp); | 1133 | run_err("error: unexpected filename: %s", cp); |
1116 | exit(1); | 1134 | exit(1); |
1117 | } | 1135 | } |
1136 | if (restrict_pattern != NULL && | ||
1137 | fnmatch(restrict_pattern, cp, 0) != 0) | ||
1138 | SCREWUP("filename does not match request"); | ||
1118 | if (targisdir) { | 1139 | if (targisdir) { |
1119 | static char *namebuf; | 1140 | static char *namebuf; |
1120 | static size_t cursize; | 1141 | static size_t cursize; |
@@ -1152,7 +1173,7 @@ sink(int argc, char **argv) | |||
1152 | goto bad; | 1173 | goto bad; |
1153 | } | 1174 | } |
1154 | vect[0] = xstrdup(np); | 1175 | vect[0] = xstrdup(np); |
1155 | sink(1, vect); | 1176 | sink(1, vect, src); |
1156 | if (setimes) { | 1177 | if (setimes) { |
1157 | setimes = 0; | 1178 | setimes = 0; |
1158 | if (utimes(vect[0], tv) < 0) | 1179 | if (utimes(vect[0], tv) < 0) |