summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-01-26 22:41:28 +0000
committerColin Watson <cjwatson@debian.org>2019-02-08 16:39:38 +0000
commit125924e47db3713a85a70e0f8d6c23818d2ea054 (patch)
treec8427bede4b2f4577f27250dbd84c8f8c50928de
parent2a8f710447442e9a03e71c022859112ec2d77d17 (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 CVE-2019-6111 Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=391ffc4b9d31fa1f4ad566499fef9176ff8a07dc Last-Update: 2019-02-08 Patch-Name: check-filenames-in-scp-client.patch
-rw-r--r--scp.112
-rw-r--r--scp.c37
2 files changed, 40 insertions, 9 deletions
diff --git a/scp.1 b/scp.1
index 0e5cc1b2d..397e77091 100644
--- a/scp.1
+++ b/scp.1
@@ -18,7 +18,7 @@
18.Nd secure copy (remote file copy program) 18.Nd secure copy (remote file copy program)
19.Sh SYNOPSIS 19.Sh SYNOPSIS
20.Nm scp 20.Nm scp
21.Op Fl 346BCpqrv 21.Op Fl 346BCpqrTv
22.Op Fl c Ar cipher 22.Op Fl c Ar cipher
23.Op Fl F Ar ssh_config 23.Op Fl F Ar ssh_config
24.Op Fl i Ar identity_file 24.Op Fl i Ar identity_file
@@ -208,6 +208,16 @@ to use for the encrypted connection.
208The program must understand 208The program must understand
209.Xr ssh 1 209.Xr ssh 1
210options. 210options.
211.It Fl T
212Disable strict filename checking.
213By default when copying files from a remote host to a local directory
214.Nm
215checks that the received filenames match those requested on the command-line
216to prevent the remote end from sending unexpected or unwanted files.
217Because of differences in how various operating systems and shells interpret
218filename wildcards, these checks may cause wanted files to be rejected.
219This option disables these checks at the expense of fully trusting that
220the server will not send unexpected filenames.
211.It Fl v 221.It Fl v
212Verbose mode. 222Verbose mode.
213Causes 223Causes
diff --git a/scp.c b/scp.c
index 1971c80cd..035037bcc 100644
--- a/scp.c
+++ b/scp.c
@@ -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>
@@ -383,14 +384,14 @@ void verifydir(char *);
383struct passwd *pwd; 384struct passwd *pwd;
384uid_t userid; 385uid_t userid;
385int errs, remin, remout; 386int errs, remin, remout;
386int pflag, iamremote, iamrecursive, targetshouldbedirectory; 387int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
387 388
388#define CMDNEEDS 64 389#define CMDNEEDS 64
389char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 390char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
390 391
391int response(void); 392int response(void);
392void rsource(char *, struct stat *); 393void rsource(char *, struct stat *);
393void sink(int, char *[]); 394void sink(int, char *[], const char *);
394void source(int, char *[]); 395void source(int, char *[]);
395void tolocal(int, char *[]); 396void tolocal(int, char *[]);
396void toremote(int, char *[]); 397void toremote(int, char *[]);
@@ -429,8 +430,9 @@ main(int argc, char **argv)
429 addargs(&args, "-oRemoteCommand=none"); 430 addargs(&args, "-oRemoteCommand=none");
430 addargs(&args, "-oRequestTTY=no"); 431 addargs(&args, "-oRequestTTY=no");
431 432
432 fflag = tflag = 0; 433 fflag = Tflag = tflag = 0;
433 while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) 434 while ((ch = getopt(argc, argv,
435 "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) {
434 switch (ch) { 436 switch (ch) {
435 /* User-visible flags. */ 437 /* User-visible flags. */
436 case '1': 438 case '1':
@@ -509,9 +511,13 @@ main(int argc, char **argv)
509 setmode(0, O_BINARY); 511 setmode(0, O_BINARY);
510#endif 512#endif
511 break; 513 break;
514 case 'T':
515 Tflag = 1;
516 break;
512 default: 517 default:
513 usage(); 518 usage();
514 } 519 }
520 }
515 argc -= optind; 521 argc -= optind;
516 argv += optind; 522 argv += optind;
517 523
@@ -542,7 +548,7 @@ main(int argc, char **argv)
542 } 548 }
543 if (tflag) { 549 if (tflag) {
544 /* Receive data. */ 550 /* Receive data. */
545 sink(argc, argv); 551 sink(argc, argv, NULL);
546 exit(errs != 0); 552 exit(errs != 0);
547 } 553 }
548 if (argc < 2) 554 if (argc < 2)
@@ -800,7 +806,7 @@ tolocal(int argc, char **argv)
800 continue; 806 continue;
801 } 807 }
802 free(bp); 808 free(bp);
803 sink(1, argv + argc - 1); 809 sink(1, argv + argc - 1, src);
804 (void) close(remin); 810 (void) close(remin);
805 remin = remout = -1; 811 remin = remout = -1;
806 } 812 }
@@ -976,7 +982,7 @@ rsource(char *name, struct stat *statp)
976 (sizeof(type) != 4 && sizeof(type) != 8)) 982 (sizeof(type) != 4 && sizeof(type) != 8))
977 983
978void 984void
979sink(int argc, char **argv) 985sink(int argc, char **argv, const char *src)
980{ 986{
981 static BUF buffer; 987 static BUF buffer;
982 struct stat stb; 988 struct stat stb;
@@ -992,6 +998,7 @@ sink(int argc, char **argv)
992 unsigned long long ull; 998 unsigned long long ull;
993 int setimes, targisdir, wrerrno = 0; 999 int setimes, targisdir, wrerrno = 0;
994 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 1000 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
1001 char *src_copy = NULL, *restrict_pattern = NULL;
995 struct timeval tv[2]; 1002 struct timeval tv[2];
996 1003
997#define atime tv[0] 1004#define atime tv[0]
@@ -1016,6 +1023,17 @@ sink(int argc, char **argv)
1016 (void) atomicio(vwrite, remout, "", 1); 1023 (void) atomicio(vwrite, remout, "", 1);
1017 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 1024 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
1018 targisdir = 1; 1025 targisdir = 1;
1026 if (src != NULL && !iamrecursive && !Tflag) {
1027 /*
1028 * Prepare to try to restrict incoming filenames to match
1029 * the requested destination file glob.
1030 */
1031 if ((src_copy = strdup(src)) == NULL)
1032 fatal("strdup failed");
1033 if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
1034 *restrict_pattern++ = '\0';
1035 }
1036 }
1019 for (first = 1;; first = 0) { 1037 for (first = 1;; first = 0) {
1020 cp = buf; 1038 cp = buf;
1021 if (atomicio(read, remin, cp, 1) != 1) 1039 if (atomicio(read, remin, cp, 1) != 1)
@@ -1120,6 +1138,9 @@ sink(int argc, char **argv)
1120 run_err("error: unexpected filename: %s", cp); 1138 run_err("error: unexpected filename: %s", cp);
1121 exit(1); 1139 exit(1);
1122 } 1140 }
1141 if (restrict_pattern != NULL &&
1142 fnmatch(restrict_pattern, cp, 0) != 0)
1143 SCREWUP("filename does not match request");
1123 if (targisdir) { 1144 if (targisdir) {
1124 static char *namebuf; 1145 static char *namebuf;
1125 static size_t cursize; 1146 static size_t cursize;
@@ -1157,7 +1178,7 @@ sink(int argc, char **argv)
1157 goto bad; 1178 goto bad;
1158 } 1179 }
1159 vect[0] = xstrdup(np); 1180 vect[0] = xstrdup(np);
1160 sink(1, vect); 1181 sink(1, vect, src);
1161 if (setimes) { 1182 if (setimes) {
1162 setimes = 0; 1183 setimes = 0;
1163 if (utimes(vect[0], tv) < 0) 1184 if (utimes(vect[0], tv) < 0)