File: CVE-2025-47203.patch

package info (click to toggle)
dropbear 2022.83-1%2Bdeb12u3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 12,216 kB
  • sloc: ansic: 96,944; sh: 4,508; perl: 774; python: 739; makefile: 694; java: 177
file content (365 lines) | stat: -rw-r--r-- 11,110 bytes parent folder | download
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
From: Matt Johnston <matt@ucc.asn.au>
Date: Mon, 5 May 2025 23:14:19 +0800
Subject: Execute multihop commands directly, no shell

This avoids problems with shell escaping if arguments contain special
characters.

Origin: https://github.com/mkj/dropbear/commit/e5a0ef27c227f7ae69d9a9fec98a056494409b9b
---
 cli-main.c    |  61 +++++++++++++++++++++++------------
 cli-runopts.c | 100 ++++++++++++++++++++++++++++++++++------------------------
 dbutil.c      |   9 ++++--
 dbutil.h      |   1 +
 runopts.h     |   5 +++
 5 files changed, 113 insertions(+), 63 deletions(-)

diff --git a/cli-main.c b/cli-main.c
index 065fd76..2fafa88 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -77,9 +77,8 @@ int main(int argc, char ** argv) {
 	}
 
 #if DROPBEAR_CLI_PROXYCMD
-	if (cli_opts.proxycmd) {
+	if (cli_opts.proxycmd || cli_opts.proxyexec) {
 		cli_proxy_cmd(&sock_in, &sock_out, &proxy_cmd_pid);
-		m_free(cli_opts.proxycmd);
 		if (signal(SIGINT, kill_proxy_sighandler) == SIG_ERR ||
 			signal(SIGTERM, kill_proxy_sighandler) == SIG_ERR ||
 			signal(SIGHUP, kill_proxy_sighandler) == SIG_ERR) {
@@ -101,7 +100,8 @@ int main(int argc, char ** argv) {
 }
 #endif /* DBMULTI stuff */
 
-static void exec_proxy_cmd(const void *user_data_cmd) {
+#if DROPBEAR_CLI_PROXYCMD
+static void shell_proxy_cmd(const void *user_data_cmd) {
 	const char *cmd = user_data_cmd;
 	char *usershell;
 
@@ -110,41 +110,62 @@ static void exec_proxy_cmd(const void *user_data_cmd) {
 	dropbear_exit("Failed to run '%s'\n", cmd);
 }
 
-#if DROPBEAR_CLI_PROXYCMD
+static void exec_proxy_cmd(const void *unused) {
+	(void)unused;
+	run_command(cli_opts.proxyexec[0], cli_opts.proxyexec, ses.maxfd);
+	dropbear_exit("Failed to run '%s'\n", cli_opts.proxyexec[0]);
+}
+
 static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out) {
-	char * ex_cmd = NULL;
-	size_t ex_cmdlen;
+	char * cmd_arg = NULL;
+	void (*exec_fn)(const void *user_data) = NULL;
 	int ret;
 
+	/* exactly one of cli_opts.proxycmd or cli_opts.proxyexec should be set */
+
 	/* File descriptor "-j &3" */
-	if (*cli_opts.proxycmd == '&') {
+	if (cli_opts.proxycmd && *cli_opts.proxycmd == '&') {
 		char *p = cli_opts.proxycmd + 1;
 		int sock = strtoul(p, &p, 10);
 		/* must be a single number, and not stdin/stdout/stderr */
 		if (sock > 2 && sock < 1024 && *p == '\0') {
 			*sock_in = sock;
 			*sock_out = sock;
-			return;
+			goto cleanup;
 		}
 	}
 
-	/* Normal proxycommand */
-
-	/* So that spawn_command knows which shell to run */
-	fill_passwd(cli_opts.own_user);
-
-	ex_cmdlen = strlen(cli_opts.proxycmd) + 6; /* "exec " + command + '\0' */
-	ex_cmd = m_malloc(ex_cmdlen);
-	snprintf(ex_cmd, ex_cmdlen, "exec %s", cli_opts.proxycmd);
+	if (cli_opts.proxycmd) {
+		/* Normal proxycommand */
+		size_t shell_cmdlen;
+		/* So that spawn_command knows which shell to run */
+		fill_passwd(cli_opts.own_user);
+
+		shell_cmdlen = strlen(cli_opts.proxycmd) + 6; /* "exec " + command + '\0' */
+		cmd_arg = m_malloc(shell_cmdlen);
+		snprintf(cmd_arg, shell_cmdlen, "exec %s", cli_opts.proxycmd);
+		exec_fn = shell_proxy_cmd;
+	} else {
+		/* No shell */
+		exec_fn = exec_proxy_cmd;
+	}
 
-	ret = spawn_command(exec_proxy_cmd, ex_cmd,
-			sock_out, sock_in, NULL, pid_out);
-	DEBUG1(("cmd: %s  pid=%d", ex_cmd,*pid_out))
-	m_free(ex_cmd);
+	ret = spawn_command(exec_fn, cmd_arg, sock_out, sock_in, NULL, pid_out);
 	if (ret == DROPBEAR_FAILURE) {
 		dropbear_exit("Failed running proxy command");
 		*sock_in = *sock_out = -1;
 	}
+
+cleanup:
+	m_free(cli_opts.proxycmd);
+	m_free(cmd_arg);
+	if (cli_opts.proxyexec) {
+		char **a = NULL;
+		for (a = cli_opts.proxyexec; *a; a++) {
+			m_free_direct(*a);
+		}
+		m_free(cli_opts.proxyexec);
+	}
 }
 
 static void kill_proxy_sighandler(int UNUSED(signo)) {
diff --git a/cli-runopts.c b/cli-runopts.c
index 632d097..06b686a 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -530,58 +530,81 @@ static void loadidentityfile(const char* filename, int warnfail) {
 
 /* Fill out -i, -y, -W options that make sense for all
  * the intermediate processes */
-static char* multihop_passthrough_args(void) {
-	char *args = NULL;
-	unsigned int len, total;
+static char** multihop_args(const char* argv0, const char* prior_hops) {
+	/* null terminated array */
+	char **args = NULL;
+	size_t max_args = 14, pos = 0, len;
 #if DROPBEAR_CLI_PUBKEY_AUTH
 	m_list_elem *iter;
 #endif
-	/* Sufficient space for non-string args */
-	len = 100;
 
-	/* String arguments have arbitrary length, so determine space required */
-	if (cli_opts.proxycmd) {
-		len += strlen(cli_opts.proxycmd);
-	}
 #if DROPBEAR_CLI_PUBKEY_AUTH
 	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
 	{
-		sign_key * key = (sign_key*)iter->item;
-		len += 4 + strlen(key->filename);
+		/* "-i file" for each */
+		max_args += 2;
 	}
 #endif
 
-	args = m_malloc(len);
-	total = 0;
+	args = m_malloc(sizeof(char*) * max_args);
+	pos = 0;
 
-	/* Create new argument string */
+	args[pos] = m_strdup(argv0);
+	pos++;
 
 	if (cli_opts.quiet) {
-		total += m_snprintf(args+total, len-total, "-q ");
+		args[pos] = m_strdup("-q");
+		pos++;
 	}
 
 	if (cli_opts.no_hostkey_check) {
-		total += m_snprintf(args+total, len-total, "-y -y ");
+		args[pos] = m_strdup("-y");
+		pos++;
+		args[pos] = m_strdup("-y");
+		pos++;
 	} else if (cli_opts.always_accept_key) {
-		total += m_snprintf(args+total, len-total, "-y ");
+		args[pos] = m_strdup("-y");
+		pos++;
 	}
 
 	if (cli_opts.proxycmd) {
-		total += m_snprintf(args+total, len-total, "-J '%s' ", cli_opts.proxycmd);
+		args[pos] = m_strdup("-J");
+		pos++;
+		args[pos] = m_strdup(cli_opts.proxycmd);
+		pos++;
 	}
 
 	if (opts.recv_window != DEFAULT_RECV_WINDOW) {
-		total += m_snprintf(args+total, len-total, "-W %u ", opts.recv_window);
+		args[pos] = m_strdup("-W");
+		pos++;
+		args[pos] = m_malloc(11);
+		m_snprintf(args[pos], 11, "%u", opts.recv_window);
+		pos++;
 	}
 
 #if DROPBEAR_CLI_PUBKEY_AUTH
 	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
 	{
 		sign_key * key = (sign_key*)iter->item;
-		total += m_snprintf(args+total, len-total, "-i %s ", key->filename);
+		args[pos] = m_strdup("-i");
+		pos++;
+		args[pos] = m_strdup(key->filename);
+		pos++;
 	}
 #endif /* DROPBEAR_CLI_PUBKEY_AUTH */
 
+	/* last hop */
+	args[pos] = m_strdup("-B");
+	pos++;
+	len = strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) + 2;
+	args[pos] = m_malloc(len);
+	snprintf(args[pos], len, "%s:%s", cli_opts.remotehost, cli_opts.remoteport);
+	pos++;
+
+	/* hostnames of prior hops */
+	args[pos] = m_strdup(prior_hops);
+	pos++;
+
 	return args;
 }
 
@@ -596,7 +619,7 @@ static char* multihop_passthrough_args(void) {
  * etc for as many hosts as we want.
  *
  * Note that "-J" arguments aren't actually used, instead
- * below sets cli_opts.proxycmd directly.
+ * below sets cli_opts.proxyexec directly.
  *
  * Ports for hosts can be specified as host/port.
  */
@@ -604,7 +627,7 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
 	char *userhostarg = NULL;
 	char *hostbuf = NULL;
 	char *last_hop = NULL;
-	char *remainder = NULL;
+	char *prior_hops = NULL;
 
 	/* both scp and rsync parse a user@host argument
 	 * and turn it into "-l user host". This breaks
@@ -622,6 +645,8 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
 	}
 	userhostarg = hostbuf;
 
+	/* Split off any last hostname and use that as remotehost/remoteport.
+	 * That is used for authorized_keys checking etc */
 	last_hop = strrchr(userhostarg, ',');
 	if (last_hop) {
 		if (last_hop == userhostarg) {
@@ -629,35 +654,28 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
 		}
 		*last_hop = '\0';
 		last_hop++;
-		remainder = userhostarg;
+		prior_hops = userhostarg;
 		userhostarg = last_hop;
 	}
 
+	/* Update cli_opts.remotehost and cli_opts.remoteport */
 	parse_hostname(userhostarg);
 
-	if (last_hop) {
-		/* Set up the proxycmd */
-		unsigned int cmd_len = 0;
-		char *passthrough_args = multihop_passthrough_args();
-		if (cli_opts.remoteport == NULL) {
-			cli_opts.remoteport = "22";
+	/* Construct any multihop proxy command. Use proxyexec to
+	 * avoid worrying about shell escaping. */
+	if (prior_hops) {
+		cli_opts.proxyexec = multihop_args(argv0, prior_hops);
+		/* Any -J argument has been copied to proxyexec */
+		if (cli_opts.proxycmd) {
+			m_free(cli_opts.proxycmd);
 		}
-		cmd_len = strlen(argv0) + strlen(remainder)
-			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
-			+ strlen(passthrough_args)
-			+ 30;
-		/* replace proxycmd. old -J arguments have been copied
-		   to passthrough_args */
-		cli_opts.proxycmd = m_realloc(cli_opts.proxycmd, cmd_len);
-		m_snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
-				argv0, cli_opts.remotehost, cli_opts.remoteport,
-				passthrough_args, remainder);
+
 #ifndef DISABLE_ZLIB
-		/* The stream will be incompressible since it's encrypted. */
+		/* This outer stream will be incompressible since it's encrypted. */
 		opts.compress_mode = DROPBEAR_COMPRESS_OFF;
 #endif
-		m_free(passthrough_args);
 	}
+
 	m_free(hostbuf);
 }
 #endif /* !DROPBEAR_CLI_MULTIHOP */
diff --git a/dbutil.c b/dbutil.c
index bd66454..910fa27 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -371,7 +371,6 @@ int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
 	char * argv[4];
 	char * baseshell = NULL;
-	unsigned int i;
 
 	baseshell = basename(usershell);
 
@@ -393,6 +392,12 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
 		argv[1] = NULL;
 	}
 
+	run_command(usershell, argv, maxfd);
+}
+
+void run_command(const char* argv0, char** args, unsigned int maxfd) {
+	unsigned int i;
+
 	/* Re-enable SIGPIPE for the executed process */
 	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
 		dropbear_exit("signal() error");
@@ -404,7 +409,7 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
 		m_close(i);
 	}
 
-	execv(usershell, argv);
+	execv(argv0, args);
 }
 
 #if DEBUG_TRACE
diff --git a/dbutil.h b/dbutil.h
index 64af170..bfc1f1f 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -63,6 +63,7 @@ char * stripcontrol(const char * text);
 int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
 		int *writefd, int *readfd, int *errfd, pid_t *pid);
 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
+void run_command(const char* argv0, char** args, unsigned int maxfd);
 #if ENABLE_CONNECT_UNIX
 int connect_unix(const char* addr);
 #endif
diff --git a/runopts.h b/runopts.h
index 1675836..11c3ef2 100644
--- a/runopts.h
+++ b/runopts.h
@@ -188,7 +188,12 @@ typedef struct cli_runopts {
 	unsigned int netcat_port;
 #endif
 #if DROPBEAR_CLI_PROXYCMD
+	/* A proxy command to run via the user's shell */
 	char *proxycmd;
+#endif
+#if DROPBEAR_CLI_MULTIHOP
+	/* Similar to proxycmd, but is arguments for execve(), not shell */
+	char **proxyexec;
 #endif
 	char *bind_address;
 	char *bind_port;