1*1b91d634SEd Maste /* $OpenBSD: mux.c,v 1.100 2023/08/18 01:37:41 djm Exp $ */ 2d4af9e69SDag-Erling Smørgrav /* 3d4af9e69SDag-Erling Smørgrav * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> 4d4af9e69SDag-Erling Smørgrav * 5d4af9e69SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6d4af9e69SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7d4af9e69SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8d4af9e69SDag-Erling Smørgrav * 9d4af9e69SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10d4af9e69SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11d4af9e69SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12d4af9e69SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13d4af9e69SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14d4af9e69SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15d4af9e69SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16d4af9e69SDag-Erling Smørgrav */ 17d4af9e69SDag-Erling Smørgrav 18d4af9e69SDag-Erling Smørgrav /* ssh session multiplexing support */ 19d4af9e69SDag-Erling Smørgrav 20b15c8340SDag-Erling Smørgrav #include "includes.h" 21b15c8340SDag-Erling Smørgrav 22d4af9e69SDag-Erling Smørgrav #include <sys/types.h> 23d4af9e69SDag-Erling Smørgrav #include <sys/stat.h> 24d4af9e69SDag-Erling Smørgrav #include <sys/socket.h> 25d4af9e69SDag-Erling Smørgrav #include <sys/un.h> 26d4af9e69SDag-Erling Smørgrav 27d4af9e69SDag-Erling Smørgrav #include <errno.h> 28d4af9e69SDag-Erling Smørgrav #include <fcntl.h> 29535af610SEd Maste #include <limits.h> 30d4af9e69SDag-Erling Smørgrav #include <signal.h> 31d4af9e69SDag-Erling Smørgrav #include <stdarg.h> 32d4af9e69SDag-Erling Smørgrav #include <stddef.h> 33d4af9e69SDag-Erling Smørgrav #include <stdlib.h> 34d4af9e69SDag-Erling Smørgrav #include <stdio.h> 35d4af9e69SDag-Erling Smørgrav #include <string.h> 36d4af9e69SDag-Erling Smørgrav #include <unistd.h> 37d4af9e69SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 38d4af9e69SDag-Erling Smørgrav #include <paths.h> 39d4af9e69SDag-Erling Smørgrav #endif 40d4af9e69SDag-Erling Smørgrav 41b15c8340SDag-Erling Smørgrav #ifdef HAVE_POLL_H 42b15c8340SDag-Erling Smørgrav #include <poll.h> 43b15c8340SDag-Erling Smørgrav #else 44b15c8340SDag-Erling Smørgrav # ifdef HAVE_SYS_POLL_H 45b15c8340SDag-Erling Smørgrav # include <sys/poll.h> 46b15c8340SDag-Erling Smørgrav # endif 47b15c8340SDag-Erling Smørgrav #endif 48b15c8340SDag-Erling Smørgrav 49d4af9e69SDag-Erling Smørgrav #ifdef HAVE_UTIL_H 50d4af9e69SDag-Erling Smørgrav # include <util.h> 51d4af9e69SDag-Erling Smørgrav #endif 52d4af9e69SDag-Erling Smørgrav 53d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 54d4af9e69SDag-Erling Smørgrav #include "xmalloc.h" 55d4af9e69SDag-Erling Smørgrav #include "log.h" 56d4af9e69SDag-Erling Smørgrav #include "ssh.h" 57e2f6069cSDag-Erling Smørgrav #include "ssh2.h" 58d4af9e69SDag-Erling Smørgrav #include "pathnames.h" 59d4af9e69SDag-Erling Smørgrav #include "misc.h" 60d4af9e69SDag-Erling Smørgrav #include "match.h" 61190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 62d4af9e69SDag-Erling Smørgrav #include "channels.h" 63d4af9e69SDag-Erling Smørgrav #include "msg.h" 64d4af9e69SDag-Erling Smørgrav #include "packet.h" 65d4af9e69SDag-Erling Smørgrav #include "monitor_fdpass.h" 66d4af9e69SDag-Erling Smørgrav #include "sshpty.h" 67190cef3dSDag-Erling Smørgrav #include "sshkey.h" 68d4af9e69SDag-Erling Smørgrav #include "readconf.h" 69d4af9e69SDag-Erling Smørgrav #include "clientloop.h" 70ca86bcf2SDag-Erling Smørgrav #include "ssherr.h" 71535af610SEd Maste #include "misc.h" 72d4af9e69SDag-Erling Smørgrav 73d4af9e69SDag-Erling Smørgrav /* from ssh.c */ 74d4af9e69SDag-Erling Smørgrav extern int tty_flag; 75d4af9e69SDag-Erling Smørgrav extern Options options; 76d4af9e69SDag-Erling Smørgrav extern char *host; 77190cef3dSDag-Erling Smørgrav extern struct sshbuf *command; 78b15c8340SDag-Erling Smørgrav extern volatile sig_atomic_t quit_pending; 79d4af9e69SDag-Erling Smørgrav 80d4af9e69SDag-Erling Smørgrav /* Context for session open confirmation callback */ 81d4af9e69SDag-Erling Smørgrav struct mux_session_confirm_ctx { 82b15c8340SDag-Erling Smørgrav u_int want_tty; 83b15c8340SDag-Erling Smørgrav u_int want_subsys; 84b15c8340SDag-Erling Smørgrav u_int want_x_fwd; 85b15c8340SDag-Erling Smørgrav u_int want_agent_fwd; 86190cef3dSDag-Erling Smørgrav struct sshbuf *cmd; 87d4af9e69SDag-Erling Smørgrav char *term; 88d4af9e69SDag-Erling Smørgrav struct termios tio; 89d4af9e69SDag-Erling Smørgrav char **env; 90e2f6069cSDag-Erling Smørgrav u_int rid; 91e2f6069cSDag-Erling Smørgrav }; 92e2f6069cSDag-Erling Smørgrav 93a0ee8cc6SDag-Erling Smørgrav /* Context for stdio fwd open confirmation callback */ 94a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx { 95a0ee8cc6SDag-Erling Smørgrav u_int rid; 96a0ee8cc6SDag-Erling Smørgrav }; 97a0ee8cc6SDag-Erling Smørgrav 98e2f6069cSDag-Erling Smørgrav /* Context for global channel callback */ 99e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx { 100e2f6069cSDag-Erling Smørgrav u_int cid; /* channel id */ 101e2f6069cSDag-Erling Smørgrav u_int rid; /* request id */ 102e2f6069cSDag-Erling Smørgrav int fid; /* forward id */ 103d4af9e69SDag-Erling Smørgrav }; 104d4af9e69SDag-Erling Smørgrav 105d4af9e69SDag-Erling Smørgrav /* fd to control socket */ 106d4af9e69SDag-Erling Smørgrav int muxserver_sock = -1; 107d4af9e69SDag-Erling Smørgrav 108b15c8340SDag-Erling Smørgrav /* client request id */ 109b15c8340SDag-Erling Smørgrav u_int muxclient_request_id = 0; 110b15c8340SDag-Erling Smørgrav 111d4af9e69SDag-Erling Smørgrav /* Multiplexing control command */ 112d4af9e69SDag-Erling Smørgrav u_int muxclient_command = 0; 113d4af9e69SDag-Erling Smørgrav 114d4af9e69SDag-Erling Smørgrav /* Set when signalled. */ 115d4af9e69SDag-Erling Smørgrav static volatile sig_atomic_t muxclient_terminate = 0; 116d4af9e69SDag-Erling Smørgrav 117d4af9e69SDag-Erling Smørgrav /* PID of multiplex server */ 118d4af9e69SDag-Erling Smørgrav static u_int muxserver_pid = 0; 119d4af9e69SDag-Erling Smørgrav 120b15c8340SDag-Erling Smørgrav static Channel *mux_listener_channel = NULL; 121d4af9e69SDag-Erling Smørgrav 122b15c8340SDag-Erling Smørgrav struct mux_master_state { 123b15c8340SDag-Erling Smørgrav int hello_rcvd; 124b15c8340SDag-Erling Smørgrav }; 125b15c8340SDag-Erling Smørgrav 126b15c8340SDag-Erling Smørgrav /* mux protocol messages */ 127b15c8340SDag-Erling Smørgrav #define MUX_MSG_HELLO 0x00000001 128b15c8340SDag-Erling Smørgrav #define MUX_C_NEW_SESSION 0x10000002 129b15c8340SDag-Erling Smørgrav #define MUX_C_ALIVE_CHECK 0x10000004 130b15c8340SDag-Erling Smørgrav #define MUX_C_TERMINATE 0x10000005 131b15c8340SDag-Erling Smørgrav #define MUX_C_OPEN_FWD 0x10000006 132b15c8340SDag-Erling Smørgrav #define MUX_C_CLOSE_FWD 0x10000007 133b15c8340SDag-Erling Smørgrav #define MUX_C_NEW_STDIO_FWD 0x10000008 134e146993eSDag-Erling Smørgrav #define MUX_C_STOP_LISTENING 0x10000009 135ca86bcf2SDag-Erling Smørgrav #define MUX_C_PROXY 0x1000000f 136b15c8340SDag-Erling Smørgrav #define MUX_S_OK 0x80000001 137b15c8340SDag-Erling Smørgrav #define MUX_S_PERMISSION_DENIED 0x80000002 138b15c8340SDag-Erling Smørgrav #define MUX_S_FAILURE 0x80000003 139b15c8340SDag-Erling Smørgrav #define MUX_S_EXIT_MESSAGE 0x80000004 140b15c8340SDag-Erling Smørgrav #define MUX_S_ALIVE 0x80000005 141b15c8340SDag-Erling Smørgrav #define MUX_S_SESSION_OPENED 0x80000006 142e2f6069cSDag-Erling Smørgrav #define MUX_S_REMOTE_PORT 0x80000007 143e146993eSDag-Erling Smørgrav #define MUX_S_TTY_ALLOC_FAIL 0x80000008 144ca86bcf2SDag-Erling Smørgrav #define MUX_S_PROXY 0x8000000f 145b15c8340SDag-Erling Smørgrav 146b15c8340SDag-Erling Smørgrav /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ 147b15c8340SDag-Erling Smørgrav #define MUX_FWD_LOCAL 1 148b15c8340SDag-Erling Smørgrav #define MUX_FWD_REMOTE 2 149b15c8340SDag-Erling Smørgrav #define MUX_FWD_DYNAMIC 3 150b15c8340SDag-Erling Smørgrav 1514f52dfbbSDag-Erling Smørgrav static void mux_session_confirm(struct ssh *, int, int, void *); 1524f52dfbbSDag-Erling Smørgrav static void mux_stdio_confirm(struct ssh *, int, int, void *); 153b15c8340SDag-Erling Smørgrav 1542f513db7SEd Maste static int mux_master_process_hello(struct ssh *, u_int, 1554f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1562f513db7SEd Maste static int mux_master_process_new_session(struct ssh *, u_int, 1574f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1582f513db7SEd Maste static int mux_master_process_alive_check(struct ssh *, u_int, 1594f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1602f513db7SEd Maste static int mux_master_process_terminate(struct ssh *, u_int, 1614f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1622f513db7SEd Maste static int mux_master_process_open_fwd(struct ssh *, u_int, 1634f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1642f513db7SEd Maste static int mux_master_process_close_fwd(struct ssh *, u_int, 1654f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1662f513db7SEd Maste static int mux_master_process_stdio_fwd(struct ssh *, u_int, 1674f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1682f513db7SEd Maste static int mux_master_process_stop_listening(struct ssh *, u_int, 1694f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 1702f513db7SEd Maste static int mux_master_process_proxy(struct ssh *, u_int, 1714f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 172b15c8340SDag-Erling Smørgrav 173b15c8340SDag-Erling Smørgrav static const struct { 174b15c8340SDag-Erling Smørgrav u_int type; 1754f52dfbbSDag-Erling Smørgrav int (*handler)(struct ssh *, u_int, Channel *, 1764f52dfbbSDag-Erling Smørgrav struct sshbuf *, struct sshbuf *); 177b15c8340SDag-Erling Smørgrav } mux_master_handlers[] = { 1782f513db7SEd Maste { MUX_MSG_HELLO, mux_master_process_hello }, 1792f513db7SEd Maste { MUX_C_NEW_SESSION, mux_master_process_new_session }, 1802f513db7SEd Maste { MUX_C_ALIVE_CHECK, mux_master_process_alive_check }, 1812f513db7SEd Maste { MUX_C_TERMINATE, mux_master_process_terminate }, 1822f513db7SEd Maste { MUX_C_OPEN_FWD, mux_master_process_open_fwd }, 1832f513db7SEd Maste { MUX_C_CLOSE_FWD, mux_master_process_close_fwd }, 1842f513db7SEd Maste { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, 1852f513db7SEd Maste { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, 1862f513db7SEd Maste { MUX_C_PROXY, mux_master_process_proxy }, 187b15c8340SDag-Erling Smørgrav { 0, NULL } 188b15c8340SDag-Erling Smørgrav }; 189b15c8340SDag-Erling Smørgrav 19019261079SEd Maste /* Cleanup callback fired on closure of mux client _session_ channel */ 191e4a9863fSDag-Erling Smørgrav static void 192f374ba41SEd Maste mux_master_session_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) 193b15c8340SDag-Erling Smørgrav { 1944f52dfbbSDag-Erling Smørgrav Channel *cc, *c = channel_by_id(ssh, cid); 195b15c8340SDag-Erling Smørgrav 19619261079SEd Maste debug3_f("entering for channel %d", cid); 197b15c8340SDag-Erling Smørgrav if (c == NULL) 19819261079SEd Maste fatal_f("channel_by_id(%i) == NULL", cid); 199b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 2004f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 20119261079SEd Maste fatal_f("channel %d missing control channel %d", 20219261079SEd Maste c->self, c->ctl_chan); 203b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 2044f52dfbbSDag-Erling Smørgrav cc->remote_id = 0; 2054f52dfbbSDag-Erling Smørgrav cc->have_remote_id = 0; 2064f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, cc); 207b15c8340SDag-Erling Smørgrav } 2084f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, c->self); 209b15c8340SDag-Erling Smørgrav } 210b15c8340SDag-Erling Smørgrav 21119261079SEd Maste /* Cleanup callback fired on closure of mux client _control_ channel */ 212b15c8340SDag-Erling Smørgrav static void 213f374ba41SEd Maste mux_master_control_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) 214b15c8340SDag-Erling Smørgrav { 2154f52dfbbSDag-Erling Smørgrav Channel *sc, *c = channel_by_id(ssh, cid); 216b15c8340SDag-Erling Smørgrav 21719261079SEd Maste debug3_f("entering for channel %d", cid); 218b15c8340SDag-Erling Smørgrav if (c == NULL) 21919261079SEd Maste fatal_f("channel_by_id(%i) == NULL", cid); 2204f52dfbbSDag-Erling Smørgrav if (c->have_remote_id) { 2214f52dfbbSDag-Erling Smørgrav if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) 22219261079SEd Maste fatal_f("channel %d missing session channel %u", 22319261079SEd Maste c->self, c->remote_id); 2244f52dfbbSDag-Erling Smørgrav c->remote_id = 0; 2254f52dfbbSDag-Erling Smørgrav c->have_remote_id = 0; 226b15c8340SDag-Erling Smørgrav sc->ctl_chan = -1; 227e4a9863fSDag-Erling Smørgrav if (sc->type != SSH_CHANNEL_OPEN && 228e4a9863fSDag-Erling Smørgrav sc->type != SSH_CHANNEL_OPENING) { 22919261079SEd Maste debug2_f("channel %d: not open", sc->self); 2304f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, sc); 231b15c8340SDag-Erling Smørgrav } else { 232b15c8340SDag-Erling Smørgrav if (sc->istate == CHAN_INPUT_OPEN) 2334f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, sc); 234b15c8340SDag-Erling Smørgrav if (sc->ostate == CHAN_OUTPUT_OPEN) 2354f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, sc); 236b15c8340SDag-Erling Smørgrav } 237b15c8340SDag-Erling Smørgrav } 2384f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, c->self); 239b15c8340SDag-Erling Smørgrav } 240b15c8340SDag-Erling Smørgrav 241b15c8340SDag-Erling Smørgrav /* Check mux client environment variables before passing them to mux master. */ 242b15c8340SDag-Erling Smørgrav static int 24338a52bd3SEd Maste env_permitted(const char *env) 244b15c8340SDag-Erling Smørgrav { 24538a52bd3SEd Maste u_int i; 24638a52bd3SEd Maste int ret; 247b15c8340SDag-Erling Smørgrav char name[1024], *cp; 248b15c8340SDag-Erling Smørgrav 249b15c8340SDag-Erling Smørgrav if ((cp = strchr(env, '=')) == NULL || cp == env) 250b15c8340SDag-Erling Smørgrav return 0; 251b15c8340SDag-Erling Smørgrav ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); 252b15c8340SDag-Erling Smørgrav if (ret <= 0 || (size_t)ret >= sizeof(name)) { 25319261079SEd Maste error_f("name '%.100s...' too long", env); 254b15c8340SDag-Erling Smørgrav return 0; 255b15c8340SDag-Erling Smørgrav } 256b15c8340SDag-Erling Smørgrav 257b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_send_env; i++) 258b15c8340SDag-Erling Smørgrav if (match_pattern(name, options.send_env[i])) 259b15c8340SDag-Erling Smørgrav return 1; 260b15c8340SDag-Erling Smørgrav 261b15c8340SDag-Erling Smørgrav return 0; 262b15c8340SDag-Erling Smørgrav } 263b15c8340SDag-Erling Smørgrav 264b15c8340SDag-Erling Smørgrav /* Mux master protocol message handlers */ 265b15c8340SDag-Erling Smørgrav 266b15c8340SDag-Erling Smørgrav static int 2672f513db7SEd Maste mux_master_process_hello(struct ssh *ssh, u_int rid, 268190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 269b15c8340SDag-Erling Smørgrav { 270b15c8340SDag-Erling Smørgrav u_int ver; 271b15c8340SDag-Erling Smørgrav struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 272190cef3dSDag-Erling Smørgrav int r; 273b15c8340SDag-Erling Smørgrav 274b15c8340SDag-Erling Smørgrav if (state == NULL) 27519261079SEd Maste fatal_f("channel %d: c->mux_ctx == NULL", c->self); 276b15c8340SDag-Erling Smørgrav if (state->hello_rcvd) { 27719261079SEd Maste error_f("HELLO received twice"); 278b15c8340SDag-Erling Smørgrav return -1; 279b15c8340SDag-Erling Smørgrav } 280190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ver)) != 0) { 28119261079SEd Maste error_fr(r, "parse"); 282b15c8340SDag-Erling Smørgrav return -1; 283b15c8340SDag-Erling Smørgrav } 284b15c8340SDag-Erling Smørgrav if (ver != SSHMUX_VER) { 28519261079SEd Maste error_f("unsupported multiplexing protocol version %u " 28619261079SEd Maste "(expected %u)", ver, SSHMUX_VER); 287b15c8340SDag-Erling Smørgrav return -1; 288b15c8340SDag-Erling Smørgrav } 28919261079SEd Maste debug2_f("channel %d client version %u", c->self, ver); 290b15c8340SDag-Erling Smørgrav 291b15c8340SDag-Erling Smørgrav /* No extensions are presently defined */ 292190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 293190cef3dSDag-Erling Smørgrav char *name = NULL; 2942f513db7SEd Maste size_t value_len = 0; 295b15c8340SDag-Erling Smørgrav 296190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || 2972f513db7SEd Maste (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) { 29819261079SEd Maste error_fr(r, "parse extension"); 299190cef3dSDag-Erling Smørgrav return -1; 300b15c8340SDag-Erling Smørgrav } 30119261079SEd Maste debug2_f("Unrecognised extension \"%s\" length %zu", 30219261079SEd Maste name, value_len); 303e4a9863fSDag-Erling Smørgrav free(name); 304b15c8340SDag-Erling Smørgrav } 305b15c8340SDag-Erling Smørgrav state->hello_rcvd = 1; 306b15c8340SDag-Erling Smørgrav return 0; 307b15c8340SDag-Erling Smørgrav } 308b15c8340SDag-Erling Smørgrav 309190cef3dSDag-Erling Smørgrav /* Enqueue a "ok" response to the reply buffer */ 310190cef3dSDag-Erling Smørgrav static void 311190cef3dSDag-Erling Smørgrav reply_ok(struct sshbuf *reply, u_int rid) 312190cef3dSDag-Erling Smørgrav { 313190cef3dSDag-Erling Smørgrav int r; 314190cef3dSDag-Erling Smørgrav 315190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 || 316190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0) 31719261079SEd Maste fatal_fr(r, "reply"); 318190cef3dSDag-Erling Smørgrav } 319190cef3dSDag-Erling Smørgrav 320190cef3dSDag-Erling Smørgrav /* Enqueue an error response to the reply buffer */ 321190cef3dSDag-Erling Smørgrav static void 322190cef3dSDag-Erling Smørgrav reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg) 323190cef3dSDag-Erling Smørgrav { 324190cef3dSDag-Erling Smørgrav int r; 325190cef3dSDag-Erling Smørgrav 326190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, type)) != 0 || 327190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 328190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(reply, msg)) != 0) 32919261079SEd Maste fatal_fr(r, "reply"); 330190cef3dSDag-Erling Smørgrav } 331190cef3dSDag-Erling Smørgrav 332b15c8340SDag-Erling Smørgrav static int 3332f513db7SEd Maste mux_master_process_new_session(struct ssh *ssh, u_int rid, 334190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 335b15c8340SDag-Erling Smørgrav { 336b15c8340SDag-Erling Smørgrav Channel *nc; 337b15c8340SDag-Erling Smørgrav struct mux_session_confirm_ctx *cctx; 338190cef3dSDag-Erling Smørgrav char *cmd, *cp; 339190cef3dSDag-Erling Smørgrav u_int i, j, env_len, escape_char, window, packetmax; 340190cef3dSDag-Erling Smørgrav int r, new_fd[3]; 341b15c8340SDag-Erling Smørgrav 342b15c8340SDag-Erling Smørgrav /* Reply for SSHMUX_COMMAND_OPEN */ 343b15c8340SDag-Erling Smørgrav cctx = xcalloc(1, sizeof(*cctx)); 344b15c8340SDag-Erling Smørgrav cctx->term = NULL; 345e2f6069cSDag-Erling Smørgrav cctx->rid = rid; 346190cef3dSDag-Erling Smørgrav cmd = NULL; 347462c32cbSDag-Erling Smørgrav cctx->env = NULL; 348462c32cbSDag-Erling Smørgrav env_len = 0; 349190cef3dSDag-Erling Smørgrav if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ 350190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 || 351190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 || 352190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 || 353190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 || 354190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &escape_char)) != 0 || 355190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 || 356190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) { 357b15c8340SDag-Erling Smørgrav malf: 358e4a9863fSDag-Erling Smørgrav free(cmd); 359462c32cbSDag-Erling Smørgrav for (j = 0; j < env_len; j++) 360e4a9863fSDag-Erling Smørgrav free(cctx->env[j]); 361e4a9863fSDag-Erling Smørgrav free(cctx->env); 362e4a9863fSDag-Erling Smørgrav free(cctx->term); 363e4a9863fSDag-Erling Smørgrav free(cctx); 36419261079SEd Maste error_f("malformed message"); 365b15c8340SDag-Erling Smørgrav return -1; 366b15c8340SDag-Erling Smørgrav } 367b15c8340SDag-Erling Smørgrav 368b15c8340SDag-Erling Smørgrav #define MUX_MAX_ENV_VARS 4096 369190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 370190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0) 371b15c8340SDag-Erling Smørgrav goto malf; 372b15c8340SDag-Erling Smørgrav if (!env_permitted(cp)) { 373e4a9863fSDag-Erling Smørgrav free(cp); 374b15c8340SDag-Erling Smørgrav continue; 375b15c8340SDag-Erling Smørgrav } 376557f75e5SDag-Erling Smørgrav cctx->env = xreallocarray(cctx->env, env_len + 2, 377b15c8340SDag-Erling Smørgrav sizeof(*cctx->env)); 378b15c8340SDag-Erling Smørgrav cctx->env[env_len++] = cp; 379b15c8340SDag-Erling Smørgrav cctx->env[env_len] = NULL; 380b15c8340SDag-Erling Smørgrav if (env_len > MUX_MAX_ENV_VARS) { 38119261079SEd Maste error_f(">%d environment variables received, " 38219261079SEd Maste "ignoring additional", MUX_MAX_ENV_VARS); 383b15c8340SDag-Erling Smørgrav break; 384b15c8340SDag-Erling Smørgrav } 385b15c8340SDag-Erling Smørgrav } 386b15c8340SDag-Erling Smørgrav 38719261079SEd Maste debug2_f("channel %d: request tty %d, X %d, agent %d, subsys %d, " 38819261079SEd Maste "term \"%s\", cmd \"%s\", env %u", c->self, 389b15c8340SDag-Erling Smørgrav cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, 390b15c8340SDag-Erling Smørgrav cctx->want_subsys, cctx->term, cmd, env_len); 391b15c8340SDag-Erling Smørgrav 392190cef3dSDag-Erling Smørgrav if ((cctx->cmd = sshbuf_new()) == NULL) 39319261079SEd Maste fatal_f("sshbuf_new"); 394190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0) 39519261079SEd Maste fatal_fr(r, "sshbuf_put"); 396e4a9863fSDag-Erling Smørgrav free(cmd); 397b15c8340SDag-Erling Smørgrav cmd = NULL; 398b15c8340SDag-Erling Smørgrav 399b15c8340SDag-Erling Smørgrav /* Gather fds from client */ 400b15c8340SDag-Erling Smørgrav for(i = 0; i < 3; i++) { 401b15c8340SDag-Erling Smørgrav if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 40219261079SEd Maste error_f("failed to receive fd %d from client", i); 403b15c8340SDag-Erling Smørgrav for (j = 0; j < i; j++) 404b15c8340SDag-Erling Smørgrav close(new_fd[j]); 405b15c8340SDag-Erling Smørgrav for (j = 0; j < env_len; j++) 406e4a9863fSDag-Erling Smørgrav free(cctx->env[j]); 407e4a9863fSDag-Erling Smørgrav free(cctx->env); 408e4a9863fSDag-Erling Smørgrav free(cctx->term); 409190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 410e4a9863fSDag-Erling Smørgrav free(cctx); 411190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 412b15c8340SDag-Erling Smørgrav "did not receive file descriptors"); 413b15c8340SDag-Erling Smørgrav return -1; 414b15c8340SDag-Erling Smørgrav } 415b15c8340SDag-Erling Smørgrav } 416b15c8340SDag-Erling Smørgrav 41719261079SEd Maste debug3_f("got fds stdin %d, stdout %d, stderr %d", 418b15c8340SDag-Erling Smørgrav new_fd[0], new_fd[1], new_fd[2]); 419b15c8340SDag-Erling Smørgrav 420b15c8340SDag-Erling Smørgrav /* XXX support multiple child sessions in future */ 4214f52dfbbSDag-Erling Smørgrav if (c->have_remote_id) { 42219261079SEd Maste debug2_f("session already open"); 423190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 424190cef3dSDag-Erling Smørgrav "Multiple sessions not supported"); 425b15c8340SDag-Erling Smørgrav cleanup: 426b15c8340SDag-Erling Smørgrav close(new_fd[0]); 427b15c8340SDag-Erling Smørgrav close(new_fd[1]); 428b15c8340SDag-Erling Smørgrav close(new_fd[2]); 429e4a9863fSDag-Erling Smørgrav free(cctx->term); 430b15c8340SDag-Erling Smørgrav if (env_len != 0) { 431b15c8340SDag-Erling Smørgrav for (i = 0; i < env_len; i++) 432e4a9863fSDag-Erling Smørgrav free(cctx->env[i]); 433e4a9863fSDag-Erling Smørgrav free(cctx->env); 434b15c8340SDag-Erling Smørgrav } 435190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 436e4a9863fSDag-Erling Smørgrav free(cctx); 437b15c8340SDag-Erling Smørgrav return 0; 438b15c8340SDag-Erling Smørgrav } 439b15c8340SDag-Erling Smørgrav 440b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 441b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 442b15c8340SDag-Erling Smørgrav if (!ask_permission("Allow shared connection to %s? ", host)) { 44319261079SEd Maste debug2_f("session refused by user"); 444190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 445190cef3dSDag-Erling Smørgrav "Permission denied"); 446b15c8340SDag-Erling Smørgrav goto cleanup; 447b15c8340SDag-Erling Smørgrav } 448b15c8340SDag-Erling Smørgrav } 449b15c8340SDag-Erling Smørgrav 450b15c8340SDag-Erling Smørgrav /* Try to pick up ttymodes from client before it goes raw */ 451b15c8340SDag-Erling Smørgrav if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) 45219261079SEd Maste error_f("tcgetattr: %s", strerror(errno)); 453b15c8340SDag-Erling Smørgrav 454b15c8340SDag-Erling Smørgrav window = CHAN_SES_WINDOW_DEFAULT; 455b15c8340SDag-Erling Smørgrav packetmax = CHAN_SES_PACKET_DEFAULT; 456b15c8340SDag-Erling Smørgrav if (cctx->want_tty) { 457b15c8340SDag-Erling Smørgrav window >>= 1; 458b15c8340SDag-Erling Smørgrav packetmax >>= 1; 459b15c8340SDag-Erling Smørgrav } 460b15c8340SDag-Erling Smørgrav 4614f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, 462b15c8340SDag-Erling Smørgrav new_fd[0], new_fd[1], new_fd[2], window, packetmax, 46319261079SEd Maste CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); 464b15c8340SDag-Erling Smørgrav 465b15c8340SDag-Erling Smørgrav nc->ctl_chan = c->self; /* link session -> control channel */ 466b15c8340SDag-Erling Smørgrav c->remote_id = nc->self; /* link control -> session channel */ 4674f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 468b15c8340SDag-Erling Smørgrav 469b15c8340SDag-Erling Smørgrav if (cctx->want_tty && escape_char != 0xffffffff) { 4704f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, nc->self, 471b15c8340SDag-Erling Smørgrav client_simple_escape_filter, NULL, 472b15c8340SDag-Erling Smørgrav client_filter_cleanup, 473b15c8340SDag-Erling Smørgrav client_new_escape_filter_ctx((int)escape_char)); 474b15c8340SDag-Erling Smørgrav } 475b15c8340SDag-Erling Smørgrav 47619261079SEd Maste debug2_f("channel_new: %d linked to control channel %d", 47719261079SEd Maste nc->self, nc->ctl_chan); 478b15c8340SDag-Erling Smørgrav 4794f52dfbbSDag-Erling Smørgrav channel_send_open(ssh, nc->self); 4804f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); 481e2f6069cSDag-Erling Smørgrav c->mux_pause = 1; /* stop handling messages until open_confirm done */ 4824f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, nc->self, 4834f52dfbbSDag-Erling Smørgrav mux_master_session_cleanup_cb, 1); 484b15c8340SDag-Erling Smørgrav 485e2f6069cSDag-Erling Smørgrav /* reply is deferred, sent by mux_session_confirm */ 486b15c8340SDag-Erling Smørgrav return 0; 487b15c8340SDag-Erling Smørgrav } 488b15c8340SDag-Erling Smørgrav 489b15c8340SDag-Erling Smørgrav static int 4902f513db7SEd Maste mux_master_process_alive_check(struct ssh *ssh, u_int rid, 491190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 492b15c8340SDag-Erling Smørgrav { 493190cef3dSDag-Erling Smørgrav int r; 494190cef3dSDag-Erling Smørgrav 49519261079SEd Maste debug2_f("channel %d: alive check", c->self); 496b15c8340SDag-Erling Smørgrav 497b15c8340SDag-Erling Smørgrav /* prepare reply */ 498190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || 499190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 500190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) 50119261079SEd Maste fatal_fr(r, "reply"); 502b15c8340SDag-Erling Smørgrav 503b15c8340SDag-Erling Smørgrav return 0; 504b15c8340SDag-Erling Smørgrav } 505b15c8340SDag-Erling Smørgrav 506b15c8340SDag-Erling Smørgrav static int 5072f513db7SEd Maste mux_master_process_terminate(struct ssh *ssh, u_int rid, 508190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 509b15c8340SDag-Erling Smørgrav { 51019261079SEd Maste debug2_f("channel %d: terminate request", c->self); 511b15c8340SDag-Erling Smørgrav 512b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 513b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 514b15c8340SDag-Erling Smørgrav if (!ask_permission("Terminate shared connection to %s? ", 515b15c8340SDag-Erling Smørgrav host)) { 51619261079SEd Maste debug2_f("termination refused by user"); 517190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 518190cef3dSDag-Erling Smørgrav "Permission denied"); 519b15c8340SDag-Erling Smørgrav return 0; 520b15c8340SDag-Erling Smørgrav } 521b15c8340SDag-Erling Smørgrav } 522b15c8340SDag-Erling Smørgrav 523b15c8340SDag-Erling Smørgrav quit_pending = 1; 524190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 525b15c8340SDag-Erling Smørgrav /* XXX exit happens too soon - message never makes it to client */ 526b15c8340SDag-Erling Smørgrav return 0; 527b15c8340SDag-Erling Smørgrav } 528b15c8340SDag-Erling Smørgrav 529b15c8340SDag-Erling Smørgrav static char * 530a0ee8cc6SDag-Erling Smørgrav format_forward(u_int ftype, struct Forward *fwd) 531b15c8340SDag-Erling Smørgrav { 532b15c8340SDag-Erling Smørgrav char *ret; 533b15c8340SDag-Erling Smørgrav 534b15c8340SDag-Erling Smørgrav switch (ftype) { 535b15c8340SDag-Erling Smørgrav case MUX_FWD_LOCAL: 536b15c8340SDag-Erling Smørgrav xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", 537a0ee8cc6SDag-Erling Smørgrav (fwd->listen_path != NULL) ? fwd->listen_path : 538b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 539a0ee8cc6SDag-Erling Smørgrav (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 540b15c8340SDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, 541a0ee8cc6SDag-Erling Smørgrav (fwd->connect_path != NULL) ? fwd->connect_path : 542b15c8340SDag-Erling Smørgrav fwd->connect_host, fwd->connect_port); 543b15c8340SDag-Erling Smørgrav break; 544b15c8340SDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 545b15c8340SDag-Erling Smørgrav xasprintf(&ret, "dynamic forward %.200s:%d -> *", 546b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 547a0ee8cc6SDag-Erling Smørgrav (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 548b15c8340SDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 549b15c8340SDag-Erling Smørgrav break; 550b15c8340SDag-Erling Smørgrav case MUX_FWD_REMOTE: 551b15c8340SDag-Erling Smørgrav xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", 552a0ee8cc6SDag-Erling Smørgrav (fwd->listen_path != NULL) ? fwd->listen_path : 553b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 554b15c8340SDag-Erling Smørgrav "LOCALHOST" : fwd->listen_host, 555b15c8340SDag-Erling Smørgrav fwd->listen_port, 556a0ee8cc6SDag-Erling Smørgrav (fwd->connect_path != NULL) ? fwd->connect_path : 557b15c8340SDag-Erling Smørgrav fwd->connect_host, fwd->connect_port); 558b15c8340SDag-Erling Smørgrav break; 559b15c8340SDag-Erling Smørgrav default: 56019261079SEd Maste fatal_f("unknown forward type %u", ftype); 561b15c8340SDag-Erling Smørgrav } 562b15c8340SDag-Erling Smørgrav return ret; 563b15c8340SDag-Erling Smørgrav } 564b15c8340SDag-Erling Smørgrav 565b15c8340SDag-Erling Smørgrav static int 566b15c8340SDag-Erling Smørgrav compare_host(const char *a, const char *b) 567b15c8340SDag-Erling Smørgrav { 568b15c8340SDag-Erling Smørgrav if (a == NULL && b == NULL) 569b15c8340SDag-Erling Smørgrav return 1; 570b15c8340SDag-Erling Smørgrav if (a == NULL || b == NULL) 571b15c8340SDag-Erling Smørgrav return 0; 572b15c8340SDag-Erling Smørgrav return strcmp(a, b) == 0; 573b15c8340SDag-Erling Smørgrav } 574b15c8340SDag-Erling Smørgrav 575b15c8340SDag-Erling Smørgrav static int 576a0ee8cc6SDag-Erling Smørgrav compare_forward(struct Forward *a, struct Forward *b) 577b15c8340SDag-Erling Smørgrav { 578b15c8340SDag-Erling Smørgrav if (!compare_host(a->listen_host, b->listen_host)) 579b15c8340SDag-Erling Smørgrav return 0; 580a0ee8cc6SDag-Erling Smørgrav if (!compare_host(a->listen_path, b->listen_path)) 581a0ee8cc6SDag-Erling Smørgrav return 0; 582b15c8340SDag-Erling Smørgrav if (a->listen_port != b->listen_port) 583b15c8340SDag-Erling Smørgrav return 0; 584b15c8340SDag-Erling Smørgrav if (!compare_host(a->connect_host, b->connect_host)) 585b15c8340SDag-Erling Smørgrav return 0; 586a0ee8cc6SDag-Erling Smørgrav if (!compare_host(a->connect_path, b->connect_path)) 587a0ee8cc6SDag-Erling Smørgrav return 0; 588b15c8340SDag-Erling Smørgrav if (a->connect_port != b->connect_port) 589b15c8340SDag-Erling Smørgrav return 0; 590b15c8340SDag-Erling Smørgrav 591b15c8340SDag-Erling Smørgrav return 1; 592b15c8340SDag-Erling Smørgrav } 593b15c8340SDag-Erling Smørgrav 594e2f6069cSDag-Erling Smørgrav static void 5954f52dfbbSDag-Erling Smørgrav mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) 596e2f6069cSDag-Erling Smørgrav { 597e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx *fctx = ctxt; 598e2f6069cSDag-Erling Smørgrav char *failmsg = NULL; 599a0ee8cc6SDag-Erling Smørgrav struct Forward *rfwd; 600e2f6069cSDag-Erling Smørgrav Channel *c; 601190cef3dSDag-Erling Smørgrav struct sshbuf *out; 60219261079SEd Maste u_int port; 603190cef3dSDag-Erling Smørgrav int r; 604e2f6069cSDag-Erling Smørgrav 6054f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { 606e2f6069cSDag-Erling Smørgrav /* no channel for reply */ 60719261079SEd Maste error_f("unknown channel"); 608e2f6069cSDag-Erling Smørgrav return; 609e2f6069cSDag-Erling Smørgrav } 610190cef3dSDag-Erling Smørgrav if ((out = sshbuf_new()) == NULL) 61119261079SEd Maste fatal_f("sshbuf_new"); 612557f75e5SDag-Erling Smørgrav if (fctx->fid >= options.num_remote_forwards || 613557f75e5SDag-Erling Smørgrav (options.remote_forwards[fctx->fid].connect_path == NULL && 614557f75e5SDag-Erling Smørgrav options.remote_forwards[fctx->fid].connect_host == NULL)) { 615e2f6069cSDag-Erling Smørgrav xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); 616e2f6069cSDag-Erling Smørgrav goto fail; 617e2f6069cSDag-Erling Smørgrav } 618e2f6069cSDag-Erling Smørgrav rfwd = &options.remote_forwards[fctx->fid]; 61919261079SEd Maste debug_f("%s for: listen %d, connect %s:%d", 620e2f6069cSDag-Erling Smørgrav type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", 621a0ee8cc6SDag-Erling Smørgrav rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : 622a0ee8cc6SDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 623e2f6069cSDag-Erling Smørgrav if (type == SSH2_MSG_REQUEST_SUCCESS) { 624e2f6069cSDag-Erling Smørgrav if (rfwd->listen_port == 0) { 62519261079SEd Maste if ((r = sshpkt_get_u32(ssh, &port)) != 0) 62619261079SEd Maste fatal_fr(r, "parse port"); 62719261079SEd Maste if (port > 65535) { 62819261079SEd Maste fatal("Invalid allocated port %u for " 62919261079SEd Maste "mux remote forward to %s:%d", port, 63019261079SEd Maste rfwd->connect_host, rfwd->connect_port); 63119261079SEd Maste } 63219261079SEd Maste rfwd->allocated_port = (int)port; 633557f75e5SDag-Erling Smørgrav debug("Allocated port %u for mux remote forward" 634e2f6069cSDag-Erling Smørgrav " to %s:%d", rfwd->allocated_port, 635e2f6069cSDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 636190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(out, 637190cef3dSDag-Erling Smørgrav MUX_S_REMOTE_PORT)) != 0 || 638190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, fctx->rid)) != 0 || 639190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, 640190cef3dSDag-Erling Smørgrav rfwd->allocated_port)) != 0) 64119261079SEd Maste fatal_fr(r, "reply"); 642190cef3dSDag-Erling Smørgrav channel_update_permission(ssh, rfwd->handle, 643462c32cbSDag-Erling Smørgrav rfwd->allocated_port); 644e2f6069cSDag-Erling Smørgrav } else { 645190cef3dSDag-Erling Smørgrav reply_ok(out, fctx->rid); 646e2f6069cSDag-Erling Smørgrav } 647e2f6069cSDag-Erling Smørgrav goto out; 648e2f6069cSDag-Erling Smørgrav } else { 649462c32cbSDag-Erling Smørgrav if (rfwd->listen_port == 0) 650190cef3dSDag-Erling Smørgrav channel_update_permission(ssh, rfwd->handle, -1); 651a0ee8cc6SDag-Erling Smørgrav if (rfwd->listen_path != NULL) 652a0ee8cc6SDag-Erling Smørgrav xasprintf(&failmsg, "remote port forwarding failed for " 653a0ee8cc6SDag-Erling Smørgrav "listen path %s", rfwd->listen_path); 654a0ee8cc6SDag-Erling Smørgrav else 655e2f6069cSDag-Erling Smørgrav xasprintf(&failmsg, "remote port forwarding failed for " 656e2f6069cSDag-Erling Smørgrav "listen port %d", rfwd->listen_port); 657557f75e5SDag-Erling Smørgrav 65819261079SEd Maste debug2_f("clearing registered forwarding for listen %d, " 65919261079SEd Maste "connect %s:%d", rfwd->listen_port, 660557f75e5SDag-Erling Smørgrav rfwd->connect_path ? rfwd->connect_path : 661557f75e5SDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 662557f75e5SDag-Erling Smørgrav 663557f75e5SDag-Erling Smørgrav free(rfwd->listen_host); 664557f75e5SDag-Erling Smørgrav free(rfwd->listen_path); 665557f75e5SDag-Erling Smørgrav free(rfwd->connect_host); 666557f75e5SDag-Erling Smørgrav free(rfwd->connect_path); 667557f75e5SDag-Erling Smørgrav memset(rfwd, 0, sizeof(*rfwd)); 668e2f6069cSDag-Erling Smørgrav } 669e2f6069cSDag-Erling Smørgrav fail: 67019261079SEd Maste error_f("%s", failmsg); 671190cef3dSDag-Erling Smørgrav reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg); 672e4a9863fSDag-Erling Smørgrav free(failmsg); 673e2f6069cSDag-Erling Smørgrav out: 674190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(c->output, out)) != 0) 67519261079SEd Maste fatal_fr(r, "enqueue"); 676190cef3dSDag-Erling Smørgrav sshbuf_free(out); 677e2f6069cSDag-Erling Smørgrav if (c->mux_pause <= 0) 67819261079SEd Maste fatal_f("mux_pause %d", c->mux_pause); 679e2f6069cSDag-Erling Smørgrav c->mux_pause = 0; /* start processing messages again */ 680e2f6069cSDag-Erling Smørgrav } 681e2f6069cSDag-Erling Smørgrav 682b15c8340SDag-Erling Smørgrav static int 6832f513db7SEd Maste mux_master_process_open_fwd(struct ssh *ssh, u_int rid, 684190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 685b15c8340SDag-Erling Smørgrav { 686a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 687b15c8340SDag-Erling Smørgrav char *fwd_desc = NULL; 688a0ee8cc6SDag-Erling Smørgrav char *listen_addr, *connect_addr; 689b15c8340SDag-Erling Smørgrav u_int ftype; 690e4a9863fSDag-Erling Smørgrav u_int lport, cport; 691190cef3dSDag-Erling Smørgrav int r, i, ret = 0, freefwd = 1; 692b15c8340SDag-Erling Smørgrav 693fc1ba28aSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 694fc1ba28aSDag-Erling Smørgrav 695a0ee8cc6SDag-Erling Smørgrav /* XXX - lport/cport check redundant */ 696190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ftype)) != 0 || 697190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || 698190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &lport)) != 0 || 699190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || 700190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cport)) != 0 || 701a0ee8cc6SDag-Erling Smørgrav (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 702a0ee8cc6SDag-Erling Smørgrav (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 70319261079SEd Maste error_f("malformed message"); 704b15c8340SDag-Erling Smørgrav ret = -1; 705b15c8340SDag-Erling Smørgrav goto out; 706b15c8340SDag-Erling Smørgrav } 707a0ee8cc6SDag-Erling Smørgrav if (*listen_addr == '\0') { 708a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 709a0ee8cc6SDag-Erling Smørgrav listen_addr = NULL; 710a0ee8cc6SDag-Erling Smørgrav } 711a0ee8cc6SDag-Erling Smørgrav if (*connect_addr == '\0') { 712a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 713a0ee8cc6SDag-Erling Smørgrav connect_addr = NULL; 714a0ee8cc6SDag-Erling Smørgrav } 715a0ee8cc6SDag-Erling Smørgrav 716a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 717e4a9863fSDag-Erling Smørgrav fwd.listen_port = lport; 718a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port == PORT_STREAMLOCAL) 719a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = listen_addr; 720a0ee8cc6SDag-Erling Smørgrav else 721a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = listen_addr; 722e4a9863fSDag-Erling Smørgrav fwd.connect_port = cport; 723a0ee8cc6SDag-Erling Smørgrav if (fwd.connect_port == PORT_STREAMLOCAL) 724a0ee8cc6SDag-Erling Smørgrav fwd.connect_path = connect_addr; 725a0ee8cc6SDag-Erling Smørgrav else 726a0ee8cc6SDag-Erling Smørgrav fwd.connect_host = connect_addr; 727b15c8340SDag-Erling Smørgrav 72819261079SEd Maste debug2_f("channel %d: request %s", c->self, 729b15c8340SDag-Erling Smørgrav (fwd_desc = format_forward(ftype, &fwd))); 730b15c8340SDag-Erling Smørgrav 731b15c8340SDag-Erling Smørgrav if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && 732b15c8340SDag-Erling Smørgrav ftype != MUX_FWD_DYNAMIC) { 73319261079SEd Maste logit_f("invalid forwarding type %u", ftype); 734b15c8340SDag-Erling Smørgrav invalid: 735a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 736a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 737190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 738190cef3dSDag-Erling Smørgrav "Invalid forwarding request"); 739b15c8340SDag-Erling Smørgrav return 0; 740b15c8340SDag-Erling Smørgrav } 741a0ee8cc6SDag-Erling Smørgrav if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { 74219261079SEd Maste logit_f("streamlocal and dynamic forwards " 74319261079SEd Maste "are mutually exclusive"); 744a0ee8cc6SDag-Erling Smørgrav goto invalid; 745a0ee8cc6SDag-Erling Smørgrav } 746a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { 74719261079SEd Maste logit_f("invalid listen port %u", fwd.listen_port); 748b15c8340SDag-Erling Smørgrav goto invalid; 749b15c8340SDag-Erling Smørgrav } 7504f52dfbbSDag-Erling Smørgrav if ((fwd.connect_port != PORT_STREAMLOCAL && 7514f52dfbbSDag-Erling Smørgrav fwd.connect_port >= 65536) || 7524f52dfbbSDag-Erling Smørgrav (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && 7534f52dfbbSDag-Erling Smørgrav fwd.connect_port == 0)) { 75419261079SEd Maste logit_f("invalid connect port %u", 755b15c8340SDag-Erling Smørgrav fwd.connect_port); 756b15c8340SDag-Erling Smørgrav goto invalid; 757b15c8340SDag-Erling Smørgrav } 7584f52dfbbSDag-Erling Smørgrav if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && 7594f52dfbbSDag-Erling Smørgrav fwd.connect_path == NULL) { 76019261079SEd Maste logit_f("missing connect host"); 761b15c8340SDag-Erling Smørgrav goto invalid; 762b15c8340SDag-Erling Smørgrav } 763b15c8340SDag-Erling Smørgrav 764b15c8340SDag-Erling Smørgrav /* Skip forwards that have already been requested */ 765b15c8340SDag-Erling Smørgrav switch (ftype) { 766b15c8340SDag-Erling Smørgrav case MUX_FWD_LOCAL: 767b15c8340SDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 768b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 769b15c8340SDag-Erling Smørgrav if (compare_forward(&fwd, 770b15c8340SDag-Erling Smørgrav options.local_forwards + i)) { 771b15c8340SDag-Erling Smørgrav exists: 77219261079SEd Maste debug2_f("found existing forwarding"); 773190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 774b15c8340SDag-Erling Smørgrav goto out; 775b15c8340SDag-Erling Smørgrav } 776b15c8340SDag-Erling Smørgrav } 777b15c8340SDag-Erling Smørgrav break; 778b15c8340SDag-Erling Smørgrav case MUX_FWD_REMOTE: 779b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 780190cef3dSDag-Erling Smørgrav if (!compare_forward(&fwd, options.remote_forwards + i)) 781190cef3dSDag-Erling Smørgrav continue; 782e2f6069cSDag-Erling Smørgrav if (fwd.listen_port != 0) 783b15c8340SDag-Erling Smørgrav goto exists; 78419261079SEd Maste debug2_f("found allocated port"); 785190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, 786190cef3dSDag-Erling Smørgrav MUX_S_REMOTE_PORT)) != 0 || 787190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 788190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, 789190cef3dSDag-Erling Smørgrav options.remote_forwards[i].allocated_port)) != 0) 79019261079SEd Maste fatal_fr(r, "reply FWD_REMOTE"); 791e2f6069cSDag-Erling Smørgrav goto out; 792e2f6069cSDag-Erling Smørgrav } 793b15c8340SDag-Erling Smørgrav break; 794b15c8340SDag-Erling Smørgrav } 795b15c8340SDag-Erling Smørgrav 796b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 797b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 798b15c8340SDag-Erling Smørgrav if (!ask_permission("Open %s on %s?", fwd_desc, host)) { 79919261079SEd Maste debug2_f("forwarding refused by user"); 800190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 801190cef3dSDag-Erling Smørgrav "Permission denied"); 802b15c8340SDag-Erling Smørgrav goto out; 803b15c8340SDag-Erling Smørgrav } 804b15c8340SDag-Erling Smørgrav } 805b15c8340SDag-Erling Smørgrav 806b15c8340SDag-Erling Smørgrav if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { 8074f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 808a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 809b15c8340SDag-Erling Smørgrav fail: 81019261079SEd Maste logit_f("requested %s failed", fwd_desc); 811190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 812190cef3dSDag-Erling Smørgrav "Port forwarding failed"); 813b15c8340SDag-Erling Smørgrav goto out; 814b15c8340SDag-Erling Smørgrav } 815b15c8340SDag-Erling Smørgrav add_local_forward(&options, &fwd); 816b15c8340SDag-Erling Smørgrav freefwd = 0; 817b15c8340SDag-Erling Smørgrav } else { 818e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx *fctx; 819e2f6069cSDag-Erling Smørgrav 8204f52dfbbSDag-Erling Smørgrav fwd.handle = channel_request_remote_forwarding(ssh, &fwd); 821462c32cbSDag-Erling Smørgrav if (fwd.handle < 0) 822b15c8340SDag-Erling Smørgrav goto fail; 823b15c8340SDag-Erling Smørgrav add_remote_forward(&options, &fwd); 824e2f6069cSDag-Erling Smørgrav fctx = xcalloc(1, sizeof(*fctx)); 825e2f6069cSDag-Erling Smørgrav fctx->cid = c->self; 826e2f6069cSDag-Erling Smørgrav fctx->rid = rid; 827e2f6069cSDag-Erling Smørgrav fctx->fid = options.num_remote_forwards - 1; 828e2f6069cSDag-Erling Smørgrav client_register_global_confirm(mux_confirm_remote_forward, 829e2f6069cSDag-Erling Smørgrav fctx); 830b15c8340SDag-Erling Smørgrav freefwd = 0; 831e2f6069cSDag-Erling Smørgrav c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ 832e2f6069cSDag-Erling Smørgrav /* delayed reply in mux_confirm_remote_forward */ 833e2f6069cSDag-Erling Smørgrav goto out; 834b15c8340SDag-Erling Smørgrav } 835190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 836b15c8340SDag-Erling Smørgrav out: 837e4a9863fSDag-Erling Smørgrav free(fwd_desc); 838b15c8340SDag-Erling Smørgrav if (freefwd) { 839e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 840a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 841e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 842a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 843b15c8340SDag-Erling Smørgrav } 844b15c8340SDag-Erling Smørgrav return ret; 845b15c8340SDag-Erling Smørgrav } 846b15c8340SDag-Erling Smørgrav 847b15c8340SDag-Erling Smørgrav static int 8482f513db7SEd Maste mux_master_process_close_fwd(struct ssh *ssh, u_int rid, 849190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 850b15c8340SDag-Erling Smørgrav { 851a0ee8cc6SDag-Erling Smørgrav struct Forward fwd, *found_fwd; 852b15c8340SDag-Erling Smørgrav char *fwd_desc = NULL; 853462c32cbSDag-Erling Smørgrav const char *error_reason = NULL; 854a0ee8cc6SDag-Erling Smørgrav char *listen_addr = NULL, *connect_addr = NULL; 855b15c8340SDag-Erling Smørgrav u_int ftype; 856190cef3dSDag-Erling Smørgrav int r, i, ret = 0; 857e4a9863fSDag-Erling Smørgrav u_int lport, cport; 858b15c8340SDag-Erling Smørgrav 859fc1ba28aSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 860fc1ba28aSDag-Erling Smørgrav 861190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ftype)) != 0 || 862190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || 863190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &lport)) != 0 || 864190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || 865190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cport)) != 0 || 866a0ee8cc6SDag-Erling Smørgrav (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 867a0ee8cc6SDag-Erling Smørgrav (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 86819261079SEd Maste error_f("malformed message"); 869b15c8340SDag-Erling Smørgrav ret = -1; 870b15c8340SDag-Erling Smørgrav goto out; 871b15c8340SDag-Erling Smørgrav } 872b15c8340SDag-Erling Smørgrav 873a0ee8cc6SDag-Erling Smørgrav if (*listen_addr == '\0') { 874a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 875a0ee8cc6SDag-Erling Smørgrav listen_addr = NULL; 876b15c8340SDag-Erling Smørgrav } 877a0ee8cc6SDag-Erling Smørgrav if (*connect_addr == '\0') { 878a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 879a0ee8cc6SDag-Erling Smørgrav connect_addr = NULL; 880b15c8340SDag-Erling Smørgrav } 881b15c8340SDag-Erling Smørgrav 882a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 883a0ee8cc6SDag-Erling Smørgrav fwd.listen_port = lport; 884a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port == PORT_STREAMLOCAL) 885a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = listen_addr; 886a0ee8cc6SDag-Erling Smørgrav else 887a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = listen_addr; 888a0ee8cc6SDag-Erling Smørgrav fwd.connect_port = cport; 889a0ee8cc6SDag-Erling Smørgrav if (fwd.connect_port == PORT_STREAMLOCAL) 890a0ee8cc6SDag-Erling Smørgrav fwd.connect_path = connect_addr; 891a0ee8cc6SDag-Erling Smørgrav else 892a0ee8cc6SDag-Erling Smørgrav fwd.connect_host = connect_addr; 893a0ee8cc6SDag-Erling Smørgrav 89419261079SEd Maste debug2_f("channel %d: request cancel %s", c->self, 895b15c8340SDag-Erling Smørgrav (fwd_desc = format_forward(ftype, &fwd))); 896b15c8340SDag-Erling Smørgrav 897462c32cbSDag-Erling Smørgrav /* make sure this has been requested */ 898462c32cbSDag-Erling Smørgrav found_fwd = NULL; 899462c32cbSDag-Erling Smørgrav switch (ftype) { 900462c32cbSDag-Erling Smørgrav case MUX_FWD_LOCAL: 901462c32cbSDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 902462c32cbSDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 903462c32cbSDag-Erling Smørgrav if (compare_forward(&fwd, 904462c32cbSDag-Erling Smørgrav options.local_forwards + i)) { 905462c32cbSDag-Erling Smørgrav found_fwd = options.local_forwards + i; 906462c32cbSDag-Erling Smørgrav break; 907462c32cbSDag-Erling Smørgrav } 908462c32cbSDag-Erling Smørgrav } 909462c32cbSDag-Erling Smørgrav break; 910462c32cbSDag-Erling Smørgrav case MUX_FWD_REMOTE: 911462c32cbSDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 912462c32cbSDag-Erling Smørgrav if (compare_forward(&fwd, 913462c32cbSDag-Erling Smørgrav options.remote_forwards + i)) { 914462c32cbSDag-Erling Smørgrav found_fwd = options.remote_forwards + i; 915462c32cbSDag-Erling Smørgrav break; 916462c32cbSDag-Erling Smørgrav } 917462c32cbSDag-Erling Smørgrav } 918462c32cbSDag-Erling Smørgrav break; 919462c32cbSDag-Erling Smørgrav } 920462c32cbSDag-Erling Smørgrav 921462c32cbSDag-Erling Smørgrav if (found_fwd == NULL) 922462c32cbSDag-Erling Smørgrav error_reason = "port not forwarded"; 923462c32cbSDag-Erling Smørgrav else if (ftype == MUX_FWD_REMOTE) { 924462c32cbSDag-Erling Smørgrav /* 925462c32cbSDag-Erling Smørgrav * This shouldn't fail unless we confused the host/port 926462c32cbSDag-Erling Smørgrav * between options.remote_forwards and permitted_opens. 927462c32cbSDag-Erling Smørgrav * However, for dynamic allocated listen ports we need 928a0ee8cc6SDag-Erling Smørgrav * to use the actual listen port. 929462c32cbSDag-Erling Smørgrav */ 9304f52dfbbSDag-Erling Smørgrav if (channel_request_rforward_cancel(ssh, found_fwd) == -1) 931462c32cbSDag-Erling Smørgrav error_reason = "port not in permitted opens"; 932462c32cbSDag-Erling Smørgrav } else { /* local and dynamic forwards */ 933462c32cbSDag-Erling Smørgrav /* Ditto */ 9344f52dfbbSDag-Erling Smørgrav if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, 935a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) == -1) 936462c32cbSDag-Erling Smørgrav error_reason = "port not found"; 937462c32cbSDag-Erling Smørgrav } 938462c32cbSDag-Erling Smørgrav 939190cef3dSDag-Erling Smørgrav if (error_reason != NULL) 940190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, error_reason); 941190cef3dSDag-Erling Smørgrav else { 942190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 943e4a9863fSDag-Erling Smørgrav free(found_fwd->listen_host); 944a0ee8cc6SDag-Erling Smørgrav free(found_fwd->listen_path); 945e4a9863fSDag-Erling Smørgrav free(found_fwd->connect_host); 946a0ee8cc6SDag-Erling Smørgrav free(found_fwd->connect_path); 947462c32cbSDag-Erling Smørgrav found_fwd->listen_host = found_fwd->connect_host = NULL; 948a0ee8cc6SDag-Erling Smørgrav found_fwd->listen_path = found_fwd->connect_path = NULL; 949462c32cbSDag-Erling Smørgrav found_fwd->listen_port = found_fwd->connect_port = 0; 950462c32cbSDag-Erling Smørgrav } 951b15c8340SDag-Erling Smørgrav out: 952e4a9863fSDag-Erling Smørgrav free(fwd_desc); 953a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 954a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 955b15c8340SDag-Erling Smørgrav 956b15c8340SDag-Erling Smørgrav return ret; 957b15c8340SDag-Erling Smørgrav } 958b15c8340SDag-Erling Smørgrav 959b15c8340SDag-Erling Smørgrav static int 9602f513db7SEd Maste mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid, 961190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 962b15c8340SDag-Erling Smørgrav { 963b15c8340SDag-Erling Smørgrav Channel *nc; 964190cef3dSDag-Erling Smørgrav char *chost = NULL; 965535af610SEd Maste u_int _cport, i, j; 966535af610SEd Maste int ok = 0, cport, r, new_fd[2]; 967a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx *cctx; 968b15c8340SDag-Erling Smørgrav 969190cef3dSDag-Erling Smørgrav if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ 970190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || 971535af610SEd Maste (r = sshbuf_get_u32(m, &_cport)) != 0) { 972e4a9863fSDag-Erling Smørgrav free(chost); 97319261079SEd Maste error_f("malformed message"); 974b15c8340SDag-Erling Smørgrav return -1; 975b15c8340SDag-Erling Smørgrav } 976535af610SEd Maste if (_cport == (u_int)PORT_STREAMLOCAL) 977535af610SEd Maste cport = PORT_STREAMLOCAL; 978535af610SEd Maste else if (_cport <= INT_MAX) 979535af610SEd Maste cport = (int)_cport; 980535af610SEd Maste else { 981535af610SEd Maste free(chost); 982535af610SEd Maste error_f("invalid port 0x%x", _cport); 983535af610SEd Maste return -1; 984535af610SEd Maste } 985b15c8340SDag-Erling Smørgrav 986535af610SEd Maste debug2_f("channel %d: stdio fwd to %s:%d", c->self, chost, cport); 987b15c8340SDag-Erling Smørgrav 988b15c8340SDag-Erling Smørgrav /* Gather fds from client */ 989b15c8340SDag-Erling Smørgrav for(i = 0; i < 2; i++) { 990b15c8340SDag-Erling Smørgrav if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 99119261079SEd Maste error_f("failed to receive fd %d from client", i); 992b15c8340SDag-Erling Smørgrav for (j = 0; j < i; j++) 993b15c8340SDag-Erling Smørgrav close(new_fd[j]); 994e4a9863fSDag-Erling Smørgrav free(chost); 995b15c8340SDag-Erling Smørgrav 996b15c8340SDag-Erling Smørgrav /* prepare reply */ 997190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 998b15c8340SDag-Erling Smørgrav "did not receive file descriptors"); 999b15c8340SDag-Erling Smørgrav return -1; 1000b15c8340SDag-Erling Smørgrav } 1001b15c8340SDag-Erling Smørgrav } 1002b15c8340SDag-Erling Smørgrav 100319261079SEd Maste debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]); 1004b15c8340SDag-Erling Smørgrav 1005b15c8340SDag-Erling Smørgrav /* XXX support multiple child sessions in future */ 10064f52dfbbSDag-Erling Smørgrav if (c->have_remote_id) { 100719261079SEd Maste debug2_f("session already open"); 1008190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 1009190cef3dSDag-Erling Smørgrav "Multiple sessions not supported"); 1010b15c8340SDag-Erling Smørgrav cleanup: 1011b15c8340SDag-Erling Smørgrav close(new_fd[0]); 1012b15c8340SDag-Erling Smørgrav close(new_fd[1]); 1013e4a9863fSDag-Erling Smørgrav free(chost); 1014b15c8340SDag-Erling Smørgrav return 0; 1015b15c8340SDag-Erling Smørgrav } 1016b15c8340SDag-Erling Smørgrav 1017b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 1018b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 1019535af610SEd Maste if (cport == PORT_STREAMLOCAL) { 1020535af610SEd Maste ok = ask_permission("Allow forward to path %s", chost); 1021535af610SEd Maste } else { 1022535af610SEd Maste ok = ask_permission("Allow forward to [%s]:%d? ", 1023535af610SEd Maste chost, cport); 1024535af610SEd Maste } 1025535af610SEd Maste if (!ok) { 102619261079SEd Maste debug2_f("stdio fwd refused by user"); 1027190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 1028190cef3dSDag-Erling Smørgrav "Permission denied"); 1029b15c8340SDag-Erling Smørgrav goto cleanup; 1030b15c8340SDag-Erling Smørgrav } 1031b15c8340SDag-Erling Smørgrav } 1032b15c8340SDag-Erling Smørgrav 103319261079SEd Maste nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1], 103419261079SEd Maste CHANNEL_NONBLOCK_STDIO); 1035190cef3dSDag-Erling Smørgrav free(chost); 1036b15c8340SDag-Erling Smørgrav 1037b15c8340SDag-Erling Smørgrav nc->ctl_chan = c->self; /* link session -> control channel */ 1038b15c8340SDag-Erling Smørgrav c->remote_id = nc->self; /* link control -> session channel */ 10394f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1040b15c8340SDag-Erling Smørgrav 104119261079SEd Maste debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan); 1042b15c8340SDag-Erling Smørgrav 10434f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, nc->self, 10444f52dfbbSDag-Erling Smørgrav mux_master_session_cleanup_cb, 1); 1045b15c8340SDag-Erling Smørgrav 1046a0ee8cc6SDag-Erling Smørgrav cctx = xcalloc(1, sizeof(*cctx)); 1047a0ee8cc6SDag-Erling Smørgrav cctx->rid = rid; 10484f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); 1049a0ee8cc6SDag-Erling Smørgrav c->mux_pause = 1; /* stop handling messages until open_confirm done */ 1050b15c8340SDag-Erling Smørgrav 1051a0ee8cc6SDag-Erling Smørgrav /* reply is deferred, sent by mux_session_confirm */ 1052b15c8340SDag-Erling Smørgrav return 0; 1053b15c8340SDag-Erling Smørgrav } 1054b15c8340SDag-Erling Smørgrav 1055a0ee8cc6SDag-Erling Smørgrav /* Callback on open confirmation in mux master for a mux stdio fwd session. */ 1056a0ee8cc6SDag-Erling Smørgrav static void 10574f52dfbbSDag-Erling Smørgrav mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) 1058a0ee8cc6SDag-Erling Smørgrav { 1059a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx *cctx = arg; 1060a0ee8cc6SDag-Erling Smørgrav Channel *c, *cc; 1061190cef3dSDag-Erling Smørgrav struct sshbuf *reply; 1062190cef3dSDag-Erling Smørgrav int r; 1063a0ee8cc6SDag-Erling Smørgrav 1064a0ee8cc6SDag-Erling Smørgrav if (cctx == NULL) 106519261079SEd Maste fatal_f("cctx == NULL"); 10664f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 106719261079SEd Maste fatal_f("no channel for id %d", id); 10684f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 106919261079SEd Maste fatal_f("channel %d lacks control channel %d", 1070a0ee8cc6SDag-Erling Smørgrav id, c->ctl_chan); 1071190cef3dSDag-Erling Smørgrav if ((reply = sshbuf_new()) == NULL) 107219261079SEd Maste fatal_f("sshbuf_new"); 1073a0ee8cc6SDag-Erling Smørgrav 1074a0ee8cc6SDag-Erling Smørgrav if (!success) { 107519261079SEd Maste debug3_f("sending failure reply"); 1076190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, cctx->rid, 1077190cef3dSDag-Erling Smørgrav "Session open refused by peer"); 1078a0ee8cc6SDag-Erling Smørgrav /* prepare reply */ 1079a0ee8cc6SDag-Erling Smørgrav goto done; 1080a0ee8cc6SDag-Erling Smørgrav } 1081a0ee8cc6SDag-Erling Smørgrav 108219261079SEd Maste debug3_f("sending success reply"); 1083a0ee8cc6SDag-Erling Smørgrav /* prepare reply */ 1084190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || 1085190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || 1086190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, c->self)) != 0) 108719261079SEd Maste fatal_fr(r, "reply"); 1088a0ee8cc6SDag-Erling Smørgrav 1089a0ee8cc6SDag-Erling Smørgrav done: 1090a0ee8cc6SDag-Erling Smørgrav /* Send reply */ 1091190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) 109219261079SEd Maste fatal_fr(r, "enqueue"); 1093190cef3dSDag-Erling Smørgrav sshbuf_free(reply); 1094a0ee8cc6SDag-Erling Smørgrav 1095a0ee8cc6SDag-Erling Smørgrav if (cc->mux_pause <= 0) 109619261079SEd Maste fatal_f("mux_pause %d", cc->mux_pause); 1097a0ee8cc6SDag-Erling Smørgrav cc->mux_pause = 0; /* start processing messages again */ 1098a0ee8cc6SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 1099a0ee8cc6SDag-Erling Smørgrav free(cctx); 1100a0ee8cc6SDag-Erling Smørgrav } 1101a0ee8cc6SDag-Erling Smørgrav 1102e146993eSDag-Erling Smørgrav static int 11032f513db7SEd Maste mux_master_process_stop_listening(struct ssh *ssh, u_int rid, 1104190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 1105e146993eSDag-Erling Smørgrav { 110619261079SEd Maste debug_f("channel %d: stop listening", c->self); 1107e146993eSDag-Erling Smørgrav 1108e146993eSDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 1109e146993eSDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 1110e146993eSDag-Erling Smørgrav if (!ask_permission("Disable further multiplexing on shared " 1111e146993eSDag-Erling Smørgrav "connection to %s? ", host)) { 111219261079SEd Maste debug2_f("stop listen refused by user"); 1113190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 1114190cef3dSDag-Erling Smørgrav "Permission denied"); 1115e146993eSDag-Erling Smørgrav return 0; 1116e146993eSDag-Erling Smørgrav } 1117e146993eSDag-Erling Smørgrav } 1118e146993eSDag-Erling Smørgrav 1119e146993eSDag-Erling Smørgrav if (mux_listener_channel != NULL) { 11204f52dfbbSDag-Erling Smørgrav channel_free(ssh, mux_listener_channel); 1121e146993eSDag-Erling Smørgrav client_stop_mux(); 1122e4a9863fSDag-Erling Smørgrav free(options.control_path); 1123e146993eSDag-Erling Smørgrav options.control_path = NULL; 1124e146993eSDag-Erling Smørgrav mux_listener_channel = NULL; 1125e146993eSDag-Erling Smørgrav muxserver_sock = -1; 1126e146993eSDag-Erling Smørgrav } 1127e146993eSDag-Erling Smørgrav 1128190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 1129e146993eSDag-Erling Smørgrav return 0; 1130e146993eSDag-Erling Smørgrav } 1131e146993eSDag-Erling Smørgrav 1132ca86bcf2SDag-Erling Smørgrav static int 11332f513db7SEd Maste mux_master_process_proxy(struct ssh *ssh, u_int rid, 1134190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 1135ca86bcf2SDag-Erling Smørgrav { 1136190cef3dSDag-Erling Smørgrav int r; 1137190cef3dSDag-Erling Smørgrav 113819261079SEd Maste debug_f("channel %d: proxy request", c->self); 1139ca86bcf2SDag-Erling Smørgrav 1140ca86bcf2SDag-Erling Smørgrav c->mux_rcb = channel_proxy_downstream; 1141190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 || 1142190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0) 114319261079SEd Maste fatal_fr(r, "reply"); 1144ca86bcf2SDag-Erling Smørgrav 1145ca86bcf2SDag-Erling Smørgrav return 0; 1146ca86bcf2SDag-Erling Smørgrav } 1147ca86bcf2SDag-Erling Smørgrav 114819261079SEd Maste /* Channel callbacks fired on read/write from mux client fd */ 1149b15c8340SDag-Erling Smørgrav static int 11504f52dfbbSDag-Erling Smørgrav mux_master_read_cb(struct ssh *ssh, Channel *c) 1151b15c8340SDag-Erling Smørgrav { 1152b15c8340SDag-Erling Smørgrav struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 1153190cef3dSDag-Erling Smørgrav struct sshbuf *in = NULL, *out = NULL; 1154190cef3dSDag-Erling Smørgrav u_int type, rid, i; 1155190cef3dSDag-Erling Smørgrav int r, ret = -1; 1156190cef3dSDag-Erling Smørgrav 1157190cef3dSDag-Erling Smørgrav if ((out = sshbuf_new()) == NULL) 115819261079SEd Maste fatal_f("sshbuf_new"); 1159b15c8340SDag-Erling Smørgrav 1160b15c8340SDag-Erling Smørgrav /* Setup ctx and */ 1161b15c8340SDag-Erling Smørgrav if (c->mux_ctx == NULL) { 1162e2f6069cSDag-Erling Smørgrav state = xcalloc(1, sizeof(*state)); 1163b15c8340SDag-Erling Smørgrav c->mux_ctx = state; 11644f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, c->self, 1165b15c8340SDag-Erling Smørgrav mux_master_control_cleanup_cb, 0); 1166b15c8340SDag-Erling Smørgrav 1167b15c8340SDag-Erling Smørgrav /* Send hello */ 1168190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || 1169190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) 117019261079SEd Maste fatal_fr(r, "reply"); 1171b15c8340SDag-Erling Smørgrav /* no extensions */ 1172190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(c->output, out)) != 0) 117319261079SEd Maste fatal_fr(r, "enqueue"); 117419261079SEd Maste debug3_f("channel %d: hello sent", c->self); 1175190cef3dSDag-Erling Smørgrav ret = 0; 1176190cef3dSDag-Erling Smørgrav goto out; 1177b15c8340SDag-Erling Smørgrav } 1178b15c8340SDag-Erling Smørgrav 1179b15c8340SDag-Erling Smørgrav /* Channel code ensures that we receive whole packets */ 1180190cef3dSDag-Erling Smørgrav if ((r = sshbuf_froms(c->input, &in)) != 0) { 1181b15c8340SDag-Erling Smørgrav malf: 118219261079SEd Maste error_f("malformed message"); 1183b15c8340SDag-Erling Smørgrav goto out; 1184b15c8340SDag-Erling Smørgrav } 1185b15c8340SDag-Erling Smørgrav 1186190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(in, &type)) != 0) 1187b15c8340SDag-Erling Smørgrav goto malf; 118819261079SEd Maste debug3_f("channel %d packet type 0x%08x len %zu", c->self, 118919261079SEd Maste type, sshbuf_len(in)); 1190b15c8340SDag-Erling Smørgrav 1191b15c8340SDag-Erling Smørgrav if (type == MUX_MSG_HELLO) 1192b15c8340SDag-Erling Smørgrav rid = 0; 1193b15c8340SDag-Erling Smørgrav else { 1194b15c8340SDag-Erling Smørgrav if (!state->hello_rcvd) { 119519261079SEd Maste error_f("expected MUX_MSG_HELLO(0x%08x), " 119619261079SEd Maste "received 0x%08x", MUX_MSG_HELLO, type); 1197b15c8340SDag-Erling Smørgrav goto out; 1198b15c8340SDag-Erling Smørgrav } 1199190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(in, &rid)) != 0) 1200b15c8340SDag-Erling Smørgrav goto malf; 1201b15c8340SDag-Erling Smørgrav } 1202b15c8340SDag-Erling Smørgrav 1203b15c8340SDag-Erling Smørgrav for (i = 0; mux_master_handlers[i].handler != NULL; i++) { 1204b15c8340SDag-Erling Smørgrav if (type == mux_master_handlers[i].type) { 12054f52dfbbSDag-Erling Smørgrav ret = mux_master_handlers[i].handler(ssh, rid, 1206190cef3dSDag-Erling Smørgrav c, in, out); 1207b15c8340SDag-Erling Smørgrav break; 1208b15c8340SDag-Erling Smørgrav } 1209b15c8340SDag-Erling Smørgrav } 1210b15c8340SDag-Erling Smørgrav if (mux_master_handlers[i].handler == NULL) { 121119261079SEd Maste error_f("unsupported mux message 0x%08x", type); 1212190cef3dSDag-Erling Smørgrav reply_error(out, MUX_S_FAILURE, rid, "unsupported request"); 1213b15c8340SDag-Erling Smørgrav ret = 0; 1214b15c8340SDag-Erling Smørgrav } 1215b15c8340SDag-Erling Smørgrav /* Enqueue reply packet */ 121619261079SEd Maste if (sshbuf_len(out) != 0 && 121719261079SEd Maste (r = sshbuf_put_stringb(c->output, out)) != 0) 121819261079SEd Maste fatal_fr(r, "enqueue"); 1219b15c8340SDag-Erling Smørgrav out: 1220190cef3dSDag-Erling Smørgrav sshbuf_free(in); 1221190cef3dSDag-Erling Smørgrav sshbuf_free(out); 1222b15c8340SDag-Erling Smørgrav return ret; 1223b15c8340SDag-Erling Smørgrav } 1224b15c8340SDag-Erling Smørgrav 1225b15c8340SDag-Erling Smørgrav void 12264f52dfbbSDag-Erling Smørgrav mux_exit_message(struct ssh *ssh, Channel *c, int exitval) 1227b15c8340SDag-Erling Smørgrav { 1228190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1229b15c8340SDag-Erling Smørgrav Channel *mux_chan; 1230190cef3dSDag-Erling Smørgrav int r; 1231b15c8340SDag-Erling Smørgrav 123219261079SEd Maste debug3_f("channel %d: exit message, exitval %d", c->self, exitval); 1233b15c8340SDag-Erling Smørgrav 12344f52dfbbSDag-Erling Smørgrav if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 123519261079SEd Maste fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); 1236b15c8340SDag-Erling Smørgrav 1237b15c8340SDag-Erling Smørgrav /* Append exit message packet to control socket output queue */ 1238190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 123919261079SEd Maste fatal_f("sshbuf_new"); 1240190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 || 1241190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, c->self)) != 0 || 1242190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, exitval)) != 0 || 1243190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) 124419261079SEd Maste fatal_fr(r, "reply"); 1245190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1246b15c8340SDag-Erling Smørgrav } 1247d4af9e69SDag-Erling Smørgrav 1248e146993eSDag-Erling Smørgrav void 12494f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(struct ssh *ssh, Channel *c) 1250e146993eSDag-Erling Smørgrav { 1251190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1252e146993eSDag-Erling Smørgrav Channel *mux_chan; 1253190cef3dSDag-Erling Smørgrav int r; 1254e146993eSDag-Erling Smørgrav 125519261079SEd Maste debug3_f("channel %d: TTY alloc failed", c->self); 1256e146993eSDag-Erling Smørgrav 12574f52dfbbSDag-Erling Smørgrav if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 125819261079SEd Maste fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); 1259e146993eSDag-Erling Smørgrav 1260e146993eSDag-Erling Smørgrav /* Append exit message packet to control socket output queue */ 1261190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 126219261079SEd Maste fatal_f("sshbuf_new"); 1263190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 || 1264190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, c->self)) != 0 || 1265190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) 126619261079SEd Maste fatal_fr(r, "reply"); 1267190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1268e146993eSDag-Erling Smørgrav } 1269e146993eSDag-Erling Smørgrav 1270d4af9e69SDag-Erling Smørgrav /* Prepare a mux master to listen on a Unix domain socket. */ 1271d4af9e69SDag-Erling Smørgrav void 12724f52dfbbSDag-Erling Smørgrav muxserver_listen(struct ssh *ssh) 1273d4af9e69SDag-Erling Smørgrav { 1274d4af9e69SDag-Erling Smørgrav mode_t old_umask; 12754a421b63SDag-Erling Smørgrav char *orig_control_path = options.control_path; 12764a421b63SDag-Erling Smørgrav char rbuf[16+1]; 12774a421b63SDag-Erling Smørgrav u_int i, r; 1278a0ee8cc6SDag-Erling Smørgrav int oerrno; 1279d4af9e69SDag-Erling Smørgrav 1280d4af9e69SDag-Erling Smørgrav if (options.control_path == NULL || 1281d4af9e69SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_NO) 1282d4af9e69SDag-Erling Smørgrav return; 1283d4af9e69SDag-Erling Smørgrav 1284d4af9e69SDag-Erling Smørgrav debug("setting up multiplex master socket"); 1285d4af9e69SDag-Erling Smørgrav 12864a421b63SDag-Erling Smørgrav /* 12874a421b63SDag-Erling Smørgrav * Use a temporary path before listen so we can pseudo-atomically 12884a421b63SDag-Erling Smørgrav * establish the listening socket in its final location to avoid 12894a421b63SDag-Erling Smørgrav * other processes racing in between bind() and listen() and hitting 12904a421b63SDag-Erling Smørgrav * an unready socket. 12914a421b63SDag-Erling Smørgrav */ 12924a421b63SDag-Erling Smørgrav for (i = 0; i < sizeof(rbuf) - 1; i++) { 12934a421b63SDag-Erling Smørgrav r = arc4random_uniform(26+26+10); 12944a421b63SDag-Erling Smørgrav rbuf[i] = (r < 26) ? 'a' + r : 12954a421b63SDag-Erling Smørgrav (r < 26*2) ? 'A' + r - 26 : 12964a421b63SDag-Erling Smørgrav '0' + r - 26 - 26; 12974a421b63SDag-Erling Smørgrav } 12984a421b63SDag-Erling Smørgrav rbuf[sizeof(rbuf) - 1] = '\0'; 12994a421b63SDag-Erling Smørgrav options.control_path = NULL; 13004a421b63SDag-Erling Smørgrav xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); 130119261079SEd Maste debug3_f("temporary control path %s", options.control_path); 13024a421b63SDag-Erling Smørgrav 1303d4af9e69SDag-Erling Smørgrav old_umask = umask(0177); 1304a0ee8cc6SDag-Erling Smørgrav muxserver_sock = unix_listener(options.control_path, 64, 0); 1305a0ee8cc6SDag-Erling Smørgrav oerrno = errno; 1306a0ee8cc6SDag-Erling Smørgrav umask(old_umask); 1307a0ee8cc6SDag-Erling Smørgrav if (muxserver_sock < 0) { 1308a0ee8cc6SDag-Erling Smørgrav if (oerrno == EINVAL || oerrno == EADDRINUSE) { 1309d4af9e69SDag-Erling Smørgrav error("ControlSocket %s already exists, " 1310d4af9e69SDag-Erling Smørgrav "disabling multiplexing", options.control_path); 13114a421b63SDag-Erling Smørgrav disable_mux_master: 1312e146993eSDag-Erling Smørgrav if (muxserver_sock != -1) { 1313d4af9e69SDag-Erling Smørgrav close(muxserver_sock); 1314d4af9e69SDag-Erling Smørgrav muxserver_sock = -1; 1315e146993eSDag-Erling Smørgrav } 1316e4a9863fSDag-Erling Smørgrav free(orig_control_path); 1317e4a9863fSDag-Erling Smørgrav free(options.control_path); 1318d4af9e69SDag-Erling Smørgrav options.control_path = NULL; 1319d4af9e69SDag-Erling Smørgrav options.control_master = SSHCTL_MASTER_NO; 1320d4af9e69SDag-Erling Smørgrav return; 1321a0ee8cc6SDag-Erling Smørgrav } else { 1322a0ee8cc6SDag-Erling Smørgrav /* unix_listener() logs the error */ 1323a0ee8cc6SDag-Erling Smørgrav cleanup_exit(255); 1324d4af9e69SDag-Erling Smørgrav } 1325a0ee8cc6SDag-Erling Smørgrav } 1326d4af9e69SDag-Erling Smørgrav 13274a421b63SDag-Erling Smørgrav /* Now atomically "move" the mux socket into position */ 13284a421b63SDag-Erling Smørgrav if (link(options.control_path, orig_control_path) != 0) { 13294a421b63SDag-Erling Smørgrav if (errno != EEXIST) { 133019261079SEd Maste fatal_f("link mux listener %s => %s: %s", 13314a421b63SDag-Erling Smørgrav options.control_path, orig_control_path, 13324a421b63SDag-Erling Smørgrav strerror(errno)); 13334a421b63SDag-Erling Smørgrav } 13344a421b63SDag-Erling Smørgrav error("ControlSocket %s already exists, disabling multiplexing", 13354a421b63SDag-Erling Smørgrav orig_control_path); 13364a421b63SDag-Erling Smørgrav unlink(options.control_path); 13374a421b63SDag-Erling Smørgrav goto disable_mux_master; 13384a421b63SDag-Erling Smørgrav } 13394a421b63SDag-Erling Smørgrav unlink(options.control_path); 1340e4a9863fSDag-Erling Smørgrav free(options.control_path); 13414a421b63SDag-Erling Smørgrav options.control_path = orig_control_path; 13424a421b63SDag-Erling Smørgrav 1343d4af9e69SDag-Erling Smørgrav set_nonblock(muxserver_sock); 1344b15c8340SDag-Erling Smørgrav 13454f52dfbbSDag-Erling Smørgrav mux_listener_channel = channel_new(ssh, "mux listener", 1346b15c8340SDag-Erling Smørgrav SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, 1347b15c8340SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 13484a421b63SDag-Erling Smørgrav 0, options.control_path, 1); 1349b15c8340SDag-Erling Smørgrav mux_listener_channel->mux_rcb = mux_master_read_cb; 135019261079SEd Maste debug3_f("mux listener channel %d fd %d", 1351b15c8340SDag-Erling Smørgrav mux_listener_channel->self, mux_listener_channel->sock); 1352d4af9e69SDag-Erling Smørgrav } 1353d4af9e69SDag-Erling Smørgrav 1354d4af9e69SDag-Erling Smørgrav /* Callback on open confirmation in mux master for a mux client session. */ 1355d4af9e69SDag-Erling Smørgrav static void 13564f52dfbbSDag-Erling Smørgrav mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) 1357d4af9e69SDag-Erling Smørgrav { 1358d4af9e69SDag-Erling Smørgrav struct mux_session_confirm_ctx *cctx = arg; 1359d4af9e69SDag-Erling Smørgrav const char *display; 1360e2f6069cSDag-Erling Smørgrav Channel *c, *cc; 1361190cef3dSDag-Erling Smørgrav int i, r; 1362190cef3dSDag-Erling Smørgrav struct sshbuf *reply; 1363d4af9e69SDag-Erling Smørgrav 1364d4af9e69SDag-Erling Smørgrav if (cctx == NULL) 136519261079SEd Maste fatal_f("cctx == NULL"); 13664f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 136719261079SEd Maste fatal_f("no channel for id %d", id); 13684f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 136919261079SEd Maste fatal_f("channel %d lacks control channel %d", 1370e2f6069cSDag-Erling Smørgrav id, c->ctl_chan); 1371190cef3dSDag-Erling Smørgrav if ((reply = sshbuf_new()) == NULL) 137219261079SEd Maste fatal_f("sshbuf_new"); 1373e2f6069cSDag-Erling Smørgrav 1374e2f6069cSDag-Erling Smørgrav if (!success) { 137519261079SEd Maste debug3_f("sending failure reply"); 1376190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, cctx->rid, 1377190cef3dSDag-Erling Smørgrav "Session open refused by peer"); 1378e2f6069cSDag-Erling Smørgrav goto done; 1379e2f6069cSDag-Erling Smørgrav } 1380d4af9e69SDag-Erling Smørgrav 1381d4af9e69SDag-Erling Smørgrav display = getenv("DISPLAY"); 1382d4af9e69SDag-Erling Smørgrav if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { 1383d4af9e69SDag-Erling Smørgrav char *proto, *data; 1384e2f6069cSDag-Erling Smørgrav 1385d4af9e69SDag-Erling Smørgrav /* Get reasonable local authentication information. */ 13864f52dfbbSDag-Erling Smørgrav if (client_x11_get_proto(ssh, display, options.xauth_location, 1387e2f6069cSDag-Erling Smørgrav options.forward_x11_trusted, options.forward_x11_timeout, 1388acc1a9efSDag-Erling Smørgrav &proto, &data) == 0) { 1389d4af9e69SDag-Erling Smørgrav /* Request forwarding with authentication spoofing. */ 1390e2f6069cSDag-Erling Smørgrav debug("Requesting X11 forwarding with authentication " 1391e2f6069cSDag-Erling Smørgrav "spoofing."); 13924f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(ssh, id, 13934f52dfbbSDag-Erling Smørgrav display, proto, data, 1); 1394e146993eSDag-Erling Smørgrav /* XXX exit_on_forward_failure */ 13954f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "X11 forwarding", 1396acc1a9efSDag-Erling Smørgrav CONFIRM_WARN); 1397acc1a9efSDag-Erling Smørgrav } 1398d4af9e69SDag-Erling Smørgrav } 1399d4af9e69SDag-Erling Smørgrav 1400d4af9e69SDag-Erling Smørgrav if (cctx->want_agent_fwd && options.forward_agent) { 1401d4af9e69SDag-Erling Smørgrav debug("Requesting authentication agent forwarding."); 14024f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); 140319261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 140419261079SEd Maste fatal_fr(r, "send"); 1405d4af9e69SDag-Erling Smørgrav } 1406d4af9e69SDag-Erling Smørgrav 14074f52dfbbSDag-Erling Smørgrav client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, 1408190cef3dSDag-Erling Smørgrav cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env); 1409d4af9e69SDag-Erling Smørgrav 141019261079SEd Maste debug3_f("sending success reply"); 1411e2f6069cSDag-Erling Smørgrav /* prepare reply */ 1412190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || 1413190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || 1414190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, c->self)) != 0) 141519261079SEd Maste fatal_fr(r, "reply"); 1416e2f6069cSDag-Erling Smørgrav 1417e2f6069cSDag-Erling Smørgrav done: 1418e2f6069cSDag-Erling Smørgrav /* Send reply */ 1419190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) 142019261079SEd Maste fatal_fr(r, "enqueue"); 1421190cef3dSDag-Erling Smørgrav sshbuf_free(reply); 1422e2f6069cSDag-Erling Smørgrav 1423e2f6069cSDag-Erling Smørgrav if (cc->mux_pause <= 0) 142419261079SEd Maste fatal_f("mux_pause %d", cc->mux_pause); 1425e2f6069cSDag-Erling Smørgrav cc->mux_pause = 0; /* start processing messages again */ 1426d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 1427190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 1428e4a9863fSDag-Erling Smørgrav free(cctx->term); 1429d4af9e69SDag-Erling Smørgrav if (cctx->env != NULL) { 1430d4af9e69SDag-Erling Smørgrav for (i = 0; cctx->env[i] != NULL; i++) 1431e4a9863fSDag-Erling Smørgrav free(cctx->env[i]); 1432e4a9863fSDag-Erling Smørgrav free(cctx->env); 1433d4af9e69SDag-Erling Smørgrav } 1434e4a9863fSDag-Erling Smørgrav free(cctx); 1435d4af9e69SDag-Erling Smørgrav } 1436d4af9e69SDag-Erling Smørgrav 1437d4af9e69SDag-Erling Smørgrav /* ** Multiplexing client support */ 1438d4af9e69SDag-Erling Smørgrav 1439d4af9e69SDag-Erling Smørgrav /* Exit signal handler */ 1440d4af9e69SDag-Erling Smørgrav static void 1441d4af9e69SDag-Erling Smørgrav control_client_sighandler(int signo) 1442d4af9e69SDag-Erling Smørgrav { 1443d4af9e69SDag-Erling Smørgrav muxclient_terminate = signo; 1444d4af9e69SDag-Erling Smørgrav } 1445d4af9e69SDag-Erling Smørgrav 1446d4af9e69SDag-Erling Smørgrav /* 1447d4af9e69SDag-Erling Smørgrav * Relay signal handler - used to pass some signals from mux client to 1448d4af9e69SDag-Erling Smørgrav * mux master. 1449d4af9e69SDag-Erling Smørgrav */ 1450d4af9e69SDag-Erling Smørgrav static void 1451d4af9e69SDag-Erling Smørgrav control_client_sigrelay(int signo) 1452d4af9e69SDag-Erling Smørgrav { 1453d4af9e69SDag-Erling Smørgrav int save_errno = errno; 1454d4af9e69SDag-Erling Smørgrav 1455d4af9e69SDag-Erling Smørgrav if (muxserver_pid > 1) 1456d4af9e69SDag-Erling Smørgrav kill(muxserver_pid, signo); 1457d4af9e69SDag-Erling Smørgrav 1458d4af9e69SDag-Erling Smørgrav errno = save_errno; 1459d4af9e69SDag-Erling Smørgrav } 1460d4af9e69SDag-Erling Smørgrav 1461d4af9e69SDag-Erling Smørgrav static int 1462535af610SEd Maste mux_client_read(int fd, struct sshbuf *b, size_t need, int timeout_ms) 1463d4af9e69SDag-Erling Smørgrav { 1464190cef3dSDag-Erling Smørgrav size_t have; 1465b15c8340SDag-Erling Smørgrav ssize_t len; 1466b15c8340SDag-Erling Smørgrav u_char *p; 1467190cef3dSDag-Erling Smørgrav int r; 1468d4af9e69SDag-Erling Smørgrav 1469190cef3dSDag-Erling Smørgrav if ((r = sshbuf_reserve(b, need, &p)) != 0) 147019261079SEd Maste fatal_fr(r, "reserve"); 1471b15c8340SDag-Erling Smørgrav for (have = 0; have < need; ) { 1472b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 1473b15c8340SDag-Erling Smørgrav errno = EINTR; 1474b15c8340SDag-Erling Smørgrav return -1; 1475b15c8340SDag-Erling Smørgrav } 1476b15c8340SDag-Erling Smørgrav len = read(fd, p + have, need - have); 147719261079SEd Maste if (len == -1) { 1478b15c8340SDag-Erling Smørgrav switch (errno) { 1479b15c8340SDag-Erling Smørgrav #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1480b15c8340SDag-Erling Smørgrav case EWOULDBLOCK: 1481b15c8340SDag-Erling Smørgrav #endif 1482b15c8340SDag-Erling Smørgrav case EAGAIN: 1483*1b91d634SEd Maste if (waitrfd(fd, &timeout_ms, 1484*1b91d634SEd Maste &muxclient_terminate) == -1 && 1485*1b91d634SEd Maste errno != EINTR) 1486535af610SEd Maste return -1; /* timeout */ 1487b15c8340SDag-Erling Smørgrav /* FALLTHROUGH */ 1488b15c8340SDag-Erling Smørgrav case EINTR: 1489b15c8340SDag-Erling Smørgrav continue; 1490b15c8340SDag-Erling Smørgrav default: 1491b15c8340SDag-Erling Smørgrav return -1; 1492b15c8340SDag-Erling Smørgrav } 1493b15c8340SDag-Erling Smørgrav } 1494b15c8340SDag-Erling Smørgrav if (len == 0) { 1495b15c8340SDag-Erling Smørgrav errno = EPIPE; 1496b15c8340SDag-Erling Smørgrav return -1; 1497b15c8340SDag-Erling Smørgrav } 1498190cef3dSDag-Erling Smørgrav have += (size_t)len; 1499b15c8340SDag-Erling Smørgrav } 1500b15c8340SDag-Erling Smørgrav return 0; 1501b15c8340SDag-Erling Smørgrav } 1502d4af9e69SDag-Erling Smørgrav 1503b15c8340SDag-Erling Smørgrav static int 1504190cef3dSDag-Erling Smørgrav mux_client_write_packet(int fd, struct sshbuf *m) 1505b15c8340SDag-Erling Smørgrav { 1506190cef3dSDag-Erling Smørgrav struct sshbuf *queue; 1507b15c8340SDag-Erling Smørgrav u_int have, need; 1508190cef3dSDag-Erling Smørgrav int r, oerrno, len; 1509190cef3dSDag-Erling Smørgrav const u_char *ptr; 1510b15c8340SDag-Erling Smørgrav struct pollfd pfd; 1511d4af9e69SDag-Erling Smørgrav 1512b15c8340SDag-Erling Smørgrav pfd.fd = fd; 1513b15c8340SDag-Erling Smørgrav pfd.events = POLLOUT; 1514190cef3dSDag-Erling Smørgrav if ((queue = sshbuf_new()) == NULL) 151519261079SEd Maste fatal_f("sshbuf_new"); 1516190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(queue, m)) != 0) 151719261079SEd Maste fatal_fr(r, "enqueue"); 1518b15c8340SDag-Erling Smørgrav 1519190cef3dSDag-Erling Smørgrav need = sshbuf_len(queue); 1520190cef3dSDag-Erling Smørgrav ptr = sshbuf_ptr(queue); 1521b15c8340SDag-Erling Smørgrav 1522b15c8340SDag-Erling Smørgrav for (have = 0; have < need; ) { 1523b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 1524190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1525b15c8340SDag-Erling Smørgrav errno = EINTR; 1526b15c8340SDag-Erling Smørgrav return -1; 1527b15c8340SDag-Erling Smørgrav } 1528b15c8340SDag-Erling Smørgrav len = write(fd, ptr + have, need - have); 152919261079SEd Maste if (len == -1) { 1530b15c8340SDag-Erling Smørgrav switch (errno) { 1531b15c8340SDag-Erling Smørgrav #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1532b15c8340SDag-Erling Smørgrav case EWOULDBLOCK: 1533b15c8340SDag-Erling Smørgrav #endif 1534b15c8340SDag-Erling Smørgrav case EAGAIN: 1535b15c8340SDag-Erling Smørgrav (void)poll(&pfd, 1, -1); 1536b15c8340SDag-Erling Smørgrav /* FALLTHROUGH */ 1537b15c8340SDag-Erling Smørgrav case EINTR: 1538b15c8340SDag-Erling Smørgrav continue; 1539b15c8340SDag-Erling Smørgrav default: 1540b15c8340SDag-Erling Smørgrav oerrno = errno; 1541190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1542b15c8340SDag-Erling Smørgrav errno = oerrno; 1543b15c8340SDag-Erling Smørgrav return -1; 1544b15c8340SDag-Erling Smørgrav } 1545b15c8340SDag-Erling Smørgrav } 1546b15c8340SDag-Erling Smørgrav if (len == 0) { 1547190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1548b15c8340SDag-Erling Smørgrav errno = EPIPE; 1549b15c8340SDag-Erling Smørgrav return -1; 1550b15c8340SDag-Erling Smørgrav } 1551b15c8340SDag-Erling Smørgrav have += (u_int)len; 1552b15c8340SDag-Erling Smørgrav } 1553190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1554b15c8340SDag-Erling Smørgrav return 0; 1555b15c8340SDag-Erling Smørgrav } 1556b15c8340SDag-Erling Smørgrav 1557b15c8340SDag-Erling Smørgrav static int 1558535af610SEd Maste mux_client_read_packet_timeout(int fd, struct sshbuf *m, int timeout_ms) 1559b15c8340SDag-Erling Smørgrav { 1560190cef3dSDag-Erling Smørgrav struct sshbuf *queue; 1561190cef3dSDag-Erling Smørgrav size_t need, have; 1562a0ee8cc6SDag-Erling Smørgrav const u_char *ptr; 1563190cef3dSDag-Erling Smørgrav int r, oerrno; 1564b15c8340SDag-Erling Smørgrav 1565190cef3dSDag-Erling Smørgrav if ((queue = sshbuf_new()) == NULL) 156619261079SEd Maste fatal_f("sshbuf_new"); 1567535af610SEd Maste if (mux_client_read(fd, queue, 4, timeout_ms) != 0) { 1568b15c8340SDag-Erling Smørgrav if ((oerrno = errno) == EPIPE) 156919261079SEd Maste debug3_f("read header failed: %s", 1570e4a9863fSDag-Erling Smørgrav strerror(errno)); 1571190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1572b15c8340SDag-Erling Smørgrav errno = oerrno; 1573b15c8340SDag-Erling Smørgrav return -1; 1574b15c8340SDag-Erling Smørgrav } 1575190cef3dSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(queue)); 1576535af610SEd Maste if (mux_client_read(fd, queue, need, timeout_ms) != 0) { 1577b15c8340SDag-Erling Smørgrav oerrno = errno; 157819261079SEd Maste debug3_f("read body failed: %s", strerror(errno)); 1579190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1580b15c8340SDag-Erling Smørgrav errno = oerrno; 1581b15c8340SDag-Erling Smørgrav return -1; 1582b15c8340SDag-Erling Smørgrav } 1583190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 || 1584190cef3dSDag-Erling Smørgrav (r = sshbuf_put(m, ptr, have)) != 0) 158519261079SEd Maste fatal_fr(r, "dequeue"); 1586190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1587b15c8340SDag-Erling Smørgrav return 0; 1588b15c8340SDag-Erling Smørgrav } 1589b15c8340SDag-Erling Smørgrav 1590b15c8340SDag-Erling Smørgrav static int 1591535af610SEd Maste mux_client_read_packet(int fd, struct sshbuf *m) 1592535af610SEd Maste { 1593535af610SEd Maste return mux_client_read_packet_timeout(fd, m, -1); 1594535af610SEd Maste } 1595535af610SEd Maste 1596535af610SEd Maste static int 1597535af610SEd Maste mux_client_hello_exchange(int fd, int timeout_ms) 1598b15c8340SDag-Erling Smørgrav { 1599190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1600b15c8340SDag-Erling Smørgrav u_int type, ver; 1601190cef3dSDag-Erling Smørgrav int r, ret = -1; 1602b15c8340SDag-Erling Smørgrav 1603190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 160419261079SEd Maste fatal_f("sshbuf_new"); 1605190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 || 1606190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0) 160719261079SEd Maste fatal_fr(r, "assemble hello"); 1608b15c8340SDag-Erling Smørgrav /* no extensions */ 1609b15c8340SDag-Erling Smørgrav 1610190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) { 161119261079SEd Maste debug_f("write packet: %s", strerror(errno)); 16124f52dfbbSDag-Erling Smørgrav goto out; 16134f52dfbbSDag-Erling Smørgrav } 1614b15c8340SDag-Erling Smørgrav 1615190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1616b15c8340SDag-Erling Smørgrav 1617b15c8340SDag-Erling Smørgrav /* Read their HELLO */ 1618535af610SEd Maste if (mux_client_read_packet_timeout(fd, m, timeout_ms) != 0) { 161919261079SEd Maste debug_f("read packet failed"); 16204f52dfbbSDag-Erling Smørgrav goto out; 1621b15c8340SDag-Erling Smørgrav } 1622b15c8340SDag-Erling Smørgrav 1623190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0) 162419261079SEd Maste fatal_fr(r, "parse type"); 16254f52dfbbSDag-Erling Smørgrav if (type != MUX_MSG_HELLO) { 162619261079SEd Maste error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type); 16274f52dfbbSDag-Erling Smørgrav goto out; 16284f52dfbbSDag-Erling Smørgrav } 1629190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ver)) != 0) 163019261079SEd Maste fatal_fr(r, "parse version"); 16314f52dfbbSDag-Erling Smørgrav if (ver != SSHMUX_VER) { 16324f52dfbbSDag-Erling Smørgrav error("Unsupported multiplexing protocol version %d " 1633b15c8340SDag-Erling Smørgrav "(expected %d)", ver, SSHMUX_VER); 16344f52dfbbSDag-Erling Smørgrav goto out; 16354f52dfbbSDag-Erling Smørgrav } 163619261079SEd Maste debug2_f("master version %u", ver); 1637b15c8340SDag-Erling Smørgrav /* No extensions are presently defined */ 1638190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 1639190cef3dSDag-Erling Smørgrav char *name = NULL; 1640b15c8340SDag-Erling Smørgrav 1641190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || 1642190cef3dSDag-Erling Smørgrav (r = sshbuf_skip_string(m)) != 0) { /* value */ 164319261079SEd Maste error_fr(r, "parse extension"); 1644190cef3dSDag-Erling Smørgrav goto out; 1645190cef3dSDag-Erling Smørgrav } 1646b15c8340SDag-Erling Smørgrav debug2("Unrecognised master extension \"%s\"", name); 1647e4a9863fSDag-Erling Smørgrav free(name); 1648b15c8340SDag-Erling Smørgrav } 16494f52dfbbSDag-Erling Smørgrav /* success */ 16504f52dfbbSDag-Erling Smørgrav ret = 0; 16514f52dfbbSDag-Erling Smørgrav out: 1652190cef3dSDag-Erling Smørgrav sshbuf_free(m); 16534f52dfbbSDag-Erling Smørgrav return ret; 1654b15c8340SDag-Erling Smørgrav } 1655b15c8340SDag-Erling Smørgrav 1656b15c8340SDag-Erling Smørgrav static u_int 1657b15c8340SDag-Erling Smørgrav mux_client_request_alive(int fd) 1658b15c8340SDag-Erling Smørgrav { 1659190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1660b15c8340SDag-Erling Smørgrav char *e; 1661b15c8340SDag-Erling Smørgrav u_int pid, type, rid; 1662190cef3dSDag-Erling Smørgrav int r; 1663b15c8340SDag-Erling Smørgrav 166419261079SEd Maste debug3_f("entering"); 1665b15c8340SDag-Erling Smørgrav 1666190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 166719261079SEd Maste fatal_f("sshbuf_new"); 1668190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 || 1669190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 167019261079SEd Maste fatal_fr(r, "assemble"); 1671b15c8340SDag-Erling Smørgrav 1672190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 167319261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 1674b15c8340SDag-Erling Smørgrav 1675190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1676b15c8340SDag-Erling Smørgrav 1677b15c8340SDag-Erling Smørgrav /* Read their reply */ 1678190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1679190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1680b15c8340SDag-Erling Smørgrav return 0; 1681b15c8340SDag-Erling Smørgrav } 1682b15c8340SDag-Erling Smørgrav 1683190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0) 168419261079SEd Maste fatal_fr(r, "parse type"); 1685b15c8340SDag-Erling Smørgrav if (type != MUX_S_ALIVE) { 1686190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 168719261079SEd Maste fatal_fr(r, "parse error message"); 168819261079SEd Maste fatal_f("master returned error: %s", e); 1689b15c8340SDag-Erling Smørgrav } 1690b15c8340SDag-Erling Smørgrav 1691190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &rid)) != 0) 169219261079SEd Maste fatal_fr(r, "parse remote ID"); 1693190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 169419261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 169519261079SEd Maste muxclient_request_id, rid); 1696190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &pid)) != 0) 169719261079SEd Maste fatal_fr(r, "parse PID"); 1698190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1699b15c8340SDag-Erling Smørgrav 170019261079SEd Maste debug3_f("done pid = %u", pid); 1701b15c8340SDag-Erling Smørgrav 1702b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1703b15c8340SDag-Erling Smørgrav 1704b15c8340SDag-Erling Smørgrav return pid; 1705b15c8340SDag-Erling Smørgrav } 1706b15c8340SDag-Erling Smørgrav 1707b15c8340SDag-Erling Smørgrav static void 1708b15c8340SDag-Erling Smørgrav mux_client_request_terminate(int fd) 1709b15c8340SDag-Erling Smørgrav { 1710190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1711b15c8340SDag-Erling Smørgrav char *e; 1712b15c8340SDag-Erling Smørgrav u_int type, rid; 1713190cef3dSDag-Erling Smørgrav int r; 1714b15c8340SDag-Erling Smørgrav 171519261079SEd Maste debug3_f("entering"); 1716b15c8340SDag-Erling Smørgrav 1717190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 171819261079SEd Maste fatal_f("sshbuf_new"); 1719190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 || 1720190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 172119261079SEd Maste fatal_fr(r, "request"); 1722b15c8340SDag-Erling Smørgrav 1723190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 172419261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 1725b15c8340SDag-Erling Smørgrav 1726190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1727b15c8340SDag-Erling Smørgrav 1728b15c8340SDag-Erling Smørgrav /* Read their reply */ 1729190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1730b15c8340SDag-Erling Smørgrav /* Remote end exited already */ 1731b15c8340SDag-Erling Smørgrav if (errno == EPIPE) { 1732190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1733b15c8340SDag-Erling Smørgrav return; 1734b15c8340SDag-Erling Smørgrav } 173519261079SEd Maste fatal_f("read from master failed: %s", strerror(errno)); 1736b15c8340SDag-Erling Smørgrav } 1737b15c8340SDag-Erling Smørgrav 1738190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1739190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 174019261079SEd Maste fatal_fr(r, "parse"); 1741190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 174219261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 174319261079SEd Maste muxclient_request_id, rid); 1744b15c8340SDag-Erling Smørgrav switch (type) { 1745b15c8340SDag-Erling Smørgrav case MUX_S_OK: 1746b15c8340SDag-Erling Smørgrav break; 1747b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1748190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 174919261079SEd Maste fatal_fr(r, "parse error message"); 1750b15c8340SDag-Erling Smørgrav fatal("Master refused termination request: %s", e); 1751b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1752190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 175319261079SEd Maste fatal_fr(r, "parse error message"); 175419261079SEd Maste fatal_f("termination request failed: %s", e); 1755b15c8340SDag-Erling Smørgrav default: 175619261079SEd Maste fatal_f("unexpected response from master 0x%08x", type); 1757b15c8340SDag-Erling Smørgrav } 1758190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1759b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1760b15c8340SDag-Erling Smørgrav } 1761b15c8340SDag-Erling Smørgrav 1762b15c8340SDag-Erling Smørgrav static int 1763a0ee8cc6SDag-Erling Smørgrav mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) 1764b15c8340SDag-Erling Smørgrav { 1765190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1766b15c8340SDag-Erling Smørgrav char *e, *fwd_desc; 1767190cef3dSDag-Erling Smørgrav const char *lhost, *chost; 1768b15c8340SDag-Erling Smørgrav u_int type, rid; 1769190cef3dSDag-Erling Smørgrav int r; 1770b15c8340SDag-Erling Smørgrav 1771b15c8340SDag-Erling Smørgrav fwd_desc = format_forward(ftype, fwd); 1772462c32cbSDag-Erling Smørgrav debug("Requesting %s %s", 1773462c32cbSDag-Erling Smørgrav cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); 1774e4a9863fSDag-Erling Smørgrav free(fwd_desc); 1775b15c8340SDag-Erling Smørgrav 1776190cef3dSDag-Erling Smørgrav type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD; 1777190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 1778190cef3dSDag-Erling Smørgrav lhost = fwd->listen_path; 1779190cef3dSDag-Erling Smørgrav else if (fwd->listen_host == NULL) 1780190cef3dSDag-Erling Smørgrav lhost = ""; 1781190cef3dSDag-Erling Smørgrav else if (*fwd->listen_host == '\0') 1782190cef3dSDag-Erling Smørgrav lhost = "*"; 1783190cef3dSDag-Erling Smørgrav else 1784190cef3dSDag-Erling Smørgrav lhost = fwd->listen_host; 1785b15c8340SDag-Erling Smørgrav 1786190cef3dSDag-Erling Smørgrav if (fwd->connect_path != NULL) 1787190cef3dSDag-Erling Smørgrav chost = fwd->connect_path; 1788190cef3dSDag-Erling Smørgrav else if (fwd->connect_host == NULL) 1789190cef3dSDag-Erling Smørgrav chost = ""; 1790190cef3dSDag-Erling Smørgrav else 1791190cef3dSDag-Erling Smørgrav chost = fwd->connect_host; 1792190cef3dSDag-Erling Smørgrav 1793190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 179419261079SEd Maste fatal_f("sshbuf_new"); 1795190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, type)) != 0 || 1796190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 1797190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, ftype)) != 0 || 1798190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, lhost)) != 0 || 1799190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 || 1800190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, chost)) != 0 || 1801190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, fwd->connect_port)) != 0) 180219261079SEd Maste fatal_fr(r, "request"); 1803190cef3dSDag-Erling Smørgrav 1804190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 180519261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 1806b15c8340SDag-Erling Smørgrav 1807190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1808b15c8340SDag-Erling Smørgrav 1809b15c8340SDag-Erling Smørgrav /* Read their reply */ 1810190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1811190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1812b15c8340SDag-Erling Smørgrav return -1; 1813b15c8340SDag-Erling Smørgrav } 1814b15c8340SDag-Erling Smørgrav 1815190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1816190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 181719261079SEd Maste fatal_fr(r, "parse"); 1818190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 181919261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 182019261079SEd Maste muxclient_request_id, rid); 1821190cef3dSDag-Erling Smørgrav 1822b15c8340SDag-Erling Smørgrav switch (type) { 1823b15c8340SDag-Erling Smørgrav case MUX_S_OK: 1824b15c8340SDag-Erling Smørgrav break; 1825e2f6069cSDag-Erling Smørgrav case MUX_S_REMOTE_PORT: 1826462c32cbSDag-Erling Smørgrav if (cancel_flag) 182719261079SEd Maste fatal_f("got MUX_S_REMOTE_PORT for cancel"); 1828190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0) 182919261079SEd Maste fatal_fr(r, "parse port"); 1830557f75e5SDag-Erling Smørgrav verbose("Allocated port %u for remote forward to %s:%d", 1831e2f6069cSDag-Erling Smørgrav fwd->allocated_port, 1832e2f6069cSDag-Erling Smørgrav fwd->connect_host ? fwd->connect_host : "", 1833e2f6069cSDag-Erling Smørgrav fwd->connect_port); 1834e2f6069cSDag-Erling Smørgrav if (muxclient_command == SSHMUX_COMMAND_FORWARD) 1835acc1a9efSDag-Erling Smørgrav fprintf(stdout, "%i\n", fwd->allocated_port); 1836e2f6069cSDag-Erling Smørgrav break; 1837b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1838190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 183919261079SEd Maste fatal_fr(r, "parse error message"); 1840190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1841b15c8340SDag-Erling Smørgrav error("Master refused forwarding request: %s", e); 1842b15c8340SDag-Erling Smørgrav return -1; 1843b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1844190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 184519261079SEd Maste fatal_fr(r, "parse error message"); 1846190cef3dSDag-Erling Smørgrav sshbuf_free(m); 184719261079SEd Maste error_f("forwarding request failed: %s", e); 1848b15c8340SDag-Erling Smørgrav return -1; 1849b15c8340SDag-Erling Smørgrav default: 185019261079SEd Maste fatal_f("unexpected response from master 0x%08x", type); 1851b15c8340SDag-Erling Smørgrav } 1852190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1853b15c8340SDag-Erling Smørgrav 1854b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1855b15c8340SDag-Erling Smørgrav return 0; 1856b15c8340SDag-Erling Smørgrav } 1857b15c8340SDag-Erling Smørgrav 1858b15c8340SDag-Erling Smørgrav static int 1859462c32cbSDag-Erling Smørgrav mux_client_forwards(int fd, int cancel_flag) 1860b15c8340SDag-Erling Smørgrav { 1861462c32cbSDag-Erling Smørgrav int i, ret = 0; 1862b15c8340SDag-Erling Smørgrav 186319261079SEd Maste debug3_f("%s forwardings: %d local, %d remote", 1864462c32cbSDag-Erling Smørgrav cancel_flag ? "cancel" : "request", 1865b15c8340SDag-Erling Smørgrav options.num_local_forwards, options.num_remote_forwards); 1866b15c8340SDag-Erling Smørgrav 1867b15c8340SDag-Erling Smørgrav /* XXX ExitOnForwardingFailure */ 1868b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 1869462c32cbSDag-Erling Smørgrav if (mux_client_forward(fd, cancel_flag, 1870b15c8340SDag-Erling Smørgrav options.local_forwards[i].connect_port == 0 ? 1871b15c8340SDag-Erling Smørgrav MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, 1872b15c8340SDag-Erling Smørgrav options.local_forwards + i) != 0) 1873462c32cbSDag-Erling Smørgrav ret = -1; 1874b15c8340SDag-Erling Smørgrav } 1875b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 1876462c32cbSDag-Erling Smørgrav if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, 1877b15c8340SDag-Erling Smørgrav options.remote_forwards + i) != 0) 1878462c32cbSDag-Erling Smørgrav ret = -1; 1879b15c8340SDag-Erling Smørgrav } 1880462c32cbSDag-Erling Smørgrav return ret; 1881b15c8340SDag-Erling Smørgrav } 1882b15c8340SDag-Erling Smørgrav 1883b15c8340SDag-Erling Smørgrav static int 1884b15c8340SDag-Erling Smørgrav mux_client_request_session(int fd) 1885b15c8340SDag-Erling Smørgrav { 1886190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1887190cef3dSDag-Erling Smørgrav char *e; 188819261079SEd Maste const char *term = NULL; 188938a52bd3SEd Maste u_int i, echar, rid, sid, esid, exitval, type, exitval_seen; 1890b15c8340SDag-Erling Smørgrav extern char **environ; 1891535af610SEd Maste int r, rawmode = 0; 1892b15c8340SDag-Erling Smørgrav 189319261079SEd Maste debug3_f("entering"); 1894b15c8340SDag-Erling Smørgrav 1895b15c8340SDag-Erling Smørgrav if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 189619261079SEd Maste error_f("master alive request failed"); 1897b15c8340SDag-Erling Smørgrav return -1; 1898b15c8340SDag-Erling Smørgrav } 1899b15c8340SDag-Erling Smørgrav 190019261079SEd Maste ssh_signal(SIGPIPE, SIG_IGN); 1901b15c8340SDag-Erling Smørgrav 190219261079SEd Maste if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) 190319261079SEd Maste fatal_f("stdfd_devnull failed"); 1904b15c8340SDag-Erling Smørgrav 190519261079SEd Maste if ((term = lookup_env_in_list("TERM", options.setenv, 190619261079SEd Maste options.num_setenv)) == NULL || *term == '\0') 190719261079SEd Maste term = getenv("TERM"); 190819261079SEd Maste 1909190cef3dSDag-Erling Smørgrav echar = 0xffffffff; 1910190cef3dSDag-Erling Smørgrav if (options.escape_char != SSH_ESCAPECHAR_NONE) 1911190cef3dSDag-Erling Smørgrav echar = (u_int)options.escape_char; 1912b15c8340SDag-Erling Smørgrav 1913190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 191419261079SEd Maste fatal_f("sshbuf_new"); 1915190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 || 1916190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 1917190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ 1918190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, tty_flag)) != 0 || 1919190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.forward_x11)) != 0 || 1920190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.forward_agent)) != 0 || 192119261079SEd Maste (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 || 1922190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, echar)) != 0 || 192319261079SEd Maste (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 || 1924190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(m, command)) != 0) 192519261079SEd Maste fatal_fr(r, "request"); 1926b15c8340SDag-Erling Smørgrav 1927b15c8340SDag-Erling Smørgrav /* Pass environment */ 1928190cef3dSDag-Erling Smørgrav if (options.num_send_env > 0 && environ != NULL) { 1929b15c8340SDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 1930190cef3dSDag-Erling Smørgrav if (!env_permitted(environ[i])) 1931190cef3dSDag-Erling Smørgrav continue; 1932190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(m, environ[i])) != 0) 193319261079SEd Maste fatal_fr(r, "request sendenv"); 1934b15c8340SDag-Erling Smørgrav } 1935b15c8340SDag-Erling Smørgrav } 1936190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 1937190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0) 193819261079SEd Maste fatal_fr(r, "request setenv"); 1939b15c8340SDag-Erling Smørgrav } 1940b15c8340SDag-Erling Smørgrav 1941190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 194219261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 1943b15c8340SDag-Erling Smørgrav 1944b15c8340SDag-Erling Smørgrav /* Send the stdio file descriptors */ 1945b15c8340SDag-Erling Smørgrav if (mm_send_fd(fd, STDIN_FILENO) == -1 || 1946b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDOUT_FILENO) == -1 || 1947b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDERR_FILENO) == -1) 194819261079SEd Maste fatal_f("send fds failed"); 1949b15c8340SDag-Erling Smørgrav 195019261079SEd Maste debug3_f("session request sent"); 1951b15c8340SDag-Erling Smørgrav 1952b15c8340SDag-Erling Smørgrav /* Read their reply */ 1953190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1954190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 195519261079SEd Maste error_f("read from master failed: %s", strerror(errno)); 1956190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1957b15c8340SDag-Erling Smørgrav return -1; 1958b15c8340SDag-Erling Smørgrav } 1959b15c8340SDag-Erling Smørgrav 1960190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1961190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 196219261079SEd Maste fatal_fr(r, "parse"); 1963190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 196419261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 196519261079SEd Maste muxclient_request_id, rid); 1966190cef3dSDag-Erling Smørgrav 1967b15c8340SDag-Erling Smørgrav switch (type) { 1968b15c8340SDag-Erling Smørgrav case MUX_S_SESSION_OPENED: 1969190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &sid)) != 0) 197019261079SEd Maste fatal_fr(r, "parse session ID"); 197119261079SEd Maste debug_f("master session id: %u", sid); 1972b15c8340SDag-Erling Smørgrav break; 1973b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1974190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 197519261079SEd Maste fatal_fr(r, "parse error message"); 19764a421b63SDag-Erling Smørgrav error("Master refused session request: %s", e); 1977190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1978b15c8340SDag-Erling Smørgrav return -1; 1979b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1980190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 198119261079SEd Maste fatal_fr(r, "parse error message"); 198219261079SEd Maste error_f("session request failed: %s", e); 1983190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1984b15c8340SDag-Erling Smørgrav return -1; 1985b15c8340SDag-Erling Smørgrav default: 1986190cef3dSDag-Erling Smørgrav sshbuf_free(m); 198719261079SEd Maste error_f("unexpected response from master 0x%08x", type); 1988b15c8340SDag-Erling Smørgrav return -1; 1989b15c8340SDag-Erling Smørgrav } 1990b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1991b15c8340SDag-Erling Smørgrav 1992acc1a9efSDag-Erling Smørgrav if (pledge("stdio proc tty", NULL) == -1) 199319261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1994acc1a9efSDag-Erling Smørgrav platform_pledge_mux(); 1995acc1a9efSDag-Erling Smørgrav 199619261079SEd Maste ssh_signal(SIGHUP, control_client_sighandler); 199719261079SEd Maste ssh_signal(SIGINT, control_client_sighandler); 199819261079SEd Maste ssh_signal(SIGTERM, control_client_sighandler); 199919261079SEd Maste ssh_signal(SIGWINCH, control_client_sigrelay); 2000b15c8340SDag-Erling Smørgrav 2001535af610SEd Maste if (options.fork_after_authentication) 2002535af610SEd Maste daemon(1, 1); 2003535af610SEd Maste else { 2004e146993eSDag-Erling Smørgrav rawmode = tty_flag; 2005535af610SEd Maste if (tty_flag) { 2006535af610SEd Maste enter_raw_mode( 2007535af610SEd Maste options.request_tty == REQUEST_TTY_FORCE); 2008535af610SEd Maste } 2009535af610SEd Maste } 2010b15c8340SDag-Erling Smørgrav 2011b15c8340SDag-Erling Smørgrav /* 2012b15c8340SDag-Erling Smørgrav * Stick around until the controlee closes the client_fd. 2013b15c8340SDag-Erling Smørgrav * Before it does, it is expected to write an exit message. 2014b15c8340SDag-Erling Smørgrav * This process must read the value and wait for the closure of 2015b15c8340SDag-Erling Smørgrav * the client_fd; if this one closes early, the multiplex master will 2016b15c8340SDag-Erling Smørgrav * terminate early too (possibly losing data). 2017b15c8340SDag-Erling Smørgrav */ 2018b15c8340SDag-Erling Smørgrav for (exitval = 255, exitval_seen = 0;;) { 2019190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2020190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) 2021b15c8340SDag-Erling Smørgrav break; 2022190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0) 202319261079SEd Maste fatal_fr(r, "parse type"); 2024e146993eSDag-Erling Smørgrav switch (type) { 2025e146993eSDag-Erling Smørgrav case MUX_S_TTY_ALLOC_FAIL: 2026190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &esid)) != 0) 202719261079SEd Maste fatal_fr(r, "parse session ID"); 2028190cef3dSDag-Erling Smørgrav if (esid != sid) 202919261079SEd Maste fatal_f("tty alloc fail on unknown session: " 203019261079SEd Maste "my id %u theirs %u", sid, esid); 2031e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == 2032e146993eSDag-Erling Smørgrav REQUEST_TTY_FORCE); 2033e146993eSDag-Erling Smørgrav rawmode = 0; 2034e146993eSDag-Erling Smørgrav continue; 2035e146993eSDag-Erling Smørgrav case MUX_S_EXIT_MESSAGE: 2036190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &esid)) != 0) 203719261079SEd Maste fatal_fr(r, "parse session ID"); 2038190cef3dSDag-Erling Smørgrav if (esid != sid) 203919261079SEd Maste fatal_f("exit on unknown session: " 204019261079SEd Maste "my id %u theirs %u", sid, esid); 2041b15c8340SDag-Erling Smørgrav if (exitval_seen) 204219261079SEd Maste fatal_f("exitval sent twice"); 2043190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &exitval)) != 0) 204419261079SEd Maste fatal_fr(r, "parse exitval"); 2045b15c8340SDag-Erling Smørgrav exitval_seen = 1; 2046e146993eSDag-Erling Smørgrav continue; 2047e146993eSDag-Erling Smørgrav default: 2048190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 204919261079SEd Maste fatal_fr(r, "parse error message"); 205019261079SEd Maste fatal_f("master returned error: %s", e); 2051e146993eSDag-Erling Smørgrav } 2052b15c8340SDag-Erling Smørgrav } 2053b15c8340SDag-Erling Smørgrav 2054b15c8340SDag-Erling Smørgrav close(fd); 2055e146993eSDag-Erling Smørgrav if (rawmode) 2056e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2057b15c8340SDag-Erling Smørgrav 2058b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 20594f52dfbbSDag-Erling Smørgrav debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); 2060b15c8340SDag-Erling Smørgrav exitval = 255; 2061b15c8340SDag-Erling Smørgrav } else if (!exitval_seen) { 2062b15c8340SDag-Erling Smørgrav debug2("Control master terminated unexpectedly"); 2063b15c8340SDag-Erling Smørgrav exitval = 255; 2064b15c8340SDag-Erling Smørgrav } else 2065b15c8340SDag-Erling Smørgrav debug2("Received exit status from master %d", exitval); 2066b15c8340SDag-Erling Smørgrav 20671323ec57SEd Maste if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO) 2068b15c8340SDag-Erling Smørgrav fprintf(stderr, "Shared connection to %s closed.\r\n", host); 2069b15c8340SDag-Erling Smørgrav 2070b15c8340SDag-Erling Smørgrav exit(exitval); 2071b15c8340SDag-Erling Smørgrav } 2072b15c8340SDag-Erling Smørgrav 2073b15c8340SDag-Erling Smørgrav static int 2074ca86bcf2SDag-Erling Smørgrav mux_client_proxy(int fd) 2075ca86bcf2SDag-Erling Smørgrav { 2076190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2077ca86bcf2SDag-Erling Smørgrav char *e; 2078ca86bcf2SDag-Erling Smørgrav u_int type, rid; 2079190cef3dSDag-Erling Smørgrav int r; 2080ca86bcf2SDag-Erling Smørgrav 2081190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 208219261079SEd Maste fatal_f("sshbuf_new"); 2083190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 || 2084190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 208519261079SEd Maste fatal_fr(r, "request"); 2086190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 208719261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 2088ca86bcf2SDag-Erling Smørgrav 2089190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2090ca86bcf2SDag-Erling Smørgrav 2091ca86bcf2SDag-Erling Smørgrav /* Read their reply */ 2092190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 2093190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2094ca86bcf2SDag-Erling Smørgrav return 0; 2095ca86bcf2SDag-Erling Smørgrav } 2096190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2097190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 209819261079SEd Maste fatal_fr(r, "parse"); 2099190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 210019261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 210119261079SEd Maste muxclient_request_id, rid); 2102190cef3dSDag-Erling Smørgrav if (type != MUX_S_PROXY) { 2103190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 210419261079SEd Maste fatal_fr(r, "parse error message"); 210519261079SEd Maste fatal_f("master returned error: %s", e); 2106190cef3dSDag-Erling Smørgrav } 2107190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2108ca86bcf2SDag-Erling Smørgrav 210919261079SEd Maste debug3_f("done"); 2110ca86bcf2SDag-Erling Smørgrav muxclient_request_id++; 2111ca86bcf2SDag-Erling Smørgrav return 0; 2112ca86bcf2SDag-Erling Smørgrav } 2113ca86bcf2SDag-Erling Smørgrav 2114ca86bcf2SDag-Erling Smørgrav static int 2115b15c8340SDag-Erling Smørgrav mux_client_request_stdio_fwd(int fd) 2116b15c8340SDag-Erling Smørgrav { 2117190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2118b15c8340SDag-Erling Smørgrav char *e; 2119b15c8340SDag-Erling Smørgrav u_int type, rid, sid; 212019261079SEd Maste int r; 2121b15c8340SDag-Erling Smørgrav 212219261079SEd Maste debug3_f("entering"); 2123b15c8340SDag-Erling Smørgrav 2124b15c8340SDag-Erling Smørgrav if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 212519261079SEd Maste error_f("master alive request failed"); 2126b15c8340SDag-Erling Smørgrav return -1; 2127b15c8340SDag-Erling Smørgrav } 2128b15c8340SDag-Erling Smørgrav 212919261079SEd Maste ssh_signal(SIGPIPE, SIG_IGN); 2130b15c8340SDag-Erling Smørgrav 213119261079SEd Maste if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) 213219261079SEd Maste fatal_f("stdfd_devnull failed"); 2133b15c8340SDag-Erling Smørgrav 2134190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 213519261079SEd Maste fatal_f("sshbuf_new"); 2136190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 || 2137190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 2138190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ 2139190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 || 2140190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0) 214119261079SEd Maste fatal_fr(r, "request"); 2142b15c8340SDag-Erling Smørgrav 2143190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 214419261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 2145b15c8340SDag-Erling Smørgrav 2146b15c8340SDag-Erling Smørgrav /* Send the stdio file descriptors */ 2147b15c8340SDag-Erling Smørgrav if (mm_send_fd(fd, STDIN_FILENO) == -1 || 2148b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDOUT_FILENO) == -1) 214919261079SEd Maste fatal_f("send fds failed"); 2150b15c8340SDag-Erling Smørgrav 2151acc1a9efSDag-Erling Smørgrav if (pledge("stdio proc tty", NULL) == -1) 215219261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 2153acc1a9efSDag-Erling Smørgrav platform_pledge_mux(); 2154acc1a9efSDag-Erling Smørgrav 215519261079SEd Maste debug3_f("stdio forward request sent"); 2156b15c8340SDag-Erling Smørgrav 2157b15c8340SDag-Erling Smørgrav /* Read their reply */ 2158190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2159b15c8340SDag-Erling Smørgrav 2160190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 216119261079SEd Maste error_f("read from master failed: %s", strerror(errno)); 2162190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2163b15c8340SDag-Erling Smørgrav return -1; 2164b15c8340SDag-Erling Smørgrav } 2165b15c8340SDag-Erling Smørgrav 2166190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2167190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 216819261079SEd Maste fatal_fr(r, "parse"); 2169190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 217019261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 217119261079SEd Maste muxclient_request_id, rid); 2172b15c8340SDag-Erling Smørgrav switch (type) { 2173b15c8340SDag-Erling Smørgrav case MUX_S_SESSION_OPENED: 2174190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &sid)) != 0) 217519261079SEd Maste fatal_fr(r, "parse session ID"); 217619261079SEd Maste debug_f("master session id: %u", sid); 2177b15c8340SDag-Erling Smørgrav break; 2178b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 2179190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 218019261079SEd Maste fatal_fr(r, "parse error message"); 2181190cef3dSDag-Erling Smørgrav sshbuf_free(m); 21824a421b63SDag-Erling Smørgrav fatal("Master refused stdio forwarding request: %s", e); 2183b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 2184190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 218519261079SEd Maste fatal_fr(r, "parse error message"); 2186190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2187a0ee8cc6SDag-Erling Smørgrav fatal("Stdio forwarding request failed: %s", e); 2188b15c8340SDag-Erling Smørgrav default: 2189190cef3dSDag-Erling Smørgrav sshbuf_free(m); 219019261079SEd Maste error_f("unexpected response from master 0x%08x", type); 2191b15c8340SDag-Erling Smørgrav return -1; 2192b15c8340SDag-Erling Smørgrav } 2193b15c8340SDag-Erling Smørgrav muxclient_request_id++; 2194b15c8340SDag-Erling Smørgrav 219519261079SEd Maste ssh_signal(SIGHUP, control_client_sighandler); 219619261079SEd Maste ssh_signal(SIGINT, control_client_sighandler); 219719261079SEd Maste ssh_signal(SIGTERM, control_client_sighandler); 219819261079SEd Maste ssh_signal(SIGWINCH, control_client_sigrelay); 2199b15c8340SDag-Erling Smørgrav 2200b15c8340SDag-Erling Smørgrav /* 2201b15c8340SDag-Erling Smørgrav * Stick around until the controlee closes the client_fd. 2202b15c8340SDag-Erling Smørgrav */ 2203190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2204190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 2205b15c8340SDag-Erling Smørgrav if (errno == EPIPE || 2206b15c8340SDag-Erling Smørgrav (errno == EINTR && muxclient_terminate != 0)) 2207b15c8340SDag-Erling Smørgrav return 0; 220819261079SEd Maste fatal_f("mux_client_read_packet: %s", strerror(errno)); 2209b15c8340SDag-Erling Smørgrav } 221019261079SEd Maste fatal_f("master returned unexpected message %u", type); 2211d4af9e69SDag-Erling Smørgrav } 2212d4af9e69SDag-Erling Smørgrav 2213e146993eSDag-Erling Smørgrav static void 2214e146993eSDag-Erling Smørgrav mux_client_request_stop_listening(int fd) 2215e146993eSDag-Erling Smørgrav { 2216190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2217e146993eSDag-Erling Smørgrav char *e; 2218e146993eSDag-Erling Smørgrav u_int type, rid; 2219190cef3dSDag-Erling Smørgrav int r; 2220e146993eSDag-Erling Smørgrav 222119261079SEd Maste debug3_f("entering"); 2222e146993eSDag-Erling Smørgrav 2223190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 222419261079SEd Maste fatal_f("sshbuf_new"); 2225190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 || 2226190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 222719261079SEd Maste fatal_fr(r, "request"); 2228e146993eSDag-Erling Smørgrav 2229190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 223019261079SEd Maste fatal_f("write packet: %s", strerror(errno)); 2231e146993eSDag-Erling Smørgrav 2232190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2233e146993eSDag-Erling Smørgrav 2234e146993eSDag-Erling Smørgrav /* Read their reply */ 2235190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) 223619261079SEd Maste fatal_f("read from master failed: %s", strerror(errno)); 2237e146993eSDag-Erling Smørgrav 2238190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2239190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 224019261079SEd Maste fatal_fr(r, "parse"); 2241190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 224219261079SEd Maste fatal_f("out of sequence reply: my id %u theirs %u", 224319261079SEd Maste muxclient_request_id, rid); 2244190cef3dSDag-Erling Smørgrav 2245e146993eSDag-Erling Smørgrav switch (type) { 2246e146993eSDag-Erling Smørgrav case MUX_S_OK: 2247e146993eSDag-Erling Smørgrav break; 2248e146993eSDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 2249190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 225019261079SEd Maste fatal_fr(r, "parse error message"); 2251e146993eSDag-Erling Smørgrav fatal("Master refused stop listening request: %s", e); 2252e146993eSDag-Erling Smørgrav case MUX_S_FAILURE: 2253190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 225419261079SEd Maste fatal_fr(r, "parse error message"); 225519261079SEd Maste fatal_f("stop listening request failed: %s", e); 2256e146993eSDag-Erling Smørgrav default: 225719261079SEd Maste fatal_f("unexpected response from master 0x%08x", type); 2258e146993eSDag-Erling Smørgrav } 2259190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2260e146993eSDag-Erling Smørgrav muxclient_request_id++; 2261e146993eSDag-Erling Smørgrav } 2262e146993eSDag-Erling Smørgrav 2263d4af9e69SDag-Erling Smørgrav /* Multiplex client main loop. */ 2264ca86bcf2SDag-Erling Smørgrav int 2265d4af9e69SDag-Erling Smørgrav muxclient(const char *path) 2266d4af9e69SDag-Erling Smørgrav { 2267d4af9e69SDag-Erling Smørgrav struct sockaddr_un addr; 2268535af610SEd Maste int sock, timeout = options.connection_timeout, timeout_ms = -1; 2269b15c8340SDag-Erling Smørgrav u_int pid; 2270d4af9e69SDag-Erling Smørgrav 2271b15c8340SDag-Erling Smørgrav if (muxclient_command == 0) { 2272076ad2f8SDag-Erling Smørgrav if (options.stdio_forward_host != NULL) 2273b15c8340SDag-Erling Smørgrav muxclient_command = SSHMUX_COMMAND_STDIO_FWD; 2274b15c8340SDag-Erling Smørgrav else 2275d4af9e69SDag-Erling Smørgrav muxclient_command = SSHMUX_COMMAND_OPEN; 2276b15c8340SDag-Erling Smørgrav } 2277d4af9e69SDag-Erling Smørgrav 2278d4af9e69SDag-Erling Smørgrav switch (options.control_master) { 2279d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_AUTO: 2280d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_AUTO_ASK: 2281d4af9e69SDag-Erling Smørgrav debug("auto-mux: Trying existing master"); 2282d4af9e69SDag-Erling Smørgrav /* FALLTHROUGH */ 2283d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_NO: 2284d4af9e69SDag-Erling Smørgrav break; 2285d4af9e69SDag-Erling Smørgrav default: 2286ca86bcf2SDag-Erling Smørgrav return -1; 2287d4af9e69SDag-Erling Smørgrav } 2288d4af9e69SDag-Erling Smørgrav 2289d4af9e69SDag-Erling Smørgrav memset(&addr, '\0', sizeof(addr)); 2290d4af9e69SDag-Erling Smørgrav addr.sun_family = AF_UNIX; 2291d4af9e69SDag-Erling Smørgrav 2292d4af9e69SDag-Erling Smørgrav if (strlcpy(addr.sun_path, path, 2293d4af9e69SDag-Erling Smørgrav sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) 2294ca86bcf2SDag-Erling Smørgrav fatal("ControlPath too long ('%s' >= %u bytes)", path, 2295ca86bcf2SDag-Erling Smørgrav (unsigned int)sizeof(addr.sun_path)); 2296d4af9e69SDag-Erling Smørgrav 229719261079SEd Maste if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 229819261079SEd Maste fatal_f("socket(): %s", strerror(errno)); 2299d4af9e69SDag-Erling Smørgrav 2300d93a896eSDag-Erling Smørgrav if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 2301b15c8340SDag-Erling Smørgrav switch (muxclient_command) { 2302b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_OPEN: 2303b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_STDIO_FWD: 2304b15c8340SDag-Erling Smørgrav break; 2305b15c8340SDag-Erling Smørgrav default: 2306d4af9e69SDag-Erling Smørgrav fatal("Control socket connect(%.100s): %s", path, 2307d4af9e69SDag-Erling Smørgrav strerror(errno)); 2308d4af9e69SDag-Erling Smørgrav } 23094a421b63SDag-Erling Smørgrav if (errno == ECONNREFUSED && 23104a421b63SDag-Erling Smørgrav options.control_master != SSHCTL_MASTER_NO) { 23114a421b63SDag-Erling Smørgrav debug("Stale control socket %.100s, unlinking", path); 23124a421b63SDag-Erling Smørgrav unlink(path); 23134a421b63SDag-Erling Smørgrav } else if (errno == ENOENT) { 2314d4af9e69SDag-Erling Smørgrav debug("Control socket \"%.100s\" does not exist", path); 23154a421b63SDag-Erling Smørgrav } else { 2316d4af9e69SDag-Erling Smørgrav error("Control socket connect(%.100s): %s", path, 2317d4af9e69SDag-Erling Smørgrav strerror(errno)); 2318d4af9e69SDag-Erling Smørgrav } 2319d4af9e69SDag-Erling Smørgrav close(sock); 2320ca86bcf2SDag-Erling Smørgrav return -1; 2321d4af9e69SDag-Erling Smørgrav } 2322b15c8340SDag-Erling Smørgrav set_nonblock(sock); 2323d4af9e69SDag-Erling Smørgrav 2324535af610SEd Maste /* Timeout on initial connection only. */ 2325535af610SEd Maste if (timeout > 0 && timeout < INT_MAX / 1000) 2326535af610SEd Maste timeout_ms = timeout * 1000; 2327535af610SEd Maste 2328535af610SEd Maste if (mux_client_hello_exchange(sock, timeout_ms) != 0) { 232919261079SEd Maste error_f("master hello exchange failed"); 2330d4af9e69SDag-Erling Smørgrav close(sock); 2331ca86bcf2SDag-Erling Smørgrav return -1; 2332d4af9e69SDag-Erling Smørgrav } 2333d4af9e69SDag-Erling Smørgrav 2334d4af9e69SDag-Erling Smørgrav switch (muxclient_command) { 2335d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_ALIVE_CHECK: 2336b15c8340SDag-Erling Smørgrav if ((pid = mux_client_request_alive(sock)) == 0) 233719261079SEd Maste fatal_f("master alive check failed"); 2338acc1a9efSDag-Erling Smørgrav fprintf(stderr, "Master running (pid=%u)\r\n", pid); 2339d4af9e69SDag-Erling Smørgrav exit(0); 2340d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_TERMINATE: 2341b15c8340SDag-Erling Smørgrav mux_client_request_terminate(sock); 2342ca86bcf2SDag-Erling Smørgrav if (options.log_level != SYSLOG_LEVEL_QUIET) 2343d4af9e69SDag-Erling Smørgrav fprintf(stderr, "Exit request sent.\r\n"); 2344d4af9e69SDag-Erling Smørgrav exit(0); 2345e2f6069cSDag-Erling Smørgrav case SSHMUX_COMMAND_FORWARD: 2346462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 0) != 0) 234719261079SEd Maste fatal_f("master forward request failed"); 2348e2f6069cSDag-Erling Smørgrav exit(0); 2349d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_OPEN: 2350462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 0) != 0) { 235119261079SEd Maste error_f("master forward request failed"); 2352ca86bcf2SDag-Erling Smørgrav return -1; 2353d4af9e69SDag-Erling Smørgrav } 2354b15c8340SDag-Erling Smørgrav mux_client_request_session(sock); 2355ca86bcf2SDag-Erling Smørgrav return -1; 2356b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_STDIO_FWD: 2357b15c8340SDag-Erling Smørgrav mux_client_request_stdio_fwd(sock); 2358b15c8340SDag-Erling Smørgrav exit(0); 2359e146993eSDag-Erling Smørgrav case SSHMUX_COMMAND_STOP: 2360e146993eSDag-Erling Smørgrav mux_client_request_stop_listening(sock); 2361ca86bcf2SDag-Erling Smørgrav if (options.log_level != SYSLOG_LEVEL_QUIET) 2362e146993eSDag-Erling Smørgrav fprintf(stderr, "Stop listening request sent.\r\n"); 2363e146993eSDag-Erling Smørgrav exit(0); 2364462c32cbSDag-Erling Smørgrav case SSHMUX_COMMAND_CANCEL_FWD: 2365462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 1) != 0) 236619261079SEd Maste error_f("master cancel forward request failed"); 2367462c32cbSDag-Erling Smørgrav exit(0); 2368ca86bcf2SDag-Erling Smørgrav case SSHMUX_COMMAND_PROXY: 2369ca86bcf2SDag-Erling Smørgrav mux_client_proxy(sock); 2370ca86bcf2SDag-Erling Smørgrav return (sock); 2371d4af9e69SDag-Erling Smørgrav default: 2372d4af9e69SDag-Erling Smørgrav fatal("unrecognised muxclient_command %d", muxclient_command); 2373d4af9e69SDag-Erling Smørgrav } 2374d4af9e69SDag-Erling Smørgrav } 2375