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