1*ba1276acSMatthew Dillon /* $OpenBSD: mux.c,v 1.101 2023/11/23 03:37:05 dtucker 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>
29*ba1276acSMatthew Dillon #include <limits.h>
3018de8d7fSPeter Avalos #include <signal.h>
3118de8d7fSPeter Avalos #include <stdarg.h>
3218de8d7fSPeter Avalos #include <stddef.h>
3318de8d7fSPeter Avalos #include <stdlib.h>
3418de8d7fSPeter Avalos #include <stdio.h>
3518de8d7fSPeter Avalos #include <string.h>
3618de8d7fSPeter Avalos #include <unistd.h>
3718de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
3818de8d7fSPeter Avalos #include <paths.h>
3918de8d7fSPeter Avalos #endif
4018de8d7fSPeter Avalos
41856ea928SPeter Avalos #ifdef HAVE_POLL_H
42856ea928SPeter Avalos #include <poll.h>
43856ea928SPeter Avalos #else
44856ea928SPeter Avalos # ifdef HAVE_SYS_POLL_H
45856ea928SPeter Avalos # include <sys/poll.h>
46856ea928SPeter Avalos # endif
47856ea928SPeter Avalos #endif
48856ea928SPeter Avalos
4918de8d7fSPeter Avalos #ifdef HAVE_UTIL_H
5018de8d7fSPeter Avalos # include <util.h>
5118de8d7fSPeter Avalos #endif
5218de8d7fSPeter Avalos
5318de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
5418de8d7fSPeter Avalos #include "xmalloc.h"
5518de8d7fSPeter Avalos #include "log.h"
5618de8d7fSPeter Avalos #include "ssh.h"
57856ea928SPeter Avalos #include "ssh2.h"
5818de8d7fSPeter Avalos #include "pathnames.h"
5918de8d7fSPeter Avalos #include "misc.h"
6018de8d7fSPeter Avalos #include "match.h"
61664f4763Szrj #include "sshbuf.h"
6218de8d7fSPeter Avalos #include "channels.h"
6318de8d7fSPeter Avalos #include "msg.h"
6418de8d7fSPeter Avalos #include "packet.h"
6518de8d7fSPeter Avalos #include "monitor_fdpass.h"
6618de8d7fSPeter Avalos #include "sshpty.h"
67664f4763Szrj #include "sshkey.h"
6818de8d7fSPeter Avalos #include "readconf.h"
6918de8d7fSPeter Avalos #include "clientloop.h"
70ce74bacaSMatthew Dillon #include "ssherr.h"
71*ba1276acSMatthew Dillon #include "misc.h"
7218de8d7fSPeter Avalos
7318de8d7fSPeter Avalos /* from ssh.c */
7418de8d7fSPeter Avalos extern int tty_flag;
7518de8d7fSPeter Avalos extern Options options;
7618de8d7fSPeter Avalos extern char *host;
77664f4763Szrj extern struct sshbuf *command;
78856ea928SPeter Avalos extern volatile sig_atomic_t quit_pending;
7918de8d7fSPeter Avalos
8018de8d7fSPeter Avalos /* Context for session open confirmation callback */
8118de8d7fSPeter Avalos struct mux_session_confirm_ctx {
82856ea928SPeter Avalos u_int want_tty;
83856ea928SPeter Avalos u_int want_subsys;
84856ea928SPeter Avalos u_int want_x_fwd;
85856ea928SPeter Avalos u_int want_agent_fwd;
86664f4763Szrj struct sshbuf *cmd;
8718de8d7fSPeter Avalos char *term;
8818de8d7fSPeter Avalos struct termios tio;
8918de8d7fSPeter Avalos char **env;
90856ea928SPeter Avalos u_int rid;
91856ea928SPeter Avalos };
92856ea928SPeter Avalos
9336e94dc5SPeter Avalos /* Context for stdio fwd open confirmation callback */
9436e94dc5SPeter Avalos struct mux_stdio_confirm_ctx {
9536e94dc5SPeter Avalos u_int rid;
9636e94dc5SPeter Avalos };
9736e94dc5SPeter Avalos
98856ea928SPeter Avalos /* Context for global channel callback */
99856ea928SPeter Avalos struct mux_channel_confirm_ctx {
100856ea928SPeter Avalos u_int cid; /* channel id */
101856ea928SPeter Avalos u_int rid; /* request id */
102856ea928SPeter Avalos int fid; /* forward id */
10318de8d7fSPeter Avalos };
10418de8d7fSPeter Avalos
10518de8d7fSPeter Avalos /* fd to control socket */
10618de8d7fSPeter Avalos int muxserver_sock = -1;
10718de8d7fSPeter Avalos
108856ea928SPeter Avalos /* client request id */
109856ea928SPeter Avalos u_int muxclient_request_id = 0;
110856ea928SPeter Avalos
11118de8d7fSPeter Avalos /* Multiplexing control command */
11218de8d7fSPeter Avalos u_int muxclient_command = 0;
11318de8d7fSPeter Avalos
11418de8d7fSPeter Avalos /* Set when signalled. */
11518de8d7fSPeter Avalos static volatile sig_atomic_t muxclient_terminate = 0;
11618de8d7fSPeter Avalos
11718de8d7fSPeter Avalos /* PID of multiplex server */
11818de8d7fSPeter Avalos static u_int muxserver_pid = 0;
11918de8d7fSPeter Avalos
120856ea928SPeter Avalos static Channel *mux_listener_channel = NULL;
12118de8d7fSPeter Avalos
122856ea928SPeter Avalos struct mux_master_state {
123856ea928SPeter Avalos int hello_rcvd;
124856ea928SPeter Avalos };
125856ea928SPeter Avalos
126856ea928SPeter Avalos /* mux protocol messages */
127856ea928SPeter Avalos #define MUX_MSG_HELLO 0x00000001
128856ea928SPeter Avalos #define MUX_C_NEW_SESSION 0x10000002
129856ea928SPeter Avalos #define MUX_C_ALIVE_CHECK 0x10000004
130856ea928SPeter Avalos #define MUX_C_TERMINATE 0x10000005
131856ea928SPeter Avalos #define MUX_C_OPEN_FWD 0x10000006
132856ea928SPeter Avalos #define MUX_C_CLOSE_FWD 0x10000007
133856ea928SPeter Avalos #define MUX_C_NEW_STDIO_FWD 0x10000008
1341c188a7fSPeter Avalos #define MUX_C_STOP_LISTENING 0x10000009
135ce74bacaSMatthew Dillon #define MUX_C_PROXY 0x1000000f
136856ea928SPeter Avalos #define MUX_S_OK 0x80000001
137856ea928SPeter Avalos #define MUX_S_PERMISSION_DENIED 0x80000002
138856ea928SPeter Avalos #define MUX_S_FAILURE 0x80000003
139856ea928SPeter Avalos #define MUX_S_EXIT_MESSAGE 0x80000004
140856ea928SPeter Avalos #define MUX_S_ALIVE 0x80000005
141856ea928SPeter Avalos #define MUX_S_SESSION_OPENED 0x80000006
142856ea928SPeter Avalos #define MUX_S_REMOTE_PORT 0x80000007
1431c188a7fSPeter Avalos #define MUX_S_TTY_ALLOC_FAIL 0x80000008
144ce74bacaSMatthew Dillon #define MUX_S_PROXY 0x8000000f
145856ea928SPeter Avalos
146856ea928SPeter Avalos /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */
147856ea928SPeter Avalos #define MUX_FWD_LOCAL 1
148856ea928SPeter Avalos #define MUX_FWD_REMOTE 2
149856ea928SPeter Avalos #define MUX_FWD_DYNAMIC 3
150856ea928SPeter Avalos
151ce74bacaSMatthew Dillon static void mux_session_confirm(struct ssh *, int, int, void *);
152ce74bacaSMatthew Dillon static void mux_stdio_confirm(struct ssh *, int, int, void *);
153856ea928SPeter Avalos
154664f4763Szrj static int mux_master_process_hello(struct ssh *, u_int,
155ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
156664f4763Szrj static int mux_master_process_new_session(struct ssh *, u_int,
157ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
158664f4763Szrj static int mux_master_process_alive_check(struct ssh *, u_int,
159ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
160664f4763Szrj static int mux_master_process_terminate(struct ssh *, u_int,
161ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
162664f4763Szrj static int mux_master_process_open_fwd(struct ssh *, u_int,
163ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
164664f4763Szrj static int mux_master_process_close_fwd(struct ssh *, u_int,
165ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
166664f4763Szrj static int mux_master_process_stdio_fwd(struct ssh *, u_int,
167ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
168664f4763Szrj static int mux_master_process_stop_listening(struct ssh *, u_int,
169ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
170664f4763Szrj static int mux_master_process_proxy(struct ssh *, u_int,
171ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *);
172856ea928SPeter Avalos
173856ea928SPeter Avalos static const struct {
174856ea928SPeter Avalos u_int type;
175ce74bacaSMatthew Dillon int (*handler)(struct ssh *, u_int, Channel *,
176ce74bacaSMatthew Dillon struct sshbuf *, struct sshbuf *);
177856ea928SPeter Avalos } mux_master_handlers[] = {
178664f4763Szrj { MUX_MSG_HELLO, mux_master_process_hello },
179664f4763Szrj { MUX_C_NEW_SESSION, mux_master_process_new_session },
180664f4763Szrj { MUX_C_ALIVE_CHECK, mux_master_process_alive_check },
181664f4763Szrj { MUX_C_TERMINATE, mux_master_process_terminate },
182664f4763Szrj { MUX_C_OPEN_FWD, mux_master_process_open_fwd },
183664f4763Szrj { MUX_C_CLOSE_FWD, mux_master_process_close_fwd },
184664f4763Szrj { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd },
185664f4763Szrj { MUX_C_STOP_LISTENING, mux_master_process_stop_listening },
186664f4763Szrj { MUX_C_PROXY, mux_master_process_proxy },
187856ea928SPeter Avalos { 0, NULL }
188856ea928SPeter Avalos };
189856ea928SPeter Avalos
19050a69bb5SSascha Wildner /* Cleanup callback fired on closure of mux client _session_ channel */
191856ea928SPeter Avalos static void
mux_master_session_cleanup_cb(struct ssh * ssh,int cid,int force,void * unused)192*ba1276acSMatthew Dillon mux_master_session_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused)
193856ea928SPeter Avalos {
194ce74bacaSMatthew Dillon Channel *cc, *c = channel_by_id(ssh, cid);
195856ea928SPeter Avalos
19650a69bb5SSascha Wildner debug3_f("entering for channel %d", cid);
197856ea928SPeter Avalos if (c == NULL)
19850a69bb5SSascha Wildner fatal_f("channel_by_id(%i) == NULL", cid);
199856ea928SPeter Avalos if (c->ctl_chan != -1) {
200ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
20150a69bb5SSascha Wildner fatal_f("channel %d missing control channel %d",
20250a69bb5SSascha Wildner c->self, c->ctl_chan);
203856ea928SPeter Avalos c->ctl_chan = -1;
204ce74bacaSMatthew Dillon cc->remote_id = 0;
205ce74bacaSMatthew Dillon cc->have_remote_id = 0;
206ce74bacaSMatthew Dillon chan_rcvd_oclose(ssh, cc);
207856ea928SPeter Avalos }
208ce74bacaSMatthew Dillon channel_cancel_cleanup(ssh, c->self);
209856ea928SPeter Avalos }
210856ea928SPeter Avalos
21150a69bb5SSascha Wildner /* Cleanup callback fired on closure of mux client _control_ channel */
212856ea928SPeter Avalos static void
mux_master_control_cleanup_cb(struct ssh * ssh,int cid,int force,void * unused)213*ba1276acSMatthew Dillon mux_master_control_cleanup_cb(struct ssh *ssh, int cid, int force, 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)243ee116499SAntonio Huete Jimenez env_permitted(const char *env)
244856ea928SPeter Avalos {
245ee116499SAntonio Huete Jimenez u_int i;
246ee116499SAntonio 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;
965*ba1276acSMatthew Dillon u_int _cport, i, j;
966*ba1276acSMatthew Dillon int ok = 0, cport, 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 ||
971*ba1276acSMatthew Dillon (r = sshbuf_get_u32(m, &_cport)) != 0) {
97236e94dc5SPeter Avalos free(chost);
97350a69bb5SSascha Wildner error_f("malformed message");
974856ea928SPeter Avalos return -1;
975856ea928SPeter Avalos }
976*ba1276acSMatthew Dillon if (_cport == (u_int)PORT_STREAMLOCAL)
977*ba1276acSMatthew Dillon cport = PORT_STREAMLOCAL;
978*ba1276acSMatthew Dillon else if (_cport <= INT_MAX)
979*ba1276acSMatthew Dillon cport = (int)_cport;
980*ba1276acSMatthew Dillon else {
981*ba1276acSMatthew Dillon free(chost);
982*ba1276acSMatthew Dillon error_f("invalid port 0x%x", _cport);
983*ba1276acSMatthew Dillon return -1;
984*ba1276acSMatthew Dillon }
985856ea928SPeter Avalos
986*ba1276acSMatthew Dillon debug2_f("channel %d: stdio fwd to %s:%d", c->self, chost, cport);
987856ea928SPeter Avalos
988856ea928SPeter Avalos /* Gather fds from client */
989856ea928SPeter Avalos for(i = 0; i < 2; i++) {
990856ea928SPeter Avalos if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) {
99150a69bb5SSascha Wildner error_f("failed to receive fd %d from client", i);
992856ea928SPeter Avalos for (j = 0; j < i; j++)
993856ea928SPeter Avalos close(new_fd[j]);
99436e94dc5SPeter Avalos free(chost);
995856ea928SPeter Avalos
996856ea928SPeter Avalos /* prepare reply */
997664f4763Szrj reply_error(reply, MUX_S_FAILURE, rid,
998856ea928SPeter Avalos "did not receive file descriptors");
999856ea928SPeter Avalos return -1;
1000856ea928SPeter Avalos }
1001856ea928SPeter Avalos }
1002856ea928SPeter Avalos
100350a69bb5SSascha Wildner debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]);
1004856ea928SPeter Avalos
1005856ea928SPeter Avalos /* XXX support multiple child sessions in future */
1006ce74bacaSMatthew Dillon if (c->have_remote_id) {
100750a69bb5SSascha Wildner debug2_f("session already open");
1008664f4763Szrj reply_error(reply, MUX_S_FAILURE, rid,
1009664f4763Szrj "Multiple sessions not supported");
1010856ea928SPeter Avalos cleanup:
1011856ea928SPeter Avalos close(new_fd[0]);
1012856ea928SPeter Avalos close(new_fd[1]);
101336e94dc5SPeter Avalos free(chost);
1014856ea928SPeter Avalos return 0;
1015856ea928SPeter Avalos }
1016856ea928SPeter Avalos
1017856ea928SPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK ||
1018856ea928SPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) {
1019*ba1276acSMatthew Dillon if (cport == PORT_STREAMLOCAL) {
1020*ba1276acSMatthew Dillon ok = ask_permission("Allow forward to path %s", chost);
1021*ba1276acSMatthew Dillon } else {
1022*ba1276acSMatthew Dillon ok = ask_permission("Allow forward to [%s]:%d? ",
1023*ba1276acSMatthew Dillon chost, cport);
1024*ba1276acSMatthew Dillon }
1025*ba1276acSMatthew Dillon if (!ok) {
102650a69bb5SSascha Wildner debug2_f("stdio fwd refused by user");
1027664f4763Szrj reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
1028664f4763Szrj "Permission denied");
1029856ea928SPeter Avalos goto cleanup;
1030856ea928SPeter Avalos }
1031856ea928SPeter Avalos }
1032856ea928SPeter Avalos
103350a69bb5SSascha Wildner nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1],
103450a69bb5SSascha Wildner CHANNEL_NONBLOCK_STDIO);
1035664f4763Szrj free(chost);
1036856ea928SPeter Avalos
1037856ea928SPeter Avalos nc->ctl_chan = c->self; /* link session -> control channel */
1038856ea928SPeter Avalos c->remote_id = nc->self; /* link control -> session channel */
1039ce74bacaSMatthew Dillon c->have_remote_id = 1;
1040856ea928SPeter Avalos
104150a69bb5SSascha Wildner debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan);
1042856ea928SPeter Avalos
1043ce74bacaSMatthew Dillon channel_register_cleanup(ssh, nc->self,
1044ce74bacaSMatthew Dillon mux_master_session_cleanup_cb, 1);
1045856ea928SPeter Avalos
104636e94dc5SPeter Avalos cctx = xcalloc(1, sizeof(*cctx));
104736e94dc5SPeter Avalos cctx->rid = rid;
1048ce74bacaSMatthew Dillon channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx);
104936e94dc5SPeter Avalos c->mux_pause = 1; /* stop handling messages until open_confirm done */
1050856ea928SPeter Avalos
105136e94dc5SPeter Avalos /* reply is deferred, sent by mux_session_confirm */
1052856ea928SPeter Avalos return 0;
1053856ea928SPeter Avalos }
1054856ea928SPeter Avalos
105536e94dc5SPeter Avalos /* Callback on open confirmation in mux master for a mux stdio fwd session. */
105636e94dc5SPeter Avalos static void
mux_stdio_confirm(struct ssh * ssh,int id,int success,void * arg)1057ce74bacaSMatthew Dillon mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg)
105836e94dc5SPeter Avalos {
105936e94dc5SPeter Avalos struct mux_stdio_confirm_ctx *cctx = arg;
106036e94dc5SPeter Avalos Channel *c, *cc;
1061664f4763Szrj struct sshbuf *reply;
1062664f4763Szrj int r;
106336e94dc5SPeter Avalos
106436e94dc5SPeter Avalos if (cctx == NULL)
106550a69bb5SSascha Wildner fatal_f("cctx == NULL");
1066ce74bacaSMatthew Dillon if ((c = channel_by_id(ssh, id)) == NULL)
106750a69bb5SSascha Wildner fatal_f("no channel for id %d", id);
1068ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
106950a69bb5SSascha Wildner fatal_f("channel %d lacks control channel %d",
107036e94dc5SPeter Avalos id, c->ctl_chan);
1071664f4763Szrj if ((reply = sshbuf_new()) == NULL)
107250a69bb5SSascha Wildner fatal_f("sshbuf_new");
107336e94dc5SPeter Avalos
107436e94dc5SPeter Avalos if (!success) {
107550a69bb5SSascha Wildner debug3_f("sending failure reply");
1076664f4763Szrj reply_error(reply, MUX_S_FAILURE, cctx->rid,
1077664f4763Szrj "Session open refused by peer");
107836e94dc5SPeter Avalos /* prepare reply */
107936e94dc5SPeter Avalos goto done;
108036e94dc5SPeter Avalos }
108136e94dc5SPeter Avalos
108250a69bb5SSascha Wildner debug3_f("sending success reply");
108336e94dc5SPeter Avalos /* prepare reply */
1084664f4763Szrj if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 ||
1085664f4763Szrj (r = sshbuf_put_u32(reply, cctx->rid)) != 0 ||
1086664f4763Szrj (r = sshbuf_put_u32(reply, c->self)) != 0)
108750a69bb5SSascha Wildner fatal_fr(r, "reply");
108836e94dc5SPeter Avalos
108936e94dc5SPeter Avalos done:
109036e94dc5SPeter Avalos /* Send reply */
1091664f4763Szrj if ((r = sshbuf_put_stringb(cc->output, reply)) != 0)
109250a69bb5SSascha Wildner fatal_fr(r, "enqueue");
1093664f4763Szrj sshbuf_free(reply);
109436e94dc5SPeter Avalos
109536e94dc5SPeter Avalos if (cc->mux_pause <= 0)
109650a69bb5SSascha Wildner fatal_f("mux_pause %d", cc->mux_pause);
109736e94dc5SPeter Avalos cc->mux_pause = 0; /* start processing messages again */
109836e94dc5SPeter Avalos c->open_confirm_ctx = NULL;
109936e94dc5SPeter Avalos free(cctx);
110036e94dc5SPeter Avalos }
110136e94dc5SPeter Avalos
11021c188a7fSPeter Avalos static int
mux_master_process_stop_listening(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)1103664f4763Szrj mux_master_process_stop_listening(struct ssh *ssh, u_int rid,
1104664f4763Szrj Channel *c, struct sshbuf *m, struct sshbuf *reply)
11051c188a7fSPeter Avalos {
110650a69bb5SSascha Wildner debug_f("channel %d: stop listening", c->self);
11071c188a7fSPeter Avalos
11081c188a7fSPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK ||
11091c188a7fSPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) {
11101c188a7fSPeter Avalos if (!ask_permission("Disable further multiplexing on shared "
11111c188a7fSPeter Avalos "connection to %s? ", host)) {
111250a69bb5SSascha Wildner debug2_f("stop listen refused by user");
1113664f4763Szrj reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
1114664f4763Szrj "Permission denied");
11151c188a7fSPeter Avalos return 0;
11161c188a7fSPeter Avalos }
11171c188a7fSPeter Avalos }
11181c188a7fSPeter Avalos
11191c188a7fSPeter Avalos if (mux_listener_channel != NULL) {
1120ce74bacaSMatthew Dillon channel_free(ssh, mux_listener_channel);
11211c188a7fSPeter Avalos client_stop_mux();
112236e94dc5SPeter Avalos free(options.control_path);
11231c188a7fSPeter Avalos options.control_path = NULL;
11241c188a7fSPeter Avalos mux_listener_channel = NULL;
11251c188a7fSPeter Avalos muxserver_sock = -1;
11261c188a7fSPeter Avalos }
11271c188a7fSPeter Avalos
1128664f4763Szrj reply_ok(reply, rid);
11291c188a7fSPeter Avalos return 0;
11301c188a7fSPeter Avalos }
11311c188a7fSPeter Avalos
1132ce74bacaSMatthew Dillon static int
mux_master_process_proxy(struct ssh * ssh,u_int rid,Channel * c,struct sshbuf * m,struct sshbuf * reply)1133664f4763Szrj mux_master_process_proxy(struct ssh *ssh, u_int rid,
1134664f4763Szrj Channel *c, struct sshbuf *m, struct sshbuf *reply)
1135ce74bacaSMatthew Dillon {
1136664f4763Szrj int r;
1137664f4763Szrj
113850a69bb5SSascha Wildner debug_f("channel %d: proxy request", c->self);
1139ce74bacaSMatthew Dillon
1140ce74bacaSMatthew Dillon c->mux_rcb = channel_proxy_downstream;
1141664f4763Szrj if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 ||
1142664f4763Szrj (r = sshbuf_put_u32(reply, rid)) != 0)
114350a69bb5SSascha Wildner fatal_fr(r, "reply");
1144ce74bacaSMatthew Dillon
1145ce74bacaSMatthew Dillon return 0;
1146ce74bacaSMatthew Dillon }
1147ce74bacaSMatthew Dillon
114850a69bb5SSascha Wildner /* Channel callbacks fired on read/write from mux client fd */
1149856ea928SPeter Avalos static int
mux_master_read_cb(struct ssh * ssh,Channel * c)1150ce74bacaSMatthew Dillon mux_master_read_cb(struct ssh *ssh, Channel *c)
1151856ea928SPeter Avalos {
1152856ea928SPeter Avalos struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx;
1153664f4763Szrj struct sshbuf *in = NULL, *out = NULL;
1154664f4763Szrj u_int type, rid, i;
1155664f4763Szrj int r, ret = -1;
1156664f4763Szrj
1157664f4763Szrj if ((out = sshbuf_new()) == NULL)
115850a69bb5SSascha Wildner fatal_f("sshbuf_new");
1159856ea928SPeter Avalos
1160856ea928SPeter Avalos /* Setup ctx and */
1161856ea928SPeter Avalos if (c->mux_ctx == NULL) {
1162856ea928SPeter Avalos state = xcalloc(1, sizeof(*state));
1163856ea928SPeter Avalos c->mux_ctx = state;
1164ce74bacaSMatthew Dillon channel_register_cleanup(ssh, c->self,
1165856ea928SPeter Avalos mux_master_control_cleanup_cb, 0);
1166856ea928SPeter Avalos
1167856ea928SPeter Avalos /* Send hello */
1168664f4763Szrj if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 ||
1169664f4763Szrj (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0)
117050a69bb5SSascha Wildner fatal_fr(r, "reply");
1171856ea928SPeter Avalos /* no extensions */
1172664f4763Szrj if ((r = sshbuf_put_stringb(c->output, out)) != 0)
117350a69bb5SSascha Wildner fatal_fr(r, "enqueue");
117450a69bb5SSascha Wildner debug3_f("channel %d: hello sent", c->self);
1175664f4763Szrj ret = 0;
1176664f4763Szrj goto out;
1177856ea928SPeter Avalos }
1178856ea928SPeter Avalos
1179856ea928SPeter Avalos /* Channel code ensures that we receive whole packets */
1180664f4763Szrj if ((r = sshbuf_froms(c->input, &in)) != 0) {
1181856ea928SPeter Avalos malf:
118250a69bb5SSascha Wildner error_f("malformed message");
1183856ea928SPeter Avalos goto out;
1184856ea928SPeter Avalos }
1185856ea928SPeter Avalos
1186664f4763Szrj if ((r = sshbuf_get_u32(in, &type)) != 0)
1187856ea928SPeter Avalos goto malf;
118850a69bb5SSascha Wildner debug3_f("channel %d packet type 0x%08x len %zu", c->self,
118950a69bb5SSascha Wildner type, sshbuf_len(in));
1190856ea928SPeter Avalos
1191856ea928SPeter Avalos if (type == MUX_MSG_HELLO)
1192856ea928SPeter Avalos rid = 0;
1193856ea928SPeter Avalos else {
1194856ea928SPeter Avalos if (!state->hello_rcvd) {
119550a69bb5SSascha Wildner error_f("expected MUX_MSG_HELLO(0x%08x), "
119650a69bb5SSascha Wildner "received 0x%08x", MUX_MSG_HELLO, type);
1197856ea928SPeter Avalos goto out;
1198856ea928SPeter Avalos }
1199664f4763Szrj if ((r = sshbuf_get_u32(in, &rid)) != 0)
1200856ea928SPeter Avalos goto malf;
1201856ea928SPeter Avalos }
1202856ea928SPeter Avalos
1203856ea928SPeter Avalos for (i = 0; mux_master_handlers[i].handler != NULL; i++) {
1204856ea928SPeter Avalos if (type == mux_master_handlers[i].type) {
1205ce74bacaSMatthew Dillon ret = mux_master_handlers[i].handler(ssh, rid,
1206664f4763Szrj c, in, out);
1207856ea928SPeter Avalos break;
1208856ea928SPeter Avalos }
1209856ea928SPeter Avalos }
1210856ea928SPeter Avalos if (mux_master_handlers[i].handler == NULL) {
121150a69bb5SSascha Wildner error_f("unsupported mux message 0x%08x", type);
1212664f4763Szrj reply_error(out, MUX_S_FAILURE, rid, "unsupported request");
1213856ea928SPeter Avalos ret = 0;
1214856ea928SPeter Avalos }
1215856ea928SPeter Avalos /* Enqueue reply packet */
121650a69bb5SSascha Wildner if (sshbuf_len(out) != 0 &&
121750a69bb5SSascha Wildner (r = sshbuf_put_stringb(c->output, out)) != 0)
121850a69bb5SSascha Wildner fatal_fr(r, "enqueue");
1219856ea928SPeter Avalos out:
1220664f4763Szrj sshbuf_free(in);
1221664f4763Szrj sshbuf_free(out);
1222856ea928SPeter Avalos return ret;
1223856ea928SPeter Avalos }
1224856ea928SPeter Avalos
1225856ea928SPeter Avalos void
mux_exit_message(struct ssh * ssh,Channel * c,int exitval)1226ce74bacaSMatthew Dillon mux_exit_message(struct ssh *ssh, Channel *c, int exitval)
1227856ea928SPeter Avalos {
1228664f4763Szrj struct sshbuf *m;
1229856ea928SPeter Avalos Channel *mux_chan;
1230664f4763Szrj int r;
1231856ea928SPeter Avalos
123250a69bb5SSascha Wildner debug3_f("channel %d: exit message, exitval %d", c->self, exitval);
1233856ea928SPeter Avalos
1234ce74bacaSMatthew Dillon if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL)
123550a69bb5SSascha Wildner fatal_f("channel %d missing mux %d", c->self, c->ctl_chan);
1236856ea928SPeter Avalos
1237856ea928SPeter Avalos /* Append exit message packet to control socket output queue */
1238664f4763Szrj if ((m = sshbuf_new()) == NULL)
123950a69bb5SSascha Wildner fatal_f("sshbuf_new");
1240664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 ||
1241664f4763Szrj (r = sshbuf_put_u32(m, c->self)) != 0 ||
1242664f4763Szrj (r = sshbuf_put_u32(m, exitval)) != 0 ||
1243664f4763Szrj (r = sshbuf_put_stringb(mux_chan->output, m)) != 0)
124450a69bb5SSascha Wildner fatal_fr(r, "reply");
1245664f4763Szrj sshbuf_free(m);
1246856ea928SPeter Avalos }
124718de8d7fSPeter Avalos
12481c188a7fSPeter Avalos void
mux_tty_alloc_failed(struct ssh * ssh,Channel * c)1249ce74bacaSMatthew Dillon mux_tty_alloc_failed(struct ssh *ssh, Channel *c)
12501c188a7fSPeter Avalos {
1251664f4763Szrj struct sshbuf *m;
12521c188a7fSPeter Avalos Channel *mux_chan;
1253664f4763Szrj int r;
12541c188a7fSPeter Avalos
125550a69bb5SSascha Wildner debug3_f("channel %d: TTY alloc failed", c->self);
12561c188a7fSPeter Avalos
1257ce74bacaSMatthew Dillon if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL)
125850a69bb5SSascha Wildner fatal_f("channel %d missing mux %d", c->self, c->ctl_chan);
12591c188a7fSPeter Avalos
12601c188a7fSPeter Avalos /* Append exit message packet to control socket output queue */
1261664f4763Szrj if ((m = sshbuf_new()) == NULL)
126250a69bb5SSascha Wildner fatal_f("sshbuf_new");
1263664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 ||
1264664f4763Szrj (r = sshbuf_put_u32(m, c->self)) != 0 ||
1265664f4763Szrj (r = sshbuf_put_stringb(mux_chan->output, m)) != 0)
126650a69bb5SSascha Wildner fatal_fr(r, "reply");
1267664f4763Szrj sshbuf_free(m);
12681c188a7fSPeter Avalos }
12691c188a7fSPeter Avalos
127018de8d7fSPeter Avalos /* Prepare a mux master to listen on a Unix domain socket. */
127118de8d7fSPeter Avalos void
muxserver_listen(struct ssh * ssh)1272ce74bacaSMatthew Dillon muxserver_listen(struct ssh *ssh)
127318de8d7fSPeter Avalos {
127418de8d7fSPeter Avalos mode_t old_umask;
12759f304aafSPeter Avalos char *orig_control_path = options.control_path;
12769f304aafSPeter Avalos char rbuf[16+1];
12779f304aafSPeter Avalos u_int i, r;
127836e94dc5SPeter Avalos int oerrno;
127918de8d7fSPeter Avalos
128018de8d7fSPeter Avalos if (options.control_path == NULL ||
128118de8d7fSPeter Avalos options.control_master == SSHCTL_MASTER_NO)
128218de8d7fSPeter Avalos return;
128318de8d7fSPeter Avalos
128418de8d7fSPeter Avalos debug("setting up multiplex master socket");
128518de8d7fSPeter Avalos
12869f304aafSPeter Avalos /*
12879f304aafSPeter Avalos * Use a temporary path before listen so we can pseudo-atomically
12889f304aafSPeter Avalos * establish the listening socket in its final location to avoid
12899f304aafSPeter Avalos * other processes racing in between bind() and listen() and hitting
12909f304aafSPeter Avalos * an unready socket.
12919f304aafSPeter Avalos */
12929f304aafSPeter Avalos for (i = 0; i < sizeof(rbuf) - 1; i++) {
12939f304aafSPeter Avalos r = arc4random_uniform(26+26+10);
12949f304aafSPeter Avalos rbuf[i] = (r < 26) ? 'a' + r :
12959f304aafSPeter Avalos (r < 26*2) ? 'A' + r - 26 :
12969f304aafSPeter Avalos '0' + r - 26 - 26;
12979f304aafSPeter Avalos }
12989f304aafSPeter Avalos rbuf[sizeof(rbuf) - 1] = '\0';
12999f304aafSPeter Avalos options.control_path = NULL;
13009f304aafSPeter Avalos xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf);
130150a69bb5SSascha Wildner debug3_f("temporary control path %s", options.control_path);
13029f304aafSPeter Avalos
130318de8d7fSPeter Avalos old_umask = umask(0177);
130436e94dc5SPeter Avalos muxserver_sock = unix_listener(options.control_path, 64, 0);
130536e94dc5SPeter Avalos oerrno = errno;
130636e94dc5SPeter Avalos umask(old_umask);
130736e94dc5SPeter Avalos if (muxserver_sock < 0) {
130836e94dc5SPeter Avalos if (oerrno == EINVAL || oerrno == EADDRINUSE) {
130918de8d7fSPeter Avalos error("ControlSocket %s already exists, "
131018de8d7fSPeter Avalos "disabling multiplexing", options.control_path);
13119f304aafSPeter Avalos disable_mux_master:
13121c188a7fSPeter Avalos if (muxserver_sock != -1) {
131318de8d7fSPeter Avalos close(muxserver_sock);
131418de8d7fSPeter Avalos muxserver_sock = -1;
13151c188a7fSPeter Avalos }
131636e94dc5SPeter Avalos free(orig_control_path);
131736e94dc5SPeter Avalos free(options.control_path);
131818de8d7fSPeter Avalos options.control_path = NULL;
131918de8d7fSPeter Avalos options.control_master = SSHCTL_MASTER_NO;
132018de8d7fSPeter Avalos return;
132136e94dc5SPeter Avalos } else {
132236e94dc5SPeter Avalos /* unix_listener() logs the error */
132336e94dc5SPeter Avalos cleanup_exit(255);
132418de8d7fSPeter Avalos }
132536e94dc5SPeter Avalos }
132618de8d7fSPeter Avalos
13279f304aafSPeter Avalos /* Now atomically "move" the mux socket into position */
13289f304aafSPeter Avalos if (link(options.control_path, orig_control_path) != 0) {
13299f304aafSPeter Avalos if (errno != EEXIST) {
133050a69bb5SSascha Wildner fatal_f("link mux listener %s => %s: %s",
13319f304aafSPeter Avalos options.control_path, orig_control_path,
13329f304aafSPeter Avalos strerror(errno));
13339f304aafSPeter Avalos }
13349f304aafSPeter Avalos error("ControlSocket %s already exists, disabling multiplexing",
13359f304aafSPeter Avalos orig_control_path);
13369f304aafSPeter Avalos unlink(options.control_path);
13379f304aafSPeter Avalos goto disable_mux_master;
13389f304aafSPeter Avalos }
13399f304aafSPeter Avalos unlink(options.control_path);
134036e94dc5SPeter Avalos free(options.control_path);
13419f304aafSPeter Avalos options.control_path = orig_control_path;
13429f304aafSPeter Avalos
134318de8d7fSPeter Avalos set_nonblock(muxserver_sock);
1344856ea928SPeter Avalos
1345ce74bacaSMatthew Dillon mux_listener_channel = channel_new(ssh, "mux listener",
1346856ea928SPeter Avalos SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1,
1347856ea928SPeter Avalos CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
13489f304aafSPeter Avalos 0, options.control_path, 1);
1349856ea928SPeter Avalos mux_listener_channel->mux_rcb = mux_master_read_cb;
135050a69bb5SSascha Wildner debug3_f("mux listener channel %d fd %d",
1351856ea928SPeter Avalos mux_listener_channel->self, mux_listener_channel->sock);
135218de8d7fSPeter Avalos }
135318de8d7fSPeter Avalos
135418de8d7fSPeter Avalos /* Callback on open confirmation in mux master for a mux client session. */
135518de8d7fSPeter Avalos static void
mux_session_confirm(struct ssh * ssh,int id,int success,void * arg)1356ce74bacaSMatthew Dillon mux_session_confirm(struct ssh *ssh, int id, int success, void *arg)
135718de8d7fSPeter Avalos {
135818de8d7fSPeter Avalos struct mux_session_confirm_ctx *cctx = arg;
135918de8d7fSPeter Avalos const char *display;
1360856ea928SPeter Avalos Channel *c, *cc;
1361664f4763Szrj int i, r;
1362664f4763Szrj struct sshbuf *reply;
136318de8d7fSPeter Avalos
136418de8d7fSPeter Avalos if (cctx == NULL)
136550a69bb5SSascha Wildner fatal_f("cctx == NULL");
1366ce74bacaSMatthew Dillon if ((c = channel_by_id(ssh, id)) == NULL)
136750a69bb5SSascha Wildner fatal_f("no channel for id %d", id);
1368ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL)
136950a69bb5SSascha Wildner fatal_f("channel %d lacks control channel %d",
1370856ea928SPeter Avalos id, c->ctl_chan);
1371664f4763Szrj if ((reply = sshbuf_new()) == NULL)
137250a69bb5SSascha Wildner fatal_f("sshbuf_new");
1373856ea928SPeter Avalos
1374856ea928SPeter Avalos if (!success) {
137550a69bb5SSascha Wildner debug3_f("sending failure reply");
1376664f4763Szrj reply_error(reply, MUX_S_FAILURE, cctx->rid,
1377664f4763Szrj "Session open refused by peer");
1378856ea928SPeter Avalos goto done;
1379856ea928SPeter Avalos }
138018de8d7fSPeter Avalos
138118de8d7fSPeter Avalos display = getenv("DISPLAY");
138218de8d7fSPeter Avalos if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
138318de8d7fSPeter Avalos char *proto, *data;
1384856ea928SPeter Avalos
138518de8d7fSPeter Avalos /* Get reasonable local authentication information. */
1386ce74bacaSMatthew Dillon if (client_x11_get_proto(ssh, display, options.xauth_location,
1387856ea928SPeter Avalos options.forward_x11_trusted, options.forward_x11_timeout,
1388e9778795SPeter Avalos &proto, &data) == 0) {
138918de8d7fSPeter Avalos /* Request forwarding with authentication spoofing. */
1390856ea928SPeter Avalos debug("Requesting X11 forwarding with authentication "
1391856ea928SPeter Avalos "spoofing.");
1392ce74bacaSMatthew Dillon x11_request_forwarding_with_spoofing(ssh, id,
1393ce74bacaSMatthew Dillon display, proto, data, 1);
13941c188a7fSPeter Avalos /* XXX exit_on_forward_failure */
1395ce74bacaSMatthew Dillon client_expect_confirm(ssh, id, "X11 forwarding",
1396e9778795SPeter Avalos CONFIRM_WARN);
1397e9778795SPeter Avalos }
139818de8d7fSPeter Avalos }
139918de8d7fSPeter Avalos
140018de8d7fSPeter Avalos if (cctx->want_agent_fwd && options.forward_agent) {
140118de8d7fSPeter Avalos debug("Requesting authentication agent forwarding.");
1402ce74bacaSMatthew Dillon channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0);
1403664f4763Szrj if ((r = sshpkt_send(ssh)) != 0)
140450a69bb5SSascha Wildner fatal_fr(r, "send");
140518de8d7fSPeter Avalos }
140618de8d7fSPeter Avalos
1407ce74bacaSMatthew Dillon client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys,
1408664f4763Szrj cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env);
140918de8d7fSPeter Avalos
141050a69bb5SSascha Wildner debug3_f("sending success reply");
1411856ea928SPeter Avalos /* prepare reply */
1412664f4763Szrj if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 ||
1413664f4763Szrj (r = sshbuf_put_u32(reply, cctx->rid)) != 0 ||
1414664f4763Szrj (r = sshbuf_put_u32(reply, c->self)) != 0)
141550a69bb5SSascha Wildner fatal_fr(r, "reply");
1416856ea928SPeter Avalos
1417856ea928SPeter Avalos done:
1418856ea928SPeter Avalos /* Send reply */
1419664f4763Szrj if ((r = sshbuf_put_stringb(cc->output, reply)) != 0)
142050a69bb5SSascha Wildner fatal_fr(r, "enqueue");
1421664f4763Szrj sshbuf_free(reply);
1422856ea928SPeter Avalos
1423856ea928SPeter Avalos if (cc->mux_pause <= 0)
142450a69bb5SSascha Wildner fatal_f("mux_pause %d", cc->mux_pause);
1425856ea928SPeter Avalos cc->mux_pause = 0; /* start processing messages again */
142618de8d7fSPeter Avalos c->open_confirm_ctx = NULL;
1427664f4763Szrj sshbuf_free(cctx->cmd);
142836e94dc5SPeter Avalos free(cctx->term);
142918de8d7fSPeter Avalos if (cctx->env != NULL) {
143018de8d7fSPeter Avalos for (i = 0; cctx->env[i] != NULL; i++)
143136e94dc5SPeter Avalos free(cctx->env[i]);
143236e94dc5SPeter Avalos free(cctx->env);
143318de8d7fSPeter Avalos }
143436e94dc5SPeter Avalos free(cctx);
143518de8d7fSPeter Avalos }
143618de8d7fSPeter Avalos
143718de8d7fSPeter Avalos /* ** Multiplexing client support */
143818de8d7fSPeter Avalos
143918de8d7fSPeter Avalos /* Exit signal handler */
144018de8d7fSPeter Avalos static void
control_client_sighandler(int signo)144118de8d7fSPeter Avalos control_client_sighandler(int signo)
144218de8d7fSPeter Avalos {
144318de8d7fSPeter Avalos muxclient_terminate = signo;
144418de8d7fSPeter Avalos }
144518de8d7fSPeter Avalos
144618de8d7fSPeter Avalos /*
144718de8d7fSPeter Avalos * Relay signal handler - used to pass some signals from mux client to
144818de8d7fSPeter Avalos * mux master.
144918de8d7fSPeter Avalos */
145018de8d7fSPeter Avalos static void
control_client_sigrelay(int signo)145118de8d7fSPeter Avalos control_client_sigrelay(int signo)
145218de8d7fSPeter Avalos {
145318de8d7fSPeter Avalos int save_errno = errno;
145418de8d7fSPeter Avalos
145518de8d7fSPeter Avalos if (muxserver_pid > 1)
145618de8d7fSPeter Avalos kill(muxserver_pid, signo);
145718de8d7fSPeter Avalos
145818de8d7fSPeter Avalos errno = save_errno;
145918de8d7fSPeter Avalos }
146018de8d7fSPeter Avalos
146118de8d7fSPeter Avalos static int
mux_client_read(int fd,struct sshbuf * b,size_t need,int timeout_ms)1462*ba1276acSMatthew Dillon mux_client_read(int fd, struct sshbuf *b, size_t need, int timeout_ms)
146318de8d7fSPeter Avalos {
1464664f4763Szrj size_t have;
1465856ea928SPeter Avalos ssize_t len;
1466856ea928SPeter Avalos u_char *p;
1467664f4763Szrj int r;
146818de8d7fSPeter Avalos
1469664f4763Szrj if ((r = sshbuf_reserve(b, need, &p)) != 0)
147050a69bb5SSascha Wildner fatal_fr(r, "reserve");
1471856ea928SPeter Avalos for (have = 0; have < need; ) {
1472856ea928SPeter Avalos if (muxclient_terminate) {
1473856ea928SPeter Avalos errno = EINTR;
1474856ea928SPeter Avalos return -1;
1475856ea928SPeter Avalos }
1476856ea928SPeter Avalos len = read(fd, p + have, need - have);
14770cbfa66cSDaniel Fojt if (len == -1) {
1478856ea928SPeter Avalos switch (errno) {
1479856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1480856ea928SPeter Avalos case EWOULDBLOCK:
1481856ea928SPeter Avalos #endif
1482856ea928SPeter Avalos case EAGAIN:
1483*ba1276acSMatthew Dillon if (waitrfd(fd, &timeout_ms,
1484*ba1276acSMatthew Dillon &muxclient_terminate) == -1 &&
1485*ba1276acSMatthew Dillon errno != EINTR)
1486*ba1276acSMatthew Dillon return -1; /* timeout */
1487856ea928SPeter Avalos /* FALLTHROUGH */
1488856ea928SPeter Avalos case EINTR:
1489856ea928SPeter Avalos continue;
1490856ea928SPeter Avalos default:
1491856ea928SPeter Avalos return -1;
1492856ea928SPeter Avalos }
1493856ea928SPeter Avalos }
1494856ea928SPeter Avalos if (len == 0) {
1495856ea928SPeter Avalos errno = EPIPE;
1496856ea928SPeter Avalos return -1;
1497856ea928SPeter Avalos }
1498664f4763Szrj have += (size_t)len;
1499856ea928SPeter Avalos }
1500856ea928SPeter Avalos return 0;
1501856ea928SPeter Avalos }
150218de8d7fSPeter Avalos
1503856ea928SPeter Avalos static int
mux_client_write_packet(int fd,struct sshbuf * m)1504664f4763Szrj mux_client_write_packet(int fd, struct sshbuf *m)
1505856ea928SPeter Avalos {
1506664f4763Szrj struct sshbuf *queue;
1507856ea928SPeter Avalos u_int have, need;
1508664f4763Szrj int r, oerrno, len;
1509664f4763Szrj const u_char *ptr;
1510856ea928SPeter Avalos struct pollfd pfd;
151118de8d7fSPeter Avalos
1512856ea928SPeter Avalos pfd.fd = fd;
1513856ea928SPeter Avalos pfd.events = POLLOUT;
1514664f4763Szrj if ((queue = sshbuf_new()) == NULL)
151550a69bb5SSascha Wildner fatal_f("sshbuf_new");
1516664f4763Szrj if ((r = sshbuf_put_stringb(queue, m)) != 0)
151750a69bb5SSascha Wildner fatal_fr(r, "enqueue");
1518856ea928SPeter Avalos
1519664f4763Szrj need = sshbuf_len(queue);
1520664f4763Szrj ptr = sshbuf_ptr(queue);
1521856ea928SPeter Avalos
1522856ea928SPeter Avalos for (have = 0; have < need; ) {
1523856ea928SPeter Avalos if (muxclient_terminate) {
1524664f4763Szrj sshbuf_free(queue);
1525856ea928SPeter Avalos errno = EINTR;
1526856ea928SPeter Avalos return -1;
1527856ea928SPeter Avalos }
1528856ea928SPeter Avalos len = write(fd, ptr + have, need - have);
15290cbfa66cSDaniel Fojt if (len == -1) {
1530856ea928SPeter Avalos switch (errno) {
1531856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1532856ea928SPeter Avalos case EWOULDBLOCK:
1533856ea928SPeter Avalos #endif
1534856ea928SPeter Avalos case EAGAIN:
1535856ea928SPeter Avalos (void)poll(&pfd, 1, -1);
1536856ea928SPeter Avalos /* FALLTHROUGH */
1537856ea928SPeter Avalos case EINTR:
1538856ea928SPeter Avalos continue;
1539856ea928SPeter Avalos default:
1540856ea928SPeter Avalos oerrno = errno;
1541664f4763Szrj sshbuf_free(queue);
1542856ea928SPeter Avalos errno = oerrno;
1543856ea928SPeter Avalos return -1;
1544856ea928SPeter Avalos }
1545856ea928SPeter Avalos }
1546856ea928SPeter Avalos if (len == 0) {
1547664f4763Szrj sshbuf_free(queue);
1548856ea928SPeter Avalos errno = EPIPE;
1549856ea928SPeter Avalos return -1;
1550856ea928SPeter Avalos }
1551856ea928SPeter Avalos have += (u_int)len;
1552856ea928SPeter Avalos }
1553664f4763Szrj sshbuf_free(queue);
1554856ea928SPeter Avalos return 0;
1555856ea928SPeter Avalos }
1556856ea928SPeter Avalos
1557856ea928SPeter Avalos static int
mux_client_read_packet_timeout(int fd,struct sshbuf * m,int timeout_ms)1558*ba1276acSMatthew Dillon mux_client_read_packet_timeout(int fd, struct sshbuf *m, int timeout_ms)
1559856ea928SPeter Avalos {
1560664f4763Szrj struct sshbuf *queue;
1561664f4763Szrj size_t need, have;
156236e94dc5SPeter Avalos const u_char *ptr;
1563664f4763Szrj int r, oerrno;
1564856ea928SPeter Avalos
1565664f4763Szrj if ((queue = sshbuf_new()) == NULL)
156650a69bb5SSascha Wildner fatal_f("sshbuf_new");
1567*ba1276acSMatthew Dillon if (mux_client_read(fd, queue, 4, timeout_ms) != 0) {
1568856ea928SPeter Avalos if ((oerrno = errno) == EPIPE)
156950a69bb5SSascha Wildner debug3_f("read header failed: %s",
157036e94dc5SPeter Avalos strerror(errno));
1571664f4763Szrj sshbuf_free(queue);
1572856ea928SPeter Avalos errno = oerrno;
1573856ea928SPeter Avalos return -1;
1574856ea928SPeter Avalos }
1575664f4763Szrj need = PEEK_U32(sshbuf_ptr(queue));
1576*ba1276acSMatthew Dillon if (mux_client_read(fd, queue, need, timeout_ms) != 0) {
1577856ea928SPeter Avalos oerrno = errno;
157850a69bb5SSascha Wildner debug3_f("read body failed: %s", strerror(errno));
1579664f4763Szrj sshbuf_free(queue);
1580856ea928SPeter Avalos errno = oerrno;
1581856ea928SPeter Avalos return -1;
1582856ea928SPeter Avalos }
1583664f4763Szrj if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 ||
1584664f4763Szrj (r = sshbuf_put(m, ptr, have)) != 0)
158550a69bb5SSascha Wildner fatal_fr(r, "dequeue");
1586664f4763Szrj sshbuf_free(queue);
1587856ea928SPeter Avalos return 0;
1588856ea928SPeter Avalos }
1589856ea928SPeter Avalos
1590856ea928SPeter Avalos static int
mux_client_read_packet(int fd,struct sshbuf * m)1591*ba1276acSMatthew Dillon mux_client_read_packet(int fd, struct sshbuf *m)
1592*ba1276acSMatthew Dillon {
1593*ba1276acSMatthew Dillon return mux_client_read_packet_timeout(fd, m, -1);
1594*ba1276acSMatthew Dillon }
1595*ba1276acSMatthew Dillon
1596*ba1276acSMatthew Dillon static int
mux_client_hello_exchange(int fd,int timeout_ms)1597*ba1276acSMatthew Dillon mux_client_hello_exchange(int fd, int timeout_ms)
1598856ea928SPeter Avalos {
1599664f4763Szrj struct sshbuf *m;
1600856ea928SPeter Avalos u_int type, ver;
1601664f4763Szrj int r, ret = -1;
1602856ea928SPeter Avalos
1603664f4763Szrj if ((m = sshbuf_new()) == NULL)
160450a69bb5SSascha Wildner fatal_f("sshbuf_new");
1605664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 ||
1606664f4763Szrj (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0)
160750a69bb5SSascha Wildner fatal_fr(r, "assemble hello");
1608856ea928SPeter Avalos /* no extensions */
1609856ea928SPeter Avalos
1610664f4763Szrj if (mux_client_write_packet(fd, m) != 0) {
161150a69bb5SSascha Wildner debug_f("write packet: %s", strerror(errno));
1612ce74bacaSMatthew Dillon goto out;
1613ce74bacaSMatthew Dillon }
1614856ea928SPeter Avalos
1615664f4763Szrj sshbuf_reset(m);
1616856ea928SPeter Avalos
1617856ea928SPeter Avalos /* Read their HELLO */
1618*ba1276acSMatthew Dillon if (mux_client_read_packet_timeout(fd, m, timeout_ms) != 0) {
161950a69bb5SSascha Wildner debug_f("read packet failed");
1620ce74bacaSMatthew Dillon goto out;
1621856ea928SPeter Avalos }
1622856ea928SPeter Avalos
1623664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0)
162450a69bb5SSascha Wildner fatal_fr(r, "parse type");
1625ce74bacaSMatthew Dillon if (type != MUX_MSG_HELLO) {
162650a69bb5SSascha Wildner error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type);
1627ce74bacaSMatthew Dillon goto out;
1628ce74bacaSMatthew Dillon }
1629664f4763Szrj if ((r = sshbuf_get_u32(m, &ver)) != 0)
163050a69bb5SSascha Wildner fatal_fr(r, "parse version");
1631ce74bacaSMatthew Dillon if (ver != SSHMUX_VER) {
1632ce74bacaSMatthew Dillon error("Unsupported multiplexing protocol version %d "
1633856ea928SPeter Avalos "(expected %d)", ver, SSHMUX_VER);
1634ce74bacaSMatthew Dillon goto out;
1635ce74bacaSMatthew Dillon }
163650a69bb5SSascha Wildner debug2_f("master version %u", ver);
1637856ea928SPeter Avalos /* No extensions are presently defined */
1638664f4763Szrj while (sshbuf_len(m) > 0) {
1639664f4763Szrj char *name = NULL;
1640856ea928SPeter Avalos
1641664f4763Szrj if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 ||
1642664f4763Szrj (r = sshbuf_skip_string(m)) != 0) { /* value */
164350a69bb5SSascha Wildner error_fr(r, "parse extension");
1644664f4763Szrj goto out;
1645664f4763Szrj }
1646856ea928SPeter Avalos debug2("Unrecognised master extension \"%s\"", name);
164736e94dc5SPeter Avalos free(name);
1648856ea928SPeter Avalos }
1649ce74bacaSMatthew Dillon /* success */
1650ce74bacaSMatthew Dillon ret = 0;
1651ce74bacaSMatthew Dillon out:
1652664f4763Szrj sshbuf_free(m);
1653ce74bacaSMatthew Dillon return ret;
1654856ea928SPeter Avalos }
1655856ea928SPeter Avalos
1656856ea928SPeter Avalos static u_int
mux_client_request_alive(int fd)1657856ea928SPeter Avalos mux_client_request_alive(int fd)
1658856ea928SPeter Avalos {
1659664f4763Szrj struct sshbuf *m;
1660856ea928SPeter Avalos char *e;
1661856ea928SPeter Avalos u_int pid, type, rid;
1662664f4763Szrj int r;
1663856ea928SPeter Avalos
166450a69bb5SSascha Wildner debug3_f("entering");
1665856ea928SPeter Avalos
1666664f4763Szrj if ((m = sshbuf_new()) == NULL)
166750a69bb5SSascha Wildner fatal_f("sshbuf_new");
1668664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 ||
1669664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
167050a69bb5SSascha Wildner fatal_fr(r, "assemble");
1671856ea928SPeter Avalos
1672664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
167350a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
1674856ea928SPeter Avalos
1675664f4763Szrj sshbuf_reset(m);
1676856ea928SPeter Avalos
1677856ea928SPeter Avalos /* Read their reply */
1678664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
1679664f4763Szrj sshbuf_free(m);
1680856ea928SPeter Avalos return 0;
1681856ea928SPeter Avalos }
1682856ea928SPeter Avalos
1683664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0)
168450a69bb5SSascha Wildner fatal_fr(r, "parse type");
1685856ea928SPeter Avalos if (type != MUX_S_ALIVE) {
1686664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
168750a69bb5SSascha Wildner fatal_fr(r, "parse error message");
168850a69bb5SSascha Wildner fatal_f("master returned error: %s", e);
1689856ea928SPeter Avalos }
1690856ea928SPeter Avalos
1691664f4763Szrj if ((r = sshbuf_get_u32(m, &rid)) != 0)
169250a69bb5SSascha Wildner fatal_fr(r, "parse remote ID");
1693664f4763Szrj if (rid != muxclient_request_id)
169450a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
169550a69bb5SSascha Wildner muxclient_request_id, rid);
1696664f4763Szrj if ((r = sshbuf_get_u32(m, &pid)) != 0)
169750a69bb5SSascha Wildner fatal_fr(r, "parse PID");
1698664f4763Szrj sshbuf_free(m);
1699856ea928SPeter Avalos
170050a69bb5SSascha Wildner debug3_f("done pid = %u", pid);
1701856ea928SPeter Avalos
1702856ea928SPeter Avalos muxclient_request_id++;
1703856ea928SPeter Avalos
1704856ea928SPeter Avalos return pid;
1705856ea928SPeter Avalos }
1706856ea928SPeter Avalos
1707856ea928SPeter Avalos static void
mux_client_request_terminate(int fd)1708856ea928SPeter Avalos mux_client_request_terminate(int fd)
1709856ea928SPeter Avalos {
1710664f4763Szrj struct sshbuf *m;
1711856ea928SPeter Avalos char *e;
1712856ea928SPeter Avalos u_int type, rid;
1713664f4763Szrj int r;
1714856ea928SPeter Avalos
171550a69bb5SSascha Wildner debug3_f("entering");
1716856ea928SPeter Avalos
1717664f4763Szrj if ((m = sshbuf_new()) == NULL)
171850a69bb5SSascha Wildner fatal_f("sshbuf_new");
1719664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 ||
1720664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
172150a69bb5SSascha Wildner fatal_fr(r, "request");
1722856ea928SPeter Avalos
1723664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
172450a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
1725856ea928SPeter Avalos
1726664f4763Szrj sshbuf_reset(m);
1727856ea928SPeter Avalos
1728856ea928SPeter Avalos /* Read their reply */
1729664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
1730856ea928SPeter Avalos /* Remote end exited already */
1731856ea928SPeter Avalos if (errno == EPIPE) {
1732664f4763Szrj sshbuf_free(m);
1733856ea928SPeter Avalos return;
1734856ea928SPeter Avalos }
173550a69bb5SSascha Wildner fatal_f("read from master failed: %s", strerror(errno));
1736856ea928SPeter Avalos }
1737856ea928SPeter Avalos
1738664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1739664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
174050a69bb5SSascha Wildner fatal_fr(r, "parse");
1741664f4763Szrj if (rid != muxclient_request_id)
174250a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
174350a69bb5SSascha Wildner muxclient_request_id, rid);
1744856ea928SPeter Avalos switch (type) {
1745856ea928SPeter Avalos case MUX_S_OK:
1746856ea928SPeter Avalos break;
1747856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED:
1748664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
174950a69bb5SSascha Wildner fatal_fr(r, "parse error message");
1750856ea928SPeter Avalos fatal("Master refused termination request: %s", e);
1751856ea928SPeter Avalos case MUX_S_FAILURE:
1752664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
175350a69bb5SSascha Wildner fatal_fr(r, "parse error message");
175450a69bb5SSascha Wildner fatal_f("termination request failed: %s", e);
1755856ea928SPeter Avalos default:
175650a69bb5SSascha Wildner fatal_f("unexpected response from master 0x%08x", type);
1757856ea928SPeter Avalos }
1758664f4763Szrj sshbuf_free(m);
1759856ea928SPeter Avalos muxclient_request_id++;
1760856ea928SPeter Avalos }
1761856ea928SPeter Avalos
1762856ea928SPeter Avalos static int
mux_client_forward(int fd,int cancel_flag,u_int ftype,struct Forward * fwd)176336e94dc5SPeter Avalos mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd)
1764856ea928SPeter Avalos {
1765664f4763Szrj struct sshbuf *m;
1766856ea928SPeter Avalos char *e, *fwd_desc;
1767664f4763Szrj const char *lhost, *chost;
1768856ea928SPeter Avalos u_int type, rid;
1769664f4763Szrj int r;
1770856ea928SPeter Avalos
1771856ea928SPeter Avalos fwd_desc = format_forward(ftype, fwd);
177299e85e0dSPeter Avalos debug("Requesting %s %s",
177399e85e0dSPeter Avalos cancel_flag ? "cancellation of" : "forwarding of", fwd_desc);
177436e94dc5SPeter Avalos free(fwd_desc);
1775856ea928SPeter Avalos
1776664f4763Szrj type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD;
1777664f4763Szrj if (fwd->listen_path != NULL)
1778664f4763Szrj lhost = fwd->listen_path;
1779664f4763Szrj else if (fwd->listen_host == NULL)
1780664f4763Szrj lhost = "";
1781664f4763Szrj else if (*fwd->listen_host == '\0')
1782664f4763Szrj lhost = "*";
1783664f4763Szrj else
1784664f4763Szrj lhost = fwd->listen_host;
1785856ea928SPeter Avalos
1786664f4763Szrj if (fwd->connect_path != NULL)
1787664f4763Szrj chost = fwd->connect_path;
1788664f4763Szrj else if (fwd->connect_host == NULL)
1789664f4763Szrj chost = "";
1790664f4763Szrj else
1791664f4763Szrj chost = fwd->connect_host;
1792664f4763Szrj
1793664f4763Szrj if ((m = sshbuf_new()) == NULL)
179450a69bb5SSascha Wildner fatal_f("sshbuf_new");
1795664f4763Szrj if ((r = sshbuf_put_u32(m, type)) != 0 ||
1796664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
1797664f4763Szrj (r = sshbuf_put_u32(m, ftype)) != 0 ||
1798664f4763Szrj (r = sshbuf_put_cstring(m, lhost)) != 0 ||
1799664f4763Szrj (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 ||
1800664f4763Szrj (r = sshbuf_put_cstring(m, chost)) != 0 ||
1801664f4763Szrj (r = sshbuf_put_u32(m, fwd->connect_port)) != 0)
180250a69bb5SSascha Wildner fatal_fr(r, "request");
1803664f4763Szrj
1804664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
180550a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
1806856ea928SPeter Avalos
1807664f4763Szrj sshbuf_reset(m);
1808856ea928SPeter Avalos
1809856ea928SPeter Avalos /* Read their reply */
1810664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
1811664f4763Szrj sshbuf_free(m);
1812856ea928SPeter Avalos return -1;
1813856ea928SPeter Avalos }
1814856ea928SPeter Avalos
1815664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1816664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
181750a69bb5SSascha Wildner fatal_fr(r, "parse");
1818664f4763Szrj if (rid != muxclient_request_id)
181950a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
182050a69bb5SSascha Wildner muxclient_request_id, rid);
1821664f4763Szrj
1822856ea928SPeter Avalos switch (type) {
1823856ea928SPeter Avalos case MUX_S_OK:
1824856ea928SPeter Avalos break;
1825856ea928SPeter Avalos case MUX_S_REMOTE_PORT:
182699e85e0dSPeter Avalos if (cancel_flag)
182750a69bb5SSascha Wildner fatal_f("got MUX_S_REMOTE_PORT for cancel");
1828664f4763Szrj if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0)
182950a69bb5SSascha Wildner fatal_fr(r, "parse port");
1830e9778795SPeter Avalos verbose("Allocated port %u for remote forward to %s:%d",
1831856ea928SPeter Avalos fwd->allocated_port,
1832856ea928SPeter Avalos fwd->connect_host ? fwd->connect_host : "",
1833856ea928SPeter Avalos fwd->connect_port);
1834856ea928SPeter Avalos if (muxclient_command == SSHMUX_COMMAND_FORWARD)
1835e9778795SPeter Avalos fprintf(stdout, "%i\n", fwd->allocated_port);
1836856ea928SPeter Avalos break;
1837856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED:
1838664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
183950a69bb5SSascha Wildner fatal_fr(r, "parse error message");
1840664f4763Szrj sshbuf_free(m);
1841856ea928SPeter Avalos error("Master refused forwarding request: %s", e);
1842856ea928SPeter Avalos return -1;
1843856ea928SPeter Avalos case MUX_S_FAILURE:
1844664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
184550a69bb5SSascha Wildner fatal_fr(r, "parse error message");
1846664f4763Szrj sshbuf_free(m);
184750a69bb5SSascha Wildner error_f("forwarding request failed: %s", e);
1848856ea928SPeter Avalos return -1;
1849856ea928SPeter Avalos default:
185050a69bb5SSascha Wildner fatal_f("unexpected response from master 0x%08x", type);
1851856ea928SPeter Avalos }
1852664f4763Szrj sshbuf_free(m);
1853856ea928SPeter Avalos
1854856ea928SPeter Avalos muxclient_request_id++;
1855856ea928SPeter Avalos return 0;
1856856ea928SPeter Avalos }
1857856ea928SPeter Avalos
1858856ea928SPeter Avalos static int
mux_client_forwards(int fd,int cancel_flag)185999e85e0dSPeter Avalos mux_client_forwards(int fd, int cancel_flag)
1860856ea928SPeter Avalos {
186199e85e0dSPeter Avalos int i, ret = 0;
1862856ea928SPeter Avalos
186350a69bb5SSascha Wildner debug3_f("%s forwardings: %d local, %d remote",
186499e85e0dSPeter Avalos cancel_flag ? "cancel" : "request",
1865856ea928SPeter Avalos options.num_local_forwards, options.num_remote_forwards);
1866856ea928SPeter Avalos
1867856ea928SPeter Avalos /* XXX ExitOnForwardingFailure */
1868856ea928SPeter Avalos for (i = 0; i < options.num_local_forwards; i++) {
186999e85e0dSPeter Avalos if (mux_client_forward(fd, cancel_flag,
1870856ea928SPeter Avalos options.local_forwards[i].connect_port == 0 ?
1871856ea928SPeter Avalos MUX_FWD_DYNAMIC : MUX_FWD_LOCAL,
1872856ea928SPeter Avalos options.local_forwards + i) != 0)
187399e85e0dSPeter Avalos ret = -1;
1874856ea928SPeter Avalos }
1875856ea928SPeter Avalos for (i = 0; i < options.num_remote_forwards; i++) {
187699e85e0dSPeter Avalos if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE,
1877856ea928SPeter Avalos options.remote_forwards + i) != 0)
187899e85e0dSPeter Avalos ret = -1;
1879856ea928SPeter Avalos }
188099e85e0dSPeter Avalos return ret;
1881856ea928SPeter Avalos }
1882856ea928SPeter Avalos
1883856ea928SPeter Avalos static int
mux_client_request_session(int fd)1884856ea928SPeter Avalos mux_client_request_session(int fd)
1885856ea928SPeter Avalos {
1886664f4763Szrj struct sshbuf *m;
1887664f4763Szrj char *e;
188850a69bb5SSascha Wildner const char *term = NULL;
1889ee116499SAntonio Huete Jimenez u_int i, echar, rid, sid, esid, exitval, type, exitval_seen;
1890856ea928SPeter Avalos extern char **environ;
1891*ba1276acSMatthew Dillon int r, rawmode = 0;
1892856ea928SPeter Avalos
189350a69bb5SSascha Wildner debug3_f("entering");
1894856ea928SPeter Avalos
1895856ea928SPeter Avalos if ((muxserver_pid = mux_client_request_alive(fd)) == 0) {
189650a69bb5SSascha Wildner error_f("master alive request failed");
1897856ea928SPeter Avalos return -1;
1898856ea928SPeter Avalos }
1899856ea928SPeter Avalos
19000cbfa66cSDaniel Fojt ssh_signal(SIGPIPE, SIG_IGN);
1901856ea928SPeter Avalos
190250a69bb5SSascha Wildner if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
190350a69bb5SSascha Wildner fatal_f("stdfd_devnull failed");
1904856ea928SPeter Avalos
190550a69bb5SSascha Wildner if ((term = lookup_env_in_list("TERM", options.setenv,
190650a69bb5SSascha Wildner options.num_setenv)) == NULL || *term == '\0')
190750a69bb5SSascha Wildner term = getenv("TERM");
190850a69bb5SSascha Wildner
1909664f4763Szrj echar = 0xffffffff;
1910664f4763Szrj if (options.escape_char != SSH_ESCAPECHAR_NONE)
1911664f4763Szrj echar = (u_int)options.escape_char;
1912856ea928SPeter Avalos
1913664f4763Szrj if ((m = sshbuf_new()) == NULL)
191450a69bb5SSascha Wildner fatal_f("sshbuf_new");
1915664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 ||
1916664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
1917664f4763Szrj (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */
1918664f4763Szrj (r = sshbuf_put_u32(m, tty_flag)) != 0 ||
1919664f4763Szrj (r = sshbuf_put_u32(m, options.forward_x11)) != 0 ||
1920664f4763Szrj (r = sshbuf_put_u32(m, options.forward_agent)) != 0 ||
192150a69bb5SSascha Wildner (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 ||
1922664f4763Szrj (r = sshbuf_put_u32(m, echar)) != 0 ||
192350a69bb5SSascha Wildner (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 ||
1924664f4763Szrj (r = sshbuf_put_stringb(m, command)) != 0)
192550a69bb5SSascha Wildner fatal_fr(r, "request");
1926856ea928SPeter Avalos
1927856ea928SPeter Avalos /* Pass environment */
1928664f4763Szrj if (options.num_send_env > 0 && environ != NULL) {
1929856ea928SPeter Avalos for (i = 0; environ[i] != NULL; i++) {
1930664f4763Szrj if (!env_permitted(environ[i]))
1931664f4763Szrj continue;
1932664f4763Szrj if ((r = sshbuf_put_cstring(m, environ[i])) != 0)
193350a69bb5SSascha Wildner fatal_fr(r, "request sendenv");
1934856ea928SPeter Avalos }
1935856ea928SPeter Avalos }
1936664f4763Szrj for (i = 0; i < options.num_setenv; i++) {
1937664f4763Szrj if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0)
193850a69bb5SSascha Wildner fatal_fr(r, "request setenv");
1939856ea928SPeter Avalos }
1940856ea928SPeter Avalos
1941664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
194250a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
1943856ea928SPeter Avalos
1944856ea928SPeter Avalos /* Send the stdio file descriptors */
1945856ea928SPeter Avalos if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
1946856ea928SPeter Avalos mm_send_fd(fd, STDOUT_FILENO) == -1 ||
1947856ea928SPeter Avalos mm_send_fd(fd, STDERR_FILENO) == -1)
194850a69bb5SSascha Wildner fatal_f("send fds failed");
1949856ea928SPeter Avalos
195050a69bb5SSascha Wildner debug3_f("session request sent");
1951856ea928SPeter Avalos
1952856ea928SPeter Avalos /* Read their reply */
1953664f4763Szrj sshbuf_reset(m);
1954664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
195550a69bb5SSascha Wildner error_f("read from master failed: %s", strerror(errno));
1956664f4763Szrj sshbuf_free(m);
1957856ea928SPeter Avalos return -1;
1958856ea928SPeter Avalos }
1959856ea928SPeter Avalos
1960664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
1961664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
196250a69bb5SSascha Wildner fatal_fr(r, "parse");
1963664f4763Szrj if (rid != muxclient_request_id)
196450a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
196550a69bb5SSascha Wildner muxclient_request_id, rid);
1966664f4763Szrj
1967856ea928SPeter Avalos switch (type) {
1968856ea928SPeter Avalos case MUX_S_SESSION_OPENED:
1969664f4763Szrj if ((r = sshbuf_get_u32(m, &sid)) != 0)
197050a69bb5SSascha Wildner fatal_fr(r, "parse session ID");
197150a69bb5SSascha Wildner debug_f("master session id: %u", sid);
1972856ea928SPeter Avalos break;
1973856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED:
1974664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
197550a69bb5SSascha Wildner fatal_fr(r, "parse error message");
19769f304aafSPeter Avalos error("Master refused session request: %s", e);
1977664f4763Szrj sshbuf_free(m);
1978856ea928SPeter Avalos return -1;
1979856ea928SPeter Avalos case MUX_S_FAILURE:
1980664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
198150a69bb5SSascha Wildner fatal_fr(r, "parse error message");
198250a69bb5SSascha Wildner error_f("session request failed: %s", e);
1983664f4763Szrj sshbuf_free(m);
1984856ea928SPeter Avalos return -1;
1985856ea928SPeter Avalos default:
1986664f4763Szrj sshbuf_free(m);
198750a69bb5SSascha Wildner error_f("unexpected response from master 0x%08x", type);
1988856ea928SPeter Avalos return -1;
1989856ea928SPeter Avalos }
1990856ea928SPeter Avalos muxclient_request_id++;
1991856ea928SPeter Avalos
1992e9778795SPeter Avalos if (pledge("stdio proc tty", NULL) == -1)
199350a69bb5SSascha Wildner fatal_f("pledge(): %s", strerror(errno));
1994e9778795SPeter Avalos platform_pledge_mux();
1995e9778795SPeter Avalos
19960cbfa66cSDaniel Fojt ssh_signal(SIGHUP, control_client_sighandler);
19970cbfa66cSDaniel Fojt ssh_signal(SIGINT, control_client_sighandler);
19980cbfa66cSDaniel Fojt ssh_signal(SIGTERM, control_client_sighandler);
19990cbfa66cSDaniel Fojt ssh_signal(SIGWINCH, control_client_sigrelay);
2000856ea928SPeter Avalos
2001*ba1276acSMatthew Dillon if (options.fork_after_authentication)
2002*ba1276acSMatthew Dillon daemon(1, 1);
2003*ba1276acSMatthew Dillon else {
20041c188a7fSPeter Avalos rawmode = tty_flag;
2005*ba1276acSMatthew Dillon if (tty_flag) {
2006*ba1276acSMatthew Dillon enter_raw_mode(
2007*ba1276acSMatthew Dillon options.request_tty == REQUEST_TTY_FORCE);
2008*ba1276acSMatthew Dillon }
2009*ba1276acSMatthew Dillon }
2010856ea928SPeter Avalos
2011856ea928SPeter Avalos /*
2012856ea928SPeter Avalos * Stick around until the controlee closes the client_fd.
2013856ea928SPeter Avalos * Before it does, it is expected to write an exit message.
2014856ea928SPeter Avalos * This process must read the value and wait for the closure of
2015856ea928SPeter Avalos * the client_fd; if this one closes early, the multiplex master will
2016856ea928SPeter Avalos * terminate early too (possibly losing data).
2017856ea928SPeter Avalos */
2018856ea928SPeter Avalos for (exitval = 255, exitval_seen = 0;;) {
2019664f4763Szrj sshbuf_reset(m);
2020664f4763Szrj if (mux_client_read_packet(fd, m) != 0)
2021856ea928SPeter Avalos break;
2022664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0)
202350a69bb5SSascha Wildner fatal_fr(r, "parse type");
20241c188a7fSPeter Avalos switch (type) {
20251c188a7fSPeter Avalos case MUX_S_TTY_ALLOC_FAIL:
2026664f4763Szrj if ((r = sshbuf_get_u32(m, &esid)) != 0)
202750a69bb5SSascha Wildner fatal_fr(r, "parse session ID");
2028664f4763Szrj if (esid != sid)
202950a69bb5SSascha Wildner fatal_f("tty alloc fail on unknown session: "
203050a69bb5SSascha Wildner "my id %u theirs %u", sid, esid);
20311c188a7fSPeter Avalos leave_raw_mode(options.request_tty ==
20321c188a7fSPeter Avalos REQUEST_TTY_FORCE);
20331c188a7fSPeter Avalos rawmode = 0;
20341c188a7fSPeter Avalos continue;
20351c188a7fSPeter Avalos case MUX_S_EXIT_MESSAGE:
2036664f4763Szrj if ((r = sshbuf_get_u32(m, &esid)) != 0)
203750a69bb5SSascha Wildner fatal_fr(r, "parse session ID");
2038664f4763Szrj if (esid != sid)
203950a69bb5SSascha Wildner fatal_f("exit on unknown session: "
204050a69bb5SSascha Wildner "my id %u theirs %u", sid, esid);
2041856ea928SPeter Avalos if (exitval_seen)
204250a69bb5SSascha Wildner fatal_f("exitval sent twice");
2043664f4763Szrj if ((r = sshbuf_get_u32(m, &exitval)) != 0)
204450a69bb5SSascha Wildner fatal_fr(r, "parse exitval");
2045856ea928SPeter Avalos exitval_seen = 1;
20461c188a7fSPeter Avalos continue;
20471c188a7fSPeter Avalos default:
2048664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
204950a69bb5SSascha Wildner fatal_fr(r, "parse error message");
205050a69bb5SSascha Wildner fatal_f("master returned error: %s", e);
20511c188a7fSPeter Avalos }
2052856ea928SPeter Avalos }
2053856ea928SPeter Avalos
2054856ea928SPeter Avalos close(fd);
20551c188a7fSPeter Avalos if (rawmode)
20561c188a7fSPeter Avalos leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
2057856ea928SPeter Avalos
2058856ea928SPeter Avalos if (muxclient_terminate) {
2059ce74bacaSMatthew Dillon debug2("Exiting on signal: %s", strsignal(muxclient_terminate));
2060856ea928SPeter Avalos exitval = 255;
2061856ea928SPeter Avalos } else if (!exitval_seen) {
2062856ea928SPeter Avalos debug2("Control master terminated unexpectedly");
2063856ea928SPeter Avalos exitval = 255;
2064856ea928SPeter Avalos } else
2065856ea928SPeter Avalos debug2("Received exit status from master %d", exitval);
2066856ea928SPeter Avalos
2067ee116499SAntonio Huete Jimenez if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO)
2068856ea928SPeter Avalos fprintf(stderr, "Shared connection to %s closed.\r\n", host);
2069856ea928SPeter Avalos
2070856ea928SPeter Avalos exit(exitval);
2071856ea928SPeter Avalos }
2072856ea928SPeter Avalos
2073856ea928SPeter Avalos static int
mux_client_proxy(int fd)2074ce74bacaSMatthew Dillon mux_client_proxy(int fd)
2075ce74bacaSMatthew Dillon {
2076664f4763Szrj struct sshbuf *m;
2077ce74bacaSMatthew Dillon char *e;
2078ce74bacaSMatthew Dillon u_int type, rid;
2079664f4763Szrj int r;
2080ce74bacaSMatthew Dillon
2081664f4763Szrj if ((m = sshbuf_new()) == NULL)
208250a69bb5SSascha Wildner fatal_f("sshbuf_new");
2083664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 ||
2084664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
208550a69bb5SSascha Wildner fatal_fr(r, "request");
2086664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
208750a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
2088ce74bacaSMatthew Dillon
2089664f4763Szrj sshbuf_reset(m);
2090ce74bacaSMatthew Dillon
2091ce74bacaSMatthew Dillon /* Read their reply */
2092664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
2093664f4763Szrj sshbuf_free(m);
2094ce74bacaSMatthew Dillon return 0;
2095ce74bacaSMatthew Dillon }
2096664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2097664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
209850a69bb5SSascha Wildner fatal_fr(r, "parse");
2099664f4763Szrj if (rid != muxclient_request_id)
210050a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
210150a69bb5SSascha Wildner muxclient_request_id, rid);
2102664f4763Szrj if (type != MUX_S_PROXY) {
2103664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
210450a69bb5SSascha Wildner fatal_fr(r, "parse error message");
210550a69bb5SSascha Wildner fatal_f("master returned error: %s", e);
2106664f4763Szrj }
2107664f4763Szrj sshbuf_free(m);
2108ce74bacaSMatthew Dillon
210950a69bb5SSascha Wildner debug3_f("done");
2110ce74bacaSMatthew Dillon muxclient_request_id++;
2111ce74bacaSMatthew Dillon return 0;
2112ce74bacaSMatthew Dillon }
2113ce74bacaSMatthew Dillon
2114ce74bacaSMatthew Dillon static int
mux_client_request_stdio_fwd(int fd)2115856ea928SPeter Avalos mux_client_request_stdio_fwd(int fd)
2116856ea928SPeter Avalos {
2117664f4763Szrj struct sshbuf *m;
2118856ea928SPeter Avalos char *e;
2119856ea928SPeter Avalos u_int type, rid, sid;
212050a69bb5SSascha Wildner int r;
2121856ea928SPeter Avalos
212250a69bb5SSascha Wildner debug3_f("entering");
2123856ea928SPeter Avalos
2124856ea928SPeter Avalos if ((muxserver_pid = mux_client_request_alive(fd)) == 0) {
212550a69bb5SSascha Wildner error_f("master alive request failed");
2126856ea928SPeter Avalos return -1;
2127856ea928SPeter Avalos }
2128856ea928SPeter Avalos
21290cbfa66cSDaniel Fojt ssh_signal(SIGPIPE, SIG_IGN);
2130856ea928SPeter Avalos
213150a69bb5SSascha Wildner if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
213250a69bb5SSascha Wildner fatal_f("stdfd_devnull failed");
2133856ea928SPeter Avalos
2134664f4763Szrj if ((m = sshbuf_new()) == NULL)
213550a69bb5SSascha Wildner fatal_f("sshbuf_new");
2136664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 ||
2137664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 ||
2138664f4763Szrj (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */
2139664f4763Szrj (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 ||
2140664f4763Szrj (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0)
214150a69bb5SSascha Wildner fatal_fr(r, "request");
2142856ea928SPeter Avalos
2143664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
214450a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
2145856ea928SPeter Avalos
2146856ea928SPeter Avalos /* Send the stdio file descriptors */
2147856ea928SPeter Avalos if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
2148856ea928SPeter Avalos mm_send_fd(fd, STDOUT_FILENO) == -1)
214950a69bb5SSascha Wildner fatal_f("send fds failed");
2150856ea928SPeter Avalos
2151e9778795SPeter Avalos if (pledge("stdio proc tty", NULL) == -1)
215250a69bb5SSascha Wildner fatal_f("pledge(): %s", strerror(errno));
2153e9778795SPeter Avalos platform_pledge_mux();
2154e9778795SPeter Avalos
215550a69bb5SSascha Wildner debug3_f("stdio forward request sent");
2156856ea928SPeter Avalos
2157856ea928SPeter Avalos /* Read their reply */
2158664f4763Szrj sshbuf_reset(m);
2159856ea928SPeter Avalos
2160664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
216150a69bb5SSascha Wildner error_f("read from master failed: %s", strerror(errno));
2162664f4763Szrj sshbuf_free(m);
2163856ea928SPeter Avalos return -1;
2164856ea928SPeter Avalos }
2165856ea928SPeter Avalos
2166664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2167664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
216850a69bb5SSascha Wildner fatal_fr(r, "parse");
2169664f4763Szrj if (rid != muxclient_request_id)
217050a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
217150a69bb5SSascha Wildner muxclient_request_id, rid);
2172856ea928SPeter Avalos switch (type) {
2173856ea928SPeter Avalos case MUX_S_SESSION_OPENED:
2174664f4763Szrj if ((r = sshbuf_get_u32(m, &sid)) != 0)
217550a69bb5SSascha Wildner fatal_fr(r, "parse session ID");
217650a69bb5SSascha Wildner debug_f("master session id: %u", sid);
2177856ea928SPeter Avalos break;
2178856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED:
2179664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
218050a69bb5SSascha Wildner fatal_fr(r, "parse error message");
2181664f4763Szrj sshbuf_free(m);
21829f304aafSPeter Avalos fatal("Master refused stdio forwarding request: %s", e);
2183856ea928SPeter Avalos case MUX_S_FAILURE:
2184664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
218550a69bb5SSascha Wildner fatal_fr(r, "parse error message");
2186664f4763Szrj sshbuf_free(m);
218736e94dc5SPeter Avalos fatal("Stdio forwarding request failed: %s", e);
2188856ea928SPeter Avalos default:
2189664f4763Szrj sshbuf_free(m);
219050a69bb5SSascha Wildner error_f("unexpected response from master 0x%08x", type);
2191856ea928SPeter Avalos return -1;
2192856ea928SPeter Avalos }
2193856ea928SPeter Avalos muxclient_request_id++;
2194856ea928SPeter Avalos
21950cbfa66cSDaniel Fojt ssh_signal(SIGHUP, control_client_sighandler);
21960cbfa66cSDaniel Fojt ssh_signal(SIGINT, control_client_sighandler);
21970cbfa66cSDaniel Fojt ssh_signal(SIGTERM, control_client_sighandler);
21980cbfa66cSDaniel Fojt ssh_signal(SIGWINCH, control_client_sigrelay);
2199856ea928SPeter Avalos
2200856ea928SPeter Avalos /*
2201856ea928SPeter Avalos * Stick around until the controlee closes the client_fd.
2202856ea928SPeter Avalos */
2203664f4763Szrj sshbuf_reset(m);
2204664f4763Szrj if (mux_client_read_packet(fd, m) != 0) {
2205856ea928SPeter Avalos if (errno == EPIPE ||
2206856ea928SPeter Avalos (errno == EINTR && muxclient_terminate != 0))
2207856ea928SPeter Avalos return 0;
220850a69bb5SSascha Wildner fatal_f("mux_client_read_packet: %s", strerror(errno));
2209856ea928SPeter Avalos }
221050a69bb5SSascha Wildner fatal_f("master returned unexpected message %u", type);
221118de8d7fSPeter Avalos }
221218de8d7fSPeter Avalos
22131c188a7fSPeter Avalos static void
mux_client_request_stop_listening(int fd)22141c188a7fSPeter Avalos mux_client_request_stop_listening(int fd)
22151c188a7fSPeter Avalos {
2216664f4763Szrj struct sshbuf *m;
22171c188a7fSPeter Avalos char *e;
22181c188a7fSPeter Avalos u_int type, rid;
2219664f4763Szrj int r;
22201c188a7fSPeter Avalos
222150a69bb5SSascha Wildner debug3_f("entering");
22221c188a7fSPeter Avalos
2223664f4763Szrj if ((m = sshbuf_new()) == NULL)
222450a69bb5SSascha Wildner fatal_f("sshbuf_new");
2225664f4763Szrj if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 ||
2226664f4763Szrj (r = sshbuf_put_u32(m, muxclient_request_id)) != 0)
222750a69bb5SSascha Wildner fatal_fr(r, "request");
22281c188a7fSPeter Avalos
2229664f4763Szrj if (mux_client_write_packet(fd, m) != 0)
223050a69bb5SSascha Wildner fatal_f("write packet: %s", strerror(errno));
22311c188a7fSPeter Avalos
2232664f4763Szrj sshbuf_reset(m);
22331c188a7fSPeter Avalos
22341c188a7fSPeter Avalos /* Read their reply */
2235664f4763Szrj if (mux_client_read_packet(fd, m) != 0)
223650a69bb5SSascha Wildner fatal_f("read from master failed: %s", strerror(errno));
22371c188a7fSPeter Avalos
2238664f4763Szrj if ((r = sshbuf_get_u32(m, &type)) != 0 ||
2239664f4763Szrj (r = sshbuf_get_u32(m, &rid)) != 0)
224050a69bb5SSascha Wildner fatal_fr(r, "parse");
2241664f4763Szrj if (rid != muxclient_request_id)
224250a69bb5SSascha Wildner fatal_f("out of sequence reply: my id %u theirs %u",
224350a69bb5SSascha Wildner muxclient_request_id, rid);
2244664f4763Szrj
22451c188a7fSPeter Avalos switch (type) {
22461c188a7fSPeter Avalos case MUX_S_OK:
22471c188a7fSPeter Avalos break;
22481c188a7fSPeter Avalos case MUX_S_PERMISSION_DENIED:
2249664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
225050a69bb5SSascha Wildner fatal_fr(r, "parse error message");
22511c188a7fSPeter Avalos fatal("Master refused stop listening request: %s", e);
22521c188a7fSPeter Avalos case MUX_S_FAILURE:
2253664f4763Szrj if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0)
225450a69bb5SSascha Wildner fatal_fr(r, "parse error message");
225550a69bb5SSascha Wildner fatal_f("stop listening request failed: %s", e);
22561c188a7fSPeter Avalos default:
225750a69bb5SSascha Wildner fatal_f("unexpected response from master 0x%08x", type);
22581c188a7fSPeter Avalos }
2259664f4763Szrj sshbuf_free(m);
22601c188a7fSPeter Avalos muxclient_request_id++;
22611c188a7fSPeter Avalos }
22621c188a7fSPeter Avalos
226318de8d7fSPeter Avalos /* Multiplex client main loop. */
2264ce74bacaSMatthew Dillon int
muxclient(const char * path)226518de8d7fSPeter Avalos muxclient(const char *path)
226618de8d7fSPeter Avalos {
226718de8d7fSPeter Avalos struct sockaddr_un addr;
2268*ba1276acSMatthew Dillon int sock, timeout = options.connection_timeout, timeout_ms = -1;
2269856ea928SPeter Avalos u_int pid;
227018de8d7fSPeter Avalos
2271856ea928SPeter Avalos if (muxclient_command == 0) {
2272e9778795SPeter Avalos if (options.stdio_forward_host != NULL)
2273856ea928SPeter Avalos muxclient_command = SSHMUX_COMMAND_STDIO_FWD;
2274856ea928SPeter Avalos else
227518de8d7fSPeter Avalos muxclient_command = SSHMUX_COMMAND_OPEN;
2276856ea928SPeter Avalos }
227718de8d7fSPeter Avalos
227818de8d7fSPeter Avalos switch (options.control_master) {
227918de8d7fSPeter Avalos case SSHCTL_MASTER_AUTO:
228018de8d7fSPeter Avalos case SSHCTL_MASTER_AUTO_ASK:
2281*ba1276acSMatthew Dillon debug("auto-mux: Trying existing master at '%s'", path);
228218de8d7fSPeter Avalos /* FALLTHROUGH */
228318de8d7fSPeter Avalos case SSHCTL_MASTER_NO:
228418de8d7fSPeter Avalos break;
228518de8d7fSPeter Avalos default:
2286ce74bacaSMatthew Dillon return -1;
228718de8d7fSPeter Avalos }
228818de8d7fSPeter Avalos
228918de8d7fSPeter Avalos memset(&addr, '\0', sizeof(addr));
229018de8d7fSPeter Avalos addr.sun_family = AF_UNIX;
229118de8d7fSPeter Avalos
229218de8d7fSPeter Avalos if (strlcpy(addr.sun_path, path,
229318de8d7fSPeter Avalos sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
2294ce74bacaSMatthew Dillon fatal("ControlPath too long ('%s' >= %u bytes)", path,
2295ce74bacaSMatthew Dillon (unsigned int)sizeof(addr.sun_path));
229618de8d7fSPeter Avalos
22970cbfa66cSDaniel Fojt if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
229850a69bb5SSascha Wildner fatal_f("socket(): %s", strerror(errno));
229918de8d7fSPeter Avalos
2300ce74bacaSMatthew Dillon if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
2301856ea928SPeter Avalos switch (muxclient_command) {
2302856ea928SPeter Avalos case SSHMUX_COMMAND_OPEN:
2303856ea928SPeter Avalos case SSHMUX_COMMAND_STDIO_FWD:
2304856ea928SPeter Avalos break;
2305856ea928SPeter Avalos default:
230618de8d7fSPeter Avalos fatal("Control socket connect(%.100s): %s", path,
230718de8d7fSPeter Avalos strerror(errno));
230818de8d7fSPeter Avalos }
23099f304aafSPeter Avalos if (errno == ECONNREFUSED &&
23109f304aafSPeter Avalos options.control_master != SSHCTL_MASTER_NO) {
23119f304aafSPeter Avalos debug("Stale control socket %.100s, unlinking", path);
23129f304aafSPeter Avalos unlink(path);
23139f304aafSPeter Avalos } else if (errno == ENOENT) {
231418de8d7fSPeter Avalos debug("Control socket \"%.100s\" does not exist", path);
23159f304aafSPeter Avalos } else {
231618de8d7fSPeter Avalos error("Control socket connect(%.100s): %s", path,
231718de8d7fSPeter Avalos strerror(errno));
231818de8d7fSPeter Avalos }
231918de8d7fSPeter Avalos close(sock);
2320ce74bacaSMatthew Dillon return -1;
232118de8d7fSPeter Avalos }
2322856ea928SPeter Avalos set_nonblock(sock);
232318de8d7fSPeter Avalos
2324*ba1276acSMatthew Dillon /* Timeout on initial connection only. */
2325*ba1276acSMatthew Dillon if (timeout > 0 && timeout < INT_MAX / 1000)
2326*ba1276acSMatthew Dillon timeout_ms = timeout * 1000;
2327*ba1276acSMatthew Dillon
2328*ba1276acSMatthew Dillon if (mux_client_hello_exchange(sock, timeout_ms) != 0) {
232950a69bb5SSascha Wildner error_f("master hello exchange failed");
233018de8d7fSPeter Avalos close(sock);
2331ce74bacaSMatthew Dillon return -1;
233218de8d7fSPeter Avalos }
233318de8d7fSPeter Avalos
233418de8d7fSPeter Avalos switch (muxclient_command) {
233518de8d7fSPeter Avalos case SSHMUX_COMMAND_ALIVE_CHECK:
2336856ea928SPeter Avalos if ((pid = mux_client_request_alive(sock)) == 0)
233750a69bb5SSascha Wildner fatal_f("master alive check failed");
2338e9778795SPeter Avalos fprintf(stderr, "Master running (pid=%u)\r\n", pid);
233918de8d7fSPeter Avalos exit(0);
234018de8d7fSPeter Avalos case SSHMUX_COMMAND_TERMINATE:
2341856ea928SPeter Avalos mux_client_request_terminate(sock);
2342ce74bacaSMatthew Dillon if (options.log_level != SYSLOG_LEVEL_QUIET)
234318de8d7fSPeter Avalos fprintf(stderr, "Exit request sent.\r\n");
234418de8d7fSPeter Avalos exit(0);
2345856ea928SPeter Avalos case SSHMUX_COMMAND_FORWARD:
234699e85e0dSPeter Avalos if (mux_client_forwards(sock, 0) != 0)
234750a69bb5SSascha Wildner fatal_f("master forward request failed");
2348856ea928SPeter Avalos exit(0);
234918de8d7fSPeter Avalos case SSHMUX_COMMAND_OPEN:
235099e85e0dSPeter Avalos if (mux_client_forwards(sock, 0) != 0) {
235150a69bb5SSascha Wildner error_f("master forward request failed");
2352ce74bacaSMatthew Dillon return -1;
235318de8d7fSPeter Avalos }
2354856ea928SPeter Avalos mux_client_request_session(sock);
2355ce74bacaSMatthew Dillon return -1;
2356856ea928SPeter Avalos case SSHMUX_COMMAND_STDIO_FWD:
2357856ea928SPeter Avalos mux_client_request_stdio_fwd(sock);
2358856ea928SPeter Avalos exit(0);
23591c188a7fSPeter Avalos case SSHMUX_COMMAND_STOP:
23601c188a7fSPeter Avalos mux_client_request_stop_listening(sock);
2361ce74bacaSMatthew Dillon if (options.log_level != SYSLOG_LEVEL_QUIET)
23621c188a7fSPeter Avalos fprintf(stderr, "Stop listening request sent.\r\n");
23631c188a7fSPeter Avalos exit(0);
236499e85e0dSPeter Avalos case SSHMUX_COMMAND_CANCEL_FWD:
236599e85e0dSPeter Avalos if (mux_client_forwards(sock, 1) != 0)
236650a69bb5SSascha Wildner error_f("master cancel forward request failed");
236799e85e0dSPeter Avalos exit(0);
2368ce74bacaSMatthew Dillon case SSHMUX_COMMAND_PROXY:
2369ce74bacaSMatthew Dillon mux_client_proxy(sock);
2370ce74bacaSMatthew Dillon return (sock);
237118de8d7fSPeter Avalos default:
237218de8d7fSPeter Avalos fatal("unrecognised muxclient_command %d", muxclient_command);
237318de8d7fSPeter Avalos }
237418de8d7fSPeter Avalos }
2375