summaryrefslogtreecommitdiff
path: root/debian/patches/check-filenames-in-scp-client.patch
blob: 519358ce6c51bb1e4dee734003b3ed9ca14487de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
From 125924e47db3713a85a70e0f8d6c23818d2ea054 Mon Sep 17 00:00:00 2001
From: "djm@openbsd.org" <djm@openbsd.org>
Date: Sat, 26 Jan 2019 22:41:28 +0000
Subject: 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
---
 scp.1 | 12 +++++++++++-
 scp.c | 37 +++++++++++++++++++++++++++++--------
 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 @@
 .Nd secure copy (remote file copy program)
 .Sh SYNOPSIS
 .Nm scp
-.Op Fl 346BCpqrv
+.Op Fl 346BCpqrTv
 .Op Fl c Ar cipher
 .Op Fl F Ar ssh_config
 .Op Fl i Ar identity_file
@@ -208,6 +208,16 @@ to use for the encrypted connection.
 The program must understand
 .Xr ssh 1
 options.
+.It Fl T
+Disable strict filename checking.
+By default when copying files from a remote host to a local directory
+.Nm
+checks that the received filenames match those requested on the command-line
+to prevent the remote end from sending unexpected or unwanted files.
+Because of differences in how various operating systems and shells interpret
+filename wildcards, these checks may cause wanted files to be rejected.
+This option disables these checks at the expense of fully trusting that
+the server will not send unexpected filenames.
 .It Fl v
 Verbose mode.
 Causes
diff --git a/scp.c b/scp.c
index 1971c80cd..035037bcc 100644
--- a/scp.c
+++ b/scp.c
@@ -94,6 +94,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <limits.h>
 #include <locale.h>
 #include <pwd.h>
@@ -383,14 +384,14 @@ void verifydir(char *);
 struct passwd *pwd;
 uid_t userid;
 int errs, remin, remout;
-int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
 
 #define	CMDNEEDS	64
 char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
 
 int response(void);
 void rsource(char *, struct stat *);
-void sink(int, char *[]);
+void sink(int, char *[], const char *);
 void source(int, char *[]);
 void tolocal(int, char *[]);
 void toremote(int, char *[]);
@@ -429,8 +430,9 @@ main(int argc, char **argv)
 	addargs(&args, "-oRemoteCommand=none");
 	addargs(&args, "-oRequestTTY=no");
 
-	fflag = tflag = 0;
-	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
+	fflag = Tflag = tflag = 0;
+	while ((ch = getopt(argc, argv,
+	    "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) {
 		switch (ch) {
 		/* User-visible flags. */
 		case '1':
@@ -509,9 +511,13 @@ main(int argc, char **argv)
 			setmode(0, O_BINARY);
 #endif
 			break;
+		case 'T':
+			Tflag = 1;
+			break;
 		default:
 			usage();
 		}
+	}
 	argc -= optind;
 	argv += optind;
 
@@ -542,7 +548,7 @@ main(int argc, char **argv)
 	}
 	if (tflag) {
 		/* Receive data. */
-		sink(argc, argv);
+		sink(argc, argv, NULL);
 		exit(errs != 0);
 	}
 	if (argc < 2)
@@ -800,7 +806,7 @@ tolocal(int argc, char **argv)
 			continue;
 		}
 		free(bp);
-		sink(1, argv + argc - 1);
+		sink(1, argv + argc - 1, src);
 		(void) close(remin);
 		remin = remout = -1;
 	}
@@ -976,7 +982,7 @@ rsource(char *name, struct stat *statp)
 	 (sizeof(type) != 4 && sizeof(type) != 8))
 
 void
-sink(int argc, char **argv)
+sink(int argc, char **argv, const char *src)
 {
 	static BUF buffer;
 	struct stat stb;
@@ -992,6 +998,7 @@ sink(int argc, char **argv)
 	unsigned long long ull;
 	int setimes, targisdir, wrerrno = 0;
 	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
+	char *src_copy = NULL, *restrict_pattern = NULL;
 	struct timeval tv[2];
 
 #define	atime	tv[0]
@@ -1016,6 +1023,17 @@ sink(int argc, char **argv)
 	(void) atomicio(vwrite, remout, "", 1);
 	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
 		targisdir = 1;
+	if (src != NULL && !iamrecursive && !Tflag) {
+		/*
+		 * Prepare to try to restrict incoming filenames to match
+		 * the requested destination file glob.
+		 */
+		if ((src_copy = strdup(src)) == NULL)
+			fatal("strdup failed");
+		if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
+			*restrict_pattern++ = '\0';
+		}
+	}
 	for (first = 1;; first = 0) {
 		cp = buf;
 		if (atomicio(read, remin, cp, 1) != 1)
@@ -1120,6 +1138,9 @@ sink(int argc, char **argv)
 			run_err("error: unexpected filename: %s", cp);
 			exit(1);
 		}
+		if (restrict_pattern != NULL &&
+		    fnmatch(restrict_pattern, cp, 0) != 0)
+			SCREWUP("filename does not match request");
 		if (targisdir) {
 			static char *namebuf;
 			static size_t cursize;
@@ -1157,7 +1178,7 @@ sink(int argc, char **argv)
 					goto bad;
 			}
 			vect[0] = xstrdup(np);
-			sink(1, vect);
+			sink(1, vect, src);
 			if (setimes) {
 				setimes = 0;
 				if (utimes(vect[0], tv) < 0)