xref: /dragonfly/crypto/openssh/mux.c (revision ee116499)
1*ee116499SAntonio Huete Jimenez /* $OpenBSD: mux.c,v 1.94 2022/06/03 04:30:47 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * Permission to use, copy, modify, and distribute this software for any
618de8d7fSPeter Avalos  * purpose with or without fee is hereby granted, provided that the above
718de8d7fSPeter Avalos  * copyright notice and this permission notice appear in all copies.
818de8d7fSPeter Avalos  *
918de8d7fSPeter Avalos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1018de8d7fSPeter Avalos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1118de8d7fSPeter Avalos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1218de8d7fSPeter Avalos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1318de8d7fSPeter Avalos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1418de8d7fSPeter Avalos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1518de8d7fSPeter Avalos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1618de8d7fSPeter Avalos  */
1718de8d7fSPeter Avalos 
1818de8d7fSPeter Avalos /* ssh session multiplexing support */
1918de8d7fSPeter Avalos 
20856ea928SPeter Avalos #include "includes.h"
21856ea928SPeter Avalos 
2218de8d7fSPeter Avalos #include <sys/types.h>
2318de8d7fSPeter Avalos #include <sys/stat.h>
2418de8d7fSPeter Avalos #include <sys/socket.h>
2518de8d7fSPeter Avalos #include <sys/un.h>
2618de8d7fSPeter Avalos 
2718de8d7fSPeter Avalos #include <errno.h>
2818de8d7fSPeter Avalos #include <fcntl.h>
2918de8d7fSPeter Avalos #include <signal.h>
3018de8d7fSPeter Avalos #include <stdarg.h>
3118de8d7fSPeter Avalos #include <stddef.h>
3218de8d7fSPeter Avalos #include <stdlib.h>
3318de8d7fSPeter Avalos #include <stdio.h>
3418de8d7fSPeter Avalos #include <string.h>
3518de8d7fSPeter Avalos #include <unistd.h>
3618de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
3718de8d7fSPeter Avalos #include <paths.h>
3818de8d7fSPeter Avalos #endif
3918de8d7fSPeter Avalos 
40856ea928SPeter Avalos #ifdef HAVE_POLL_H
41856ea928SPeter Avalos #include <poll.h>
42856ea928SPeter Avalos #else
43856ea928SPeter Avalos # ifdef HAVE_SYS_POLL_H
44856ea928SPeter Avalos #  include <sys/poll.h>
45856ea928SPeter Avalos # endif
46856ea928SPeter Avalos #endif
47856ea928SPeter Avalos 
4818de8d7fSPeter Avalos #ifdef HAVE_UTIL_H
4918de8d7fSPeter Avalos # include <util.h>
5018de8d7fSPeter Avalos #endif
5118de8d7fSPeter Avalos 
5218de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
5318de8d7fSPeter Avalos #include "xmalloc.h"
5418de8d7fSPeter Avalos #include "log.h"
5518de8d7fSPeter Avalos #include "ssh.h"
56856ea928SPeter Avalos #include "ssh2.h"
5718de8d7fSPeter Avalos #include "pathnames.h"
5818de8d7fSPeter Avalos #include "misc.h"
5918de8d7fSPeter Avalos #include "match.h"
60664f4763Szrj #include "sshbuf.h"
6118de8d7fSPeter Avalos #include "channels.h"
6218de8d7fSPeter Avalos #include "msg.h"
6318de8d7fSPeter Avalos #include "packet.h"
6418de8d7fSPeter Avalos #include "monitor_fdpass.h"
6518de8d7fSPeter Avalos #include "sshpty.h"
66664f4763Szrj #include "sshkey.h"
6718de8d7fSPeter Avalos #include "readconf.h"
6818de8d7fSPeter Avalos #include "clientloop.h"
69ce74bacaSMatthew Dillon #include "ssherr.h"
7018de8d7fSPeter Avalos 
7118de8d7fSPeter Avalos /* from ssh.c */
7218de8d7fSPeter Avalos extern int tty_flag;
7318de8d7fSPeter Avalos extern Options options;
7418de8d7fSPeter Avalos extern char *host;
75664f4763Szrj extern struct sshbuf *command;
76856ea928SPeter Avalos extern volatile sig_atomic_t quit_pending;
7718de8d7fSPeter Avalos 
7818de8d7fSPeter Avalos /* Context for session open confirmation callback */
7918de8d7fSPeter Avalos struct mux_session_confirm_ctx {
80856ea928SPeter Avalos 	u_int want_tty;
81856ea928SPeter Avalos 	u_int want_subsys;
82856ea928SPeter Avalos 	u_int want_x_fwd;
83856ea928SPeter Avalos 	u_int want_agent_fwd;
84664f4763Szrj 	struct sshbuf *cmd;
8518de8d7fSPeter Avalos 	char *term;
8618de8d7fSPeter Avalos 	struct termios tio;
8718de8d7fSPeter Avalos 	char **env;
88856ea928SPeter Avalos 	u_int rid;
89856ea928SPeter Avalos };
90856ea928SPeter Avalos 
9136e94dc5SPeter Avalos /* Context for stdio fwd open confirmation callback */
9236e94dc5SPeter Avalos struct mux_stdio_confirm_ctx {
9336e94dc5SPeter Avalos 	u_int rid;
9436e94dc5SPeter Avalos };
9536e94dc5SPeter Avalos 
96856ea928SPeter Avalos /* Context for global channel callback */
97856ea928SPeter Avalos struct mux_channel_confirm_ctx {
98856ea928SPeter Avalos 	u_int cid;	/* channel id */
99856ea928SPeter Avalos 	u_int rid;	/* request id */
100856ea928SPeter Avalos 	int fid;	/* forward id */
10118de8d7fSPeter Avalos };
10218de8d7fSPeter Avalos 
10318de8d7fSPeter Avalos /* fd to control socket */
10418de8d7fSPeter Avalos int muxserver_sock = -1;
10518de8d7fSPeter Avalos 
106856ea928SPeter Avalos /* client request id */
107856ea928SPeter Avalos u_int muxclient_request_id = 0;
108856ea928SPeter Avalos 
10918de8d7fSPeter Avalos /* Multiplexing control command */
11018de8d7fSPeter Avalos u_int muxclient_command = 0;
11118de8d7fSPeter Avalos 
11218de8d7fSPeter Avalos /* Set when signalled. */
11318de8d7fSPeter Avalos static volatile sig_atomic_t muxclient_terminate = 0;
11418de8d7fSPeter Avalos 
11518de8d7fSPeter Avalos /* PID of multiplex server */
11618de8d7fSPeter Avalos static u_int muxserver_pid = 0;
11718de8d7fSPeter Avalos 
118856ea928SPeter Avalos static Channel *mux_listener_channel = NULL;
11918de8d7fSPeter Avalos 
120856ea928SPeter Avalos struct mux_master_state {
121856ea928SPeter Avalos 	int hello_rcvd;
122856ea928SPeter Avalos };
123856ea928SPeter Avalos 
124856ea928SPeter Avalos /* mux protocol messages */
125856ea928SPeter Avalos #define MUX_MSG_HELLO		0x00000001
126856ea928SPeter Avalos #define MUX_C_NEW_SESSION	0x10000002
127856ea928SPeter Avalos #define MUX_C_ALIVE_CHECK	0x10000004
128856ea928SPeter Avalos #define MUX_C_TERMINATE		0x10000005
129856ea928SPeter Avalos #define MUX_C_OPEN_FWD		0x10000006
130856ea928SPeter Avalos #define MUX_C_CLOSE_FWD		0x10000007
131856ea928SPeter Avalos #define MUX_C_NEW_STDIO_FWD	0x10000008
1321c188a7fSPeter Avalos #define MUX_C_STOP_LISTENING	0x10000009
133ce74bacaSMatthew Dillon #define MUX_C_PROXY		0x1000000f
134856ea928SPeter Avalos #define MUX_S_OK		0x80000001
135856ea928SPeter Avalos #define MUX_S_PERMISSION_DENIED	0x80000002
136856ea928SPeter Avalos #define MUX_S_FAILURE		0x80000003
137856ea928SPeter Avalos #define MUX_S_EXIT_MESSAGE	0x80000004
138856ea928SPeter Avalos #define MUX_S_ALIVE		0x80000005
139856ea928SPeter Avalos #define MUX_S_SESSION_OPENED	0x80000006
140856ea928SPeter Avalos #define MUX_S_REMOTE_PORT	0x80000007
1411c188a7fSPeter Avalos #define MUX_S_TTY_ALLOC_FAIL	0x80000008
142ce74bacaSMatthew Dillon #define MUX_S_PROXY		0x8000000f
143856ea928SPeter Avalos 
144856ea928SPeter Avalos /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */
145856ea928SPeter Avalos #define MUX_FWD_LOCAL   1
146856ea928SPeter Avalos #define MUX_FWD_REMOTE  2
147856ea928SPeter Avalos #define MUX_FWD_DYNAMIC 3
148856ea928SPeter Avalos 
149ce74bacaSMatthew Dillon static void mux_session_confirm(struct ssh *, int, int, void *);
150ce74bacaSMatthew Dillon static void mux_stdio_confirm(struct ssh *, int, int, void *);
151856ea928SPeter Avalos 
152664f4763Szrj static int mux_master_process_hello(struct ssh *, u_int,
153ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
154664f4763Szrj static int mux_master_process_new_session(struct ssh *, u_int,
155ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
156664f4763Szrj static int mux_master_process_alive_check(struct ssh *, u_int,
157ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
158664f4763Szrj static int mux_master_process_terminate(struct ssh *, u_int,
159ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
160664f4763Szrj static int mux_master_process_open_fwd(struct ssh *, u_int,
161ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
162664f4763Szrj static int mux_master_process_close_fwd(struct ssh *, u_int,
163ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
164664f4763Szrj static int mux_master_process_stdio_fwd(struct ssh *, u_int,
165ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
166664f4763Szrj static int mux_master_process_stop_listening(struct ssh *, u_int,
167ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
168664f4763Szrj static int mux_master_process_proxy(struct ssh *, u_int,
169ce74bacaSMatthew Dillon 	    Channel *, struct sshbuf *, struct sshbuf *);
170856ea928SPeter Avalos 
171856ea928SPeter Avalos static const struct {
172856ea928SPeter Avalos 	u_int type;
173ce74bacaSMatthew Dillon 	int (*handler)(struct ssh *, u_int, Channel *,
174ce74bacaSMatthew Dillon 	    struct sshbuf *, struct sshbuf *);
175856ea928SPeter Avalos } mux_master_handlers[] = {
176664f4763Szrj 	{ MUX_MSG_HELLO, mux_master_process_hello },
177664f4763Szrj 	{ MUX_C_NEW_SESSION, mux_master_process_new_session },
178664f4763Szrj 	{ MUX_C_ALIVE_CHECK, mux_master_process_alive_check },
179664f4763Szrj 	{ MUX_C_TERMINATE, mux_master_process_terminate },
180664f4763Szrj 	{ MUX_C_OPEN_FWD, mux_master_process_open_fwd },
181664f4763Szrj 	{ MUX_C_CLOSE_FWD, mux_master_process_close_fwd },
182664f4763Szrj 	{ MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd },
183664f4763Szrj 	{ MUX_C_STOP_LISTENING, mux_master_process_stop_listening },
184664f4763Szrj 	{ MUX_C_PROXY, mux_master_process_proxy },
185856ea928SPeter Avalos 	{ 0, NULL }
186856ea928SPeter Avalos };
187856ea928SPeter Avalos 
18850a69bb5SSascha Wildner /* Cleanup callback fired on closure of mux client _session_ channel */
189856ea928SPeter Avalos /* ARGSUSED */
190856ea928SPeter Avalos static void
mux_master_session_cleanup_cb(struct ssh * ssh,int cid,void * unused)191ce74bacaSMatthew Dillon mux_master_session_cleanup_cb(struct ssh *ssh, int cid, void *unused)
192856ea928SPeter Avalos {
193ce74bacaSMatthew Dillon 	Channel *cc, *c = channel_by_id(ssh, cid);
194856ea928SPeter Avalos 
19550a69bb5SSascha Wildner 	debug3_f("entering for channel %d", cid);
196856ea928SPeter Avalos 	if (c == NULL)
19750a69bb5SSascha Wildner 		fatal_f("channel_by_id(%i) == NULL", cid);
198856ea928SPeter Avalos 	if (c->ctl_chan != -1) {
199ce74bacaSMatthew Dillon 		if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
20050a69bb5SSascha Wildner 			fatal_f("channel %d missing control channel %d",
20150a69bb5SSascha Wildner 			    c->self, c->ctl_chan);
202856ea928SPeter Avalos 		c->ctl_chan = -1;
203ce74bacaSMatthew Dillon 		cc->remote_id = 0;
204ce74bacaSMatthew Dillon 		cc->have_remote_id = 0;
205ce74bacaSMatthew Dillon 		chan_rcvd_oclose(ssh, cc);
206856ea928SPeter Avalos 	}
207ce74bacaSMatthew Dillon 	channel_cancel_cleanup(ssh, c->self);
208856ea928SPeter Avalos }
209856ea928SPeter Avalos 
21050a69bb5SSascha Wildner /* Cleanup callback fired on closure of mux client _control_ channel */
211856ea928SPeter Avalos /* ARGSUSED */
212856ea928SPeter Avalos static void
mux_master_control_cleanup_cb(struct ssh * ssh,int cid,void * unused)213ce74bacaSMatthew Dillon mux_master_control_cleanup_cb(struct ssh *ssh, int cid, void *unused)
214856ea928SPeter Avalos {
215ce74bacaSMatthew Dillon 	Channel *sc, *c = channel_by_id(ssh, cid);
216856ea928SPeter Avalos 
21750a69bb5SSascha Wildner 	debug3_f("entering for channel %d", cid);
218856ea928SPeter Avalos 	if (c == NULL)
21950a69bb5SSascha Wildner 		fatal_f("channel_by_id(%i) == NULL", cid);
220ce74bacaSMatthew Dillon 	if (c->have_remote_id) {
221ce74bacaSMatthew Dillon 		if ((sc = channel_by_id(ssh, c->remote_id)) == NULL)
22250a69bb5SSascha Wildner 			fatal_f("channel %d missing session channel %u",
22350a69bb5SSascha Wildner 			    c->self, c->remote_id);
224ce74bacaSMatthew Dillon 		c->remote_id = 0;
225ce74bacaSMatthew Dillon 		c->have_remote_id = 0;
226856ea928SPeter Avalos 		sc->ctl_chan = -1;
22736e94dc5SPeter Avalos 		if (sc->type != SSH_CHANNEL_OPEN &&
22836e94dc5SPeter Avalos 		    sc->type != SSH_CHANNEL_OPENING) {
22950a69bb5SSascha Wildner 			debug2_f("channel %d: not open", sc->self);
230ce74bacaSMatthew Dillon 			chan_mark_dead(ssh, sc);
231856ea928SPeter Avalos 		} else {
232856ea928SPeter Avalos 			if (sc->istate == CHAN_INPUT_OPEN)
233ce74bacaSMatthew Dillon 				chan_read_failed(ssh, sc);
234856ea928SPeter Avalos 			if (sc->ostate == CHAN_OUTPUT_OPEN)
235ce74bacaSMatthew Dillon 				chan_write_failed(ssh, sc);
236856ea928SPeter Avalos 		}
237856ea928SPeter Avalos 	}
238ce74bacaSMatthew Dillon 	channel_cancel_cleanup(ssh, c->self);
239856ea928SPeter Avalos }
240856ea928SPeter Avalos 
241856ea928SPeter Avalos /* Check mux client environment variables before passing them to mux master. */
242856ea928SPeter Avalos static int
env_permitted(const char * env)243*ee116499SAntonio Huete Jimenez env_permitted(const char *env)
244856ea928SPeter Avalos {
245*ee116499SAntonio Huete Jimenez 	u_int i;
246*ee116499SAntonio Huete Jimenez 	int ret;
247856ea928SPeter Avalos 	char name[1024], *cp;
248856ea928SPeter Avalos 
249856ea928SPeter Avalos 	if ((cp = strchr(env, '=')) == NULL || cp == env)
250856ea928SPeter Avalos 		return 0;
251856ea928SPeter Avalos 	ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env);
252856ea928SPeter Avalos 	if (ret <= 0 || (size_t)ret >= sizeof(name)) {
25350a69bb5SSascha Wildner 		error_f("name '%.100s...' too long", env);
254856ea928SPeter Avalos 		return 0;
255856ea928SPeter Avalos 	}
256856ea928SPeter Avalos 
257856ea928SPeter Avalos 	for (i = 0; i < options.num_send_env; i++)
258856ea928SPeter Avalos 		if (match_pattern(name, options.send_env[i]))
259856ea928SPeter Avalos 			return 1;
260856ea928SPeter Avalos 
261856ea928SPeter Avalos 	return 0;
262856ea928SPeter Avalos }
263856ea928SPeter Avalos 
264856ea928SPeter Avalos /* Mux master protocol message handlers */
265856ea928SPeter Avalos 
266856ea928SPeter Avalos static int
mux_master_process_hello(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)267664f4763Szrj mux_master_process_hello(struct ssh *ssh, u_int rid,
268664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
269856ea928SPeter Avalos {
270856ea928SPeter Avalos 	u_int ver;
271856ea928SPeter Avalos 	struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx;
272664f4763Szrj 	int r;
273856ea928SPeter Avalos 
274856ea928SPeter Avalos 	if (state == NULL)
27550a69bb5SSascha Wildner 		fatal_f("channel %d: c->mux_ctx == NULL", c->self);
276856ea928SPeter Avalos 	if (state->hello_rcvd) {
27750a69bb5SSascha Wildner 		error_f("HELLO received twice");
278856ea928SPeter Avalos 		return -1;
279856ea928SPeter Avalos 	}
280664f4763Szrj 	if ((r = sshbuf_get_u32(m, &ver)) != 0) {
28150a69bb5SSascha Wildner 		error_fr(r, "parse");
282856ea928SPeter Avalos 		return -1;
283856ea928SPeter Avalos 	}
284856ea928SPeter Avalos 	if (ver != SSHMUX_VER) {
28550a69bb5SSascha Wildner 		error_f("unsupported multiplexing protocol version %u "
28650a69bb5SSascha Wildner 		    "(expected %u)", ver, SSHMUX_VER);
287856ea928SPeter Avalos 		return -1;
288856ea928SPeter Avalos 	}
28950a69bb5SSascha Wildner 	debug2_f("channel %d client version %u", c->self, ver);
290856ea928SPeter Avalos 
291856ea928SPeter Avalos 	/* No extensions are presently defined */
292664f4763Szrj 	while (sshbuf_len(m) > 0) {
293664f4763Szrj 		char *name = NULL;
294664f4763Szrj 		size_t value_len = 0;
295856ea928SPeter Avalos 
296664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 ||
297664f4763Szrj 		    (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) {
29850a69bb5SSascha Wildner 			error_fr(r, "parse extension");
299664f4763Szrj 			return -1;
300856ea928SPeter Avalos 		}
30150a69bb5SSascha Wildner 		debug2_f("Unrecognised extension \"%s\" length %zu",
30250a69bb5SSascha Wildner 		    name, value_len);
30336e94dc5SPeter Avalos 		free(name);
304856ea928SPeter Avalos 	}
305856ea928SPeter Avalos 	state->hello_rcvd = 1;
306856ea928SPeter Avalos 	return 0;
307856ea928SPeter Avalos }
308856ea928SPeter Avalos 
309664f4763Szrj /* Enqueue a "ok" response to the reply buffer */
310664f4763Szrj static void
reply_ok(struct sshbuf * reply,u_int rid)311664f4763Szrj reply_ok(struct sshbuf *reply, u_int rid)
312664f4763Szrj {
313664f4763Szrj 	int r;
314664f4763Szrj 
315664f4763Szrj 	if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 ||
316664f4763Szrj 	    (r = sshbuf_put_u32(reply, rid)) != 0)
31750a69bb5SSascha Wildner 		fatal_fr(r, "reply");
318664f4763Szrj }
319664f4763Szrj 
320664f4763Szrj /* Enqueue an error response to the reply buffer */
321664f4763Szrj static void
reply_error(struct sshbuf * reply,u_int type,u_int rid,const char * msg)322664f4763Szrj reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg)
323664f4763Szrj {
324664f4763Szrj 	int r;
325664f4763Szrj 
326664f4763Szrj 	if ((r = sshbuf_put_u32(reply, type)) != 0 ||
327664f4763Szrj 	    (r = sshbuf_put_u32(reply, rid)) != 0 ||
328664f4763Szrj 	    (r = sshbuf_put_cstring(reply, msg)) != 0)
32950a69bb5SSascha Wildner 		fatal_fr(r, "reply");
330664f4763Szrj }
331664f4763Szrj 
332856ea928SPeter Avalos static int
mux_master_process_new_session(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)333664f4763Szrj mux_master_process_new_session(struct ssh *ssh, u_int rid,
334664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
335856ea928SPeter Avalos {
336856ea928SPeter Avalos 	Channel *nc;
337856ea928SPeter Avalos 	struct mux_session_confirm_ctx *cctx;
338664f4763Szrj 	char *cmd, *cp;
339664f4763Szrj 	u_int i, j, env_len, escape_char, window, packetmax;
340664f4763Szrj 	int r, new_fd[3];
341856ea928SPeter Avalos 
342856ea928SPeter Avalos 	/* Reply for SSHMUX_COMMAND_OPEN */
343856ea928SPeter Avalos 	cctx = xcalloc(1, sizeof(*cctx));
344856ea928SPeter Avalos 	cctx->term = NULL;
345856ea928SPeter Avalos 	cctx->rid = rid;
346664f4763Szrj 	cmd = NULL;
34799e85e0dSPeter Avalos 	cctx->env = NULL;
34899e85e0dSPeter Avalos 	env_len = 0;
349664f4763Szrj 	if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */
350664f4763Szrj 	    (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 ||
351664f4763Szrj 	    (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 ||
352664f4763Szrj 	    (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 ||
353664f4763Szrj 	    (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 ||
354664f4763Szrj 	    (r = sshbuf_get_u32(m, &escape_char)) != 0 ||
355664f4763Szrj 	    (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 ||
356664f4763Szrj 	    (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) {
357856ea928SPeter Avalos  malf:
35836e94dc5SPeter Avalos 		free(cmd);
35999e85e0dSPeter Avalos 		for (j = 0; j < env_len; j++)
36036e94dc5SPeter Avalos 			free(cctx->env[j]);
36136e94dc5SPeter Avalos 		free(cctx->env);
36236e94dc5SPeter Avalos 		free(cctx->term);
36336e94dc5SPeter Avalos 		free(cctx);
36450a69bb5SSascha Wildner 		error_f("malformed message");
365856ea928SPeter Avalos 		return -1;
366856ea928SPeter Avalos 	}
367856ea928SPeter Avalos 
368856ea928SPeter Avalos #define MUX_MAX_ENV_VARS	4096
369664f4763Szrj 	while (sshbuf_len(m) > 0) {
370664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0)
371856ea928SPeter Avalos 			goto malf;
372856ea928SPeter Avalos 		if (!env_permitted(cp)) {
37336e94dc5SPeter Avalos 			free(cp);
374856ea928SPeter Avalos 			continue;
375856ea928SPeter Avalos 		}
376e9778795SPeter Avalos 		cctx->env = xreallocarray(cctx->env, env_len + 2,
377856ea928SPeter Avalos 		    sizeof(*cctx->env));
378856ea928SPeter Avalos 		cctx->env[env_len++] = cp;
379856ea928SPeter Avalos 		cctx->env[env_len] = NULL;
380856ea928SPeter Avalos 		if (env_len > MUX_MAX_ENV_VARS) {
38150a69bb5SSascha Wildner 			error_f(">%d environment variables received, "
38250a69bb5SSascha Wildner 			    "ignoring additional", MUX_MAX_ENV_VARS);
383856ea928SPeter Avalos 			break;
384856ea928SPeter Avalos 		}
385856ea928SPeter Avalos 	}
386856ea928SPeter Avalos 
38750a69bb5SSascha Wildner 	debug2_f("channel %d: request tty %d, X %d, agent %d, subsys %d, "
38850a69bb5SSascha Wildner 	    "term \"%s\", cmd \"%s\", env %u", c->self,
389856ea928SPeter Avalos 	    cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd,
390856ea928SPeter Avalos 	    cctx->want_subsys, cctx->term, cmd, env_len);
391856ea928SPeter Avalos 
392664f4763Szrj 	if ((cctx->cmd = sshbuf_new()) == NULL)
39350a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
394664f4763Szrj 	if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0)
39550a69bb5SSascha Wildner 		fatal_fr(r, "sshbuf_put");
39636e94dc5SPeter Avalos 	free(cmd);
397856ea928SPeter Avalos 	cmd = NULL;
398856ea928SPeter Avalos 
399856ea928SPeter Avalos 	/* Gather fds from client */
400856ea928SPeter Avalos 	for(i = 0; i < 3; i++) {
401856ea928SPeter Avalos 		if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) {
40250a69bb5SSascha Wildner 			error_f("failed to receive fd %d from client", i);
403856ea928SPeter Avalos 			for (j = 0; j < i; j++)
404856ea928SPeter Avalos 				close(new_fd[j]);
405856ea928SPeter Avalos 			for (j = 0; j < env_len; j++)
40636e94dc5SPeter Avalos 				free(cctx->env[j]);
40736e94dc5SPeter Avalos 			free(cctx->env);
40836e94dc5SPeter Avalos 			free(cctx->term);
409664f4763Szrj 			sshbuf_free(cctx->cmd);
41036e94dc5SPeter Avalos 			free(cctx);
411664f4763Szrj 			reply_error(reply, MUX_S_FAILURE, rid,
412856ea928SPeter Avalos 			    "did not receive file descriptors");
413856ea928SPeter Avalos 			return -1;
414856ea928SPeter Avalos 		}
415856ea928SPeter Avalos 	}
416856ea928SPeter Avalos 
41750a69bb5SSascha Wildner 	debug3_f("got fds stdin %d, stdout %d, stderr %d",
418856ea928SPeter Avalos 	    new_fd[0], new_fd[1], new_fd[2]);
419856ea928SPeter Avalos 
420856ea928SPeter Avalos 	/* XXX support multiple child sessions in future */
421ce74bacaSMatthew Dillon 	if (c->have_remote_id) {
42250a69bb5SSascha Wildner 		debug2_f("session already open");
423664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, rid,
424664f4763Szrj 		    "Multiple sessions not supported");
425856ea928SPeter Avalos  cleanup:
426856ea928SPeter Avalos 		close(new_fd[0]);
427856ea928SPeter Avalos 		close(new_fd[1]);
428856ea928SPeter Avalos 		close(new_fd[2]);
42936e94dc5SPeter Avalos 		free(cctx->term);
430856ea928SPeter Avalos 		if (env_len != 0) {
431856ea928SPeter Avalos 			for (i = 0; i < env_len; i++)
43236e94dc5SPeter Avalos 				free(cctx->env[i]);
43336e94dc5SPeter Avalos 			free(cctx->env);
434856ea928SPeter Avalos 		}
435664f4763Szrj 		sshbuf_free(cctx->cmd);
43636e94dc5SPeter Avalos 		free(cctx);
437856ea928SPeter Avalos 		return 0;
438856ea928SPeter Avalos 	}
439856ea928SPeter Avalos 
440856ea928SPeter Avalos 	if (options.control_master == SSHCTL_MASTER_ASK ||
441856ea928SPeter Avalos 	    options.control_master == SSHCTL_MASTER_AUTO_ASK) {
442856ea928SPeter Avalos 		if (!ask_permission("Allow shared connection to %s? ", host)) {
44350a69bb5SSascha Wildner 			debug2_f("session refused by user");
444664f4763Szrj 			reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
445664f4763Szrj 			    "Permission denied");
446856ea928SPeter Avalos 			goto cleanup;
447856ea928SPeter Avalos 		}
448856ea928SPeter Avalos 	}
449856ea928SPeter Avalos 
450856ea928SPeter Avalos 	/* Try to pick up ttymodes from client before it goes raw */
451856ea928SPeter Avalos 	if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
45250a69bb5SSascha Wildner 		error_f("tcgetattr: %s", strerror(errno));
453856ea928SPeter Avalos 
454856ea928SPeter Avalos 	window = CHAN_SES_WINDOW_DEFAULT;
455856ea928SPeter Avalos 	packetmax = CHAN_SES_PACKET_DEFAULT;
456856ea928SPeter Avalos 	if (cctx->want_tty) {
457856ea928SPeter Avalos 		window >>= 1;
458856ea928SPeter Avalos 		packetmax >>= 1;
459856ea928SPeter Avalos 	}
460856ea928SPeter Avalos 
461ce74bacaSMatthew Dillon 	nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING,
462856ea928SPeter Avalos 	    new_fd[0], new_fd[1], new_fd[2], window, packetmax,
46350a69bb5SSascha Wildner 	    CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO);
464856ea928SPeter Avalos 
465856ea928SPeter Avalos 	nc->ctl_chan = c->self;		/* link session -> control channel */
466856ea928SPeter Avalos 	c->remote_id = nc->self;	/* link control -> session channel */
467ce74bacaSMatthew Dillon 	c->have_remote_id = 1;
468856ea928SPeter Avalos 
469856ea928SPeter Avalos 	if (cctx->want_tty && escape_char != 0xffffffff) {
470ce74bacaSMatthew Dillon 		channel_register_filter(ssh, nc->self,
471856ea928SPeter Avalos 		    client_simple_escape_filter, NULL,
472856ea928SPeter Avalos 		    client_filter_cleanup,
473856ea928SPeter Avalos 		    client_new_escape_filter_ctx((int)escape_char));
474856ea928SPeter Avalos 	}
475856ea928SPeter Avalos 
47650a69bb5SSascha Wildner 	debug2_f("channel_new: %d linked to control channel %d",
47750a69bb5SSascha Wildner 	    nc->self, nc->ctl_chan);
478856ea928SPeter Avalos 
479ce74bacaSMatthew Dillon 	channel_send_open(ssh, nc->self);
480ce74bacaSMatthew Dillon 	channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx);
481856ea928SPeter Avalos 	c->mux_pause = 1; /* stop handling messages until open_confirm done */
482ce74bacaSMatthew Dillon 	channel_register_cleanup(ssh, nc->self,
483ce74bacaSMatthew Dillon 	    mux_master_session_cleanup_cb, 1);
484856ea928SPeter Avalos 
485856ea928SPeter Avalos 	/* reply is deferred, sent by mux_session_confirm */
486856ea928SPeter Avalos 	return 0;
487856ea928SPeter Avalos }
488856ea928SPeter Avalos 
489856ea928SPeter Avalos static int
mux_master_process_alive_check(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)490664f4763Szrj mux_master_process_alive_check(struct ssh *ssh, u_int rid,
491664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
492856ea928SPeter Avalos {
493664f4763Szrj 	int r;
494664f4763Szrj 
49550a69bb5SSascha Wildner 	debug2_f("channel %d: alive check", c->self);
496856ea928SPeter Avalos 
497856ea928SPeter Avalos 	/* prepare reply */
498664f4763Szrj 	if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 ||
499664f4763Szrj 	    (r = sshbuf_put_u32(reply, rid)) != 0 ||
500664f4763Szrj 	    (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0)
50150a69bb5SSascha Wildner 		fatal_fr(r, "reply");
502856ea928SPeter Avalos 
503856ea928SPeter Avalos 	return 0;
504856ea928SPeter Avalos }
505856ea928SPeter Avalos 
506856ea928SPeter Avalos static int
mux_master_process_terminate(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)507664f4763Szrj mux_master_process_terminate(struct ssh *ssh, u_int rid,
508664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
509856ea928SPeter Avalos {
51050a69bb5SSascha Wildner 	debug2_f("channel %d: terminate request", c->self);
511856ea928SPeter Avalos 
512856ea928SPeter Avalos 	if (options.control_master == SSHCTL_MASTER_ASK ||
513856ea928SPeter Avalos 	    options.control_master == SSHCTL_MASTER_AUTO_ASK) {
514856ea928SPeter Avalos 		if (!ask_permission("Terminate shared connection to %s? ",
515856ea928SPeter Avalos 		    host)) {
51650a69bb5SSascha Wildner 			debug2_f("termination refused by user");
517664f4763Szrj 			reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
518664f4763Szrj 			    "Permission denied");
519856ea928SPeter Avalos 			return 0;
520856ea928SPeter Avalos 		}
521856ea928SPeter Avalos 	}
522856ea928SPeter Avalos 
523856ea928SPeter Avalos 	quit_pending = 1;
524664f4763Szrj 	reply_ok(reply, rid);
525856ea928SPeter Avalos 	/* XXX exit happens too soon - message never makes it to client */
526856ea928SPeter Avalos 	return 0;
527856ea928SPeter Avalos }
528856ea928SPeter Avalos 
529856ea928SPeter Avalos static char *
format_forward(u_int ftype,struct Forward * fwd)53036e94dc5SPeter Avalos format_forward(u_int ftype, struct Forward *fwd)
531856ea928SPeter Avalos {
532856ea928SPeter Avalos 	char *ret;
533856ea928SPeter Avalos 
534856ea928SPeter Avalos 	switch (ftype) {
535856ea928SPeter Avalos 	case MUX_FWD_LOCAL:
536856ea928SPeter Avalos 		xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d",
53736e94dc5SPeter Avalos 		    (fwd->listen_path != NULL) ? fwd->listen_path :
538856ea928SPeter Avalos 		    (fwd->listen_host == NULL) ?
53936e94dc5SPeter Avalos 		    (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") :
540856ea928SPeter Avalos 		    fwd->listen_host, fwd->listen_port,
54136e94dc5SPeter Avalos 		    (fwd->connect_path != NULL) ? fwd->connect_path :
542856ea928SPeter Avalos 		    fwd->connect_host, fwd->connect_port);
543856ea928SPeter Avalos 		break;
544856ea928SPeter Avalos 	case MUX_FWD_DYNAMIC:
545856ea928SPeter Avalos 		xasprintf(&ret, "dynamic forward %.200s:%d -> *",
546856ea928SPeter Avalos 		    (fwd->listen_host == NULL) ?
54736e94dc5SPeter Avalos 		    (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") :
548856ea928SPeter Avalos 		    fwd->listen_host, fwd->listen_port);
549856ea928SPeter Avalos 		break;
550856ea928SPeter Avalos 	case MUX_FWD_REMOTE:
551856ea928SPeter Avalos 		xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d",
55236e94dc5SPeter Avalos 		    (fwd->listen_path != NULL) ? fwd->listen_path :
553856ea928SPeter Avalos 		    (fwd->listen_host == NULL) ?
554856ea928SPeter Avalos 		    "LOCALHOST" : fwd->listen_host,
555856ea928SPeter Avalos 		    fwd->listen_port,
55636e94dc5SPeter Avalos 		    (fwd->connect_path != NULL) ? fwd->connect_path :
557856ea928SPeter Avalos 		    fwd->connect_host, fwd->connect_port);
558856ea928SPeter Avalos 		break;
559856ea928SPeter Avalos 	default:
56050a69bb5SSascha Wildner 		fatal_f("unknown forward type %u", ftype);
561856ea928SPeter Avalos 	}
562856ea928SPeter Avalos 	return ret;
563856ea928SPeter Avalos }
564856ea928SPeter Avalos 
565856ea928SPeter Avalos static int
compare_host(const char * a,const char * b)566856ea928SPeter Avalos compare_host(const char *a, const char *b)
567856ea928SPeter Avalos {
568856ea928SPeter Avalos 	if (a == NULL && b == NULL)
569856ea928SPeter Avalos 		return 1;
570856ea928SPeter Avalos 	if (a == NULL || b == NULL)
571856ea928SPeter Avalos 		return 0;
572856ea928SPeter Avalos 	return strcmp(a, b) == 0;
573856ea928SPeter Avalos }
574856ea928SPeter Avalos 
575856ea928SPeter Avalos static int
compare_forward(struct Forward * a,struct Forward * b)57636e94dc5SPeter Avalos compare_forward(struct Forward *a, struct Forward *b)
577856ea928SPeter Avalos {
578856ea928SPeter Avalos 	if (!compare_host(a->listen_host, b->listen_host))
579856ea928SPeter Avalos 		return 0;
58036e94dc5SPeter Avalos 	if (!compare_host(a->listen_path, b->listen_path))
58136e94dc5SPeter Avalos 		return 0;
582856ea928SPeter Avalos 	if (a->listen_port != b->listen_port)
583856ea928SPeter Avalos 		return 0;
584856ea928SPeter Avalos 	if (!compare_host(a->connect_host, b->connect_host))
585856ea928SPeter Avalos 		return 0;
58636e94dc5SPeter Avalos 	if (!compare_host(a->connect_path, b->connect_path))
58736e94dc5SPeter Avalos 		return 0;
588856ea928SPeter Avalos 	if (a->connect_port != b->connect_port)
589856ea928SPeter Avalos 		return 0;
590856ea928SPeter Avalos 
591856ea928SPeter Avalos 	return 1;
592856ea928SPeter Avalos }
593856ea928SPeter Avalos 
594856ea928SPeter Avalos static void
mux_confirm_remote_forward(struct ssh * ssh,int type,u_int32_t seq,void * ctxt)595ce74bacaSMatthew Dillon mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)
596856ea928SPeter Avalos {
597856ea928SPeter Avalos 	struct mux_channel_confirm_ctx *fctx = ctxt;
598856ea928SPeter Avalos 	char *failmsg = NULL;
59936e94dc5SPeter Avalos 	struct Forward *rfwd;
600856ea928SPeter Avalos 	Channel *c;
601664f4763Szrj 	struct sshbuf *out;
602664f4763Szrj 	u_int port;
603664f4763Szrj 	int r;
604856ea928SPeter Avalos 
605ce74bacaSMatthew Dillon 	if ((c = channel_by_id(ssh, fctx->cid)) == NULL) {
606856ea928SPeter Avalos 		/* no channel for reply */
60750a69bb5SSascha Wildner 		error_f("unknown channel");
608856ea928SPeter Avalos 		return;
609856ea928SPeter Avalos 	}
610664f4763Szrj 	if ((out = sshbuf_new()) == NULL)
61150a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
612e9778795SPeter Avalos 	if (fctx->fid >= options.num_remote_forwards ||
613e9778795SPeter Avalos 	    (options.remote_forwards[fctx->fid].connect_path == NULL &&
614e9778795SPeter Avalos 	    options.remote_forwards[fctx->fid].connect_host == NULL)) {
615856ea928SPeter Avalos 		xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid);
616856ea928SPeter Avalos 		goto fail;
617856ea928SPeter Avalos 	}
618856ea928SPeter Avalos 	rfwd = &options.remote_forwards[fctx->fid];
61950a69bb5SSascha Wildner 	debug_f("%s for: listen %d, connect %s:%d",
620856ea928SPeter Avalos 	    type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure",
62136e94dc5SPeter Avalos 	    rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path :
62236e94dc5SPeter Avalos 	    rfwd->connect_host, rfwd->connect_port);
623856ea928SPeter Avalos 	if (type == SSH2_MSG_REQUEST_SUCCESS) {
624856ea928SPeter Avalos 		if (rfwd->listen_port == 0) {
625664f4763Szrj 			if ((r = sshpkt_get_u32(ssh, &port)) != 0)
62650a69bb5SSascha Wildner 				fatal_fr(r, "parse port");
627664f4763Szrj 			if (port > 65535) {
628664f4763Szrj 				fatal("Invalid allocated port %u for "
629664f4763Szrj 				    "mux remote forward to %s:%d", port,
630664f4763Szrj 				    rfwd->connect_host, rfwd->connect_port);
631664f4763Szrj 			}
632664f4763Szrj 			rfwd->allocated_port = (int)port;
633e9778795SPeter Avalos 			debug("Allocated port %u for mux remote forward"
634856ea928SPeter Avalos 			    " to %s:%d", rfwd->allocated_port,
635856ea928SPeter Avalos 			    rfwd->connect_host, rfwd->connect_port);
636664f4763Szrj 			if ((r = sshbuf_put_u32(out,
637664f4763Szrj 			    MUX_S_REMOTE_PORT)) != 0 ||
638664f4763Szrj 			    (r = sshbuf_put_u32(out, fctx->rid)) != 0 ||
639664f4763Szrj 			    (r = sshbuf_put_u32(out,
640664f4763Szrj 			    rfwd->allocated_port)) != 0)
64150a69bb5SSascha Wildner 				fatal_fr(r, "reply");
642664f4763Szrj 			channel_update_permission(ssh, rfwd->handle,
64399e85e0dSPeter Avalos 			    rfwd->allocated_port);
644856ea928SPeter Avalos 		} else {
645664f4763Szrj 			reply_ok(out, fctx->rid);
646856ea928SPeter Avalos 		}
647856ea928SPeter Avalos 		goto out;
648856ea928SPeter Avalos 	} else {
64999e85e0dSPeter Avalos 		if (rfwd->listen_port == 0)
650664f4763Szrj 			channel_update_permission(ssh, rfwd->handle, -1);
65136e94dc5SPeter Avalos 		if (rfwd->listen_path != NULL)
65236e94dc5SPeter Avalos 			xasprintf(&failmsg, "remote port forwarding failed for "
65336e94dc5SPeter Avalos 			    "listen path %s", rfwd->listen_path);
65436e94dc5SPeter Avalos 		else
655856ea928SPeter Avalos 			xasprintf(&failmsg, "remote port forwarding failed for "
656856ea928SPeter Avalos 			    "listen port %d", rfwd->listen_port);
657e9778795SPeter Avalos 
65850a69bb5SSascha Wildner 		debug2_f("clearing registered forwarding for listen %d, "
65950a69bb5SSascha Wildner 		    "connect %s:%d", rfwd->listen_port,
660e9778795SPeter Avalos 		    rfwd->connect_path ? rfwd->connect_path :
661e9778795SPeter Avalos 		    rfwd->connect_host, rfwd->connect_port);
662e9778795SPeter Avalos 
663e9778795SPeter Avalos 		free(rfwd->listen_host);
664e9778795SPeter Avalos 		free(rfwd->listen_path);
665e9778795SPeter Avalos 		free(rfwd->connect_host);
666e9778795SPeter Avalos 		free(rfwd->connect_path);
667e9778795SPeter Avalos 		memset(rfwd, 0, sizeof(*rfwd));
668856ea928SPeter Avalos 	}
669856ea928SPeter Avalos  fail:
67050a69bb5SSascha Wildner 	error_f("%s", failmsg);
671664f4763Szrj 	reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg);
67236e94dc5SPeter Avalos 	free(failmsg);
673856ea928SPeter Avalos  out:
674664f4763Szrj 	if ((r = sshbuf_put_stringb(c->output, out)) != 0)
67550a69bb5SSascha Wildner 		fatal_fr(r, "enqueue");
676664f4763Szrj 	sshbuf_free(out);
677856ea928SPeter Avalos 	if (c->mux_pause <= 0)
67850a69bb5SSascha Wildner 		fatal_f("mux_pause %d", c->mux_pause);
679856ea928SPeter Avalos 	c->mux_pause = 0; /* start processing messages again */
680856ea928SPeter Avalos }
681856ea928SPeter Avalos 
682856ea928SPeter Avalos static int
mux_master_process_open_fwd(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)683664f4763Szrj mux_master_process_open_fwd(struct ssh *ssh, u_int rid,
684664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
685856ea928SPeter Avalos {
68636e94dc5SPeter Avalos 	struct Forward fwd;
687856ea928SPeter Avalos 	char *fwd_desc = NULL;
68836e94dc5SPeter Avalos 	char *listen_addr, *connect_addr;
689856ea928SPeter Avalos 	u_int ftype;
69036e94dc5SPeter Avalos 	u_int lport, cport;
691664f4763Szrj 	int r, i, ret = 0, freefwd = 1;
692856ea928SPeter Avalos 
693e9778795SPeter Avalos 	memset(&fwd, 0, sizeof(fwd));
694e9778795SPeter Avalos 
69536e94dc5SPeter Avalos 	/* XXX - lport/cport check redundant */
696664f4763Szrj 	if ((r = sshbuf_get_u32(m, &ftype)) != 0 ||
697664f4763Szrj 	    (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 ||
698664f4763Szrj 	    (r = sshbuf_get_u32(m, &lport)) != 0 ||
699664f4763Szrj 	    (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 ||
700664f4763Szrj 	    (r = sshbuf_get_u32(m, &cport)) != 0 ||
70136e94dc5SPeter Avalos 	    (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) ||
70236e94dc5SPeter Avalos 	    (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) {
70350a69bb5SSascha Wildner 		error_f("malformed message");
704856ea928SPeter Avalos 		ret = -1;
705856ea928SPeter Avalos 		goto out;
706856ea928SPeter Avalos 	}
70736e94dc5SPeter Avalos 	if (*listen_addr == '\0') {
70836e94dc5SPeter Avalos 		free(listen_addr);
70936e94dc5SPeter Avalos 		listen_addr = NULL;
71036e94dc5SPeter Avalos 	}
71136e94dc5SPeter Avalos 	if (*connect_addr == '\0') {
71236e94dc5SPeter Avalos 		free(connect_addr);
71336e94dc5SPeter Avalos 		connect_addr = NULL;
71436e94dc5SPeter Avalos 	}
715856ea928SPeter Avalos 
71636e94dc5SPeter Avalos 	memset(&fwd, 0, sizeof(fwd));
71736e94dc5SPeter Avalos 	fwd.listen_port = lport;
71836e94dc5SPeter Avalos 	if (fwd.listen_port == PORT_STREAMLOCAL)
71936e94dc5SPeter Avalos 		fwd.listen_path = listen_addr;
72036e94dc5SPeter Avalos 	else
72136e94dc5SPeter Avalos 		fwd.listen_host = listen_addr;
72236e94dc5SPeter Avalos 	fwd.connect_port = cport;
72336e94dc5SPeter Avalos 	if (fwd.connect_port == PORT_STREAMLOCAL)
72436e94dc5SPeter Avalos 		fwd.connect_path = connect_addr;
72536e94dc5SPeter Avalos 	else
72636e94dc5SPeter Avalos 		fwd.connect_host = connect_addr;
727856ea928SPeter Avalos 
72850a69bb5SSascha Wildner 	debug2_f("channel %d: request %s", c->self,
729856ea928SPeter Avalos 	    (fwd_desc = format_forward(ftype, &fwd)));
730856ea928SPeter Avalos 
731856ea928SPeter Avalos 	if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE &&
732856ea928SPeter Avalos 	    ftype != MUX_FWD_DYNAMIC) {
73350a69bb5SSascha Wildner 		logit_f("invalid forwarding type %u", ftype);
734856ea928SPeter Avalos  invalid:
73536e94dc5SPeter Avalos 		free(listen_addr);
73636e94dc5SPeter Avalos 		free(connect_addr);
737664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, rid,
738664f4763Szrj 		    "Invalid forwarding request");
739856ea928SPeter Avalos 		return 0;
740856ea928SPeter Avalos 	}
74136e94dc5SPeter Avalos 	if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) {
74250a69bb5SSascha Wildner 		logit_f("streamlocal and dynamic forwards "
74350a69bb5SSascha Wildner 		    "are mutually exclusive");
74436e94dc5SPeter Avalos 		goto invalid;
74536e94dc5SPeter Avalos 	}
74636e94dc5SPeter Avalos 	if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) {
74750a69bb5SSascha Wildner 		logit_f("invalid listen port %u", fwd.listen_port);
748856ea928SPeter Avalos 		goto invalid;
749856ea928SPeter Avalos 	}
750ce74bacaSMatthew Dillon 	if ((fwd.connect_port != PORT_STREAMLOCAL &&
751ce74bacaSMatthew Dillon 	    fwd.connect_port >= 65536) ||
752ce74bacaSMatthew Dillon 	    (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE &&
753ce74bacaSMatthew Dillon 	    fwd.connect_port == 0)) {
75450a69bb5SSascha Wildner 		logit_f("invalid connect port %u",
755856ea928SPeter Avalos 		    fwd.connect_port);
756856ea928SPeter Avalos 		goto invalid;
757856ea928SPeter Avalos 	}
758ce74bacaSMatthew Dillon 	if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL &&
759ce74bacaSMatthew Dillon 	    fwd.connect_path == NULL) {
76050a69bb5SSascha Wildner 		logit_f("missing connect host");
761856ea928SPeter Avalos 		goto invalid;
762856ea928SPeter Avalos 	}
763856ea928SPeter Avalos 
764856ea928SPeter Avalos 	/* Skip forwards that have already been requested */
765856ea928SPeter Avalos 	switch (ftype) {
766856ea928SPeter Avalos 	case MUX_FWD_LOCAL:
767856ea928SPeter Avalos 	case MUX_FWD_DYNAMIC:
768856ea928SPeter Avalos 		for (i = 0; i < options.num_local_forwards; i++) {
769856ea928SPeter Avalos 			if (compare_forward(&fwd,
770856ea928SPeter Avalos 			    options.local_forwards + i)) {
771856ea928SPeter Avalos  exists:
77250a69bb5SSascha Wildner 				debug2_f("found existing forwarding");
773664f4763Szrj 				reply_ok(reply, rid);
774856ea928SPeter Avalos 				goto out;
775856ea928SPeter Avalos 			}
776856ea928SPeter Avalos 		}
777856ea928SPeter Avalos 		break;
778856ea928SPeter Avalos 	case MUX_FWD_REMOTE:
779856ea928SPeter Avalos 		for (i = 0; i < options.num_remote_forwards; i++) {
780664f4763Szrj 			if (!compare_forward(&fwd, options.remote_forwards + i))
781664f4763Szrj 				continue;
782856ea928SPeter Avalos 			if (fwd.listen_port != 0)
783856ea928SPeter Avalos 				goto exists;
78450a69bb5SSascha Wildner 			debug2_f("found allocated port");
785664f4763Szrj 			if ((r = sshbuf_put_u32(reply,
786664f4763Szrj 			    MUX_S_REMOTE_PORT)) != 0 ||
787664f4763Szrj 			    (r = sshbuf_put_u32(reply, rid)) != 0 ||
788664f4763Szrj 			    (r = sshbuf_put_u32(reply,
789664f4763Szrj 			    options.remote_forwards[i].allocated_port)) != 0)
79050a69bb5SSascha Wildner 				fatal_fr(r, "reply FWD_REMOTE");
791856ea928SPeter Avalos 			goto out;
792856ea928SPeter Avalos 		}
793856ea928SPeter Avalos 		break;
794856ea928SPeter Avalos 	}
795856ea928SPeter Avalos 
796856ea928SPeter Avalos 	if (options.control_master == SSHCTL_MASTER_ASK ||
797856ea928SPeter Avalos 	    options.control_master == SSHCTL_MASTER_AUTO_ASK) {
798856ea928SPeter Avalos 		if (!ask_permission("Open %s on %s?", fwd_desc, host)) {
79950a69bb5SSascha Wildner 			debug2_f("forwarding refused by user");
800664f4763Szrj 			reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
801664f4763Szrj 			    "Permission denied");
802856ea928SPeter Avalos 			goto out;
803856ea928SPeter Avalos 		}
804856ea928SPeter Avalos 	}
805856ea928SPeter Avalos 
806856ea928SPeter Avalos 	if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) {
807ce74bacaSMatthew Dillon 		if (!channel_setup_local_fwd_listener(ssh, &fwd,
80836e94dc5SPeter Avalos 		    &options.fwd_opts)) {
809856ea928SPeter Avalos  fail:
81050a69bb5SSascha Wildner 			logit_f("requested %s failed", fwd_desc);
811664f4763Szrj 			reply_error(reply, MUX_S_FAILURE, rid,
812664f4763Szrj 			    "Port forwarding failed");
813856ea928SPeter Avalos 			goto out;
814856ea928SPeter Avalos 		}
815856ea928SPeter Avalos 		add_local_forward(&options, &fwd);
816856ea928SPeter Avalos 		freefwd = 0;
817856ea928SPeter Avalos 	} else {
818856ea928SPeter Avalos 		struct mux_channel_confirm_ctx *fctx;
819856ea928SPeter Avalos 
820ce74bacaSMatthew Dillon 		fwd.handle = channel_request_remote_forwarding(ssh, &fwd);
82199e85e0dSPeter Avalos 		if (fwd.handle < 0)
822856ea928SPeter Avalos 			goto fail;
823856ea928SPeter Avalos 		add_remote_forward(&options, &fwd);
824856ea928SPeter Avalos 		fctx = xcalloc(1, sizeof(*fctx));
825856ea928SPeter Avalos 		fctx->cid = c->self;
826856ea928SPeter Avalos 		fctx->rid = rid;
827856ea928SPeter Avalos 		fctx->fid = options.num_remote_forwards - 1;
828856ea928SPeter Avalos 		client_register_global_confirm(mux_confirm_remote_forward,
829856ea928SPeter Avalos 		    fctx);
830856ea928SPeter Avalos 		freefwd = 0;
831856ea928SPeter Avalos 		c->mux_pause = 1; /* wait for mux_confirm_remote_forward */
832856ea928SPeter Avalos 		/* delayed reply in mux_confirm_remote_forward */
833856ea928SPeter Avalos 		goto out;
834856ea928SPeter Avalos 	}
835664f4763Szrj 	reply_ok(reply, rid);
836856ea928SPeter Avalos  out:
83736e94dc5SPeter Avalos 	free(fwd_desc);
838856ea928SPeter Avalos 	if (freefwd) {
83936e94dc5SPeter Avalos 		free(fwd.listen_host);
84036e94dc5SPeter Avalos 		free(fwd.listen_path);
84136e94dc5SPeter Avalos 		free(fwd.connect_host);
84236e94dc5SPeter Avalos 		free(fwd.connect_path);
843856ea928SPeter Avalos 	}
844856ea928SPeter Avalos 	return ret;
845856ea928SPeter Avalos }
846856ea928SPeter Avalos 
847856ea928SPeter Avalos static int
mux_master_process_close_fwd(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)848664f4763Szrj mux_master_process_close_fwd(struct ssh *ssh, u_int rid,
849664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
850856ea928SPeter Avalos {
85136e94dc5SPeter Avalos 	struct Forward fwd, *found_fwd;
852856ea928SPeter Avalos 	char *fwd_desc = NULL;
85399e85e0dSPeter Avalos 	const char *error_reason = NULL;
85436e94dc5SPeter Avalos 	char *listen_addr = NULL, *connect_addr = NULL;
855856ea928SPeter Avalos 	u_int ftype;
856664f4763Szrj 	int r, i, ret = 0;
85736e94dc5SPeter Avalos 	u_int lport, cport;
858856ea928SPeter Avalos 
859e9778795SPeter Avalos 	memset(&fwd, 0, sizeof(fwd));
860e9778795SPeter Avalos 
861664f4763Szrj 	if ((r = sshbuf_get_u32(m, &ftype)) != 0 ||
862664f4763Szrj 	    (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 ||
863664f4763Szrj 	    (r = sshbuf_get_u32(m, &lport)) != 0 ||
864664f4763Szrj 	    (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 ||
865664f4763Szrj 	    (r = sshbuf_get_u32(m, &cport)) != 0 ||
86636e94dc5SPeter Avalos 	    (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) ||
86736e94dc5SPeter Avalos 	    (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) {
86850a69bb5SSascha Wildner 		error_f("malformed message");
869856ea928SPeter Avalos 		ret = -1;
870856ea928SPeter Avalos 		goto out;
871856ea928SPeter Avalos 	}
872856ea928SPeter Avalos 
87336e94dc5SPeter Avalos 	if (*listen_addr == '\0') {
87436e94dc5SPeter Avalos 		free(listen_addr);
87536e94dc5SPeter Avalos 		listen_addr = NULL;
876856ea928SPeter Avalos 	}
87736e94dc5SPeter Avalos 	if (*connect_addr == '\0') {
87836e94dc5SPeter Avalos 		free(connect_addr);
87936e94dc5SPeter Avalos 		connect_addr = NULL;
880856ea928SPeter Avalos 	}
881856ea928SPeter Avalos 
88236e94dc5SPeter Avalos 	memset(&fwd, 0, sizeof(fwd));
88336e94dc5SPeter Avalos 	fwd.listen_port = lport;
88436e94dc5SPeter Avalos 	if (fwd.listen_port == PORT_STREAMLOCAL)
88536e94dc5SPeter Avalos 		fwd.listen_path = listen_addr;
88636e94dc5SPeter Avalos 	else
88736e94dc5SPeter Avalos 		fwd.listen_host = listen_addr;
88836e94dc5SPeter Avalos 	fwd.connect_port = cport;
88936e94dc5SPeter Avalos 	if (fwd.connect_port == PORT_STREAMLOCAL)
89036e94dc5SPeter Avalos 		fwd.connect_path = connect_addr;
89136e94dc5SPeter Avalos 	else
89236e94dc5SPeter Avalos 		fwd.connect_host = connect_addr;
89336e94dc5SPeter Avalos 
89450a69bb5SSascha Wildner 	debug2_f("channel %d: request cancel %s", c->self,
895856ea928SPeter Avalos 	    (fwd_desc = format_forward(ftype, &fwd)));
896856ea928SPeter Avalos 
89799e85e0dSPeter Avalos 	/* make sure this has been requested */
89899e85e0dSPeter Avalos 	found_fwd = NULL;
89999e85e0dSPeter Avalos 	switch (ftype) {
90099e85e0dSPeter Avalos 	case MUX_FWD_LOCAL:
90199e85e0dSPeter Avalos 	case MUX_FWD_DYNAMIC:
90299e85e0dSPeter Avalos 		for (i = 0; i < options.num_local_forwards; i++) {
90399e85e0dSPeter Avalos 			if (compare_forward(&fwd,
90499e85e0dSPeter Avalos 			    options.local_forwards + i)) {
90599e85e0dSPeter Avalos 				found_fwd = options.local_forwards + i;
90699e85e0dSPeter Avalos 				break;
90799e85e0dSPeter Avalos 			}
90899e85e0dSPeter Avalos 		}
90999e85e0dSPeter Avalos 		break;
91099e85e0dSPeter Avalos 	case MUX_FWD_REMOTE:
91199e85e0dSPeter Avalos 		for (i = 0; i < options.num_remote_forwards; i++) {
91299e85e0dSPeter Avalos 			if (compare_forward(&fwd,
91399e85e0dSPeter Avalos 			    options.remote_forwards + i)) {
91499e85e0dSPeter Avalos 				found_fwd = options.remote_forwards + i;
91599e85e0dSPeter Avalos 				break;
91699e85e0dSPeter Avalos 			}
91799e85e0dSPeter Avalos 		}
91899e85e0dSPeter Avalos 		break;
91999e85e0dSPeter Avalos 	}
92099e85e0dSPeter Avalos 
92199e85e0dSPeter Avalos 	if (found_fwd == NULL)
92299e85e0dSPeter Avalos 		error_reason = "port not forwarded";
92399e85e0dSPeter Avalos 	else if (ftype == MUX_FWD_REMOTE) {
92499e85e0dSPeter Avalos 		/*
92599e85e0dSPeter Avalos 		 * This shouldn't fail unless we confused the host/port
92699e85e0dSPeter Avalos 		 * between options.remote_forwards and permitted_opens.
92799e85e0dSPeter Avalos 		 * However, for dynamic allocated listen ports we need
92836e94dc5SPeter Avalos 		 * to use the actual listen port.
92999e85e0dSPeter Avalos 		 */
930ce74bacaSMatthew Dillon 		if (channel_request_rforward_cancel(ssh, found_fwd) == -1)
93199e85e0dSPeter Avalos 			error_reason = "port not in permitted opens";
93299e85e0dSPeter Avalos 	} else {	/* local and dynamic forwards */
93399e85e0dSPeter Avalos 		/* Ditto */
934ce74bacaSMatthew Dillon 		if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port,
93536e94dc5SPeter Avalos 		    &options.fwd_opts) == -1)
93699e85e0dSPeter Avalos 			error_reason = "port not found";
93799e85e0dSPeter Avalos 	}
93899e85e0dSPeter Avalos 
939664f4763Szrj 	if (error_reason != NULL)
940664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, rid, error_reason);
941664f4763Szrj 	else {
942664f4763Szrj 		reply_ok(reply, rid);
94336e94dc5SPeter Avalos 		free(found_fwd->listen_host);
94436e94dc5SPeter Avalos 		free(found_fwd->listen_path);
94536e94dc5SPeter Avalos 		free(found_fwd->connect_host);
94636e94dc5SPeter Avalos 		free(found_fwd->connect_path);
94799e85e0dSPeter Avalos 		found_fwd->listen_host = found_fwd->connect_host = NULL;
94836e94dc5SPeter Avalos 		found_fwd->listen_path = found_fwd->connect_path = NULL;
94999e85e0dSPeter Avalos 		found_fwd->listen_port = found_fwd->connect_port = 0;
95099e85e0dSPeter Avalos 	}
951856ea928SPeter Avalos  out:
95236e94dc5SPeter Avalos 	free(fwd_desc);
95336e94dc5SPeter Avalos 	free(listen_addr);
95436e94dc5SPeter Avalos 	free(connect_addr);
955856ea928SPeter Avalos 
956856ea928SPeter Avalos 	return ret;
957856ea928SPeter Avalos }
958856ea928SPeter Avalos 
959856ea928SPeter Avalos static int
mux_master_process_stdio_fwd(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)960664f4763Szrj mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid,
961664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
962856ea928SPeter Avalos {
963856ea928SPeter Avalos 	Channel *nc;
964664f4763Szrj 	char *chost = NULL;
965856ea928SPeter Avalos 	u_int cport, i, j;
966664f4763Szrj 	int r, new_fd[2];
96736e94dc5SPeter Avalos 	struct mux_stdio_confirm_ctx *cctx;
968856ea928SPeter Avalos 
969664f4763Szrj 	if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */
970664f4763Szrj 	    (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 ||
971664f4763Szrj 	    (r = sshbuf_get_u32(m, &cport)) != 0) {
97236e94dc5SPeter Avalos 		free(chost);
97350a69bb5SSascha Wildner 		error_f("malformed message");
974856ea928SPeter Avalos 		return -1;
975856ea928SPeter Avalos 	}
976856ea928SPeter Avalos 
97750a69bb5SSascha Wildner 	debug2_f("channel %d: stdio fwd to %s:%u", c->self, chost, cport);
978856ea928SPeter Avalos 
979856ea928SPeter Avalos 	/* Gather fds from client */
980856ea928SPeter Avalos 	for(i = 0; i < 2; i++) {
981856ea928SPeter Avalos 		if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) {
98250a69bb5SSascha Wildner 			error_f("failed to receive fd %d from client", i);
983856ea928SPeter Avalos 			for (j = 0; j < i; j++)
984856ea928SPeter Avalos 				close(new_fd[j]);
98536e94dc5SPeter Avalos 			free(chost);
986856ea928SPeter Avalos 
987856ea928SPeter Avalos 			/* prepare reply */
988664f4763Szrj 			reply_error(reply, MUX_S_FAILURE, rid,
989856ea928SPeter Avalos 			    "did not receive file descriptors");
990856ea928SPeter Avalos 			return -1;
991856ea928SPeter Avalos 		}
992856ea928SPeter Avalos 	}
993856ea928SPeter Avalos 
99450a69bb5SSascha Wildner 	debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]);
995856ea928SPeter Avalos 
996856ea928SPeter Avalos 	/* XXX support multiple child sessions in future */
997ce74bacaSMatthew Dillon 	if (c->have_remote_id) {
99850a69bb5SSascha Wildner 		debug2_f("session already open");
999664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, rid,
1000664f4763Szrj 		    "Multiple sessions not supported");
1001856ea928SPeter Avalos  cleanup:
1002856ea928SPeter Avalos 		close(new_fd[0]);
1003856ea928SPeter Avalos 		close(new_fd[1]);
100436e94dc5SPeter Avalos 		free(chost);
1005856ea928SPeter Avalos 		return 0;
1006856ea928SPeter Avalos 	}
1007856ea928SPeter Avalos 
1008856ea928SPeter Avalos 	if (options.control_master == SSHCTL_MASTER_ASK ||
1009856ea928SPeter Avalos 	    options.control_master == SSHCTL_MASTER_AUTO_ASK) {
10109f304aafSPeter Avalos 		if (!ask_permission("Allow forward to %s:%u? ",
1011856ea928SPeter Avalos 		    chost, cport)) {
101250a69bb5SSascha Wildner 			debug2_f("stdio fwd refused by user");
1013664f4763Szrj 			reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
1014664f4763Szrj 			    "Permission denied");
1015856ea928SPeter Avalos 			goto cleanup;
1016856ea928SPeter Avalos 		}
1017856ea928SPeter Avalos 	}
1018856ea928SPeter Avalos 
101950a69bb5SSascha Wildner 	nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1],
102050a69bb5SSascha Wildner 	    CHANNEL_NONBLOCK_STDIO);
1021664f4763Szrj 	free(chost);
1022856ea928SPeter Avalos 
1023856ea928SPeter Avalos 	nc->ctl_chan = c->self;		/* link session -> control channel */
1024856ea928SPeter Avalos 	c->remote_id = nc->self;	/* link control -> session channel */
1025ce74bacaSMatthew Dillon 	c->have_remote_id = 1;
1026856ea928SPeter Avalos 
102750a69bb5SSascha Wildner 	debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan);
1028856ea928SPeter Avalos 
1029ce74bacaSMatthew Dillon 	channel_register_cleanup(ssh, nc->self,
1030ce74bacaSMatthew Dillon 	    mux_master_session_cleanup_cb, 1);
1031856ea928SPeter Avalos 
103236e94dc5SPeter Avalos 	cctx = xcalloc(1, sizeof(*cctx));
103336e94dc5SPeter Avalos 	cctx->rid = rid;
1034ce74bacaSMatthew Dillon 	channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx);
103536e94dc5SPeter Avalos 	c->mux_pause = 1; /* stop handling messages until open_confirm done */
1036856ea928SPeter Avalos 
103736e94dc5SPeter Avalos 	/* reply is deferred, sent by mux_session_confirm */
1038856ea928SPeter Avalos 	return 0;
1039856ea928SPeter Avalos }
1040856ea928SPeter Avalos 
104136e94dc5SPeter Avalos /* Callback on open confirmation in mux master for a mux stdio fwd session. */
104236e94dc5SPeter Avalos static void
mux_stdio_confirm(struct ssh * ssh,int id,int success,void * arg)1043ce74bacaSMatthew Dillon mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg)
104436e94dc5SPeter Avalos {
104536e94dc5SPeter Avalos 	struct mux_stdio_confirm_ctx *cctx = arg;
104636e94dc5SPeter Avalos 	Channel *c, *cc;
1047664f4763Szrj 	struct sshbuf *reply;
1048664f4763Szrj 	int r;
104936e94dc5SPeter Avalos 
105036e94dc5SPeter Avalos 	if (cctx == NULL)
105150a69bb5SSascha Wildner 		fatal_f("cctx == NULL");
1052ce74bacaSMatthew Dillon 	if ((c = channel_by_id(ssh, id)) == NULL)
105350a69bb5SSascha Wildner 		fatal_f("no channel for id %d", id);
1054ce74bacaSMatthew Dillon 	if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
105550a69bb5SSascha Wildner 		fatal_f("channel %d lacks control channel %d",
105636e94dc5SPeter Avalos 		    id, c->ctl_chan);
1057664f4763Szrj 	if ((reply = sshbuf_new()) == NULL)
105850a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
105936e94dc5SPeter Avalos 
106036e94dc5SPeter Avalos 	if (!success) {
106150a69bb5SSascha Wildner 		debug3_f("sending failure reply");
1062664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, cctx->rid,
1063664f4763Szrj 		    "Session open refused by peer");
106436e94dc5SPeter Avalos 		/* prepare reply */
106536e94dc5SPeter Avalos 		goto done;
106636e94dc5SPeter Avalos 	}
106736e94dc5SPeter Avalos 
106850a69bb5SSascha Wildner 	debug3_f("sending success reply");
106936e94dc5SPeter Avalos 	/* prepare reply */
1070664f4763Szrj 	if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 ||
1071664f4763Szrj 	    (r = sshbuf_put_u32(reply, cctx->rid)) != 0 ||
1072664f4763Szrj 	    (r = sshbuf_put_u32(reply, c->self)) != 0)
107350a69bb5SSascha Wildner 		fatal_fr(r, "reply");
107436e94dc5SPeter Avalos 
107536e94dc5SPeter Avalos  done:
107636e94dc5SPeter Avalos 	/* Send reply */
1077664f4763Szrj 	if ((r = sshbuf_put_stringb(cc->output, reply)) != 0)
107850a69bb5SSascha Wildner 		fatal_fr(r, "enqueue");
1079664f4763Szrj 	sshbuf_free(reply);
108036e94dc5SPeter Avalos 
108136e94dc5SPeter Avalos 	if (cc->mux_pause <= 0)
108250a69bb5SSascha Wildner 		fatal_f("mux_pause %d", cc->mux_pause);
108336e94dc5SPeter Avalos 	cc->mux_pause = 0; /* start processing messages again */
108436e94dc5SPeter Avalos 	c->open_confirm_ctx = NULL;
108536e94dc5SPeter Avalos 	free(cctx);
108636e94dc5SPeter Avalos }
108736e94dc5SPeter Avalos 
10881c188a7fSPeter Avalos static int
mux_master_process_stop_listening(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)1089664f4763Szrj mux_master_process_stop_listening(struct ssh *ssh, u_int rid,
1090664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
10911c188a7fSPeter Avalos {
109250a69bb5SSascha Wildner 	debug_f("channel %d: stop listening", c->self);
10931c188a7fSPeter Avalos 
10941c188a7fSPeter Avalos 	if (options.control_master == SSHCTL_MASTER_ASK ||
10951c188a7fSPeter Avalos 	    options.control_master == SSHCTL_MASTER_AUTO_ASK) {
10961c188a7fSPeter Avalos 		if (!ask_permission("Disable further multiplexing on shared "
10971c188a7fSPeter Avalos 		    "connection to %s? ", host)) {
109850a69bb5SSascha Wildner 			debug2_f("stop listen refused by user");
1099664f4763Szrj 			reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
1100664f4763Szrj 			    "Permission denied");
11011c188a7fSPeter Avalos 			return 0;
11021c188a7fSPeter Avalos 		}
11031c188a7fSPeter Avalos 	}
11041c188a7fSPeter Avalos 
11051c188a7fSPeter Avalos 	if (mux_listener_channel != NULL) {
1106ce74bacaSMatthew Dillon 		channel_free(ssh, mux_listener_channel);
11071c188a7fSPeter Avalos 		client_stop_mux();
110836e94dc5SPeter Avalos 		free(options.control_path);
11091c188a7fSPeter Avalos 		options.control_path = NULL;
11101c188a7fSPeter Avalos 		mux_listener_channel = NULL;
11111c188a7fSPeter Avalos 		muxserver_sock = -1;
11121c188a7fSPeter Avalos 	}
11131c188a7fSPeter Avalos 
1114664f4763Szrj 	reply_ok(reply, rid);
11151c188a7fSPeter Avalos 	return 0;
11161c188a7fSPeter Avalos }
11171c188a7fSPeter Avalos 
1118ce74bacaSMatthew Dillon static int
mux_master_process_proxy(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)1119664f4763Szrj mux_master_process_proxy(struct ssh *ssh, u_int rid,
1120664f4763Szrj     Channel *c, struct sshbuf *m, struct sshbuf *reply)
1121ce74bacaSMatthew Dillon {
1122664f4763Szrj 	int r;
1123664f4763Szrj 
112450a69bb5SSascha Wildner 	debug_f("channel %d: proxy request", c->self);
1125ce74bacaSMatthew Dillon 
1126ce74bacaSMatthew Dillon 	c->mux_rcb = channel_proxy_downstream;
1127664f4763Szrj 	if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 ||
1128664f4763Szrj 	    (r = sshbuf_put_u32(reply, rid)) != 0)
112950a69bb5SSascha Wildner 		fatal_fr(r, "reply");
1130ce74bacaSMatthew Dillon 
1131ce74bacaSMatthew Dillon 	return 0;
1132ce74bacaSMatthew Dillon }
1133ce74bacaSMatthew Dillon 
113450a69bb5SSascha Wildner /* Channel callbacks fired on read/write from mux client fd */
1135856ea928SPeter Avalos static int
mux_master_read_cb(struct ssh * ssh,Channel * c)1136ce74bacaSMatthew Dillon mux_master_read_cb(struct ssh *ssh, Channel *c)
1137856ea928SPeter Avalos {
1138856ea928SPeter Avalos 	struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx;
1139664f4763Szrj 	struct sshbuf *in = NULL, *out = NULL;
1140664f4763Szrj 	u_int type, rid, i;
1141664f4763Szrj 	int r, ret = -1;
1142664f4763Szrj 
1143664f4763Szrj 	if ((out = sshbuf_new()) == NULL)
114450a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1145856ea928SPeter Avalos 
1146856ea928SPeter Avalos 	/* Setup ctx and  */
1147856ea928SPeter Avalos 	if (c->mux_ctx == NULL) {
1148856ea928SPeter Avalos 		state = xcalloc(1, sizeof(*state));
1149856ea928SPeter Avalos 		c->mux_ctx = state;
1150ce74bacaSMatthew Dillon 		channel_register_cleanup(ssh, c->self,
1151856ea928SPeter Avalos 		    mux_master_control_cleanup_cb, 0);
1152856ea928SPeter Avalos 
1153856ea928SPeter Avalos 		/* Send hello */
1154664f4763Szrj 		if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 ||
1155664f4763Szrj 		    (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0)
115650a69bb5SSascha Wildner 			fatal_fr(r, "reply");
1157856ea928SPeter Avalos 		/* no extensions */
1158664f4763Szrj 		if ((r = sshbuf_put_stringb(c->output, out)) != 0)
115950a69bb5SSascha Wildner 			fatal_fr(r, "enqueue");
116050a69bb5SSascha Wildner 		debug3_f("channel %d: hello sent", c->self);
1161664f4763Szrj 		ret = 0;
1162664f4763Szrj 		goto out;
1163856ea928SPeter Avalos 	}
1164856ea928SPeter Avalos 
1165856ea928SPeter Avalos 	/* Channel code ensures that we receive whole packets */
1166664f4763Szrj 	if ((r = sshbuf_froms(c->input, &in)) != 0) {
1167856ea928SPeter Avalos  malf:
116850a69bb5SSascha Wildner 		error_f("malformed message");
1169856ea928SPeter Avalos 		goto out;
1170856ea928SPeter Avalos 	}
1171856ea928SPeter Avalos 
1172664f4763Szrj 	if ((r = sshbuf_get_u32(in, &type)) != 0)
1173856ea928SPeter Avalos 		goto malf;
117450a69bb5SSascha Wildner 	debug3_f("channel %d packet type 0x%08x len %zu", c->self,
117550a69bb5SSascha Wildner 	    type, sshbuf_len(in));
1176856ea928SPeter Avalos 
1177856ea928SPeter Avalos 	if (type == MUX_MSG_HELLO)
1178856ea928SPeter Avalos 		rid = 0;
1179856ea928SPeter Avalos 	else {
1180856ea928SPeter Avalos 		if (!state->hello_rcvd) {
118150a69bb5SSascha Wildner 			error_f("expected MUX_MSG_HELLO(0x%08x), "
118250a69bb5SSascha Wildner 			    "received 0x%08x", MUX_MSG_HELLO, type);
1183856ea928SPeter Avalos 			goto out;
1184856ea928SPeter Avalos 		}
1185664f4763Szrj 		if ((r = sshbuf_get_u32(in, &rid)) != 0)
1186856ea928SPeter Avalos 			goto malf;
1187856ea928SPeter Avalos 	}
1188856ea928SPeter Avalos 
1189856ea928SPeter Avalos 	for (i = 0; mux_master_handlers[i].handler != NULL; i++) {
1190856ea928SPeter Avalos 		if (type == mux_master_handlers[i].type) {
1191ce74bacaSMatthew Dillon 			ret = mux_master_handlers[i].handler(ssh, rid,
1192664f4763Szrj 			    c, in, out);
1193856ea928SPeter Avalos 			break;
1194856ea928SPeter Avalos 		}
1195856ea928SPeter Avalos 	}
1196856ea928SPeter Avalos 	if (mux_master_handlers[i].handler == NULL) {
119750a69bb5SSascha Wildner 		error_f("unsupported mux message 0x%08x", type);
1198664f4763Szrj 		reply_error(out, MUX_S_FAILURE, rid, "unsupported request");
1199856ea928SPeter Avalos 		ret = 0;
1200856ea928SPeter Avalos 	}
1201856ea928SPeter Avalos 	/* Enqueue reply packet */
120250a69bb5SSascha Wildner 	if (sshbuf_len(out) != 0 &&
120350a69bb5SSascha Wildner 	    (r = sshbuf_put_stringb(c->output, out)) != 0)
120450a69bb5SSascha Wildner 		fatal_fr(r, "enqueue");
1205856ea928SPeter Avalos  out:
1206664f4763Szrj 	sshbuf_free(in);
1207664f4763Szrj 	sshbuf_free(out);
1208856ea928SPeter Avalos 	return ret;
1209856ea928SPeter Avalos }
1210856ea928SPeter Avalos 
1211856ea928SPeter Avalos void
mux_exit_message(struct ssh * ssh,Channel * c,int exitval)1212ce74bacaSMatthew Dillon mux_exit_message(struct ssh *ssh, Channel *c, int exitval)
1213856ea928SPeter Avalos {
1214664f4763Szrj 	struct sshbuf *m;
1215856ea928SPeter Avalos 	Channel *mux_chan;
1216664f4763Szrj 	int r;
1217856ea928SPeter Avalos 
121850a69bb5SSascha Wildner 	debug3_f("channel %d: exit message, exitval %d", c->self, exitval);
1219856ea928SPeter Avalos 
1220ce74bacaSMatthew Dillon 	if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL)
122150a69bb5SSascha Wildner 		fatal_f("channel %d missing mux %d", c->self, c->ctl_chan);
1222856ea928SPeter Avalos 
1223856ea928SPeter Avalos 	/* Append exit message packet to control socket output queue */
1224664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
122550a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1226664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 ||
1227664f4763Szrj 	    (r = sshbuf_put_u32(m, c->self)) != 0 ||
1228664f4763Szrj 	    (r = sshbuf_put_u32(m, exitval)) != 0 ||
1229664f4763Szrj 	    (r = sshbuf_put_stringb(mux_chan->output, m)) != 0)
123050a69bb5SSascha Wildner 		fatal_fr(r, "reply");
1231664f4763Szrj 	sshbuf_free(m);
1232856ea928SPeter Avalos }
123318de8d7fSPeter Avalos 
12341c188a7fSPeter Avalos void
mux_tty_alloc_failed(struct ssh * ssh,Channel * c)1235ce74bacaSMatthew Dillon mux_tty_alloc_failed(struct ssh *ssh, Channel *c)
12361c188a7fSPeter Avalos {
1237664f4763Szrj 	struct sshbuf *m;
12381c188a7fSPeter Avalos 	Channel *mux_chan;
1239664f4763Szrj 	int r;
12401c188a7fSPeter Avalos 
124150a69bb5SSascha Wildner 	debug3_f("channel %d: TTY alloc failed", c->self);
12421c188a7fSPeter Avalos 
1243ce74bacaSMatthew Dillon 	if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL)
124450a69bb5SSascha Wildner 		fatal_f("channel %d missing mux %d", c->self, c->ctl_chan);
12451c188a7fSPeter Avalos 
12461c188a7fSPeter Avalos 	/* Append exit message packet to control socket output queue */
1247664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
124850a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1249664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 ||
1250664f4763Szrj 	    (r = sshbuf_put_u32(m, c->self)) != 0 ||
1251664f4763Szrj 	    (r = sshbuf_put_stringb(mux_chan->output, m)) != 0)
125250a69bb5SSascha Wildner 		fatal_fr(r, "reply");
1253664f4763Szrj 	sshbuf_free(m);
12541c188a7fSPeter Avalos }
12551c188a7fSPeter Avalos 
125618de8d7fSPeter Avalos /* Prepare a mux master to listen on a Unix domain socket. */
125718de8d7fSPeter Avalos void
muxserver_listen(struct ssh * ssh)1258ce74bacaSMatthew Dillon muxserver_listen(struct ssh *ssh)
125918de8d7fSPeter Avalos {
126018de8d7fSPeter Avalos 	mode_t old_umask;
12619f304aafSPeter Avalos 	char *orig_control_path = options.control_path;
12629f304aafSPeter Avalos 	char rbuf[16+1];
12639f304aafSPeter Avalos 	u_int i, r;
126436e94dc5SPeter Avalos 	int oerrno;
126518de8d7fSPeter Avalos 
126618de8d7fSPeter Avalos 	if (options.control_path == NULL ||
126718de8d7fSPeter Avalos 	    options.control_master == SSHCTL_MASTER_NO)
126818de8d7fSPeter Avalos 		return;
126918de8d7fSPeter Avalos 
127018de8d7fSPeter Avalos 	debug("setting up multiplex master socket");
127118de8d7fSPeter Avalos 
12729f304aafSPeter Avalos 	/*
12739f304aafSPeter Avalos 	 * Use a temporary path before listen so we can pseudo-atomically
12749f304aafSPeter Avalos 	 * establish the listening socket in its final location to avoid
12759f304aafSPeter Avalos 	 * other processes racing in between bind() and listen() and hitting
12769f304aafSPeter Avalos 	 * an unready socket.
12779f304aafSPeter Avalos 	 */
12789f304aafSPeter Avalos 	for (i = 0; i < sizeof(rbuf) - 1; i++) {
12799f304aafSPeter Avalos 		r = arc4random_uniform(26+26+10);
12809f304aafSPeter Avalos 		rbuf[i] = (r < 26) ? 'a' + r :
12819f304aafSPeter Avalos 		    (r < 26*2) ? 'A' + r - 26 :
12829f304aafSPeter Avalos 		    '0' + r - 26 - 26;
12839f304aafSPeter Avalos 	}
12849f304aafSPeter Avalos 	rbuf[sizeof(rbuf) - 1] = '\0';
12859f304aafSPeter Avalos 	options.control_path = NULL;
12869f304aafSPeter Avalos 	xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf);
128750a69bb5SSascha Wildner 	debug3_f("temporary control path %s", options.control_path);
12889f304aafSPeter Avalos 
128918de8d7fSPeter Avalos 	old_umask = umask(0177);
129036e94dc5SPeter Avalos 	muxserver_sock = unix_listener(options.control_path, 64, 0);
129136e94dc5SPeter Avalos 	oerrno = errno;
129236e94dc5SPeter Avalos 	umask(old_umask);
129336e94dc5SPeter Avalos 	if (muxserver_sock < 0) {
129436e94dc5SPeter Avalos 		if (oerrno == EINVAL || oerrno == EADDRINUSE) {
129518de8d7fSPeter Avalos 			error("ControlSocket %s already exists, "
129618de8d7fSPeter Avalos 			    "disabling multiplexing", options.control_path);
12979f304aafSPeter Avalos  disable_mux_master:
12981c188a7fSPeter Avalos 			if (muxserver_sock != -1) {
129918de8d7fSPeter Avalos 				close(muxserver_sock);
130018de8d7fSPeter Avalos 				muxserver_sock = -1;
13011c188a7fSPeter Avalos 			}
130236e94dc5SPeter Avalos 			free(orig_control_path);
130336e94dc5SPeter Avalos 			free(options.control_path);
130418de8d7fSPeter Avalos 			options.control_path = NULL;
130518de8d7fSPeter Avalos 			options.control_master = SSHCTL_MASTER_NO;
130618de8d7fSPeter Avalos 			return;
130736e94dc5SPeter Avalos 		} else {
130836e94dc5SPeter Avalos 			/* unix_listener() logs the error */
130936e94dc5SPeter Avalos 			cleanup_exit(255);
131018de8d7fSPeter Avalos 		}
131136e94dc5SPeter Avalos 	}
131218de8d7fSPeter Avalos 
13139f304aafSPeter Avalos 	/* Now atomically "move" the mux socket into position */
13149f304aafSPeter Avalos 	if (link(options.control_path, orig_control_path) != 0) {
13159f304aafSPeter Avalos 		if (errno != EEXIST) {
131650a69bb5SSascha Wildner 			fatal_f("link mux listener %s => %s: %s",
13179f304aafSPeter Avalos 			    options.control_path, orig_control_path,
13189f304aafSPeter Avalos 			    strerror(errno));
13199f304aafSPeter Avalos 		}
13209f304aafSPeter Avalos 		error("ControlSocket %s already exists, disabling multiplexing",
13219f304aafSPeter Avalos 		    orig_control_path);
13229f304aafSPeter Avalos 		unlink(options.control_path);
13239f304aafSPeter Avalos 		goto disable_mux_master;
13249f304aafSPeter Avalos 	}
13259f304aafSPeter Avalos 	unlink(options.control_path);
132636e94dc5SPeter Avalos 	free(options.control_path);
13279f304aafSPeter Avalos 	options.control_path = orig_control_path;
13289f304aafSPeter Avalos 
132918de8d7fSPeter Avalos 	set_nonblock(muxserver_sock);
1330856ea928SPeter Avalos 
1331ce74bacaSMatthew Dillon 	mux_listener_channel = channel_new(ssh, "mux listener",
1332856ea928SPeter Avalos 	    SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1,
1333856ea928SPeter Avalos 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
13349f304aafSPeter Avalos 	    0, options.control_path, 1);
1335856ea928SPeter Avalos 	mux_listener_channel->mux_rcb = mux_master_read_cb;
133650a69bb5SSascha Wildner 	debug3_f("mux listener channel %d fd %d",
1337856ea928SPeter Avalos 	    mux_listener_channel->self, mux_listener_channel->sock);
133818de8d7fSPeter Avalos }
133918de8d7fSPeter Avalos 
134018de8d7fSPeter Avalos /* Callback on open confirmation in mux master for a mux client session. */
134118de8d7fSPeter Avalos static void
mux_session_confirm(struct ssh * ssh,int id,int success,void * arg)1342ce74bacaSMatthew Dillon mux_session_confirm(struct ssh *ssh, int id, int success, void *arg)
134318de8d7fSPeter Avalos {
134418de8d7fSPeter Avalos 	struct mux_session_confirm_ctx *cctx = arg;
134518de8d7fSPeter Avalos 	const char *display;
1346856ea928SPeter Avalos 	Channel *c, *cc;
1347664f4763Szrj 	int i, r;
1348664f4763Szrj 	struct sshbuf *reply;
134918de8d7fSPeter Avalos 
135018de8d7fSPeter Avalos 	if (cctx == NULL)
135150a69bb5SSascha Wildner 		fatal_f("cctx == NULL");
1352ce74bacaSMatthew Dillon 	if ((c = channel_by_id(ssh, id)) == NULL)
135350a69bb5SSascha Wildner 		fatal_f("no channel for id %d", id);
1354ce74bacaSMatthew Dillon 	if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
135550a69bb5SSascha Wildner 		fatal_f("channel %d lacks control channel %d",
1356856ea928SPeter Avalos 		    id, c->ctl_chan);
1357664f4763Szrj 	if ((reply = sshbuf_new()) == NULL)
135850a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1359856ea928SPeter Avalos 
1360856ea928SPeter Avalos 	if (!success) {
136150a69bb5SSascha Wildner 		debug3_f("sending failure reply");
1362664f4763Szrj 		reply_error(reply, MUX_S_FAILURE, cctx->rid,
1363664f4763Szrj 		    "Session open refused by peer");
1364856ea928SPeter Avalos 		goto done;
1365856ea928SPeter Avalos 	}
136618de8d7fSPeter Avalos 
136718de8d7fSPeter Avalos 	display = getenv("DISPLAY");
136818de8d7fSPeter Avalos 	if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
136918de8d7fSPeter Avalos 		char *proto, *data;
1370856ea928SPeter Avalos 
137118de8d7fSPeter Avalos 		/* Get reasonable local authentication information. */
1372ce74bacaSMatthew Dillon 		if (client_x11_get_proto(ssh, display, options.xauth_location,
1373856ea928SPeter Avalos 		    options.forward_x11_trusted, options.forward_x11_timeout,
1374e9778795SPeter Avalos 		    &proto, &data) == 0) {
137518de8d7fSPeter Avalos 			/* Request forwarding with authentication spoofing. */
1376856ea928SPeter Avalos 			debug("Requesting X11 forwarding with authentication "
1377856ea928SPeter Avalos 			    "spoofing.");
1378ce74bacaSMatthew Dillon 			x11_request_forwarding_with_spoofing(ssh, id,
1379ce74bacaSMatthew Dillon 			    display, proto, data, 1);
13801c188a7fSPeter Avalos 			/* XXX exit_on_forward_failure */
1381ce74bacaSMatthew Dillon 			client_expect_confirm(ssh, id, "X11 forwarding",
1382e9778795SPeter Avalos 			    CONFIRM_WARN);
1383e9778795SPeter Avalos 		}
138418de8d7fSPeter Avalos 	}
138518de8d7fSPeter Avalos 
138618de8d7fSPeter Avalos 	if (cctx->want_agent_fwd && options.forward_agent) {
138718de8d7fSPeter Avalos 		debug("Requesting authentication agent forwarding.");
1388ce74bacaSMatthew Dillon 		channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0);
1389664f4763Szrj 		if ((r = sshpkt_send(ssh)) != 0)
139050a69bb5SSascha Wildner 			fatal_fr(r, "send");
139118de8d7fSPeter Avalos 	}
139218de8d7fSPeter Avalos 
1393ce74bacaSMatthew Dillon 	client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys,
1394664f4763Szrj 	    cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env);
139518de8d7fSPeter Avalos 
139650a69bb5SSascha Wildner 	debug3_f("sending success reply");
1397856ea928SPeter Avalos 	/* prepare reply */
1398664f4763Szrj 	if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 ||
1399664f4763Szrj 	    (r = sshbuf_put_u32(reply, cctx->rid)) != 0 ||
1400664f4763Szrj 	    (r = sshbuf_put_u32(reply, c->self)) != 0)
140150a69bb5SSascha Wildner 		fatal_fr(r, "reply");
1402856ea928SPeter Avalos 
1403856ea928SPeter Avalos  done:
1404856ea928SPeter Avalos 	/* Send reply */
1405664f4763Szrj 	if ((r = sshbuf_put_stringb(cc->output, reply)) != 0)
140650a69bb5SSascha Wildner 		fatal_fr(r, "enqueue");
1407664f4763Szrj 	sshbuf_free(reply);
1408856ea928SPeter Avalos 
1409856ea928SPeter Avalos 	if (cc->mux_pause <= 0)
141050a69bb5SSascha Wildner 		fatal_f("mux_pause %d", cc->mux_pause);
1411856ea928SPeter Avalos 	cc->mux_pause = 0; /* start processing messages again */
141218de8d7fSPeter Avalos 	c->open_confirm_ctx = NULL;
1413664f4763Szrj 	sshbuf_free(cctx->cmd);
141436e94dc5SPeter Avalos 	free(cctx->term);
141518de8d7fSPeter Avalos 	if (cctx->env != NULL) {
141618de8d7fSPeter Avalos 		for (i = 0; cctx->env[i] != NULL; i++)
141736e94dc5SPeter Avalos 			free(cctx->env[i]);
141836e94dc5SPeter Avalos 		free(cctx->env);
141918de8d7fSPeter Avalos 	}
142036e94dc5SPeter Avalos 	free(cctx);
142118de8d7fSPeter Avalos }
142218de8d7fSPeter Avalos 
142318de8d7fSPeter Avalos /* ** Multiplexing client support */
142418de8d7fSPeter Avalos 
142518de8d7fSPeter Avalos /* Exit signal handler */
142618de8d7fSPeter Avalos static void
control_client_sighandler(int signo)142718de8d7fSPeter Avalos control_client_sighandler(int signo)
142818de8d7fSPeter Avalos {
142918de8d7fSPeter Avalos 	muxclient_terminate = signo;
143018de8d7fSPeter Avalos }
143118de8d7fSPeter Avalos 
143218de8d7fSPeter Avalos /*
143318de8d7fSPeter Avalos  * Relay signal handler - used to pass some signals from mux client to
143418de8d7fSPeter Avalos  * mux master.
143518de8d7fSPeter Avalos  */
143618de8d7fSPeter Avalos static void
control_client_sigrelay(int signo)143718de8d7fSPeter Avalos control_client_sigrelay(int signo)
143818de8d7fSPeter Avalos {
143918de8d7fSPeter Avalos 	int save_errno = errno;
144018de8d7fSPeter Avalos 
144118de8d7fSPeter Avalos 	if (muxserver_pid > 1)
144218de8d7fSPeter Avalos 		kill(muxserver_pid, signo);
144318de8d7fSPeter Avalos 
144418de8d7fSPeter Avalos 	errno = save_errno;
144518de8d7fSPeter Avalos }
144618de8d7fSPeter Avalos 
144718de8d7fSPeter Avalos static int
mux_client_read(int fd,struct sshbuf * b,size_t need)1448664f4763Szrj mux_client_read(int fd, struct sshbuf *b, size_t need)
144918de8d7fSPeter Avalos {
1450664f4763Szrj 	size_t have;
1451856ea928SPeter Avalos 	ssize_t len;
1452856ea928SPeter Avalos 	u_char *p;
1453856ea928SPeter Avalos 	struct pollfd pfd;
1454664f4763Szrj 	int r;
145518de8d7fSPeter Avalos 
1456856ea928SPeter Avalos 	pfd.fd = fd;
1457856ea928SPeter Avalos 	pfd.events = POLLIN;
1458664f4763Szrj 	if ((r = sshbuf_reserve(b, need, &p)) != 0)
145950a69bb5SSascha Wildner 		fatal_fr(r, "reserve");
1460856ea928SPeter Avalos 	for (have = 0; have < need; ) {
1461856ea928SPeter Avalos 		if (muxclient_terminate) {
1462856ea928SPeter Avalos 			errno = EINTR;
1463856ea928SPeter Avalos 			return -1;
1464856ea928SPeter Avalos 		}
1465856ea928SPeter Avalos 		len = read(fd, p + have, need - have);
14660cbfa66cSDaniel Fojt 		if (len == -1) {
1467856ea928SPeter Avalos 			switch (errno) {
1468856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1469856ea928SPeter Avalos 			case EWOULDBLOCK:
1470856ea928SPeter Avalos #endif
1471856ea928SPeter Avalos 			case EAGAIN:
1472856ea928SPeter Avalos 				(void)poll(&pfd, 1, -1);
1473856ea928SPeter Avalos 				/* FALLTHROUGH */
1474856ea928SPeter Avalos 			case EINTR:
1475856ea928SPeter Avalos 				continue;
1476856ea928SPeter Avalos 			default:
1477856ea928SPeter Avalos 				return -1;
1478856ea928SPeter Avalos 			}
1479856ea928SPeter Avalos 		}
1480856ea928SPeter Avalos 		if (len == 0) {
1481856ea928SPeter Avalos 			errno = EPIPE;
1482856ea928SPeter Avalos 			return -1;
1483856ea928SPeter Avalos 		}
1484664f4763Szrj 		have += (size_t)len;
1485856ea928SPeter Avalos 	}
1486856ea928SPeter Avalos 	return 0;
1487856ea928SPeter Avalos }
148818de8d7fSPeter Avalos 
1489856ea928SPeter Avalos static int
mux_client_write_packet(int fd,struct sshbuf * m)1490664f4763Szrj mux_client_write_packet(int fd, struct sshbuf *m)
1491856ea928SPeter Avalos {
1492664f4763Szrj 	struct sshbuf *queue;
1493856ea928SPeter Avalos 	u_int have, need;
1494664f4763Szrj 	int r, oerrno, len;
1495664f4763Szrj 	const u_char *ptr;
1496856ea928SPeter Avalos 	struct pollfd pfd;
149718de8d7fSPeter Avalos 
1498856ea928SPeter Avalos 	pfd.fd = fd;
1499856ea928SPeter Avalos 	pfd.events = POLLOUT;
1500664f4763Szrj 	if ((queue = sshbuf_new()) == NULL)
150150a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1502664f4763Szrj 	if ((r = sshbuf_put_stringb(queue, m)) != 0)
150350a69bb5SSascha Wildner 		fatal_fr(r, "enqueue");
1504856ea928SPeter Avalos 
1505664f4763Szrj 	need = sshbuf_len(queue);
1506664f4763Szrj 	ptr = sshbuf_ptr(queue);
1507856ea928SPeter Avalos 
1508856ea928SPeter Avalos 	for (have = 0; have < need; ) {
1509856ea928SPeter Avalos 		if (muxclient_terminate) {
1510664f4763Szrj 			sshbuf_free(queue);
1511856ea928SPeter Avalos 			errno = EINTR;
1512856ea928SPeter Avalos 			return -1;
1513856ea928SPeter Avalos 		}
1514856ea928SPeter Avalos 		len = write(fd, ptr + have, need - have);
15150cbfa66cSDaniel Fojt 		if (len == -1) {
1516856ea928SPeter Avalos 			switch (errno) {
1517856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1518856ea928SPeter Avalos 			case EWOULDBLOCK:
1519856ea928SPeter Avalos #endif
1520856ea928SPeter Avalos 			case EAGAIN:
1521856ea928SPeter Avalos 				(void)poll(&pfd, 1, -1);
1522856ea928SPeter Avalos 				/* FALLTHROUGH */
1523856ea928SPeter Avalos 			case EINTR:
1524856ea928SPeter Avalos 				continue;
1525856ea928SPeter Avalos 			default:
1526856ea928SPeter Avalos 				oerrno = errno;
1527664f4763Szrj 				sshbuf_free(queue);
1528856ea928SPeter Avalos 				errno = oerrno;
1529856ea928SPeter Avalos 				return -1;
1530856ea928SPeter Avalos 			}
1531856ea928SPeter Avalos 		}
1532856ea928SPeter Avalos 		if (len == 0) {
1533664f4763Szrj 			sshbuf_free(queue);
1534856ea928SPeter Avalos 			errno = EPIPE;
1535856ea928SPeter Avalos 			return -1;
1536856ea928SPeter Avalos 		}
1537856ea928SPeter Avalos 		have += (u_int)len;
1538856ea928SPeter Avalos 	}
1539664f4763Szrj 	sshbuf_free(queue);
1540856ea928SPeter Avalos 	return 0;
1541856ea928SPeter Avalos }
1542856ea928SPeter Avalos 
1543856ea928SPeter Avalos static int
mux_client_read_packet(int fd,struct sshbuf * m)1544664f4763Szrj mux_client_read_packet(int fd, struct sshbuf *m)
1545856ea928SPeter Avalos {
1546664f4763Szrj 	struct sshbuf *queue;
1547664f4763Szrj 	size_t need, have;
154836e94dc5SPeter Avalos 	const u_char *ptr;
1549664f4763Szrj 	int r, oerrno;
1550856ea928SPeter Avalos 
1551664f4763Szrj 	if ((queue = sshbuf_new()) == NULL)
155250a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1553664f4763Szrj 	if (mux_client_read(fd, queue, 4) != 0) {
1554856ea928SPeter Avalos 		if ((oerrno = errno) == EPIPE)
155550a69bb5SSascha Wildner 			debug3_f("read header failed: %s",
155636e94dc5SPeter Avalos 			    strerror(errno));
1557664f4763Szrj 		sshbuf_free(queue);
1558856ea928SPeter Avalos 		errno = oerrno;
1559856ea928SPeter Avalos 		return -1;
1560856ea928SPeter Avalos 	}
1561664f4763Szrj 	need = PEEK_U32(sshbuf_ptr(queue));
1562664f4763Szrj 	if (mux_client_read(fd, queue, need) != 0) {
1563856ea928SPeter Avalos 		oerrno = errno;
156450a69bb5SSascha Wildner 		debug3_f("read body failed: %s", strerror(errno));
1565664f4763Szrj 		sshbuf_free(queue);
1566856ea928SPeter Avalos 		errno = oerrno;
1567856ea928SPeter Avalos 		return -1;
1568856ea928SPeter Avalos 	}
1569664f4763Szrj 	if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 ||
1570664f4763Szrj 	    (r = sshbuf_put(m, ptr, have)) != 0)
157150a69bb5SSascha Wildner 		fatal_fr(r, "dequeue");
1572664f4763Szrj 	sshbuf_free(queue);
1573856ea928SPeter Avalos 	return 0;
1574856ea928SPeter Avalos }
1575856ea928SPeter Avalos 
1576856ea928SPeter Avalos static int
mux_client_hello_exchange(int fd)1577856ea928SPeter Avalos mux_client_hello_exchange(int fd)
1578856ea928SPeter Avalos {
1579664f4763Szrj 	struct sshbuf *m;
1580856ea928SPeter Avalos 	u_int type, ver;
1581664f4763Szrj 	int r, ret = -1;
1582856ea928SPeter Avalos 
1583664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
158450a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1585664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 ||
1586664f4763Szrj 	    (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0)
158750a69bb5SSascha Wildner 		fatal_fr(r, "assemble hello");
1588856ea928SPeter Avalos 	/* no extensions */
1589856ea928SPeter Avalos 
1590664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0) {
159150a69bb5SSascha Wildner 		debug_f("write packet: %s", strerror(errno));
1592ce74bacaSMatthew Dillon 		goto out;
1593ce74bacaSMatthew Dillon 	}
1594856ea928SPeter Avalos 
1595664f4763Szrj 	sshbuf_reset(m);
1596856ea928SPeter Avalos 
1597856ea928SPeter Avalos 	/* Read their HELLO */
1598664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
159950a69bb5SSascha Wildner 		debug_f("read packet failed");
1600ce74bacaSMatthew Dillon 		goto out;
1601856ea928SPeter Avalos 	}
1602856ea928SPeter Avalos 
1603664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0)
160450a69bb5SSascha Wildner 		fatal_fr(r, "parse type");
1605ce74bacaSMatthew Dillon 	if (type != MUX_MSG_HELLO) {
160650a69bb5SSascha Wildner 		error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type);
1607ce74bacaSMatthew Dillon 		goto out;
1608ce74bacaSMatthew Dillon 	}
1609664f4763Szrj 	if ((r = sshbuf_get_u32(m, &ver)) != 0)
161050a69bb5SSascha Wildner 		fatal_fr(r, "parse version");
1611ce74bacaSMatthew Dillon 	if (ver != SSHMUX_VER) {
1612ce74bacaSMatthew Dillon 		error("Unsupported multiplexing protocol version %d "
1613856ea928SPeter Avalos 		    "(expected %d)", ver, SSHMUX_VER);
1614ce74bacaSMatthew Dillon 		goto out;
1615ce74bacaSMatthew Dillon 	}
161650a69bb5SSascha Wildner 	debug2_f("master version %u", ver);
1617856ea928SPeter Avalos 	/* No extensions are presently defined */
1618664f4763Szrj 	while (sshbuf_len(m) > 0) {
1619664f4763Szrj 		char *name = NULL;
1620856ea928SPeter Avalos 
1621664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 ||
1622664f4763Szrj 		    (r = sshbuf_skip_string(m)) != 0) { /* value */
162350a69bb5SSascha Wildner 			error_fr(r, "parse extension");
1624664f4763Szrj 			goto out;
1625664f4763Szrj 		}
1626856ea928SPeter Avalos 		debug2("Unrecognised master extension \"%s\"", name);
162736e94dc5SPeter Avalos 		free(name);
1628856ea928SPeter Avalos 	}
1629ce74bacaSMatthew Dillon 	/* success */
1630ce74bacaSMatthew Dillon 	ret = 0;
1631ce74bacaSMatthew Dillon  out:
1632664f4763Szrj 	sshbuf_free(m);
1633ce74bacaSMatthew Dillon 	return ret;
1634856ea928SPeter Avalos }
1635856ea928SPeter Avalos 
1636856ea928SPeter Avalos static u_int
mux_client_request_alive(int fd)1637856ea928SPeter Avalos mux_client_request_alive(int fd)
1638856ea928SPeter Avalos {
1639664f4763Szrj 	struct sshbuf *m;
1640856ea928SPeter Avalos 	char *e;
1641856ea928SPeter Avalos 	u_int pid, type, rid;
1642664f4763Szrj 	int r;
1643856ea928SPeter Avalos 
164450a69bb5SSascha Wildner 	debug3_f("entering");
1645856ea928SPeter Avalos 
1646664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
164750a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1648664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 ||
1649664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
165050a69bb5SSascha Wildner 		fatal_fr(r, "assemble");
1651856ea928SPeter Avalos 
1652664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
165350a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
1654856ea928SPeter Avalos 
1655664f4763Szrj 	sshbuf_reset(m);
1656856ea928SPeter Avalos 
1657856ea928SPeter Avalos 	/* Read their reply */
1658664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
1659664f4763Szrj 		sshbuf_free(m);
1660856ea928SPeter Avalos 		return 0;
1661856ea928SPeter Avalos 	}
1662856ea928SPeter Avalos 
1663664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0)
166450a69bb5SSascha Wildner 		fatal_fr(r, "parse type");
1665856ea928SPeter Avalos 	if (type != MUX_S_ALIVE) {
1666664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
166750a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
166850a69bb5SSascha Wildner 		fatal_f("master returned error: %s", e);
1669856ea928SPeter Avalos 	}
1670856ea928SPeter Avalos 
1671664f4763Szrj 	if ((r = sshbuf_get_u32(m, &rid)) != 0)
167250a69bb5SSascha Wildner 		fatal_fr(r, "parse remote ID");
1673664f4763Szrj 	if (rid != muxclient_request_id)
167450a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
167550a69bb5SSascha Wildner 		    muxclient_request_id, rid);
1676664f4763Szrj 	if ((r = sshbuf_get_u32(m, &pid)) != 0)
167750a69bb5SSascha Wildner 		fatal_fr(r, "parse PID");
1678664f4763Szrj 	sshbuf_free(m);
1679856ea928SPeter Avalos 
168050a69bb5SSascha Wildner 	debug3_f("done pid = %u", pid);
1681856ea928SPeter Avalos 
1682856ea928SPeter Avalos 	muxclient_request_id++;
1683856ea928SPeter Avalos 
1684856ea928SPeter Avalos 	return pid;
1685856ea928SPeter Avalos }
1686856ea928SPeter Avalos 
1687856ea928SPeter Avalos static void
mux_client_request_terminate(int fd)1688856ea928SPeter Avalos mux_client_request_terminate(int fd)
1689856ea928SPeter Avalos {
1690664f4763Szrj 	struct sshbuf *m;
1691856ea928SPeter Avalos 	char *e;
1692856ea928SPeter Avalos 	u_int type, rid;
1693664f4763Szrj 	int r;
1694856ea928SPeter Avalos 
169550a69bb5SSascha Wildner 	debug3_f("entering");
1696856ea928SPeter Avalos 
1697664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
169850a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1699664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 ||
1700664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
170150a69bb5SSascha Wildner 		fatal_fr(r, "request");
1702856ea928SPeter Avalos 
1703664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
170450a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
1705856ea928SPeter Avalos 
1706664f4763Szrj 	sshbuf_reset(m);
1707856ea928SPeter Avalos 
1708856ea928SPeter Avalos 	/* Read their reply */
1709664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
1710856ea928SPeter Avalos 		/* Remote end exited already */
1711856ea928SPeter Avalos 		if (errno == EPIPE) {
1712664f4763Szrj 			sshbuf_free(m);
1713856ea928SPeter Avalos 			return;
1714856ea928SPeter Avalos 		}
171550a69bb5SSascha Wildner 		fatal_f("read from master failed: %s", strerror(errno));
1716856ea928SPeter Avalos 	}
1717856ea928SPeter Avalos 
1718664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1719664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
172050a69bb5SSascha Wildner 		fatal_fr(r, "parse");
1721664f4763Szrj 	if (rid != muxclient_request_id)
172250a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
172350a69bb5SSascha Wildner 		    muxclient_request_id, rid);
1724856ea928SPeter Avalos 	switch (type) {
1725856ea928SPeter Avalos 	case MUX_S_OK:
1726856ea928SPeter Avalos 		break;
1727856ea928SPeter Avalos 	case MUX_S_PERMISSION_DENIED:
1728664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
172950a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
1730856ea928SPeter Avalos 		fatal("Master refused termination request: %s", e);
1731856ea928SPeter Avalos 	case MUX_S_FAILURE:
1732664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
173350a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
173450a69bb5SSascha Wildner 		fatal_f("termination request failed: %s", e);
1735856ea928SPeter Avalos 	default:
173650a69bb5SSascha Wildner 		fatal_f("unexpected response from master 0x%08x", type);
1737856ea928SPeter Avalos 	}
1738664f4763Szrj 	sshbuf_free(m);
1739856ea928SPeter Avalos 	muxclient_request_id++;
1740856ea928SPeter Avalos }
1741856ea928SPeter Avalos 
1742856ea928SPeter Avalos static int
mux_client_forward(int fd,int cancel_flag,u_int ftype,struct Forward * fwd)174336e94dc5SPeter Avalos mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd)
1744856ea928SPeter Avalos {
1745664f4763Szrj 	struct sshbuf *m;
1746856ea928SPeter Avalos 	char *e, *fwd_desc;
1747664f4763Szrj 	const char *lhost, *chost;
1748856ea928SPeter Avalos 	u_int type, rid;
1749664f4763Szrj 	int r;
1750856ea928SPeter Avalos 
1751856ea928SPeter Avalos 	fwd_desc = format_forward(ftype, fwd);
175299e85e0dSPeter Avalos 	debug("Requesting %s %s",
175399e85e0dSPeter Avalos 	    cancel_flag ? "cancellation of" : "forwarding of", fwd_desc);
175436e94dc5SPeter Avalos 	free(fwd_desc);
1755856ea928SPeter Avalos 
1756664f4763Szrj 	type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD;
1757664f4763Szrj 	if (fwd->listen_path != NULL)
1758664f4763Szrj 		lhost = fwd->listen_path;
1759664f4763Szrj 	else if (fwd->listen_host == NULL)
1760664f4763Szrj 		lhost = "";
1761664f4763Szrj 	else if (*fwd->listen_host == '\0')
1762664f4763Szrj 		lhost = "*";
1763664f4763Szrj 	else
1764664f4763Szrj 		lhost = fwd->listen_host;
1765856ea928SPeter Avalos 
1766664f4763Szrj 	if (fwd->connect_path != NULL)
1767664f4763Szrj 		chost = fwd->connect_path;
1768664f4763Szrj 	else if (fwd->connect_host == NULL)
1769664f4763Szrj 		chost = "";
1770664f4763Szrj 	else
1771664f4763Szrj 		chost = fwd->connect_host;
1772664f4763Szrj 
1773664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
177450a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1775664f4763Szrj 	if ((r = sshbuf_put_u32(m, type)) != 0 ||
1776664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
1777664f4763Szrj 	    (r = sshbuf_put_u32(m, ftype)) != 0 ||
1778664f4763Szrj 	    (r = sshbuf_put_cstring(m, lhost)) != 0 ||
1779664f4763Szrj 	    (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 ||
1780664f4763Szrj 	    (r = sshbuf_put_cstring(m, chost)) != 0 ||
1781664f4763Szrj 	    (r = sshbuf_put_u32(m, fwd->connect_port)) != 0)
178250a69bb5SSascha Wildner 		fatal_fr(r, "request");
1783664f4763Szrj 
1784664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
178550a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
1786856ea928SPeter Avalos 
1787664f4763Szrj 	sshbuf_reset(m);
1788856ea928SPeter Avalos 
1789856ea928SPeter Avalos 	/* Read their reply */
1790664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
1791664f4763Szrj 		sshbuf_free(m);
1792856ea928SPeter Avalos 		return -1;
1793856ea928SPeter Avalos 	}
1794856ea928SPeter Avalos 
1795664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1796664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
179750a69bb5SSascha Wildner 		fatal_fr(r, "parse");
1798664f4763Szrj 	if (rid != muxclient_request_id)
179950a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
180050a69bb5SSascha Wildner 		    muxclient_request_id, rid);
1801664f4763Szrj 
1802856ea928SPeter Avalos 	switch (type) {
1803856ea928SPeter Avalos 	case MUX_S_OK:
1804856ea928SPeter Avalos 		break;
1805856ea928SPeter Avalos 	case MUX_S_REMOTE_PORT:
180699e85e0dSPeter Avalos 		if (cancel_flag)
180750a69bb5SSascha Wildner 			fatal_f("got MUX_S_REMOTE_PORT for cancel");
1808664f4763Szrj 		if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0)
180950a69bb5SSascha Wildner 			fatal_fr(r, "parse port");
1810e9778795SPeter Avalos 		verbose("Allocated port %u for remote forward to %s:%d",
1811856ea928SPeter Avalos 		    fwd->allocated_port,
1812856ea928SPeter Avalos 		    fwd->connect_host ? fwd->connect_host : "",
1813856ea928SPeter Avalos 		    fwd->connect_port);
1814856ea928SPeter Avalos 		if (muxclient_command == SSHMUX_COMMAND_FORWARD)
1815e9778795SPeter Avalos 			fprintf(stdout, "%i\n", fwd->allocated_port);
1816856ea928SPeter Avalos 		break;
1817856ea928SPeter Avalos 	case MUX_S_PERMISSION_DENIED:
1818664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
181950a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
1820664f4763Szrj 		sshbuf_free(m);
1821856ea928SPeter Avalos 		error("Master refused forwarding request: %s", e);
1822856ea928SPeter Avalos 		return -1;
1823856ea928SPeter Avalos 	case MUX_S_FAILURE:
1824664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
182550a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
1826664f4763Szrj 		sshbuf_free(m);
182750a69bb5SSascha Wildner 		error_f("forwarding request failed: %s", e);
1828856ea928SPeter Avalos 		return -1;
1829856ea928SPeter Avalos 	default:
183050a69bb5SSascha Wildner 		fatal_f("unexpected response from master 0x%08x", type);
1831856ea928SPeter Avalos 	}
1832664f4763Szrj 	sshbuf_free(m);
1833856ea928SPeter Avalos 
1834856ea928SPeter Avalos 	muxclient_request_id++;
1835856ea928SPeter Avalos 	return 0;
1836856ea928SPeter Avalos }
1837856ea928SPeter Avalos 
1838856ea928SPeter Avalos static int
mux_client_forwards(int fd,int cancel_flag)183999e85e0dSPeter Avalos mux_client_forwards(int fd, int cancel_flag)
1840856ea928SPeter Avalos {
184199e85e0dSPeter Avalos 	int i, ret = 0;
1842856ea928SPeter Avalos 
184350a69bb5SSascha Wildner 	debug3_f("%s forwardings: %d local, %d remote",
184499e85e0dSPeter Avalos 	    cancel_flag ? "cancel" : "request",
1845856ea928SPeter Avalos 	    options.num_local_forwards, options.num_remote_forwards);
1846856ea928SPeter Avalos 
1847856ea928SPeter Avalos 	/* XXX ExitOnForwardingFailure */
1848856ea928SPeter Avalos 	for (i = 0; i < options.num_local_forwards; i++) {
184999e85e0dSPeter Avalos 		if (mux_client_forward(fd, cancel_flag,
1850856ea928SPeter Avalos 		    options.local_forwards[i].connect_port == 0 ?
1851856ea928SPeter Avalos 		    MUX_FWD_DYNAMIC : MUX_FWD_LOCAL,
1852856ea928SPeter Avalos 		    options.local_forwards + i) != 0)
185399e85e0dSPeter Avalos 			ret = -1;
1854856ea928SPeter Avalos 	}
1855856ea928SPeter Avalos 	for (i = 0; i < options.num_remote_forwards; i++) {
185699e85e0dSPeter Avalos 		if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE,
1857856ea928SPeter Avalos 		    options.remote_forwards + i) != 0)
185899e85e0dSPeter Avalos 			ret = -1;
1859856ea928SPeter Avalos 	}
186099e85e0dSPeter Avalos 	return ret;
1861856ea928SPeter Avalos }
1862856ea928SPeter Avalos 
1863856ea928SPeter Avalos static int
mux_client_request_session(int fd)1864856ea928SPeter Avalos mux_client_request_session(int fd)
1865856ea928SPeter Avalos {
1866664f4763Szrj 	struct sshbuf *m;
1867664f4763Szrj 	char *e;
186850a69bb5SSascha Wildner 	const char *term = NULL;
1869*ee116499SAntonio Huete Jimenez 	u_int i, echar, rid, sid, esid, exitval, type, exitval_seen;
1870856ea928SPeter Avalos 	extern char **environ;
1871*ee116499SAntonio Huete Jimenez 	int r, rawmode;
1872856ea928SPeter Avalos 
187350a69bb5SSascha Wildner 	debug3_f("entering");
1874856ea928SPeter Avalos 
1875856ea928SPeter Avalos 	if ((muxserver_pid = mux_client_request_alive(fd)) == 0) {
187650a69bb5SSascha Wildner 		error_f("master alive request failed");
1877856ea928SPeter Avalos 		return -1;
1878856ea928SPeter Avalos 	}
1879856ea928SPeter Avalos 
18800cbfa66cSDaniel Fojt 	ssh_signal(SIGPIPE, SIG_IGN);
1881856ea928SPeter Avalos 
188250a69bb5SSascha Wildner 	if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
188350a69bb5SSascha Wildner 		fatal_f("stdfd_devnull failed");
1884856ea928SPeter Avalos 
188550a69bb5SSascha Wildner 	if ((term = lookup_env_in_list("TERM", options.setenv,
188650a69bb5SSascha Wildner 	    options.num_setenv)) == NULL || *term == '\0')
188750a69bb5SSascha Wildner 		term = getenv("TERM");
188850a69bb5SSascha Wildner 
1889664f4763Szrj 	echar = 0xffffffff;
1890664f4763Szrj 	if (options.escape_char != SSH_ESCAPECHAR_NONE)
1891664f4763Szrj 	    echar = (u_int)options.escape_char;
1892856ea928SPeter Avalos 
1893664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
189450a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
1895664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 ||
1896664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
1897664f4763Szrj 	    (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */
1898664f4763Szrj 	    (r = sshbuf_put_u32(m, tty_flag)) != 0 ||
1899664f4763Szrj 	    (r = sshbuf_put_u32(m, options.forward_x11)) != 0 ||
1900664f4763Szrj 	    (r = sshbuf_put_u32(m, options.forward_agent)) != 0 ||
190150a69bb5SSascha Wildner 	    (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 ||
1902664f4763Szrj 	    (r = sshbuf_put_u32(m, echar)) != 0 ||
190350a69bb5SSascha Wildner 	    (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 ||
1904664f4763Szrj 	    (r = sshbuf_put_stringb(m, command)) != 0)
190550a69bb5SSascha Wildner 		fatal_fr(r, "request");
1906856ea928SPeter Avalos 
1907856ea928SPeter Avalos 	/* Pass environment */
1908664f4763Szrj 	if (options.num_send_env > 0 && environ != NULL) {
1909856ea928SPeter Avalos 		for (i = 0; environ[i] != NULL; i++) {
1910664f4763Szrj 			if (!env_permitted(environ[i]))
1911664f4763Szrj 				continue;
1912664f4763Szrj 			if ((r = sshbuf_put_cstring(m, environ[i])) != 0)
191350a69bb5SSascha Wildner 				fatal_fr(r, "request sendenv");
1914856ea928SPeter Avalos 		}
1915856ea928SPeter Avalos 	}
1916664f4763Szrj 	for (i = 0; i < options.num_setenv; i++) {
1917664f4763Szrj 		if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0)
191850a69bb5SSascha Wildner 			fatal_fr(r, "request setenv");
1919856ea928SPeter Avalos 	}
1920856ea928SPeter Avalos 
1921664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
192250a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
1923856ea928SPeter Avalos 
1924856ea928SPeter Avalos 	/* Send the stdio file descriptors */
1925856ea928SPeter Avalos 	if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
1926856ea928SPeter Avalos 	    mm_send_fd(fd, STDOUT_FILENO) == -1 ||
1927856ea928SPeter Avalos 	    mm_send_fd(fd, STDERR_FILENO) == -1)
192850a69bb5SSascha Wildner 		fatal_f("send fds failed");
1929856ea928SPeter Avalos 
193050a69bb5SSascha Wildner 	debug3_f("session request sent");
1931856ea928SPeter Avalos 
1932856ea928SPeter Avalos 	/* Read their reply */
1933664f4763Szrj 	sshbuf_reset(m);
1934664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
193550a69bb5SSascha Wildner 		error_f("read from master failed: %s", strerror(errno));
1936664f4763Szrj 		sshbuf_free(m);
1937856ea928SPeter Avalos 		return -1;
1938856ea928SPeter Avalos 	}
1939856ea928SPeter Avalos 
1940664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1941664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
194250a69bb5SSascha Wildner 		fatal_fr(r, "parse");
1943664f4763Szrj 	if (rid != muxclient_request_id)
194450a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
194550a69bb5SSascha Wildner 		    muxclient_request_id, rid);
1946664f4763Szrj 
1947856ea928SPeter Avalos 	switch (type) {
1948856ea928SPeter Avalos 	case MUX_S_SESSION_OPENED:
1949664f4763Szrj 		if ((r = sshbuf_get_u32(m, &sid)) != 0)
195050a69bb5SSascha Wildner 			fatal_fr(r, "parse session ID");
195150a69bb5SSascha Wildner 		debug_f("master session id: %u", sid);
1952856ea928SPeter Avalos 		break;
1953856ea928SPeter Avalos 	case MUX_S_PERMISSION_DENIED:
1954664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
195550a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
19569f304aafSPeter Avalos 		error("Master refused session request: %s", e);
1957664f4763Szrj 		sshbuf_free(m);
1958856ea928SPeter Avalos 		return -1;
1959856ea928SPeter Avalos 	case MUX_S_FAILURE:
1960664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
196150a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
196250a69bb5SSascha Wildner 		error_f("session request failed: %s", e);
1963664f4763Szrj 		sshbuf_free(m);
1964856ea928SPeter Avalos 		return -1;
1965856ea928SPeter Avalos 	default:
1966664f4763Szrj 		sshbuf_free(m);
196750a69bb5SSascha Wildner 		error_f("unexpected response from master 0x%08x", type);
1968856ea928SPeter Avalos 		return -1;
1969856ea928SPeter Avalos 	}
1970856ea928SPeter Avalos 	muxclient_request_id++;
1971856ea928SPeter Avalos 
1972e9778795SPeter Avalos 	if (pledge("stdio proc tty", NULL) == -1)
197350a69bb5SSascha Wildner 		fatal_f("pledge(): %s", strerror(errno));
1974e9778795SPeter Avalos 	platform_pledge_mux();
1975e9778795SPeter Avalos 
19760cbfa66cSDaniel Fojt 	ssh_signal(SIGHUP, control_client_sighandler);
19770cbfa66cSDaniel Fojt 	ssh_signal(SIGINT, control_client_sighandler);
19780cbfa66cSDaniel Fojt 	ssh_signal(SIGTERM, control_client_sighandler);
19790cbfa66cSDaniel Fojt 	ssh_signal(SIGWINCH, control_client_sigrelay);
1980856ea928SPeter Avalos 
19811c188a7fSPeter Avalos 	rawmode = tty_flag;
1982856ea928SPeter Avalos 	if (tty_flag)
19831c188a7fSPeter Avalos 		enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
1984856ea928SPeter Avalos 
1985856ea928SPeter Avalos 	/*
1986856ea928SPeter Avalos 	 * Stick around until the controlee closes the client_fd.
1987856ea928SPeter Avalos 	 * Before it does, it is expected to write an exit message.
1988856ea928SPeter Avalos 	 * This process must read the value and wait for the closure of
1989856ea928SPeter Avalos 	 * the client_fd; if this one closes early, the multiplex master will
1990856ea928SPeter Avalos 	 * terminate early too (possibly losing data).
1991856ea928SPeter Avalos 	 */
1992856ea928SPeter Avalos 	for (exitval = 255, exitval_seen = 0;;) {
1993664f4763Szrj 		sshbuf_reset(m);
1994664f4763Szrj 		if (mux_client_read_packet(fd, m) != 0)
1995856ea928SPeter Avalos 			break;
1996664f4763Szrj 		if ((r = sshbuf_get_u32(m, &type)) != 0)
199750a69bb5SSascha Wildner 			fatal_fr(r, "parse type");
19981c188a7fSPeter Avalos 		switch (type) {
19991c188a7fSPeter Avalos 		case MUX_S_TTY_ALLOC_FAIL:
2000664f4763Szrj 			if ((r = sshbuf_get_u32(m, &esid)) != 0)
200150a69bb5SSascha Wildner 				fatal_fr(r, "parse session ID");
2002664f4763Szrj 			if (esid != sid)
200350a69bb5SSascha Wildner 				fatal_f("tty alloc fail on unknown session: "
200450a69bb5SSascha Wildner 				    "my id %u theirs %u", sid, esid);
20051c188a7fSPeter Avalos 			leave_raw_mode(options.request_tty ==
20061c188a7fSPeter Avalos 			    REQUEST_TTY_FORCE);
20071c188a7fSPeter Avalos 			rawmode = 0;
20081c188a7fSPeter Avalos 			continue;
20091c188a7fSPeter Avalos 		case MUX_S_EXIT_MESSAGE:
2010664f4763Szrj 			if ((r = sshbuf_get_u32(m, &esid)) != 0)
201150a69bb5SSascha Wildner 				fatal_fr(r, "parse session ID");
2012664f4763Szrj 			if (esid != sid)
201350a69bb5SSascha Wildner 				fatal_f("exit on unknown session: "
201450a69bb5SSascha Wildner 				    "my id %u theirs %u", sid, esid);
2015856ea928SPeter Avalos 			if (exitval_seen)
201650a69bb5SSascha Wildner 				fatal_f("exitval sent twice");
2017664f4763Szrj 			if ((r = sshbuf_get_u32(m, &exitval)) != 0)
201850a69bb5SSascha Wildner 				fatal_fr(r, "parse exitval");
2019856ea928SPeter Avalos 			exitval_seen = 1;
20201c188a7fSPeter Avalos 			continue;
20211c188a7fSPeter Avalos 		default:
2022664f4763Szrj 			if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
202350a69bb5SSascha Wildner 				fatal_fr(r, "parse error message");
202450a69bb5SSascha Wildner 			fatal_f("master returned error: %s", e);
20251c188a7fSPeter Avalos 		}
2026856ea928SPeter Avalos 	}
2027856ea928SPeter Avalos 
2028856ea928SPeter Avalos 	close(fd);
20291c188a7fSPeter Avalos 	if (rawmode)
20301c188a7fSPeter Avalos 		leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
2031856ea928SPeter Avalos 
2032856ea928SPeter Avalos 	if (muxclient_terminate) {
2033ce74bacaSMatthew Dillon 		debug2("Exiting on signal: %s", strsignal(muxclient_terminate));
2034856ea928SPeter Avalos 		exitval = 255;
2035856ea928SPeter Avalos 	} else if (!exitval_seen) {
2036856ea928SPeter Avalos 		debug2("Control master terminated unexpectedly");
2037856ea928SPeter Avalos 		exitval = 255;
2038856ea928SPeter Avalos 	} else
2039856ea928SPeter Avalos 		debug2("Received exit status from master %d", exitval);
2040856ea928SPeter Avalos 
2041*ee116499SAntonio Huete Jimenez 	if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO)
2042856ea928SPeter Avalos 		fprintf(stderr, "Shared connection to %s closed.\r\n", host);
2043856ea928SPeter Avalos 
2044856ea928SPeter Avalos 	exit(exitval);
2045856ea928SPeter Avalos }
2046856ea928SPeter Avalos 
2047856ea928SPeter Avalos static int
mux_client_proxy(int fd)2048ce74bacaSMatthew Dillon mux_client_proxy(int fd)
2049ce74bacaSMatthew Dillon {
2050664f4763Szrj 	struct sshbuf *m;
2051ce74bacaSMatthew Dillon 	char *e;
2052ce74bacaSMatthew Dillon 	u_int type, rid;
2053664f4763Szrj 	int r;
2054ce74bacaSMatthew Dillon 
2055664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
205650a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
2057664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 ||
2058664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
205950a69bb5SSascha Wildner 		fatal_fr(r, "request");
2060664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
206150a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
2062ce74bacaSMatthew Dillon 
2063664f4763Szrj 	sshbuf_reset(m);
2064ce74bacaSMatthew Dillon 
2065ce74bacaSMatthew Dillon 	/* Read their reply */
2066664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
2067664f4763Szrj 		sshbuf_free(m);
2068ce74bacaSMatthew Dillon 		return 0;
2069ce74bacaSMatthew Dillon 	}
2070664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2071664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
207250a69bb5SSascha Wildner 		fatal_fr(r, "parse");
2073664f4763Szrj 	if (rid != muxclient_request_id)
207450a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
207550a69bb5SSascha Wildner 		    muxclient_request_id, rid);
2076664f4763Szrj 	if (type != MUX_S_PROXY) {
2077664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
207850a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
207950a69bb5SSascha Wildner 		fatal_f("master returned error: %s", e);
2080664f4763Szrj 	}
2081664f4763Szrj 	sshbuf_free(m);
2082ce74bacaSMatthew Dillon 
208350a69bb5SSascha Wildner 	debug3_f("done");
2084ce74bacaSMatthew Dillon 	muxclient_request_id++;
2085ce74bacaSMatthew Dillon 	return 0;
2086ce74bacaSMatthew Dillon }
2087ce74bacaSMatthew Dillon 
2088ce74bacaSMatthew Dillon static int
mux_client_request_stdio_fwd(int fd)2089856ea928SPeter Avalos mux_client_request_stdio_fwd(int fd)
2090856ea928SPeter Avalos {
2091664f4763Szrj 	struct sshbuf *m;
2092856ea928SPeter Avalos 	char *e;
2093856ea928SPeter Avalos 	u_int type, rid, sid;
209450a69bb5SSascha Wildner 	int r;
2095856ea928SPeter Avalos 
209650a69bb5SSascha Wildner 	debug3_f("entering");
2097856ea928SPeter Avalos 
2098856ea928SPeter Avalos 	if ((muxserver_pid = mux_client_request_alive(fd)) == 0) {
209950a69bb5SSascha Wildner 		error_f("master alive request failed");
2100856ea928SPeter Avalos 		return -1;
2101856ea928SPeter Avalos 	}
2102856ea928SPeter Avalos 
21030cbfa66cSDaniel Fojt 	ssh_signal(SIGPIPE, SIG_IGN);
2104856ea928SPeter Avalos 
210550a69bb5SSascha Wildner 	if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
210650a69bb5SSascha Wildner 		fatal_f("stdfd_devnull failed");
2107856ea928SPeter Avalos 
2108664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
210950a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
2110664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 ||
2111664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
2112664f4763Szrj 	    (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */
2113664f4763Szrj 	    (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 ||
2114664f4763Szrj 	    (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0)
211550a69bb5SSascha Wildner 		fatal_fr(r, "request");
2116856ea928SPeter Avalos 
2117664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
211850a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
2119856ea928SPeter Avalos 
2120856ea928SPeter Avalos 	/* Send the stdio file descriptors */
2121856ea928SPeter Avalos 	if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
2122856ea928SPeter Avalos 	    mm_send_fd(fd, STDOUT_FILENO) == -1)
212350a69bb5SSascha Wildner 		fatal_f("send fds failed");
2124856ea928SPeter Avalos 
2125e9778795SPeter Avalos 	if (pledge("stdio proc tty", NULL) == -1)
212650a69bb5SSascha Wildner 		fatal_f("pledge(): %s", strerror(errno));
2127e9778795SPeter Avalos 	platform_pledge_mux();
2128e9778795SPeter Avalos 
212950a69bb5SSascha Wildner 	debug3_f("stdio forward request sent");
2130856ea928SPeter Avalos 
2131856ea928SPeter Avalos 	/* Read their reply */
2132664f4763Szrj 	sshbuf_reset(m);
2133856ea928SPeter Avalos 
2134664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
213550a69bb5SSascha Wildner 		error_f("read from master failed: %s", strerror(errno));
2136664f4763Szrj 		sshbuf_free(m);
2137856ea928SPeter Avalos 		return -1;
2138856ea928SPeter Avalos 	}
2139856ea928SPeter Avalos 
2140664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2141664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
214250a69bb5SSascha Wildner 		fatal_fr(r, "parse");
2143664f4763Szrj 	if (rid != muxclient_request_id)
214450a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
214550a69bb5SSascha Wildner 		    muxclient_request_id, rid);
2146856ea928SPeter Avalos 	switch (type) {
2147856ea928SPeter Avalos 	case MUX_S_SESSION_OPENED:
2148664f4763Szrj 		if ((r = sshbuf_get_u32(m, &sid)) != 0)
214950a69bb5SSascha Wildner 			fatal_fr(r, "parse session ID");
215050a69bb5SSascha Wildner 		debug_f("master session id: %u", sid);
2151856ea928SPeter Avalos 		break;
2152856ea928SPeter Avalos 	case MUX_S_PERMISSION_DENIED:
2153664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
215450a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
2155664f4763Szrj 		sshbuf_free(m);
21569f304aafSPeter Avalos 		fatal("Master refused stdio forwarding request: %s", e);
2157856ea928SPeter Avalos 	case MUX_S_FAILURE:
2158664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
215950a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
2160664f4763Szrj 		sshbuf_free(m);
216136e94dc5SPeter Avalos 		fatal("Stdio forwarding request failed: %s", e);
2162856ea928SPeter Avalos 	default:
2163664f4763Szrj 		sshbuf_free(m);
216450a69bb5SSascha Wildner 		error_f("unexpected response from master 0x%08x", type);
2165856ea928SPeter Avalos 		return -1;
2166856ea928SPeter Avalos 	}
2167856ea928SPeter Avalos 	muxclient_request_id++;
2168856ea928SPeter Avalos 
21690cbfa66cSDaniel Fojt 	ssh_signal(SIGHUP, control_client_sighandler);
21700cbfa66cSDaniel Fojt 	ssh_signal(SIGINT, control_client_sighandler);
21710cbfa66cSDaniel Fojt 	ssh_signal(SIGTERM, control_client_sighandler);
21720cbfa66cSDaniel Fojt 	ssh_signal(SIGWINCH, control_client_sigrelay);
2173856ea928SPeter Avalos 
2174856ea928SPeter Avalos 	/*
2175856ea928SPeter Avalos 	 * Stick around until the controlee closes the client_fd.
2176856ea928SPeter Avalos 	 */
2177664f4763Szrj 	sshbuf_reset(m);
2178664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0) {
2179856ea928SPeter Avalos 		if (errno == EPIPE ||
2180856ea928SPeter Avalos 		    (errno == EINTR && muxclient_terminate != 0))
2181856ea928SPeter Avalos 			return 0;
218250a69bb5SSascha Wildner 		fatal_f("mux_client_read_packet: %s", strerror(errno));
2183856ea928SPeter Avalos 	}
218450a69bb5SSascha Wildner 	fatal_f("master returned unexpected message %u", type);
218518de8d7fSPeter Avalos }
218618de8d7fSPeter Avalos 
21871c188a7fSPeter Avalos static void
mux_client_request_stop_listening(int fd)21881c188a7fSPeter Avalos mux_client_request_stop_listening(int fd)
21891c188a7fSPeter Avalos {
2190664f4763Szrj 	struct sshbuf *m;
21911c188a7fSPeter Avalos 	char *e;
21921c188a7fSPeter Avalos 	u_int type, rid;
2193664f4763Szrj 	int r;
21941c188a7fSPeter Avalos 
219550a69bb5SSascha Wildner 	debug3_f("entering");
21961c188a7fSPeter Avalos 
2197664f4763Szrj 	if ((m = sshbuf_new()) == NULL)
219850a69bb5SSascha Wildner 		fatal_f("sshbuf_new");
2199664f4763Szrj 	if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 ||
2200664f4763Szrj 	    (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
220150a69bb5SSascha Wildner 		fatal_fr(r, "request");
22021c188a7fSPeter Avalos 
2203664f4763Szrj 	if (mux_client_write_packet(fd, m) != 0)
220450a69bb5SSascha Wildner 		fatal_f("write packet: %s", strerror(errno));
22051c188a7fSPeter Avalos 
2206664f4763Szrj 	sshbuf_reset(m);
22071c188a7fSPeter Avalos 
22081c188a7fSPeter Avalos 	/* Read their reply */
2209664f4763Szrj 	if (mux_client_read_packet(fd, m) != 0)
221050a69bb5SSascha Wildner 		fatal_f("read from master failed: %s", strerror(errno));
22111c188a7fSPeter Avalos 
2212664f4763Szrj 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2213664f4763Szrj 	    (r = sshbuf_get_u32(m, &rid)) != 0)
221450a69bb5SSascha Wildner 		fatal_fr(r, "parse");
2215664f4763Szrj 	if (rid != muxclient_request_id)
221650a69bb5SSascha Wildner 		fatal_f("out of sequence reply: my id %u theirs %u",
221750a69bb5SSascha Wildner 		    muxclient_request_id, rid);
2218664f4763Szrj 
22191c188a7fSPeter Avalos 	switch (type) {
22201c188a7fSPeter Avalos 	case MUX_S_OK:
22211c188a7fSPeter Avalos 		break;
22221c188a7fSPeter Avalos 	case MUX_S_PERMISSION_DENIED:
2223664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
222450a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
22251c188a7fSPeter Avalos 		fatal("Master refused stop listening request: %s", e);
22261c188a7fSPeter Avalos 	case MUX_S_FAILURE:
2227664f4763Szrj 		if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
222850a69bb5SSascha Wildner 			fatal_fr(r, "parse error message");
222950a69bb5SSascha Wildner 		fatal_f("stop listening request failed: %s", e);
22301c188a7fSPeter Avalos 	default:
223150a69bb5SSascha Wildner 		fatal_f("unexpected response from master 0x%08x", type);
22321c188a7fSPeter Avalos 	}
2233664f4763Szrj 	sshbuf_free(m);
22341c188a7fSPeter Avalos 	muxclient_request_id++;
22351c188a7fSPeter Avalos }
22361c188a7fSPeter Avalos 
223718de8d7fSPeter Avalos /* Multiplex client main loop. */
2238ce74bacaSMatthew Dillon int
muxclient(const char * path)223918de8d7fSPeter Avalos muxclient(const char *path)
224018de8d7fSPeter Avalos {
224118de8d7fSPeter Avalos 	struct sockaddr_un addr;
2242856ea928SPeter Avalos 	int sock;
2243856ea928SPeter Avalos 	u_int pid;
224418de8d7fSPeter Avalos 
2245856ea928SPeter Avalos 	if (muxclient_command == 0) {
2246e9778795SPeter Avalos 		if (options.stdio_forward_host != NULL)
2247856ea928SPeter Avalos 			muxclient_command = SSHMUX_COMMAND_STDIO_FWD;
2248856ea928SPeter Avalos 		else
224918de8d7fSPeter Avalos 			muxclient_command = SSHMUX_COMMAND_OPEN;
2250856ea928SPeter Avalos 	}
225118de8d7fSPeter Avalos 
225218de8d7fSPeter Avalos 	switch (options.control_master) {
225318de8d7fSPeter Avalos 	case SSHCTL_MASTER_AUTO:
225418de8d7fSPeter Avalos 	case SSHCTL_MASTER_AUTO_ASK:
225518de8d7fSPeter Avalos 		debug("auto-mux: Trying existing master");
225618de8d7fSPeter Avalos 		/* FALLTHROUGH */
225718de8d7fSPeter Avalos 	case SSHCTL_MASTER_NO:
225818de8d7fSPeter Avalos 		break;
225918de8d7fSPeter Avalos 	default:
2260ce74bacaSMatthew Dillon 		return -1;
226118de8d7fSPeter Avalos 	}
226218de8d7fSPeter Avalos 
226318de8d7fSPeter Avalos 	memset(&addr, '\0', sizeof(addr));
226418de8d7fSPeter Avalos 	addr.sun_family = AF_UNIX;
226518de8d7fSPeter Avalos 
226618de8d7fSPeter Avalos 	if (strlcpy(addr.sun_path, path,
226718de8d7fSPeter Avalos 	    sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
2268ce74bacaSMatthew Dillon 		fatal("ControlPath too long ('%s' >= %u bytes)", path,
2269ce74bacaSMatthew Dillon 		    (unsigned int)sizeof(addr.sun_path));
227018de8d7fSPeter Avalos 
22710cbfa66cSDaniel Fojt 	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
227250a69bb5SSascha Wildner 		fatal_f("socket(): %s", strerror(errno));
227318de8d7fSPeter Avalos 
2274ce74bacaSMatthew Dillon 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
2275856ea928SPeter Avalos 		switch (muxclient_command) {
2276856ea928SPeter Avalos 		case SSHMUX_COMMAND_OPEN:
2277856ea928SPeter Avalos 		case SSHMUX_COMMAND_STDIO_FWD:
2278856ea928SPeter Avalos 			break;
2279856ea928SPeter Avalos 		default:
228018de8d7fSPeter Avalos 			fatal("Control socket connect(%.100s): %s", path,
228118de8d7fSPeter Avalos 			    strerror(errno));
228218de8d7fSPeter Avalos 		}
22839f304aafSPeter Avalos 		if (errno == ECONNREFUSED &&
22849f304aafSPeter Avalos 		    options.control_master != SSHCTL_MASTER_NO) {
22859f304aafSPeter Avalos 			debug("Stale control socket %.100s, unlinking", path);
22869f304aafSPeter Avalos 			unlink(path);
22879f304aafSPeter Avalos 		} else if (errno == ENOENT) {
228818de8d7fSPeter Avalos 			debug("Control socket \"%.100s\" does not exist", path);
22899f304aafSPeter Avalos 		} else {
229018de8d7fSPeter Avalos 			error("Control socket connect(%.100s): %s", path,
229118de8d7fSPeter Avalos 			    strerror(errno));
229218de8d7fSPeter Avalos 		}
229318de8d7fSPeter Avalos 		close(sock);
2294ce74bacaSMatthew Dillon 		return -1;
229518de8d7fSPeter Avalos 	}
2296856ea928SPeter Avalos 	set_nonblock(sock);
229718de8d7fSPeter Avalos 
2298856ea928SPeter Avalos 	if (mux_client_hello_exchange(sock) != 0) {
229950a69bb5SSascha Wildner 		error_f("master hello exchange failed");
230018de8d7fSPeter Avalos 		close(sock);
2301ce74bacaSMatthew Dillon 		return -1;
230218de8d7fSPeter Avalos 	}
230318de8d7fSPeter Avalos 
230418de8d7fSPeter Avalos 	switch (muxclient_command) {
230518de8d7fSPeter Avalos 	case SSHMUX_COMMAND_ALIVE_CHECK:
2306856ea928SPeter Avalos 		if ((pid = mux_client_request_alive(sock)) == 0)
230750a69bb5SSascha Wildner 			fatal_f("master alive check failed");
2308e9778795SPeter Avalos 		fprintf(stderr, "Master running (pid=%u)\r\n", pid);
230918de8d7fSPeter Avalos 		exit(0);
231018de8d7fSPeter Avalos 	case SSHMUX_COMMAND_TERMINATE:
2311856ea928SPeter Avalos 		mux_client_request_terminate(sock);
2312ce74bacaSMatthew Dillon 		if (options.log_level != SYSLOG_LEVEL_QUIET)
231318de8d7fSPeter Avalos 			fprintf(stderr, "Exit request sent.\r\n");
231418de8d7fSPeter Avalos 		exit(0);
2315856ea928SPeter Avalos 	case SSHMUX_COMMAND_FORWARD:
231699e85e0dSPeter Avalos 		if (mux_client_forwards(sock, 0) != 0)
231750a69bb5SSascha Wildner 			fatal_f("master forward request failed");
2318856ea928SPeter Avalos 		exit(0);
231918de8d7fSPeter Avalos 	case SSHMUX_COMMAND_OPEN:
232099e85e0dSPeter Avalos 		if (mux_client_forwards(sock, 0) != 0) {
232150a69bb5SSascha Wildner 			error_f("master forward request failed");
2322ce74bacaSMatthew Dillon 			return -1;
232318de8d7fSPeter Avalos 		}
2324856ea928SPeter Avalos 		mux_client_request_session(sock);
2325ce74bacaSMatthew Dillon 		return -1;
2326856ea928SPeter Avalos 	case SSHMUX_COMMAND_STDIO_FWD:
2327856ea928SPeter Avalos 		mux_client_request_stdio_fwd(sock);
2328856ea928SPeter Avalos 		exit(0);
23291c188a7fSPeter Avalos 	case SSHMUX_COMMAND_STOP:
23301c188a7fSPeter Avalos 		mux_client_request_stop_listening(sock);
2331ce74bacaSMatthew Dillon 		if (options.log_level != SYSLOG_LEVEL_QUIET)
23321c188a7fSPeter Avalos 			fprintf(stderr, "Stop listening request sent.\r\n");
23331c188a7fSPeter Avalos 		exit(0);
233499e85e0dSPeter Avalos 	case SSHMUX_COMMAND_CANCEL_FWD:
233599e85e0dSPeter Avalos 		if (mux_client_forwards(sock, 1) != 0)
233650a69bb5SSascha Wildner 			error_f("master cancel forward request failed");
233799e85e0dSPeter Avalos 		exit(0);
2338ce74bacaSMatthew Dillon 	case SSHMUX_COMMAND_PROXY:
2339ce74bacaSMatthew Dillon 		mux_client_proxy(sock);
2340ce74bacaSMatthew Dillon 		return (sock);
234118de8d7fSPeter Avalos 	default:
234218de8d7fSPeter Avalos 		fatal("unrecognised muxclient_command %d", muxclient_command);
234318de8d7fSPeter Avalos 	}
234418de8d7fSPeter Avalos }
2345