diff options
-rw-r--r-- | debian/.git-dpm | 4 | ||||
-rw-r--r-- | debian/changelog | 3 | ||||
-rw-r--r-- | debian/patches/check-filenames-in-scp-client.patch | 187 | ||||
-rw-r--r-- | debian/patches/series | 1 | ||||
-rw-r--r-- | scp.1 | 12 | ||||
-rw-r--r-- | scp.c | 37 |
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 |
2 | 2a8f710447442e9a03e71c022859112ec2d77d17 | 2 | 125924e47db3713a85a70e0f8d6c23818d2ea054 |
3 | 2a8f710447442e9a03e71c022859112ec2d77d17 | 3 | 125924e47db3713a85a70e0f8d6c23818d2ea054 |
4 | 3d246f10429fc9a37b98eabef94fe8dc7c61002b | 4 | 3d246f10429fc9a37b98eabef94fe8dc7c61002b |
5 | 3d246f10429fc9a37b98eabef94fe8dc7c61002b | 5 | 3d246f10429fc9a37b98eabef94fe8dc7c61002b |
6 | openssh_7.9p1.orig.tar.gz | 6 | openssh_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 @@ | |||
1 | From 125924e47db3713a85a70e0f8d6c23818d2ea054 Mon Sep 17 00:00:00 2001 | ||
2 | From: "djm@openbsd.org" <djm@openbsd.org> | ||
3 | Date: Sat, 26 Jan 2019 22:41:28 +0000 | ||
4 | Subject: upstream: check in scp client that filenames sent during | ||
5 | |||
6 | remote->local directory copies satisfy the wildcard specified by the user. | ||
7 | |||
8 | This checking provides some protection against a malicious server | ||
9 | sending unexpected filenames, but it comes at a risk of rejecting wanted | ||
10 | files due to differences between client and server wildcard expansion rules. | ||
11 | |||
12 | For this reason, this also adds a new -T flag to disable the check. | ||
13 | |||
14 | reported by Harry Sintonen | ||
15 | fix approach suggested by markus@; | ||
16 | has been in snaps for ~1wk courtesy deraadt@ | ||
17 | |||
18 | OpenBSD-Commit-ID: 00f44b50d2be8e321973f3c6d014260f8f7a8eda | ||
19 | |||
20 | CVE-2019-6111 | ||
21 | |||
22 | Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=391ffc4b9d31fa1f4ad566499fef9176ff8a07dc | ||
23 | Last-Update: 2019-02-08 | ||
24 | |||
25 | Patch-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 | |||
31 | diff --git a/scp.1 b/scp.1 | ||
32 | index 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 | ||
61 | diff --git a/scp.c b/scp.c | ||
62 | index 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 | |||
27 | scp-disallow-dot-or-empty-filename.patch | 27 | scp-disallow-dot-or-empty-filename.patch |
28 | sanitize-scp-filenames-via-snmprintf.patch | 28 | sanitize-scp-filenames-via-snmprintf.patch |
29 | have-progressmeter-force-update-at-beginning-and-end-transfer.patch | 29 | have-progressmeter-force-update-at-beginning-and-end-transfer.patch |
30 | check-filenames-in-scp-client.patch | ||
@@ -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. | |||
208 | The program must understand | 208 | The program must understand |
209 | .Xr ssh 1 | 209 | .Xr ssh 1 |
210 | options. | 210 | options. |
211 | .It Fl T | ||
212 | Disable strict filename checking. | ||
213 | By default when copying files from a remote host to a local directory | ||
214 | .Nm | ||
215 | checks that the received filenames match those requested on the command-line | ||
216 | to prevent the remote end from sending unexpected or unwanted files. | ||
217 | Because of differences in how various operating systems and shells interpret | ||
218 | filename wildcards, these checks may cause wanted files to be rejected. | ||
219 | This option disables these checks at the expense of fully trusting that | ||
220 | the server will not send unexpected filenames. | ||
211 | .It Fl v | 221 | .It Fl v |
212 | Verbose mode. | 222 | Verbose mode. |
213 | Causes | 223 | Causes |
@@ -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 *); | |||
383 | struct passwd *pwd; | 384 | struct passwd *pwd; |
384 | uid_t userid; | 385 | uid_t userid; |
385 | int errs, remin, remout; | 386 | int errs, remin, remout; |
386 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | 387 | int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; |
387 | 388 | ||
388 | #define CMDNEEDS 64 | 389 | #define CMDNEEDS 64 |
389 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | 390 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ |
390 | 391 | ||
391 | int response(void); | 392 | int response(void); |
392 | void rsource(char *, struct stat *); | 393 | void rsource(char *, struct stat *); |
393 | void sink(int, char *[]); | 394 | void sink(int, char *[], const char *); |
394 | void source(int, char *[]); | 395 | void source(int, char *[]); |
395 | void tolocal(int, char *[]); | 396 | void tolocal(int, char *[]); |
396 | void toremote(int, char *[]); | 397 | void 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 | ||
978 | void | 984 | void |
979 | sink(int argc, char **argv) | 985 | sink(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) |