summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2019-02-08 16:25:52 +0000
committerColin Watson <cjwatson@debian.org>2019-02-08 16:39:47 +0000
commitf3357d7941c5ccc3d772e4461b1e2d137909670b (patch)
treed9d49a45c6b73a94d5e99a87385a5256c8832582
parentbbf87fe18360e9dabdeb9af0cadff0f8087a5492 (diff)
parent125924e47db3713a85a70e0f8d6c23818d2ea054 (diff)
scp: Check remote->local directory copy filenames
CVE-2019-6111
-rw-r--r--debian/.git-dpm4
-rw-r--r--debian/changelog3
-rw-r--r--debian/patches/check-filenames-in-scp-client.patch187
-rw-r--r--debian/patches/series1
-rw-r--r--scp.112
-rw-r--r--scp.c37
6 files changed, 233 insertions, 11 deletions
diff --git a/debian/.git-dpm b/debian/.git-dpm
index 992d37c60..8a18e26dc 100644
--- a/debian/.git-dpm
+++ b/debian/.git-dpm
@@ -1,6 +1,6 @@
1# see git-dpm(1) from git-dpm package 1# see git-dpm(1) from git-dpm package
22a8f710447442e9a03e71c022859112ec2d77d17 2125924e47db3713a85a70e0f8d6c23818d2ea054
32a8f710447442e9a03e71c022859112ec2d77d17 3125924e47db3713a85a70e0f8d6c23818d2ea054
43d246f10429fc9a37b98eabef94fe8dc7c61002b 43d246f10429fc9a37b98eabef94fe8dc7c61002b
53d246f10429fc9a37b98eabef94fe8dc7c61002b 53d246f10429fc9a37b98eabef94fe8dc7c61002b
6openssh_7.9p1.orig.tar.gz 6openssh_7.9p1.orig.tar.gz
diff --git a/debian/changelog b/debian/changelog
index cc1cfd5f1..41b42221d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,9 @@ openssh (1:7.9p1-6) UNRELEASED; urgency=medium
2 2
3 * CVE-2019-6109: Apply upstream patches to sanitize scp filenames via 3 * CVE-2019-6109: Apply upstream patches to sanitize scp filenames via
4 snmprintf (closes: #793412). 4 snmprintf (closes: #793412).
5 * CVE-2019-6111: Apply upstream patch to check in scp client that
6 filenames sent during remote->local directory copies satisfy the
7 wildcard specified by the user.
5 8
6 -- Colin Watson <cjwatson@debian.org> Fri, 08 Feb 2019 15:41:25 +0000 9 -- Colin Watson <cjwatson@debian.org> Fri, 08 Feb 2019 15:41:25 +0000
7 10
diff --git a/debian/patches/check-filenames-in-scp-client.patch b/debian/patches/check-filenames-in-scp-client.patch
new file mode 100644
index 000000000..519358ce6
--- /dev/null
+++ b/debian/patches/check-filenames-in-scp-client.patch
@@ -0,0 +1,187 @@
1From 125924e47db3713a85a70e0f8d6c23818d2ea054 Mon Sep 17 00:00:00 2001
2From: "djm@openbsd.org" <djm@openbsd.org>
3Date: Sat, 26 Jan 2019 22:41:28 +0000
4Subject: upstream: check in scp client that filenames sent during
5
6remote->local directory copies satisfy the wildcard specified by the user.
7
8This checking provides some protection against a malicious server
9sending unexpected filenames, but it comes at a risk of rejecting wanted
10files due to differences between client and server wildcard expansion rules.
11
12For this reason, this also adds a new -T flag to disable the check.
13
14reported by Harry Sintonen
15fix approach suggested by markus@;
16has been in snaps for ~1wk courtesy deraadt@
17
18OpenBSD-Commit-ID: 00f44b50d2be8e321973f3c6d014260f8f7a8eda
19
20CVE-2019-6111
21
22Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=391ffc4b9d31fa1f4ad566499fef9176ff8a07dc
23Last-Update: 2019-02-08
24
25Patch-Name: check-filenames-in-scp-client.patch
26---
27 scp.1 | 12 +++++++++++-
28 scp.c | 37 +++++++++++++++++++++++++++++--------
29 2 files changed, 40 insertions(+), 9 deletions(-)
30
31diff --git a/scp.1 b/scp.1
32index 0e5cc1b2d..397e77091 100644
33--- a/scp.1
34+++ b/scp.1
35@@ -18,7 +18,7 @@
36 .Nd secure copy (remote file copy program)
37 .Sh SYNOPSIS
38 .Nm scp
39-.Op Fl 346BCpqrv
40+.Op Fl 346BCpqrTv
41 .Op Fl c Ar cipher
42 .Op Fl F Ar ssh_config
43 .Op Fl i Ar identity_file
44@@ -208,6 +208,16 @@ to use for the encrypted connection.
45 The program must understand
46 .Xr ssh 1
47 options.
48+.It Fl T
49+Disable strict filename checking.
50+By default when copying files from a remote host to a local directory
51+.Nm
52+checks that the received filenames match those requested on the command-line
53+to prevent the remote end from sending unexpected or unwanted files.
54+Because of differences in how various operating systems and shells interpret
55+filename wildcards, these checks may cause wanted files to be rejected.
56+This option disables these checks at the expense of fully trusting that
57+the server will not send unexpected filenames.
58 .It Fl v
59 Verbose mode.
60 Causes
61diff --git a/scp.c b/scp.c
62index 1971c80cd..035037bcc 100644
63--- a/scp.c
64+++ b/scp.c
65@@ -94,6 +94,7 @@
66 #include <dirent.h>
67 #include <errno.h>
68 #include <fcntl.h>
69+#include <fnmatch.h>
70 #include <limits.h>
71 #include <locale.h>
72 #include <pwd.h>
73@@ -383,14 +384,14 @@ void verifydir(char *);
74 struct passwd *pwd;
75 uid_t userid;
76 int errs, remin, remout;
77-int pflag, iamremote, iamrecursive, targetshouldbedirectory;
78+int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
79
80 #define CMDNEEDS 64
81 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
82
83 int response(void);
84 void rsource(char *, struct stat *);
85-void sink(int, char *[]);
86+void sink(int, char *[], const char *);
87 void source(int, char *[]);
88 void tolocal(int, char *[]);
89 void toremote(int, char *[]);
90@@ -429,8 +430,9 @@ main(int argc, char **argv)
91 addargs(&args, "-oRemoteCommand=none");
92 addargs(&args, "-oRequestTTY=no");
93
94- fflag = tflag = 0;
95- while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
96+ fflag = Tflag = tflag = 0;
97+ while ((ch = getopt(argc, argv,
98+ "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) {
99 switch (ch) {
100 /* User-visible flags. */
101 case '1':
102@@ -509,9 +511,13 @@ main(int argc, char **argv)
103 setmode(0, O_BINARY);
104 #endif
105 break;
106+ case 'T':
107+ Tflag = 1;
108+ break;
109 default:
110 usage();
111 }
112+ }
113 argc -= optind;
114 argv += optind;
115
116@@ -542,7 +548,7 @@ main(int argc, char **argv)
117 }
118 if (tflag) {
119 /* Receive data. */
120- sink(argc, argv);
121+ sink(argc, argv, NULL);
122 exit(errs != 0);
123 }
124 if (argc < 2)
125@@ -800,7 +806,7 @@ tolocal(int argc, char **argv)
126 continue;
127 }
128 free(bp);
129- sink(1, argv + argc - 1);
130+ sink(1, argv + argc - 1, src);
131 (void) close(remin);
132 remin = remout = -1;
133 }
134@@ -976,7 +982,7 @@ rsource(char *name, struct stat *statp)
135 (sizeof(type) != 4 && sizeof(type) != 8))
136
137 void
138-sink(int argc, char **argv)
139+sink(int argc, char **argv, const char *src)
140 {
141 static BUF buffer;
142 struct stat stb;
143@@ -992,6 +998,7 @@ sink(int argc, char **argv)
144 unsigned long long ull;
145 int setimes, targisdir, wrerrno = 0;
146 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
147+ char *src_copy = NULL, *restrict_pattern = NULL;
148 struct timeval tv[2];
149
150 #define atime tv[0]
151@@ -1016,6 +1023,17 @@ sink(int argc, char **argv)
152 (void) atomicio(vwrite, remout, "", 1);
153 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
154 targisdir = 1;
155+ if (src != NULL && !iamrecursive && !Tflag) {
156+ /*
157+ * Prepare to try to restrict incoming filenames to match
158+ * the requested destination file glob.
159+ */
160+ if ((src_copy = strdup(src)) == NULL)
161+ fatal("strdup failed");
162+ if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
163+ *restrict_pattern++ = '\0';
164+ }
165+ }
166 for (first = 1;; first = 0) {
167 cp = buf;
168 if (atomicio(read, remin, cp, 1) != 1)
169@@ -1120,6 +1138,9 @@ sink(int argc, char **argv)
170 run_err("error: unexpected filename: %s", cp);
171 exit(1);
172 }
173+ if (restrict_pattern != NULL &&
174+ fnmatch(restrict_pattern, cp, 0) != 0)
175+ SCREWUP("filename does not match request");
176 if (targisdir) {
177 static char *namebuf;
178 static size_t cursize;
179@@ -1157,7 +1178,7 @@ sink(int argc, char **argv)
180 goto bad;
181 }
182 vect[0] = xstrdup(np);
183- sink(1, vect);
184+ sink(1, vect, src);
185 if (setimes) {
186 setimes = 0;
187 if (utimes(vect[0], tv) < 0)
diff --git a/debian/patches/series b/debian/patches/series
index 710e56b7b..4403cba3b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -27,3 +27,4 @@ conch-old-privkey-format.patch
27scp-disallow-dot-or-empty-filename.patch 27scp-disallow-dot-or-empty-filename.patch
28sanitize-scp-filenames-via-snmprintf.patch 28sanitize-scp-filenames-via-snmprintf.patch
29have-progressmeter-force-update-at-beginning-and-end-transfer.patch 29have-progressmeter-force-update-at-beginning-and-end-transfer.patch
30check-filenames-in-scp-client.patch
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)