summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/recipes-core/dropbear/dropbear/0001-Avoid-unused-variable-with-DROPBEAR_CLI_PUBKEY_AUTH-.patch27
-rw-r--r--meta/recipes-core/dropbear/dropbear/0001-Handle-arbitrary-length-paths-and-commands-in-multih.patch63
-rw-r--r--meta/recipes-core/dropbear/dropbear/0001-add-o-BatchMode-and-also-forward-this-when-multihop-.patch81
-rw-r--r--meta/recipes-core/dropbear/dropbear/0001-cli-runopts.c-add-missing-DROPBEAR_CLI_PUBKEY_AUTH.patch29
-rw-r--r--meta/recipes-core/dropbear/dropbear/CVE-2025-47203.patch367
-rw-r--r--meta/recipes-core/dropbear/dropbear_2022.83.bb5
6 files changed, 572 insertions, 0 deletions
diff --git a/meta/recipes-core/dropbear/dropbear/0001-Avoid-unused-variable-with-DROPBEAR_CLI_PUBKEY_AUTH-.patch b/meta/recipes-core/dropbear/dropbear/0001-Avoid-unused-variable-with-DROPBEAR_CLI_PUBKEY_AUTH-.patch
new file mode 100644
index 0000000000..fbe200151e
--- /dev/null
+++ b/meta/recipes-core/dropbear/dropbear/0001-Avoid-unused-variable-with-DROPBEAR_CLI_PUBKEY_AUTH-.patch
@@ -0,0 +1,27 @@
1From d59436a4d56de58b856142a5d489a4a8fc7382ed Mon Sep 17 00:00:00 2001
2From: Matt Johnston <matt@ucc.asn.au>
3Date: Mon, 8 Apr 2024 22:01:21 +0800
4Subject: [PATCH] Avoid unused variable with DROPBEAR_CLI_PUBKEY_AUTH 0
5
6Fixes PR #291
7
8Upstream-Status: Backport [https://github.com/mkj/dropbear/commit/d59436a4d56de58b856142a5d489a4a8fc7382ed]
9Signed-off-by: Peter Marko <peter.marko@siemens.com>
10---
11 cli-runopts.c | 2 ++
12 1 file changed, 2 insertions(+)
13
14diff --git a/cli-runopts.c b/cli-runopts.c
15index b853a13..6668aee 100644
16--- a/cli-runopts.c
17+++ b/cli-runopts.c
18@@ -533,7 +533,9 @@ static void loadidentityfile(const char* filename, int warnfail) {
19 static char* multihop_passthrough_args(void) {
20 char *args = NULL;
21 unsigned int len, total;
22+#if DROPBEAR_CLI_PUBKEY_AUTH
23 m_list_elem *iter;
24+#endif
25 /* Sufficient space for non-string args */
26 len = 100;
27
diff --git a/meta/recipes-core/dropbear/dropbear/0001-Handle-arbitrary-length-paths-and-commands-in-multih.patch b/meta/recipes-core/dropbear/dropbear/0001-Handle-arbitrary-length-paths-and-commands-in-multih.patch
new file mode 100644
index 0000000000..062f215398
--- /dev/null
+++ b/meta/recipes-core/dropbear/dropbear/0001-Handle-arbitrary-length-paths-and-commands-in-multih.patch
@@ -0,0 +1,63 @@
1From 697b1f86c0b2b0caf12e9e32bab29161093ab5d4 Mon Sep 17 00:00:00 2001
2From: Matt Johnston <matt@ucc.asn.au>
3Date: Mon, 1 Apr 2024 11:50:26 +0800
4Subject: [PATCH] Handle arbitrary length paths and commands in
5 multihop_passthrough_args()
6
7Upstream-Status: Backport [https://github.com/mkj/dropbear/commit/697b1f86c0b2b0caf12e9e32bab29161093ab5d4]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 cli-runopts.c | 30 +++++++++++++++++++++---------
11 1 file changed, 21 insertions(+), 9 deletions(-)
12
13diff --git a/cli-runopts.c b/cli-runopts.c
14index 37ea61d..219fc53 100644
15--- a/cli-runopts.c
16+++ b/cli-runopts.c
17@@ -528,15 +528,29 @@ static void loadidentityfile(const char* filename, int warnfail) {
18
19 #if DROPBEAR_CLI_MULTIHOP
20
21-static char*
22-multihop_passthrough_args() {
23- char *ret, args[256];
24+/* Fill out -i, -y, -W options that make sense for all
25+ * the intermediate processes */
26+static char* multihop_passthrough_args(void) {
27+ char *args = NULL;
28 unsigned int len, total;
29 m_list_elem *iter;
30- /* Fill out -i, -y, -W options that make sense for all
31- * the intermediate processes */
32+ /* Sufficient space for non-string args */
33+ len = 100;
34+
35+ /* String arguments have arbitrary length, so determine space required */
36+ if (cli_opts.proxycmd) {
37+ len += strlen(cli_opts.proxycmd);
38+ }
39+ for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
40+ {
41+ sign_key * key = (sign_key*)iter->item;
42+ len += 4 + strlen(key->filename);
43+ }
44+
45+ args = m_malloc(len);
46 total = 0;
47- len = 255;
48+
49+ /* Create new argument string */
50
51 if (cli_opts.quiet) {
52 total += m_snprintf(args+total, len-total, "-q ");
53@@ -564,9 +578,7 @@ multihop_passthrough_args() {
54 }
55 #endif /* DROPBEAR_CLI_PUBKEY_AUTH */
56
57- ret = m_malloc(total + 1);
58- strcpy(ret,args);
59- return ret;
60+ return args;
61 }
62
63 /* Sets up 'onion-forwarding' connections. This will spawn
diff --git a/meta/recipes-core/dropbear/dropbear/0001-add-o-BatchMode-and-also-forward-this-when-multihop-.patch b/meta/recipes-core/dropbear/dropbear/0001-add-o-BatchMode-and-also-forward-this-when-multihop-.patch
new file mode 100644
index 0000000000..c15da6e099
--- /dev/null
+++ b/meta/recipes-core/dropbear/dropbear/0001-add-o-BatchMode-and-also-forward-this-when-multihop-.patch
@@ -0,0 +1,81 @@
1From 2f1177e55f33afd676e08c9449ab7ab517fc3b30 Mon Sep 17 00:00:00 2001
2From: HansH111 <hans@atbas.org>
3Date: Sat, 24 Feb 2024 08:29:30 +0000
4Subject: [PATCH] add -o BatchMode and also forward this when multihop
5 destination is used
6
7Upstream-Status: Backport [https://github.com/mkj/dropbear/commit/2f1177e55f33afd676e08c9449ab7ab517fc3b30]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 cli-runopts.c | 33 +++++++++++----------------------
11 1 file changed, 11 insertions(+), 22 deletions(-)
12
13diff --git a/cli-runopts.c b/cli-runopts.c
14index 38a73f7..37ea61d 100644
15--- a/cli-runopts.c
16+++ b/cli-runopts.c
17@@ -530,53 +530,42 @@ static void loadidentityfile(const char* filename, int warnfail) {
18
19 static char*
20 multihop_passthrough_args() {
21- char *ret;
22+ char *ret, args[256];
23 unsigned int len, total;
24 m_list_elem *iter;
25 /* Fill out -i, -y, -W options that make sense for all
26- * the intermediate processes */
27- len = 30; /* space for "-q -y -y -W <size>\0" */
28-#if DROPBEAR_CLI_PUBKEY_AUTH
29- for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
30- {
31- sign_key * key = (sign_key*)iter->item;
32- len += 3 + strlen(key->filename);
33- }
34-#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
35- if (cli_opts.proxycmd) {
36- /* "-J 'cmd'" */
37- len += 6 + strlen(cli_opts.proxycmd);
38- }
39-
40- ret = m_malloc(len);
41+ * the intermediate processes */
42 total = 0;
43+ len = 255;
44
45 if (cli_opts.quiet) {
46- total += m_snprintf(ret+total, len-total, "-q ");
47+ total += m_snprintf(args+total, len-total, "-q ");
48 }
49
50 if (cli_opts.no_hostkey_check) {
51- total += m_snprintf(ret+total, len-total, "-y -y ");
52+ total += m_snprintf(args+total, len-total, "-y -y ");
53 } else if (cli_opts.always_accept_key) {
54- total += m_snprintf(ret+total, len-total, "-y ");
55+ total += m_snprintf(args+total, len-total, "-y ");
56 }
57
58 if (cli_opts.proxycmd) {
59- total += m_snprintf(ret+total, len-total, "-J '%s' ", cli_opts.proxycmd);
60+ total += m_snprintf(args+total, len-total, "-J '%s' ", cli_opts.proxycmd);
61 }
62
63 if (opts.recv_window != DEFAULT_RECV_WINDOW) {
64- total += m_snprintf(ret+total, len-total, "-W %u ", opts.recv_window);
65+ total += m_snprintf(args+total, len-total, "-W %u ", opts.recv_window);
66 }
67
68 #if DROPBEAR_CLI_PUBKEY_AUTH
69 for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
70 {
71 sign_key * key = (sign_key*)iter->item;
72- total += m_snprintf(ret+total, len-total, "-i %s ", key->filename);
73+ total += m_snprintf(args+total, len-total, "-i %s ", key->filename);
74 }
75 #endif /* DROPBEAR_CLI_PUBKEY_AUTH */
76
77+ ret = m_malloc(total + 1);
78+ strcpy(ret,args);
79 return ret;
80 }
81
diff --git a/meta/recipes-core/dropbear/dropbear/0001-cli-runopts.c-add-missing-DROPBEAR_CLI_PUBKEY_AUTH.patch b/meta/recipes-core/dropbear/dropbear/0001-cli-runopts.c-add-missing-DROPBEAR_CLI_PUBKEY_AUTH.patch
new file mode 100644
index 0000000000..da7de00389
--- /dev/null
+++ b/meta/recipes-core/dropbear/dropbear/0001-cli-runopts.c-add-missing-DROPBEAR_CLI_PUBKEY_AUTH.patch
@@ -0,0 +1,29 @@
1From dd03da772bfad6174425066ff9752b60e25ed183 Mon Sep 17 00:00:00 2001
2From: Sergey Ponomarev <stokito@gmail.com>
3Date: Sun, 7 Apr 2024 21:16:50 +0300
4Subject: [PATCH] cli-runopts.c add missing DROPBEAR_CLI_PUBKEY_AUTH
5
6Upstream-Status: Backport [https://github.com/mkj/dropbear/commit/dd03da772bfad6174425066ff9752b60e25ed183]
7Signed-off-by: Peter Marko <peter.marko@siemens.com>
8---
9 cli-runopts.c | 2 ++
10 1 file changed, 2 insertions(+)
11
12diff --git a/cli-runopts.c b/cli-runopts.c
13index 219fc53..b853a13 100644
14--- a/cli-runopts.c
15+++ b/cli-runopts.c
16@@ -541,11 +541,13 @@ static char* multihop_passthrough_args(void) {
17 if (cli_opts.proxycmd) {
18 len += strlen(cli_opts.proxycmd);
19 }
20+#if DROPBEAR_CLI_PUBKEY_AUTH
21 for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
22 {
23 sign_key * key = (sign_key*)iter->item;
24 len += 4 + strlen(key->filename);
25 }
26+#endif
27
28 args = m_malloc(len);
29 total = 0;
diff --git a/meta/recipes-core/dropbear/dropbear/CVE-2025-47203.patch b/meta/recipes-core/dropbear/dropbear/CVE-2025-47203.patch
new file mode 100644
index 0000000000..513fbafce0
--- /dev/null
+++ b/meta/recipes-core/dropbear/dropbear/CVE-2025-47203.patch
@@ -0,0 +1,367 @@
1From e5a0ef27c227f7ae69d9a9fec98a056494409b9b Mon Sep 17 00:00:00 2001
2From: Matt Johnston <matt@ucc.asn.au>
3Date: Mon, 5 May 2025 23:14:19 +0800
4Subject: [PATCH] Execute multihop commands directly, no shell
5
6This avoids problems with shell escaping if arguments contain special
7characters.
8
9CVE: CVE-2025-47203
10Upstream-Status: Backport [https://github.com/mkj/dropbear/commit/e5a0ef27c227f7ae69d9a9fec98a056494409b9b]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 cli-main.c | 59 +++++++++++++++++++----------
14 cli-runopts.c | 100 +++++++++++++++++++++++++++++---------------------
15 dbutil.c | 9 ++++-
16 dbutil.h | 1 +
17 runopts.h | 5 +++
18 5 files changed, 112 insertions(+), 62 deletions(-)
19
20diff --git a/cli-main.c b/cli-main.c
21index 065fd76..2fafa88 100644
22--- a/cli-main.c
23+++ b/cli-main.c
24@@ -77,9 +77,8 @@ int main(int argc, char ** argv) {
25 }
26
27 #if DROPBEAR_CLI_PROXYCMD
28- if (cli_opts.proxycmd) {
29+ if (cli_opts.proxycmd || cli_opts.proxyexec) {
30 cli_proxy_cmd(&sock_in, &sock_out, &proxy_cmd_pid);
31- m_free(cli_opts.proxycmd);
32 if (signal(SIGINT, kill_proxy_sighandler) == SIG_ERR ||
33 signal(SIGTERM, kill_proxy_sighandler) == SIG_ERR ||
34 signal(SIGHUP, kill_proxy_sighandler) == SIG_ERR) {
35@@ -101,7 +100,8 @@ int main(int argc, char ** argv) {
36 }
37 #endif /* DBMULTI stuff */
38
39-static void exec_proxy_cmd(const void *user_data_cmd) {
40+#if DROPBEAR_CLI_PROXYCMD
41+static void shell_proxy_cmd(const void *user_data_cmd) {
42 const char *cmd = user_data_cmd;
43 char *usershell;
44
45@@ -110,41 +110,62 @@ static void exec_proxy_cmd(const void *user_data_cmd) {
46 dropbear_exit("Failed to run '%s'\n", cmd);
47 }
48
49-#if DROPBEAR_CLI_PROXYCMD
50+static void exec_proxy_cmd(const void *unused) {
51+ (void)unused;
52+ run_command(cli_opts.proxyexec[0], cli_opts.proxyexec, ses.maxfd);
53+ dropbear_exit("Failed to run '%s'\n", cli_opts.proxyexec[0]);
54+}
55+
56 static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out) {
57- char * ex_cmd = NULL;
58- size_t ex_cmdlen;
59+ char * cmd_arg = NULL;
60+ void (*exec_fn)(const void *user_data) = NULL;
61 int ret;
62
63+ /* exactly one of cli_opts.proxycmd or cli_opts.proxyexec should be set */
64+
65 /* File descriptor "-j &3" */
66- if (*cli_opts.proxycmd == '&') {
67+ if (cli_opts.proxycmd && *cli_opts.proxycmd == '&') {
68 char *p = cli_opts.proxycmd + 1;
69 int sock = strtoul(p, &p, 10);
70 /* must be a single number, and not stdin/stdout/stderr */
71 if (sock > 2 && sock < 1024 && *p == '\0') {
72 *sock_in = sock;
73 *sock_out = sock;
74- return;
75+ goto cleanup;
76 }
77 }
78
79- /* Normal proxycommand */
80+ if (cli_opts.proxycmd) {
81+ /* Normal proxycommand */
82+ size_t shell_cmdlen;
83+ /* So that spawn_command knows which shell to run */
84+ fill_passwd(cli_opts.own_user);
85
86- /* So that spawn_command knows which shell to run */
87- fill_passwd(cli_opts.own_user);
88+ shell_cmdlen = strlen(cli_opts.proxycmd) + 6; /* "exec " + command + '\0' */
89+ cmd_arg = m_malloc(shell_cmdlen);
90+ snprintf(cmd_arg, shell_cmdlen, "exec %s", cli_opts.proxycmd);
91+ exec_fn = shell_proxy_cmd;
92+ } else {
93+ /* No shell */
94+ exec_fn = exec_proxy_cmd;
95+ }
96
97- ex_cmdlen = strlen(cli_opts.proxycmd) + 6; /* "exec " + command + '\0' */
98- ex_cmd = m_malloc(ex_cmdlen);
99- snprintf(ex_cmd, ex_cmdlen, "exec %s", cli_opts.proxycmd);
100-
101- ret = spawn_command(exec_proxy_cmd, ex_cmd,
102- sock_out, sock_in, NULL, pid_out);
103- DEBUG1(("cmd: %s pid=%d", ex_cmd,*pid_out))
104- m_free(ex_cmd);
105+ ret = spawn_command(exec_fn, cmd_arg, sock_out, sock_in, NULL, pid_out);
106 if (ret == DROPBEAR_FAILURE) {
107 dropbear_exit("Failed running proxy command");
108 *sock_in = *sock_out = -1;
109 }
110+
111+cleanup:
112+ m_free(cli_opts.proxycmd);
113+ m_free(cmd_arg);
114+ if (cli_opts.proxyexec) {
115+ char **a = NULL;
116+ for (a = cli_opts.proxyexec; *a; a++) {
117+ m_free_direct(*a);
118+ }
119+ m_free(cli_opts.proxyexec);
120+ }
121 }
122
123 static void kill_proxy_sighandler(int UNUSED(signo)) {
124diff --git a/cli-runopts.c b/cli-runopts.c
125index 6668aee..b9add84 100644
126--- a/cli-runopts.c
127+++ b/cli-runopts.c
128@@ -530,58 +530,81 @@ static void loadidentityfile(const char* filename, int warnfail) {
129
130 /* Fill out -i, -y, -W options that make sense for all
131 * the intermediate processes */
132-static char* multihop_passthrough_args(void) {
133- char *args = NULL;
134- unsigned int len, total;
135+static char** multihop_args(const char* argv0, const char* prior_hops) {
136+ /* null terminated array */
137+ char **args = NULL;
138+ size_t max_args = 14, pos = 0, len;
139 #if DROPBEAR_CLI_PUBKEY_AUTH
140 m_list_elem *iter;
141 #endif
142- /* Sufficient space for non-string args */
143- len = 100;
144
145- /* String arguments have arbitrary length, so determine space required */
146- if (cli_opts.proxycmd) {
147- len += strlen(cli_opts.proxycmd);
148- }
149 #if DROPBEAR_CLI_PUBKEY_AUTH
150 for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
151 {
152- sign_key * key = (sign_key*)iter->item;
153- len += 4 + strlen(key->filename);
154+ /* "-i file" for each */
155+ max_args += 2;
156 }
157 #endif
158
159- args = m_malloc(len);
160- total = 0;
161+ args = m_malloc(sizeof(char*) * max_args);
162+ pos = 0;
163
164- /* Create new argument string */
165+ args[pos] = m_strdup(argv0);
166+ pos++;
167
168 if (cli_opts.quiet) {
169- total += m_snprintf(args+total, len-total, "-q ");
170+ args[pos] = m_strdup("-q");
171+ pos++;
172 }
173
174 if (cli_opts.no_hostkey_check) {
175- total += m_snprintf(args+total, len-total, "-y -y ");
176+ args[pos] = m_strdup("-y");
177+ pos++;
178+ args[pos] = m_strdup("-y");
179+ pos++;
180 } else if (cli_opts.always_accept_key) {
181- total += m_snprintf(args+total, len-total, "-y ");
182+ args[pos] = m_strdup("-y");
183+ pos++;
184 }
185
186 if (cli_opts.proxycmd) {
187- total += m_snprintf(args+total, len-total, "-J '%s' ", cli_opts.proxycmd);
188+ args[pos] = m_strdup("-J");
189+ pos++;
190+ args[pos] = m_strdup(cli_opts.proxycmd);
191+ pos++;
192 }
193
194 if (opts.recv_window != DEFAULT_RECV_WINDOW) {
195- total += m_snprintf(args+total, len-total, "-W %u ", opts.recv_window);
196+ args[pos] = m_strdup("-W");
197+ pos++;
198+ args[pos] = m_malloc(11);
199+ m_snprintf(args[pos], 11, "%u", opts.recv_window);
200+ pos++;
201 }
202
203 #if DROPBEAR_CLI_PUBKEY_AUTH
204 for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
205 {
206 sign_key * key = (sign_key*)iter->item;
207- total += m_snprintf(args+total, len-total, "-i %s ", key->filename);
208+ args[pos] = m_strdup("-i");
209+ pos++;
210+ args[pos] = m_strdup(key->filename);
211+ pos++;
212 }
213 #endif /* DROPBEAR_CLI_PUBKEY_AUTH */
214
215+ /* last hop */
216+ args[pos] = m_strdup("-B");
217+ pos++;
218+ len = strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) + 2;
219+ args[pos] = m_malloc(len);
220+ snprintf(args[pos], len, "%s:%s", cli_opts.remotehost, cli_opts.remoteport);
221+ pos++;
222+
223+ /* hostnames of prior hops */
224+ args[pos] = m_strdup(prior_hops);
225+ pos++;
226+
227 return args;
228 }
229
230@@ -596,7 +619,7 @@ static char* multihop_passthrough_args(void) {
231 * etc for as many hosts as we want.
232 *
233 * Note that "-J" arguments aren't actually used, instead
234- * below sets cli_opts.proxycmd directly.
235+ * below sets cli_opts.proxyexec directly.
236 *
237 * Ports for hosts can be specified as host/port.
238 */
239@@ -604,7 +627,7 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
240 char *userhostarg = NULL;
241 char *hostbuf = NULL;
242 char *last_hop = NULL;
243- char *remainder = NULL;
244+ char *prior_hops = NULL;
245
246 /* both scp and rsync parse a user@host argument
247 * and turn it into "-l user host". This breaks
248@@ -622,6 +645,8 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
249 }
250 userhostarg = hostbuf;
251
252+ /* Split off any last hostname and use that as remotehost/remoteport.
253+ * That is used for authorized_keys checking etc */
254 last_hop = strrchr(userhostarg, ',');
255 if (last_hop) {
256 if (last_hop == userhostarg) {
257@@ -629,35 +654,28 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
258 }
259 *last_hop = '\0';
260 last_hop++;
261- remainder = userhostarg;
262+ prior_hops = userhostarg;
263 userhostarg = last_hop;
264 }
265
266+ /* Update cli_opts.remotehost and cli_opts.remoteport */
267 parse_hostname(userhostarg);
268
269- if (last_hop) {
270- /* Set up the proxycmd */
271- unsigned int cmd_len = 0;
272- char *passthrough_args = multihop_passthrough_args();
273- if (cli_opts.remoteport == NULL) {
274- cli_opts.remoteport = "22";
275+ /* Construct any multihop proxy command. Use proxyexec to
276+ * avoid worrying about shell escaping. */
277+ if (prior_hops) {
278+ cli_opts.proxyexec = multihop_args(argv0, prior_hops);
279+ /* Any -J argument has been copied to proxyexec */
280+ if (cli_opts.proxycmd) {
281+ m_free(cli_opts.proxycmd);
282 }
283- cmd_len = strlen(argv0) + strlen(remainder)
284- + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
285- + strlen(passthrough_args)
286- + 30;
287- /* replace proxycmd. old -J arguments have been copied
288- to passthrough_args */
289- cli_opts.proxycmd = m_realloc(cli_opts.proxycmd, cmd_len);
290- m_snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
291- argv0, cli_opts.remotehost, cli_opts.remoteport,
292- passthrough_args, remainder);
293+
294 #ifndef DISABLE_ZLIB
295- /* The stream will be incompressible since it's encrypted. */
296+ /* This outer stream will be incompressible since it's encrypted. */
297 opts.compress_mode = DROPBEAR_COMPRESS_OFF;
298 #endif
299- m_free(passthrough_args);
300 }
301+
302 m_free(hostbuf);
303 }
304 #endif /* !DROPBEAR_CLI_MULTIHOP */
305diff --git a/dbutil.c b/dbutil.c
306index bd66454..910fa27 100644
307--- a/dbutil.c
308+++ b/dbutil.c
309@@ -371,7 +371,6 @@ int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
310 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
311 char * argv[4];
312 char * baseshell = NULL;
313- unsigned int i;
314
315 baseshell = basename(usershell);
316
317@@ -393,6 +392,12 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
318 argv[1] = NULL;
319 }
320
321+ run_command(usershell, argv, maxfd);
322+}
323+
324+void run_command(const char* argv0, char** args, unsigned int maxfd) {
325+ unsigned int i;
326+
327 /* Re-enable SIGPIPE for the executed process */
328 if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
329 dropbear_exit("signal() error");
330@@ -404,7 +409,7 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
331 m_close(i);
332 }
333
334- execv(usershell, argv);
335+ execv(argv0, args);
336 }
337
338 #if DEBUG_TRACE
339diff --git a/dbutil.h b/dbutil.h
340index 64af170..bfc1f1f 100644
341--- a/dbutil.h
342+++ b/dbutil.h
343@@ -63,6 +63,7 @@ char * stripcontrol(const char * text);
344 int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
345 int *writefd, int *readfd, int *errfd, pid_t *pid);
346 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
347+void run_command(const char* argv0, char** args, unsigned int maxfd);
348 #if ENABLE_CONNECT_UNIX
349 int connect_unix(const char* addr);
350 #endif
351diff --git a/runopts.h b/runopts.h
352index 1675836..11c3ef2 100644
353--- a/runopts.h
354+++ b/runopts.h
355@@ -188,7 +188,12 @@ typedef struct cli_runopts {
356 unsigned int netcat_port;
357 #endif
358 #if DROPBEAR_CLI_PROXYCMD
359+ /* A proxy command to run via the user's shell */
360 char *proxycmd;
361+#endif
362+#if DROPBEAR_CLI_MULTIHOP
363+ /* Similar to proxycmd, but is arguments for execve(), not shell */
364+ char **proxyexec;
365 #endif
366 char *bind_address;
367 char *bind_port;
diff --git a/meta/recipes-core/dropbear/dropbear_2022.83.bb b/meta/recipes-core/dropbear/dropbear_2022.83.bb
index 772e08eaed..2ed8d2c2a1 100644
--- a/meta/recipes-core/dropbear/dropbear_2022.83.bb
+++ b/meta/recipes-core/dropbear/dropbear_2022.83.bb
@@ -24,6 +24,11 @@ SRC_URI = "http://matt.ucc.asn.au/dropbear/releases/dropbear-${PV}.tar.bz2 \
24 ${@bb.utils.contains('PACKAGECONFIG', 'disable-weak-ciphers', 'file://dropbear-disable-weak-ciphers.patch', '', d)} \ 24 ${@bb.utils.contains('PACKAGECONFIG', 'disable-weak-ciphers', 'file://dropbear-disable-weak-ciphers.patch', '', d)} \
25 file://CVE-2023-36328.patch \ 25 file://CVE-2023-36328.patch \
26 file://CVE-2023-48795.patch \ 26 file://CVE-2023-48795.patch \
27 file://0001-add-o-BatchMode-and-also-forward-this-when-multihop-.patch \
28 file://0001-Handle-arbitrary-length-paths-and-commands-in-multih.patch \
29 file://0001-cli-runopts.c-add-missing-DROPBEAR_CLI_PUBKEY_AUTH.patch \
30 file://0001-Avoid-unused-variable-with-DROPBEAR_CLI_PUBKEY_AUTH-.patch \
31 file://CVE-2025-47203.patch \
27 " 32 "
28 33
29SRC_URI[sha256sum] = "bc5a121ffbc94b5171ad5ebe01be42746d50aa797c9549a4639894a16749443b" 34SRC_URI[sha256sum] = "bc5a121ffbc94b5171ad5ebe01be42746d50aa797c9549a4639894a16749443b"