1*2f513db7SEd Maste /* $OpenBSD: mux.c,v 1.77 2018/09/26 07:32:44 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" 21cf783db1SDag-Erling Smørgrav __RCSID("$FreeBSD$"); 22b15c8340SDag-Erling Smørgrav 23d4af9e69SDag-Erling Smørgrav #include <sys/types.h> 24d4af9e69SDag-Erling Smørgrav #include <sys/stat.h> 25d4af9e69SDag-Erling Smørgrav #include <sys/socket.h> 26d4af9e69SDag-Erling Smørgrav #include <sys/un.h> 27d4af9e69SDag-Erling Smørgrav 28d4af9e69SDag-Erling Smørgrav #include <errno.h> 29d4af9e69SDag-Erling Smørgrav #include <fcntl.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" 71d4af9e69SDag-Erling Smørgrav 72d4af9e69SDag-Erling Smørgrav /* from ssh.c */ 73d4af9e69SDag-Erling Smørgrav extern int tty_flag; 74d4af9e69SDag-Erling Smørgrav extern Options options; 75d4af9e69SDag-Erling Smørgrav extern int stdin_null_flag; 76d4af9e69SDag-Erling Smørgrav extern char *host; 77b15c8340SDag-Erling Smørgrav extern int subsystem_flag; 78190cef3dSDag-Erling Smørgrav extern struct sshbuf *command; 79b15c8340SDag-Erling Smørgrav extern volatile sig_atomic_t quit_pending; 80d4af9e69SDag-Erling Smørgrav 81d4af9e69SDag-Erling Smørgrav /* Context for session open confirmation callback */ 82d4af9e69SDag-Erling Smørgrav struct mux_session_confirm_ctx { 83b15c8340SDag-Erling Smørgrav u_int want_tty; 84b15c8340SDag-Erling Smørgrav u_int want_subsys; 85b15c8340SDag-Erling Smørgrav u_int want_x_fwd; 86b15c8340SDag-Erling Smørgrav u_int want_agent_fwd; 87190cef3dSDag-Erling Smørgrav struct sshbuf *cmd; 88d4af9e69SDag-Erling Smørgrav char *term; 89d4af9e69SDag-Erling Smørgrav struct termios tio; 90d4af9e69SDag-Erling Smørgrav char **env; 91e2f6069cSDag-Erling Smørgrav u_int rid; 92e2f6069cSDag-Erling Smørgrav }; 93e2f6069cSDag-Erling Smørgrav 94a0ee8cc6SDag-Erling Smørgrav /* Context for stdio fwd open confirmation callback */ 95a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx { 96a0ee8cc6SDag-Erling Smørgrav u_int rid; 97a0ee8cc6SDag-Erling Smørgrav }; 98a0ee8cc6SDag-Erling Smørgrav 99e2f6069cSDag-Erling Smørgrav /* Context for global channel callback */ 100e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx { 101e2f6069cSDag-Erling Smørgrav u_int cid; /* channel id */ 102e2f6069cSDag-Erling Smørgrav u_int rid; /* request id */ 103e2f6069cSDag-Erling Smørgrav int fid; /* forward id */ 104d4af9e69SDag-Erling Smørgrav }; 105d4af9e69SDag-Erling Smørgrav 106d4af9e69SDag-Erling Smørgrav /* fd to control socket */ 107d4af9e69SDag-Erling Smørgrav int muxserver_sock = -1; 108d4af9e69SDag-Erling Smørgrav 109b15c8340SDag-Erling Smørgrav /* client request id */ 110b15c8340SDag-Erling Smørgrav u_int muxclient_request_id = 0; 111b15c8340SDag-Erling Smørgrav 112d4af9e69SDag-Erling Smørgrav /* Multiplexing control command */ 113d4af9e69SDag-Erling Smørgrav u_int muxclient_command = 0; 114d4af9e69SDag-Erling Smørgrav 115d4af9e69SDag-Erling Smørgrav /* Set when signalled. */ 116d4af9e69SDag-Erling Smørgrav static volatile sig_atomic_t muxclient_terminate = 0; 117d4af9e69SDag-Erling Smørgrav 118d4af9e69SDag-Erling Smørgrav /* PID of multiplex server */ 119d4af9e69SDag-Erling Smørgrav static u_int muxserver_pid = 0; 120d4af9e69SDag-Erling Smørgrav 121b15c8340SDag-Erling Smørgrav static Channel *mux_listener_channel = NULL; 122d4af9e69SDag-Erling Smørgrav 123b15c8340SDag-Erling Smørgrav struct mux_master_state { 124b15c8340SDag-Erling Smørgrav int hello_rcvd; 125b15c8340SDag-Erling Smørgrav }; 126b15c8340SDag-Erling Smørgrav 127b15c8340SDag-Erling Smørgrav /* mux protocol messages */ 128b15c8340SDag-Erling Smørgrav #define MUX_MSG_HELLO 0x00000001 129b15c8340SDag-Erling Smørgrav #define MUX_C_NEW_SESSION 0x10000002 130b15c8340SDag-Erling Smørgrav #define MUX_C_ALIVE_CHECK 0x10000004 131b15c8340SDag-Erling Smørgrav #define MUX_C_TERMINATE 0x10000005 132b15c8340SDag-Erling Smørgrav #define MUX_C_OPEN_FWD 0x10000006 133b15c8340SDag-Erling Smørgrav #define MUX_C_CLOSE_FWD 0x10000007 134b15c8340SDag-Erling Smørgrav #define MUX_C_NEW_STDIO_FWD 0x10000008 135e146993eSDag-Erling Smørgrav #define MUX_C_STOP_LISTENING 0x10000009 136ca86bcf2SDag-Erling Smørgrav #define MUX_C_PROXY 0x1000000f 137b15c8340SDag-Erling Smørgrav #define MUX_S_OK 0x80000001 138b15c8340SDag-Erling Smørgrav #define MUX_S_PERMISSION_DENIED 0x80000002 139b15c8340SDag-Erling Smørgrav #define MUX_S_FAILURE 0x80000003 140b15c8340SDag-Erling Smørgrav #define MUX_S_EXIT_MESSAGE 0x80000004 141b15c8340SDag-Erling Smørgrav #define MUX_S_ALIVE 0x80000005 142b15c8340SDag-Erling Smørgrav #define MUX_S_SESSION_OPENED 0x80000006 143e2f6069cSDag-Erling Smørgrav #define MUX_S_REMOTE_PORT 0x80000007 144e146993eSDag-Erling Smørgrav #define MUX_S_TTY_ALLOC_FAIL 0x80000008 145ca86bcf2SDag-Erling Smørgrav #define MUX_S_PROXY 0x8000000f 146b15c8340SDag-Erling Smørgrav 147b15c8340SDag-Erling Smørgrav /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ 148b15c8340SDag-Erling Smørgrav #define MUX_FWD_LOCAL 1 149b15c8340SDag-Erling Smørgrav #define MUX_FWD_REMOTE 2 150b15c8340SDag-Erling Smørgrav #define MUX_FWD_DYNAMIC 3 151b15c8340SDag-Erling Smørgrav 1524f52dfbbSDag-Erling Smørgrav static void mux_session_confirm(struct ssh *, int, int, void *); 1534f52dfbbSDag-Erling Smørgrav static void mux_stdio_confirm(struct ssh *, int, int, void *); 154b15c8340SDag-Erling Smørgrav 155*2f513db7SEd Maste static int mux_master_process_hello(struct ssh *, u_int, 1564f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 157*2f513db7SEd Maste static int mux_master_process_new_session(struct ssh *, u_int, 1584f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 159*2f513db7SEd Maste static int mux_master_process_alive_check(struct ssh *, u_int, 1604f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 161*2f513db7SEd Maste static int mux_master_process_terminate(struct ssh *, u_int, 1624f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 163*2f513db7SEd Maste static int mux_master_process_open_fwd(struct ssh *, u_int, 1644f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 165*2f513db7SEd Maste static int mux_master_process_close_fwd(struct ssh *, u_int, 1664f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 167*2f513db7SEd Maste static int mux_master_process_stdio_fwd(struct ssh *, u_int, 1684f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 169*2f513db7SEd Maste static int mux_master_process_stop_listening(struct ssh *, u_int, 1704f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 171*2f513db7SEd Maste static int mux_master_process_proxy(struct ssh *, u_int, 1724f52dfbbSDag-Erling Smørgrav Channel *, struct sshbuf *, struct sshbuf *); 173b15c8340SDag-Erling Smørgrav 174b15c8340SDag-Erling Smørgrav static const struct { 175b15c8340SDag-Erling Smørgrav u_int type; 1764f52dfbbSDag-Erling Smørgrav int (*handler)(struct ssh *, u_int, Channel *, 1774f52dfbbSDag-Erling Smørgrav struct sshbuf *, struct sshbuf *); 178b15c8340SDag-Erling Smørgrav } mux_master_handlers[] = { 179*2f513db7SEd Maste { MUX_MSG_HELLO, mux_master_process_hello }, 180*2f513db7SEd Maste { MUX_C_NEW_SESSION, mux_master_process_new_session }, 181*2f513db7SEd Maste { MUX_C_ALIVE_CHECK, mux_master_process_alive_check }, 182*2f513db7SEd Maste { MUX_C_TERMINATE, mux_master_process_terminate }, 183*2f513db7SEd Maste { MUX_C_OPEN_FWD, mux_master_process_open_fwd }, 184*2f513db7SEd Maste { MUX_C_CLOSE_FWD, mux_master_process_close_fwd }, 185*2f513db7SEd Maste { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, 186*2f513db7SEd Maste { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, 187*2f513db7SEd Maste { MUX_C_PROXY, mux_master_process_proxy }, 188b15c8340SDag-Erling Smørgrav { 0, NULL } 189b15c8340SDag-Erling Smørgrav }; 190b15c8340SDag-Erling Smørgrav 191b15c8340SDag-Erling Smørgrav /* Cleanup callback fired on closure of mux slave _session_ channel */ 192b15c8340SDag-Erling Smørgrav /* ARGSUSED */ 193e4a9863fSDag-Erling Smørgrav static void 1944f52dfbbSDag-Erling Smørgrav mux_master_session_cleanup_cb(struct ssh *ssh, int cid, void *unused) 195b15c8340SDag-Erling Smørgrav { 1964f52dfbbSDag-Erling Smørgrav Channel *cc, *c = channel_by_id(ssh, cid); 197b15c8340SDag-Erling Smørgrav 198b15c8340SDag-Erling Smørgrav debug3("%s: entering for channel %d", __func__, cid); 199b15c8340SDag-Erling Smørgrav if (c == NULL) 200b15c8340SDag-Erling Smørgrav fatal("%s: channel_by_id(%i) == NULL", __func__, cid); 201b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 2024f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 203b15c8340SDag-Erling Smørgrav fatal("%s: channel %d missing control channel %d", 204b15c8340SDag-Erling Smørgrav __func__, c->self, c->ctl_chan); 205b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 2064f52dfbbSDag-Erling Smørgrav cc->remote_id = 0; 2074f52dfbbSDag-Erling Smørgrav cc->have_remote_id = 0; 2084f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, cc); 209b15c8340SDag-Erling Smørgrav } 2104f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, c->self); 211b15c8340SDag-Erling Smørgrav } 212b15c8340SDag-Erling Smørgrav 213b15c8340SDag-Erling Smørgrav /* Cleanup callback fired on closure of mux slave _control_ channel */ 214b15c8340SDag-Erling Smørgrav /* ARGSUSED */ 215b15c8340SDag-Erling Smørgrav static void 2164f52dfbbSDag-Erling Smørgrav mux_master_control_cleanup_cb(struct ssh *ssh, int cid, void *unused) 217b15c8340SDag-Erling Smørgrav { 2184f52dfbbSDag-Erling Smørgrav Channel *sc, *c = channel_by_id(ssh, cid); 219b15c8340SDag-Erling Smørgrav 220b15c8340SDag-Erling Smørgrav debug3("%s: entering for channel %d", __func__, cid); 221b15c8340SDag-Erling Smørgrav if (c == NULL) 222b15c8340SDag-Erling Smørgrav fatal("%s: channel_by_id(%i) == NULL", __func__, cid); 2234f52dfbbSDag-Erling Smørgrav if (c->have_remote_id) { 2244f52dfbbSDag-Erling Smørgrav if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) 2254f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d missing session channel %u", 226b15c8340SDag-Erling Smørgrav __func__, c->self, c->remote_id); 2274f52dfbbSDag-Erling Smørgrav c->remote_id = 0; 2284f52dfbbSDag-Erling Smørgrav c->have_remote_id = 0; 229b15c8340SDag-Erling Smørgrav sc->ctl_chan = -1; 230e4a9863fSDag-Erling Smørgrav if (sc->type != SSH_CHANNEL_OPEN && 231e4a9863fSDag-Erling Smørgrav sc->type != SSH_CHANNEL_OPENING) { 232b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: not open", __func__, sc->self); 2334f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, sc); 234b15c8340SDag-Erling Smørgrav } else { 235b15c8340SDag-Erling Smørgrav if (sc->istate == CHAN_INPUT_OPEN) 2364f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, sc); 237b15c8340SDag-Erling Smørgrav if (sc->ostate == CHAN_OUTPUT_OPEN) 2384f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, sc); 239b15c8340SDag-Erling Smørgrav } 240b15c8340SDag-Erling Smørgrav } 2414f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, c->self); 242b15c8340SDag-Erling Smørgrav } 243b15c8340SDag-Erling Smørgrav 244b15c8340SDag-Erling Smørgrav /* Check mux client environment variables before passing them to mux master. */ 245b15c8340SDag-Erling Smørgrav static int 246b15c8340SDag-Erling Smørgrav env_permitted(char *env) 247b15c8340SDag-Erling Smørgrav { 248b15c8340SDag-Erling Smørgrav int i, ret; 249b15c8340SDag-Erling Smørgrav char name[1024], *cp; 250b15c8340SDag-Erling Smørgrav 251b15c8340SDag-Erling Smørgrav if ((cp = strchr(env, '=')) == NULL || cp == env) 252b15c8340SDag-Erling Smørgrav return 0; 253b15c8340SDag-Erling Smørgrav ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); 254b15c8340SDag-Erling Smørgrav if (ret <= 0 || (size_t)ret >= sizeof(name)) { 255*2f513db7SEd Maste error("%s: name '%.100s...' too long", __func__, env); 256b15c8340SDag-Erling Smørgrav return 0; 257b15c8340SDag-Erling Smørgrav } 258b15c8340SDag-Erling Smørgrav 259b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_send_env; i++) 260b15c8340SDag-Erling Smørgrav if (match_pattern(name, options.send_env[i])) 261b15c8340SDag-Erling Smørgrav return 1; 262b15c8340SDag-Erling Smørgrav 263b15c8340SDag-Erling Smørgrav return 0; 264b15c8340SDag-Erling Smørgrav } 265b15c8340SDag-Erling Smørgrav 266b15c8340SDag-Erling Smørgrav /* Mux master protocol message handlers */ 267b15c8340SDag-Erling Smørgrav 268b15c8340SDag-Erling Smørgrav static int 269*2f513db7SEd Maste mux_master_process_hello(struct ssh *ssh, u_int rid, 270190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 271b15c8340SDag-Erling Smørgrav { 272b15c8340SDag-Erling Smørgrav u_int ver; 273b15c8340SDag-Erling Smørgrav struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 274190cef3dSDag-Erling Smørgrav int r; 275b15c8340SDag-Erling Smørgrav 276b15c8340SDag-Erling Smørgrav if (state == NULL) 277b15c8340SDag-Erling Smørgrav fatal("%s: channel %d: c->mux_ctx == NULL", __func__, c->self); 278b15c8340SDag-Erling Smørgrav if (state->hello_rcvd) { 279b15c8340SDag-Erling Smørgrav error("%s: HELLO received twice", __func__); 280b15c8340SDag-Erling Smørgrav return -1; 281b15c8340SDag-Erling Smørgrav } 282190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ver)) != 0) { 283190cef3dSDag-Erling Smørgrav error("%s: malformed message: %s", __func__, ssh_err(r)); 284b15c8340SDag-Erling Smørgrav return -1; 285b15c8340SDag-Erling Smørgrav } 286b15c8340SDag-Erling Smørgrav if (ver != SSHMUX_VER) { 287*2f513db7SEd Maste error("%s: unsupported multiplexing protocol version %u " 288*2f513db7SEd Maste "(expected %u)", __func__, ver, SSHMUX_VER); 289b15c8340SDag-Erling Smørgrav return -1; 290b15c8340SDag-Erling Smørgrav } 291b15c8340SDag-Erling Smørgrav debug2("%s: channel %d slave version %u", __func__, c->self, ver); 292b15c8340SDag-Erling Smørgrav 293b15c8340SDag-Erling Smørgrav /* No extensions are presently defined */ 294190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 295190cef3dSDag-Erling Smørgrav char *name = NULL; 296*2f513db7SEd Maste size_t value_len = 0; 297b15c8340SDag-Erling Smørgrav 298190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || 299*2f513db7SEd Maste (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) { 300190cef3dSDag-Erling Smørgrav error("%s: malformed extension: %s", 301190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 302190cef3dSDag-Erling Smørgrav return -1; 303b15c8340SDag-Erling Smørgrav } 304*2f513db7SEd Maste debug2("%s: Unrecognised extension \"%s\" length %zu", 305*2f513db7SEd Maste __func__, name, value_len); 306e4a9863fSDag-Erling Smørgrav free(name); 307b15c8340SDag-Erling Smørgrav } 308b15c8340SDag-Erling Smørgrav state->hello_rcvd = 1; 309b15c8340SDag-Erling Smørgrav return 0; 310b15c8340SDag-Erling Smørgrav } 311b15c8340SDag-Erling Smørgrav 312190cef3dSDag-Erling Smørgrav /* Enqueue a "ok" response to the reply buffer */ 313190cef3dSDag-Erling Smørgrav static void 314190cef3dSDag-Erling Smørgrav reply_ok(struct sshbuf *reply, u_int rid) 315190cef3dSDag-Erling Smørgrav { 316190cef3dSDag-Erling Smørgrav int r; 317190cef3dSDag-Erling Smørgrav 318190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 || 319190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0) 320190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 321190cef3dSDag-Erling Smørgrav } 322190cef3dSDag-Erling Smørgrav 323190cef3dSDag-Erling Smørgrav /* Enqueue an error response to the reply buffer */ 324190cef3dSDag-Erling Smørgrav static void 325190cef3dSDag-Erling Smørgrav reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg) 326190cef3dSDag-Erling Smørgrav { 327190cef3dSDag-Erling Smørgrav int r; 328190cef3dSDag-Erling Smørgrav 329190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, type)) != 0 || 330190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 331190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(reply, msg)) != 0) 332190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 333190cef3dSDag-Erling Smørgrav } 334190cef3dSDag-Erling Smørgrav 335b15c8340SDag-Erling Smørgrav static int 336*2f513db7SEd Maste mux_master_process_new_session(struct ssh *ssh, u_int rid, 337190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 338b15c8340SDag-Erling Smørgrav { 339b15c8340SDag-Erling Smørgrav Channel *nc; 340b15c8340SDag-Erling Smørgrav struct mux_session_confirm_ctx *cctx; 341190cef3dSDag-Erling Smørgrav char *cmd, *cp; 342190cef3dSDag-Erling Smørgrav u_int i, j, env_len, escape_char, window, packetmax; 343190cef3dSDag-Erling Smørgrav int r, new_fd[3]; 344b15c8340SDag-Erling Smørgrav 345b15c8340SDag-Erling Smørgrav /* Reply for SSHMUX_COMMAND_OPEN */ 346b15c8340SDag-Erling Smørgrav cctx = xcalloc(1, sizeof(*cctx)); 347b15c8340SDag-Erling Smørgrav cctx->term = NULL; 348e2f6069cSDag-Erling Smørgrav cctx->rid = rid; 349190cef3dSDag-Erling Smørgrav cmd = NULL; 350462c32cbSDag-Erling Smørgrav cctx->env = NULL; 351462c32cbSDag-Erling Smørgrav env_len = 0; 352190cef3dSDag-Erling Smørgrav if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ 353190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 || 354190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 || 355190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 || 356190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 || 357190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &escape_char)) != 0 || 358190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 || 359190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) { 360b15c8340SDag-Erling Smørgrav malf: 361e4a9863fSDag-Erling Smørgrav free(cmd); 362462c32cbSDag-Erling Smørgrav for (j = 0; j < env_len; j++) 363e4a9863fSDag-Erling Smørgrav free(cctx->env[j]); 364e4a9863fSDag-Erling Smørgrav free(cctx->env); 365e4a9863fSDag-Erling Smørgrav free(cctx->term); 366e4a9863fSDag-Erling Smørgrav free(cctx); 367b15c8340SDag-Erling Smørgrav error("%s: malformed message", __func__); 368b15c8340SDag-Erling Smørgrav return -1; 369b15c8340SDag-Erling Smørgrav } 370b15c8340SDag-Erling Smørgrav 371b15c8340SDag-Erling Smørgrav #define MUX_MAX_ENV_VARS 4096 372190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 373190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0) 374b15c8340SDag-Erling Smørgrav goto malf; 375b15c8340SDag-Erling Smørgrav if (!env_permitted(cp)) { 376e4a9863fSDag-Erling Smørgrav free(cp); 377b15c8340SDag-Erling Smørgrav continue; 378b15c8340SDag-Erling Smørgrav } 379557f75e5SDag-Erling Smørgrav cctx->env = xreallocarray(cctx->env, env_len + 2, 380b15c8340SDag-Erling Smørgrav sizeof(*cctx->env)); 381b15c8340SDag-Erling Smørgrav cctx->env[env_len++] = cp; 382b15c8340SDag-Erling Smørgrav cctx->env[env_len] = NULL; 383b15c8340SDag-Erling Smørgrav if (env_len > MUX_MAX_ENV_VARS) { 384*2f513db7SEd Maste error("%s: >%d environment variables received, " 385*2f513db7SEd Maste "ignoring additional", __func__, MUX_MAX_ENV_VARS); 386b15c8340SDag-Erling Smørgrav break; 387b15c8340SDag-Erling Smørgrav } 388b15c8340SDag-Erling Smørgrav } 389b15c8340SDag-Erling Smørgrav 390b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: request tty %d, X %d, agent %d, subsys %d, " 391b15c8340SDag-Erling Smørgrav "term \"%s\", cmd \"%s\", env %u", __func__, c->self, 392b15c8340SDag-Erling Smørgrav cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, 393b15c8340SDag-Erling Smørgrav cctx->want_subsys, cctx->term, cmd, env_len); 394b15c8340SDag-Erling Smørgrav 395190cef3dSDag-Erling Smørgrav if ((cctx->cmd = sshbuf_new()) == NULL) 396190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 397190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0) 398190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put: %s", __func__, ssh_err(r)); 399e4a9863fSDag-Erling Smørgrav free(cmd); 400b15c8340SDag-Erling Smørgrav cmd = NULL; 401b15c8340SDag-Erling Smørgrav 402b15c8340SDag-Erling Smørgrav /* Gather fds from client */ 403b15c8340SDag-Erling Smørgrav for(i = 0; i < 3; i++) { 404b15c8340SDag-Erling Smørgrav if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 405b15c8340SDag-Erling Smørgrav error("%s: failed to receive fd %d from slave", 406b15c8340SDag-Erling Smørgrav __func__, i); 407b15c8340SDag-Erling Smørgrav for (j = 0; j < i; j++) 408b15c8340SDag-Erling Smørgrav close(new_fd[j]); 409b15c8340SDag-Erling Smørgrav for (j = 0; j < env_len; j++) 410e4a9863fSDag-Erling Smørgrav free(cctx->env[j]); 411e4a9863fSDag-Erling Smørgrav free(cctx->env); 412e4a9863fSDag-Erling Smørgrav free(cctx->term); 413190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 414e4a9863fSDag-Erling Smørgrav free(cctx); 415190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 416b15c8340SDag-Erling Smørgrav "did not receive file descriptors"); 417b15c8340SDag-Erling Smørgrav return -1; 418b15c8340SDag-Erling Smørgrav } 419b15c8340SDag-Erling Smørgrav } 420b15c8340SDag-Erling Smørgrav 421b15c8340SDag-Erling Smørgrav debug3("%s: got fds stdin %d, stdout %d, stderr %d", __func__, 422b15c8340SDag-Erling Smørgrav new_fd[0], new_fd[1], new_fd[2]); 423b15c8340SDag-Erling Smørgrav 424b15c8340SDag-Erling Smørgrav /* XXX support multiple child sessions in future */ 4254f52dfbbSDag-Erling Smørgrav if (c->have_remote_id) { 426b15c8340SDag-Erling Smørgrav debug2("%s: session already open", __func__); 427190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 428190cef3dSDag-Erling Smørgrav "Multiple sessions not supported"); 429b15c8340SDag-Erling Smørgrav cleanup: 430b15c8340SDag-Erling Smørgrav close(new_fd[0]); 431b15c8340SDag-Erling Smørgrav close(new_fd[1]); 432b15c8340SDag-Erling Smørgrav close(new_fd[2]); 433e4a9863fSDag-Erling Smørgrav free(cctx->term); 434b15c8340SDag-Erling Smørgrav if (env_len != 0) { 435b15c8340SDag-Erling Smørgrav for (i = 0; i < env_len; i++) 436e4a9863fSDag-Erling Smørgrav free(cctx->env[i]); 437e4a9863fSDag-Erling Smørgrav free(cctx->env); 438b15c8340SDag-Erling Smørgrav } 439190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 440e4a9863fSDag-Erling Smørgrav free(cctx); 441b15c8340SDag-Erling Smørgrav return 0; 442b15c8340SDag-Erling Smørgrav } 443b15c8340SDag-Erling Smørgrav 444b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 445b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 446b15c8340SDag-Erling Smørgrav if (!ask_permission("Allow shared connection to %s? ", host)) { 447b15c8340SDag-Erling Smørgrav debug2("%s: session refused by user", __func__); 448190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 449190cef3dSDag-Erling Smørgrav "Permission denied"); 450b15c8340SDag-Erling Smørgrav goto cleanup; 451b15c8340SDag-Erling Smørgrav } 452b15c8340SDag-Erling Smørgrav } 453b15c8340SDag-Erling Smørgrav 454b15c8340SDag-Erling Smørgrav /* Try to pick up ttymodes from client before it goes raw */ 455b15c8340SDag-Erling Smørgrav if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) 456b15c8340SDag-Erling Smørgrav error("%s: tcgetattr: %s", __func__, strerror(errno)); 457b15c8340SDag-Erling Smørgrav 458b15c8340SDag-Erling Smørgrav /* enable nonblocking unless tty */ 459b15c8340SDag-Erling Smørgrav if (!isatty(new_fd[0])) 460b15c8340SDag-Erling Smørgrav set_nonblock(new_fd[0]); 461b15c8340SDag-Erling Smørgrav if (!isatty(new_fd[1])) 462b15c8340SDag-Erling Smørgrav set_nonblock(new_fd[1]); 463b15c8340SDag-Erling Smørgrav if (!isatty(new_fd[2])) 464b15c8340SDag-Erling Smørgrav set_nonblock(new_fd[2]); 465b15c8340SDag-Erling Smørgrav 466b15c8340SDag-Erling Smørgrav window = CHAN_SES_WINDOW_DEFAULT; 467b15c8340SDag-Erling Smørgrav packetmax = CHAN_SES_PACKET_DEFAULT; 468b15c8340SDag-Erling Smørgrav if (cctx->want_tty) { 469b15c8340SDag-Erling Smørgrav window >>= 1; 470b15c8340SDag-Erling Smørgrav packetmax >>= 1; 471b15c8340SDag-Erling Smørgrav } 472b15c8340SDag-Erling Smørgrav 4734f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, 474b15c8340SDag-Erling Smørgrav new_fd[0], new_fd[1], new_fd[2], window, packetmax, 475b15c8340SDag-Erling Smørgrav CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); 476b15c8340SDag-Erling Smørgrav 477b15c8340SDag-Erling Smørgrav nc->ctl_chan = c->self; /* link session -> control channel */ 478b15c8340SDag-Erling Smørgrav c->remote_id = nc->self; /* link control -> session channel */ 4794f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 480b15c8340SDag-Erling Smørgrav 481b15c8340SDag-Erling Smørgrav if (cctx->want_tty && escape_char != 0xffffffff) { 4824f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, nc->self, 483b15c8340SDag-Erling Smørgrav client_simple_escape_filter, NULL, 484b15c8340SDag-Erling Smørgrav client_filter_cleanup, 485b15c8340SDag-Erling Smørgrav client_new_escape_filter_ctx((int)escape_char)); 486b15c8340SDag-Erling Smørgrav } 487b15c8340SDag-Erling Smørgrav 488b15c8340SDag-Erling Smørgrav debug2("%s: channel_new: %d linked to control channel %d", 489b15c8340SDag-Erling Smørgrav __func__, nc->self, nc->ctl_chan); 490b15c8340SDag-Erling Smørgrav 4914f52dfbbSDag-Erling Smørgrav channel_send_open(ssh, nc->self); 4924f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); 493e2f6069cSDag-Erling Smørgrav c->mux_pause = 1; /* stop handling messages until open_confirm done */ 4944f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, nc->self, 4954f52dfbbSDag-Erling Smørgrav mux_master_session_cleanup_cb, 1); 496b15c8340SDag-Erling Smørgrav 497e2f6069cSDag-Erling Smørgrav /* reply is deferred, sent by mux_session_confirm */ 498b15c8340SDag-Erling Smørgrav return 0; 499b15c8340SDag-Erling Smørgrav } 500b15c8340SDag-Erling Smørgrav 501b15c8340SDag-Erling Smørgrav static int 502*2f513db7SEd Maste mux_master_process_alive_check(struct ssh *ssh, u_int rid, 503190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 504b15c8340SDag-Erling Smørgrav { 505190cef3dSDag-Erling Smørgrav int r; 506190cef3dSDag-Erling Smørgrav 507b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: alive check", __func__, c->self); 508b15c8340SDag-Erling Smørgrav 509b15c8340SDag-Erling Smørgrav /* prepare reply */ 510190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || 511190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 512190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) 513190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 514b15c8340SDag-Erling Smørgrav 515b15c8340SDag-Erling Smørgrav return 0; 516b15c8340SDag-Erling Smørgrav } 517b15c8340SDag-Erling Smørgrav 518b15c8340SDag-Erling Smørgrav static int 519*2f513db7SEd Maste mux_master_process_terminate(struct ssh *ssh, u_int rid, 520190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 521b15c8340SDag-Erling Smørgrav { 522b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: terminate request", __func__, c->self); 523b15c8340SDag-Erling Smørgrav 524b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 525b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 526b15c8340SDag-Erling Smørgrav if (!ask_permission("Terminate shared connection to %s? ", 527b15c8340SDag-Erling Smørgrav host)) { 528b15c8340SDag-Erling Smørgrav debug2("%s: termination refused by user", __func__); 529190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 530190cef3dSDag-Erling Smørgrav "Permission denied"); 531b15c8340SDag-Erling Smørgrav return 0; 532b15c8340SDag-Erling Smørgrav } 533b15c8340SDag-Erling Smørgrav } 534b15c8340SDag-Erling Smørgrav 535b15c8340SDag-Erling Smørgrav quit_pending = 1; 536190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 537b15c8340SDag-Erling Smørgrav /* XXX exit happens too soon - message never makes it to client */ 538b15c8340SDag-Erling Smørgrav return 0; 539b15c8340SDag-Erling Smørgrav } 540b15c8340SDag-Erling Smørgrav 541b15c8340SDag-Erling Smørgrav static char * 542a0ee8cc6SDag-Erling Smørgrav format_forward(u_int ftype, struct Forward *fwd) 543b15c8340SDag-Erling Smørgrav { 544b15c8340SDag-Erling Smørgrav char *ret; 545b15c8340SDag-Erling Smørgrav 546b15c8340SDag-Erling Smørgrav switch (ftype) { 547b15c8340SDag-Erling Smørgrav case MUX_FWD_LOCAL: 548b15c8340SDag-Erling Smørgrav xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", 549a0ee8cc6SDag-Erling Smørgrav (fwd->listen_path != NULL) ? fwd->listen_path : 550b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 551a0ee8cc6SDag-Erling Smørgrav (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 552b15c8340SDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, 553a0ee8cc6SDag-Erling Smørgrav (fwd->connect_path != NULL) ? fwd->connect_path : 554b15c8340SDag-Erling Smørgrav fwd->connect_host, fwd->connect_port); 555b15c8340SDag-Erling Smørgrav break; 556b15c8340SDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 557b15c8340SDag-Erling Smørgrav xasprintf(&ret, "dynamic forward %.200s:%d -> *", 558b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 559a0ee8cc6SDag-Erling Smørgrav (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 560b15c8340SDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 561b15c8340SDag-Erling Smørgrav break; 562b15c8340SDag-Erling Smørgrav case MUX_FWD_REMOTE: 563b15c8340SDag-Erling Smørgrav xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", 564a0ee8cc6SDag-Erling Smørgrav (fwd->listen_path != NULL) ? fwd->listen_path : 565b15c8340SDag-Erling Smørgrav (fwd->listen_host == NULL) ? 566b15c8340SDag-Erling Smørgrav "LOCALHOST" : fwd->listen_host, 567b15c8340SDag-Erling Smørgrav fwd->listen_port, 568a0ee8cc6SDag-Erling Smørgrav (fwd->connect_path != NULL) ? fwd->connect_path : 569b15c8340SDag-Erling Smørgrav fwd->connect_host, fwd->connect_port); 570b15c8340SDag-Erling Smørgrav break; 571b15c8340SDag-Erling Smørgrav default: 572b15c8340SDag-Erling Smørgrav fatal("%s: unknown forward type %u", __func__, ftype); 573b15c8340SDag-Erling Smørgrav } 574b15c8340SDag-Erling Smørgrav return ret; 575b15c8340SDag-Erling Smørgrav } 576b15c8340SDag-Erling Smørgrav 577b15c8340SDag-Erling Smørgrav static int 578b15c8340SDag-Erling Smørgrav compare_host(const char *a, const char *b) 579b15c8340SDag-Erling Smørgrav { 580b15c8340SDag-Erling Smørgrav if (a == NULL && b == NULL) 581b15c8340SDag-Erling Smørgrav return 1; 582b15c8340SDag-Erling Smørgrav if (a == NULL || b == NULL) 583b15c8340SDag-Erling Smørgrav return 0; 584b15c8340SDag-Erling Smørgrav return strcmp(a, b) == 0; 585b15c8340SDag-Erling Smørgrav } 586b15c8340SDag-Erling Smørgrav 587b15c8340SDag-Erling Smørgrav static int 588a0ee8cc6SDag-Erling Smørgrav compare_forward(struct Forward *a, struct Forward *b) 589b15c8340SDag-Erling Smørgrav { 590b15c8340SDag-Erling Smørgrav if (!compare_host(a->listen_host, b->listen_host)) 591b15c8340SDag-Erling Smørgrav return 0; 592a0ee8cc6SDag-Erling Smørgrav if (!compare_host(a->listen_path, b->listen_path)) 593a0ee8cc6SDag-Erling Smørgrav return 0; 594b15c8340SDag-Erling Smørgrav if (a->listen_port != b->listen_port) 595b15c8340SDag-Erling Smørgrav return 0; 596b15c8340SDag-Erling Smørgrav if (!compare_host(a->connect_host, b->connect_host)) 597b15c8340SDag-Erling Smørgrav return 0; 598a0ee8cc6SDag-Erling Smørgrav if (!compare_host(a->connect_path, b->connect_path)) 599a0ee8cc6SDag-Erling Smørgrav return 0; 600b15c8340SDag-Erling Smørgrav if (a->connect_port != b->connect_port) 601b15c8340SDag-Erling Smørgrav return 0; 602b15c8340SDag-Erling Smørgrav 603b15c8340SDag-Erling Smørgrav return 1; 604b15c8340SDag-Erling Smørgrav } 605b15c8340SDag-Erling Smørgrav 606e2f6069cSDag-Erling Smørgrav static void 6074f52dfbbSDag-Erling Smørgrav mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) 608e2f6069cSDag-Erling Smørgrav { 609e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx *fctx = ctxt; 610e2f6069cSDag-Erling Smørgrav char *failmsg = NULL; 611a0ee8cc6SDag-Erling Smørgrav struct Forward *rfwd; 612e2f6069cSDag-Erling Smørgrav Channel *c; 613190cef3dSDag-Erling Smørgrav struct sshbuf *out; 614190cef3dSDag-Erling Smørgrav int r; 615e2f6069cSDag-Erling Smørgrav 6164f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { 617e2f6069cSDag-Erling Smørgrav /* no channel for reply */ 618e2f6069cSDag-Erling Smørgrav error("%s: unknown channel", __func__); 619e2f6069cSDag-Erling Smørgrav return; 620e2f6069cSDag-Erling Smørgrav } 621190cef3dSDag-Erling Smørgrav if ((out = sshbuf_new()) == NULL) 622190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 623557f75e5SDag-Erling Smørgrav if (fctx->fid >= options.num_remote_forwards || 624557f75e5SDag-Erling Smørgrav (options.remote_forwards[fctx->fid].connect_path == NULL && 625557f75e5SDag-Erling Smørgrav options.remote_forwards[fctx->fid].connect_host == NULL)) { 626e2f6069cSDag-Erling Smørgrav xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); 627e2f6069cSDag-Erling Smørgrav goto fail; 628e2f6069cSDag-Erling Smørgrav } 629e2f6069cSDag-Erling Smørgrav rfwd = &options.remote_forwards[fctx->fid]; 630e2f6069cSDag-Erling Smørgrav debug("%s: %s for: listen %d, connect %s:%d", __func__, 631e2f6069cSDag-Erling Smørgrav type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", 632a0ee8cc6SDag-Erling Smørgrav rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : 633a0ee8cc6SDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 634e2f6069cSDag-Erling Smørgrav if (type == SSH2_MSG_REQUEST_SUCCESS) { 635e2f6069cSDag-Erling Smørgrav if (rfwd->listen_port == 0) { 636e2f6069cSDag-Erling Smørgrav rfwd->allocated_port = packet_get_int(); 637557f75e5SDag-Erling Smørgrav debug("Allocated port %u for mux remote forward" 638e2f6069cSDag-Erling Smørgrav " to %s:%d", rfwd->allocated_port, 639e2f6069cSDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 640190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(out, 641190cef3dSDag-Erling Smørgrav MUX_S_REMOTE_PORT)) != 0 || 642190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, fctx->rid)) != 0 || 643190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, 644190cef3dSDag-Erling Smørgrav rfwd->allocated_port)) != 0) 645190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 646190cef3dSDag-Erling Smørgrav channel_update_permission(ssh, rfwd->handle, 647462c32cbSDag-Erling Smørgrav rfwd->allocated_port); 648e2f6069cSDag-Erling Smørgrav } else { 649190cef3dSDag-Erling Smørgrav reply_ok(out, fctx->rid); 650e2f6069cSDag-Erling Smørgrav } 651e2f6069cSDag-Erling Smørgrav goto out; 652e2f6069cSDag-Erling Smørgrav } else { 653462c32cbSDag-Erling Smørgrav if (rfwd->listen_port == 0) 654190cef3dSDag-Erling Smørgrav channel_update_permission(ssh, rfwd->handle, -1); 655a0ee8cc6SDag-Erling Smørgrav if (rfwd->listen_path != NULL) 656a0ee8cc6SDag-Erling Smørgrav xasprintf(&failmsg, "remote port forwarding failed for " 657a0ee8cc6SDag-Erling Smørgrav "listen path %s", rfwd->listen_path); 658a0ee8cc6SDag-Erling Smørgrav else 659e2f6069cSDag-Erling Smørgrav xasprintf(&failmsg, "remote port forwarding failed for " 660e2f6069cSDag-Erling Smørgrav "listen port %d", rfwd->listen_port); 661557f75e5SDag-Erling Smørgrav 662557f75e5SDag-Erling Smørgrav debug2("%s: clearing registered forwarding for listen %d, " 663557f75e5SDag-Erling Smørgrav "connect %s:%d", __func__, rfwd->listen_port, 664557f75e5SDag-Erling Smørgrav rfwd->connect_path ? rfwd->connect_path : 665557f75e5SDag-Erling Smørgrav rfwd->connect_host, rfwd->connect_port); 666557f75e5SDag-Erling Smørgrav 667557f75e5SDag-Erling Smørgrav free(rfwd->listen_host); 668557f75e5SDag-Erling Smørgrav free(rfwd->listen_path); 669557f75e5SDag-Erling Smørgrav free(rfwd->connect_host); 670557f75e5SDag-Erling Smørgrav free(rfwd->connect_path); 671557f75e5SDag-Erling Smørgrav memset(rfwd, 0, sizeof(*rfwd)); 672e2f6069cSDag-Erling Smørgrav } 673e2f6069cSDag-Erling Smørgrav fail: 674e2f6069cSDag-Erling Smørgrav error("%s: %s", __func__, failmsg); 675190cef3dSDag-Erling Smørgrav reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg); 676e4a9863fSDag-Erling Smørgrav free(failmsg); 677e2f6069cSDag-Erling Smørgrav out: 678190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(c->output, out)) != 0) 679190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", __func__, ssh_err(r)); 680190cef3dSDag-Erling Smørgrav sshbuf_free(out); 681e2f6069cSDag-Erling Smørgrav if (c->mux_pause <= 0) 682e2f6069cSDag-Erling Smørgrav fatal("%s: mux_pause %d", __func__, c->mux_pause); 683e2f6069cSDag-Erling Smørgrav c->mux_pause = 0; /* start processing messages again */ 684e2f6069cSDag-Erling Smørgrav } 685e2f6069cSDag-Erling Smørgrav 686b15c8340SDag-Erling Smørgrav static int 687*2f513db7SEd Maste mux_master_process_open_fwd(struct ssh *ssh, u_int rid, 688190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 689b15c8340SDag-Erling Smørgrav { 690a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 691b15c8340SDag-Erling Smørgrav char *fwd_desc = NULL; 692a0ee8cc6SDag-Erling Smørgrav char *listen_addr, *connect_addr; 693b15c8340SDag-Erling Smørgrav u_int ftype; 694e4a9863fSDag-Erling Smørgrav u_int lport, cport; 695190cef3dSDag-Erling Smørgrav int r, i, ret = 0, freefwd = 1; 696b15c8340SDag-Erling Smørgrav 697fc1ba28aSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 698fc1ba28aSDag-Erling Smørgrav 699a0ee8cc6SDag-Erling Smørgrav /* XXX - lport/cport check redundant */ 700190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ftype)) != 0 || 701190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || 702190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &lport)) != 0 || 703190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || 704190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cport)) != 0 || 705a0ee8cc6SDag-Erling Smørgrav (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 706a0ee8cc6SDag-Erling Smørgrav (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 707b15c8340SDag-Erling Smørgrav error("%s: malformed message", __func__); 708b15c8340SDag-Erling Smørgrav ret = -1; 709b15c8340SDag-Erling Smørgrav goto out; 710b15c8340SDag-Erling Smørgrav } 711a0ee8cc6SDag-Erling Smørgrav if (*listen_addr == '\0') { 712a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 713a0ee8cc6SDag-Erling Smørgrav listen_addr = NULL; 714a0ee8cc6SDag-Erling Smørgrav } 715a0ee8cc6SDag-Erling Smørgrav if (*connect_addr == '\0') { 716a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 717a0ee8cc6SDag-Erling Smørgrav connect_addr = NULL; 718a0ee8cc6SDag-Erling Smørgrav } 719a0ee8cc6SDag-Erling Smørgrav 720a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 721e4a9863fSDag-Erling Smørgrav fwd.listen_port = lport; 722a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port == PORT_STREAMLOCAL) 723a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = listen_addr; 724a0ee8cc6SDag-Erling Smørgrav else 725a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = listen_addr; 726e4a9863fSDag-Erling Smørgrav fwd.connect_port = cport; 727a0ee8cc6SDag-Erling Smørgrav if (fwd.connect_port == PORT_STREAMLOCAL) 728a0ee8cc6SDag-Erling Smørgrav fwd.connect_path = connect_addr; 729a0ee8cc6SDag-Erling Smørgrav else 730a0ee8cc6SDag-Erling Smørgrav fwd.connect_host = connect_addr; 731b15c8340SDag-Erling Smørgrav 732b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: request %s", __func__, c->self, 733b15c8340SDag-Erling Smørgrav (fwd_desc = format_forward(ftype, &fwd))); 734b15c8340SDag-Erling Smørgrav 735b15c8340SDag-Erling Smørgrav if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && 736b15c8340SDag-Erling Smørgrav ftype != MUX_FWD_DYNAMIC) { 737b15c8340SDag-Erling Smørgrav logit("%s: invalid forwarding type %u", __func__, ftype); 738b15c8340SDag-Erling Smørgrav invalid: 739a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 740a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 741190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 742190cef3dSDag-Erling Smørgrav "Invalid forwarding request"); 743b15c8340SDag-Erling Smørgrav return 0; 744b15c8340SDag-Erling Smørgrav } 745a0ee8cc6SDag-Erling Smørgrav if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { 746a0ee8cc6SDag-Erling Smørgrav logit("%s: streamlocal and dynamic forwards " 747a0ee8cc6SDag-Erling Smørgrav "are mutually exclusive", __func__); 748a0ee8cc6SDag-Erling Smørgrav goto invalid; 749a0ee8cc6SDag-Erling Smørgrav } 750a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { 751b15c8340SDag-Erling Smørgrav logit("%s: invalid listen port %u", __func__, 752b15c8340SDag-Erling Smørgrav fwd.listen_port); 753b15c8340SDag-Erling Smørgrav goto invalid; 754b15c8340SDag-Erling Smørgrav } 7554f52dfbbSDag-Erling Smørgrav if ((fwd.connect_port != PORT_STREAMLOCAL && 7564f52dfbbSDag-Erling Smørgrav fwd.connect_port >= 65536) || 7574f52dfbbSDag-Erling Smørgrav (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && 7584f52dfbbSDag-Erling Smørgrav fwd.connect_port == 0)) { 759b15c8340SDag-Erling Smørgrav logit("%s: invalid connect port %u", __func__, 760b15c8340SDag-Erling Smørgrav fwd.connect_port); 761b15c8340SDag-Erling Smørgrav goto invalid; 762b15c8340SDag-Erling Smørgrav } 7634f52dfbbSDag-Erling Smørgrav if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && 7644f52dfbbSDag-Erling Smørgrav fwd.connect_path == NULL) { 765b15c8340SDag-Erling Smørgrav logit("%s: missing connect host", __func__); 766b15c8340SDag-Erling Smørgrav goto invalid; 767b15c8340SDag-Erling Smørgrav } 768b15c8340SDag-Erling Smørgrav 769b15c8340SDag-Erling Smørgrav /* Skip forwards that have already been requested */ 770b15c8340SDag-Erling Smørgrav switch (ftype) { 771b15c8340SDag-Erling Smørgrav case MUX_FWD_LOCAL: 772b15c8340SDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 773b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 774b15c8340SDag-Erling Smørgrav if (compare_forward(&fwd, 775b15c8340SDag-Erling Smørgrav options.local_forwards + i)) { 776b15c8340SDag-Erling Smørgrav exists: 777b15c8340SDag-Erling Smørgrav debug2("%s: found existing forwarding", 778b15c8340SDag-Erling Smørgrav __func__); 779190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 780b15c8340SDag-Erling Smørgrav goto out; 781b15c8340SDag-Erling Smørgrav } 782b15c8340SDag-Erling Smørgrav } 783b15c8340SDag-Erling Smørgrav break; 784b15c8340SDag-Erling Smørgrav case MUX_FWD_REMOTE: 785b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 786190cef3dSDag-Erling Smørgrav if (!compare_forward(&fwd, options.remote_forwards + i)) 787190cef3dSDag-Erling Smørgrav continue; 788e2f6069cSDag-Erling Smørgrav if (fwd.listen_port != 0) 789b15c8340SDag-Erling Smørgrav goto exists; 790190cef3dSDag-Erling Smørgrav debug2("%s: found allocated port", __func__); 791190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, 792190cef3dSDag-Erling Smørgrav MUX_S_REMOTE_PORT)) != 0 || 793190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0 || 794190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, 795190cef3dSDag-Erling Smørgrav options.remote_forwards[i].allocated_port)) != 0) 796190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 797e2f6069cSDag-Erling Smørgrav goto out; 798e2f6069cSDag-Erling Smørgrav } 799b15c8340SDag-Erling Smørgrav break; 800b15c8340SDag-Erling Smørgrav } 801b15c8340SDag-Erling Smørgrav 802b15c8340SDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 803b15c8340SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 804b15c8340SDag-Erling Smørgrav if (!ask_permission("Open %s on %s?", fwd_desc, host)) { 805b15c8340SDag-Erling Smørgrav debug2("%s: forwarding refused by user", __func__); 806190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 807190cef3dSDag-Erling Smørgrav "Permission denied"); 808b15c8340SDag-Erling Smørgrav goto out; 809b15c8340SDag-Erling Smørgrav } 810b15c8340SDag-Erling Smørgrav } 811b15c8340SDag-Erling Smørgrav 812b15c8340SDag-Erling Smørgrav if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { 8134f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 814a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 815b15c8340SDag-Erling Smørgrav fail: 816*2f513db7SEd Maste logit("%s: requested %s failed", __func__, fwd_desc); 817190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 818190cef3dSDag-Erling Smørgrav "Port forwarding failed"); 819b15c8340SDag-Erling Smørgrav goto out; 820b15c8340SDag-Erling Smørgrav } 821b15c8340SDag-Erling Smørgrav add_local_forward(&options, &fwd); 822b15c8340SDag-Erling Smørgrav freefwd = 0; 823b15c8340SDag-Erling Smørgrav } else { 824e2f6069cSDag-Erling Smørgrav struct mux_channel_confirm_ctx *fctx; 825e2f6069cSDag-Erling Smørgrav 8264f52dfbbSDag-Erling Smørgrav fwd.handle = channel_request_remote_forwarding(ssh, &fwd); 827462c32cbSDag-Erling Smørgrav if (fwd.handle < 0) 828b15c8340SDag-Erling Smørgrav goto fail; 829b15c8340SDag-Erling Smørgrav add_remote_forward(&options, &fwd); 830e2f6069cSDag-Erling Smørgrav fctx = xcalloc(1, sizeof(*fctx)); 831e2f6069cSDag-Erling Smørgrav fctx->cid = c->self; 832e2f6069cSDag-Erling Smørgrav fctx->rid = rid; 833e2f6069cSDag-Erling Smørgrav fctx->fid = options.num_remote_forwards - 1; 834e2f6069cSDag-Erling Smørgrav client_register_global_confirm(mux_confirm_remote_forward, 835e2f6069cSDag-Erling Smørgrav fctx); 836b15c8340SDag-Erling Smørgrav freefwd = 0; 837e2f6069cSDag-Erling Smørgrav c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ 838e2f6069cSDag-Erling Smørgrav /* delayed reply in mux_confirm_remote_forward */ 839e2f6069cSDag-Erling Smørgrav goto out; 840b15c8340SDag-Erling Smørgrav } 841190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 842b15c8340SDag-Erling Smørgrav out: 843e4a9863fSDag-Erling Smørgrav free(fwd_desc); 844b15c8340SDag-Erling Smørgrav if (freefwd) { 845e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 846a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 847e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 848a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 849b15c8340SDag-Erling Smørgrav } 850b15c8340SDag-Erling Smørgrav return ret; 851b15c8340SDag-Erling Smørgrav } 852b15c8340SDag-Erling Smørgrav 853b15c8340SDag-Erling Smørgrav static int 854*2f513db7SEd Maste mux_master_process_close_fwd(struct ssh *ssh, u_int rid, 855190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 856b15c8340SDag-Erling Smørgrav { 857a0ee8cc6SDag-Erling Smørgrav struct Forward fwd, *found_fwd; 858b15c8340SDag-Erling Smørgrav char *fwd_desc = NULL; 859462c32cbSDag-Erling Smørgrav const char *error_reason = NULL; 860a0ee8cc6SDag-Erling Smørgrav char *listen_addr = NULL, *connect_addr = NULL; 861b15c8340SDag-Erling Smørgrav u_int ftype; 862190cef3dSDag-Erling Smørgrav int r, i, ret = 0; 863e4a9863fSDag-Erling Smørgrav u_int lport, cport; 864b15c8340SDag-Erling Smørgrav 865fc1ba28aSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 866fc1ba28aSDag-Erling Smørgrav 867190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ftype)) != 0 || 868190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || 869190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &lport)) != 0 || 870190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || 871190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cport)) != 0 || 872a0ee8cc6SDag-Erling Smørgrav (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 873a0ee8cc6SDag-Erling Smørgrav (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 874b15c8340SDag-Erling Smørgrav error("%s: malformed message", __func__); 875b15c8340SDag-Erling Smørgrav ret = -1; 876b15c8340SDag-Erling Smørgrav goto out; 877b15c8340SDag-Erling Smørgrav } 878b15c8340SDag-Erling Smørgrav 879a0ee8cc6SDag-Erling Smørgrav if (*listen_addr == '\0') { 880a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 881a0ee8cc6SDag-Erling Smørgrav listen_addr = NULL; 882b15c8340SDag-Erling Smørgrav } 883a0ee8cc6SDag-Erling Smørgrav if (*connect_addr == '\0') { 884a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 885a0ee8cc6SDag-Erling Smørgrav connect_addr = NULL; 886b15c8340SDag-Erling Smørgrav } 887b15c8340SDag-Erling Smørgrav 888a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 889a0ee8cc6SDag-Erling Smørgrav fwd.listen_port = lport; 890a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port == PORT_STREAMLOCAL) 891a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = listen_addr; 892a0ee8cc6SDag-Erling Smørgrav else 893a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = listen_addr; 894a0ee8cc6SDag-Erling Smørgrav fwd.connect_port = cport; 895a0ee8cc6SDag-Erling Smørgrav if (fwd.connect_port == PORT_STREAMLOCAL) 896a0ee8cc6SDag-Erling Smørgrav fwd.connect_path = connect_addr; 897a0ee8cc6SDag-Erling Smørgrav else 898a0ee8cc6SDag-Erling Smørgrav fwd.connect_host = connect_addr; 899a0ee8cc6SDag-Erling Smørgrav 900462c32cbSDag-Erling Smørgrav debug2("%s: channel %d: request cancel %s", __func__, c->self, 901b15c8340SDag-Erling Smørgrav (fwd_desc = format_forward(ftype, &fwd))); 902b15c8340SDag-Erling Smørgrav 903462c32cbSDag-Erling Smørgrav /* make sure this has been requested */ 904462c32cbSDag-Erling Smørgrav found_fwd = NULL; 905462c32cbSDag-Erling Smørgrav switch (ftype) { 906462c32cbSDag-Erling Smørgrav case MUX_FWD_LOCAL: 907462c32cbSDag-Erling Smørgrav case MUX_FWD_DYNAMIC: 908462c32cbSDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 909462c32cbSDag-Erling Smørgrav if (compare_forward(&fwd, 910462c32cbSDag-Erling Smørgrav options.local_forwards + i)) { 911462c32cbSDag-Erling Smørgrav found_fwd = options.local_forwards + i; 912462c32cbSDag-Erling Smørgrav break; 913462c32cbSDag-Erling Smørgrav } 914462c32cbSDag-Erling Smørgrav } 915462c32cbSDag-Erling Smørgrav break; 916462c32cbSDag-Erling Smørgrav case MUX_FWD_REMOTE: 917462c32cbSDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 918462c32cbSDag-Erling Smørgrav if (compare_forward(&fwd, 919462c32cbSDag-Erling Smørgrav options.remote_forwards + i)) { 920462c32cbSDag-Erling Smørgrav found_fwd = options.remote_forwards + i; 921462c32cbSDag-Erling Smørgrav break; 922462c32cbSDag-Erling Smørgrav } 923462c32cbSDag-Erling Smørgrav } 924462c32cbSDag-Erling Smørgrav break; 925462c32cbSDag-Erling Smørgrav } 926462c32cbSDag-Erling Smørgrav 927462c32cbSDag-Erling Smørgrav if (found_fwd == NULL) 928462c32cbSDag-Erling Smørgrav error_reason = "port not forwarded"; 929462c32cbSDag-Erling Smørgrav else if (ftype == MUX_FWD_REMOTE) { 930462c32cbSDag-Erling Smørgrav /* 931462c32cbSDag-Erling Smørgrav * This shouldn't fail unless we confused the host/port 932462c32cbSDag-Erling Smørgrav * between options.remote_forwards and permitted_opens. 933462c32cbSDag-Erling Smørgrav * However, for dynamic allocated listen ports we need 934a0ee8cc6SDag-Erling Smørgrav * to use the actual listen port. 935462c32cbSDag-Erling Smørgrav */ 9364f52dfbbSDag-Erling Smørgrav if (channel_request_rforward_cancel(ssh, found_fwd) == -1) 937462c32cbSDag-Erling Smørgrav error_reason = "port not in permitted opens"; 938462c32cbSDag-Erling Smørgrav } else { /* local and dynamic forwards */ 939462c32cbSDag-Erling Smørgrav /* Ditto */ 9404f52dfbbSDag-Erling Smørgrav if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, 941a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) == -1) 942462c32cbSDag-Erling Smørgrav error_reason = "port not found"; 943462c32cbSDag-Erling Smørgrav } 944462c32cbSDag-Erling Smørgrav 945190cef3dSDag-Erling Smørgrav if (error_reason != NULL) 946190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, error_reason); 947190cef3dSDag-Erling Smørgrav else { 948190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 949e4a9863fSDag-Erling Smørgrav free(found_fwd->listen_host); 950a0ee8cc6SDag-Erling Smørgrav free(found_fwd->listen_path); 951e4a9863fSDag-Erling Smørgrav free(found_fwd->connect_host); 952a0ee8cc6SDag-Erling Smørgrav free(found_fwd->connect_path); 953462c32cbSDag-Erling Smørgrav found_fwd->listen_host = found_fwd->connect_host = NULL; 954a0ee8cc6SDag-Erling Smørgrav found_fwd->listen_path = found_fwd->connect_path = NULL; 955462c32cbSDag-Erling Smørgrav found_fwd->listen_port = found_fwd->connect_port = 0; 956462c32cbSDag-Erling Smørgrav } 957b15c8340SDag-Erling Smørgrav out: 958e4a9863fSDag-Erling Smørgrav free(fwd_desc); 959a0ee8cc6SDag-Erling Smørgrav free(listen_addr); 960a0ee8cc6SDag-Erling Smørgrav free(connect_addr); 961b15c8340SDag-Erling Smørgrav 962b15c8340SDag-Erling Smørgrav return ret; 963b15c8340SDag-Erling Smørgrav } 964b15c8340SDag-Erling Smørgrav 965b15c8340SDag-Erling Smørgrav static int 966*2f513db7SEd Maste mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid, 967190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 968b15c8340SDag-Erling Smørgrav { 969b15c8340SDag-Erling Smørgrav Channel *nc; 970190cef3dSDag-Erling Smørgrav char *chost = NULL; 971b15c8340SDag-Erling Smørgrav u_int cport, i, j; 972190cef3dSDag-Erling Smørgrav int r, new_fd[2]; 973a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx *cctx; 974b15c8340SDag-Erling Smørgrav 975190cef3dSDag-Erling Smørgrav if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ 976190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || 977190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &cport)) != 0) { 978e4a9863fSDag-Erling Smørgrav free(chost); 979b15c8340SDag-Erling Smørgrav error("%s: malformed message", __func__); 980b15c8340SDag-Erling Smørgrav return -1; 981b15c8340SDag-Erling Smørgrav } 982b15c8340SDag-Erling Smørgrav 983b15c8340SDag-Erling Smørgrav debug2("%s: channel %d: request stdio fwd to %s:%u", 984b15c8340SDag-Erling Smørgrav __func__, c->self, chost, cport); 985b15c8340SDag-Erling Smørgrav 986b15c8340SDag-Erling Smørgrav /* Gather fds from client */ 987b15c8340SDag-Erling Smørgrav for(i = 0; i < 2; i++) { 988b15c8340SDag-Erling Smørgrav if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 989b15c8340SDag-Erling Smørgrav error("%s: failed to receive fd %d from slave", 990b15c8340SDag-Erling Smørgrav __func__, i); 991b15c8340SDag-Erling Smørgrav for (j = 0; j < i; j++) 992b15c8340SDag-Erling Smørgrav close(new_fd[j]); 993e4a9863fSDag-Erling Smørgrav free(chost); 994b15c8340SDag-Erling Smørgrav 995b15c8340SDag-Erling Smørgrav /* prepare reply */ 996190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, rid, 997b15c8340SDag-Erling Smørgrav "did not receive file descriptors"); 998b15c8340SDag-Erling Smørgrav return -1; 999b15c8340SDag-Erling Smørgrav } 1000b15c8340SDag-Erling Smørgrav } 1001b15c8340SDag-Erling Smørgrav 1002b15c8340SDag-Erling Smørgrav debug3("%s: got fds stdin %d, stdout %d", __func__, 1003b15c8340SDag-Erling Smørgrav 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) { 1007b15c8340SDag-Erling Smørgrav debug2("%s: session already open", __func__); 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) { 10194a421b63SDag-Erling Smørgrav if (!ask_permission("Allow forward to %s:%u? ", 1020b15c8340SDag-Erling Smørgrav chost, cport)) { 1021b15c8340SDag-Erling Smørgrav debug2("%s: stdio fwd refused by user", __func__); 1022190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 1023190cef3dSDag-Erling Smørgrav "Permission denied"); 1024b15c8340SDag-Erling Smørgrav goto cleanup; 1025b15c8340SDag-Erling Smørgrav } 1026b15c8340SDag-Erling Smørgrav } 1027b15c8340SDag-Erling Smørgrav 1028b15c8340SDag-Erling Smørgrav /* enable nonblocking unless tty */ 1029b15c8340SDag-Erling Smørgrav if (!isatty(new_fd[0])) 1030b15c8340SDag-Erling Smørgrav set_nonblock(new_fd[0]); 1031b15c8340SDag-Erling Smørgrav if (!isatty(new_fd[1])) 1032b15c8340SDag-Erling Smørgrav set_nonblock(new_fd[1]); 1033b15c8340SDag-Erling Smørgrav 10344f52dfbbSDag-Erling Smørgrav nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1]); 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 1041b15c8340SDag-Erling Smørgrav debug2("%s: channel_new: %d linked to control channel %d", 1042b15c8340SDag-Erling Smørgrav __func__, nc->self, nc->ctl_chan); 1043b15c8340SDag-Erling Smørgrav 10444f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, nc->self, 10454f52dfbbSDag-Erling Smørgrav mux_master_session_cleanup_cb, 1); 1046b15c8340SDag-Erling Smørgrav 1047a0ee8cc6SDag-Erling Smørgrav cctx = xcalloc(1, sizeof(*cctx)); 1048a0ee8cc6SDag-Erling Smørgrav cctx->rid = rid; 10494f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); 1050a0ee8cc6SDag-Erling Smørgrav c->mux_pause = 1; /* stop handling messages until open_confirm done */ 1051b15c8340SDag-Erling Smørgrav 1052a0ee8cc6SDag-Erling Smørgrav /* reply is deferred, sent by mux_session_confirm */ 1053b15c8340SDag-Erling Smørgrav return 0; 1054b15c8340SDag-Erling Smørgrav } 1055b15c8340SDag-Erling Smørgrav 1056a0ee8cc6SDag-Erling Smørgrav /* Callback on open confirmation in mux master for a mux stdio fwd session. */ 1057a0ee8cc6SDag-Erling Smørgrav static void 10584f52dfbbSDag-Erling Smørgrav mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) 1059a0ee8cc6SDag-Erling Smørgrav { 1060a0ee8cc6SDag-Erling Smørgrav struct mux_stdio_confirm_ctx *cctx = arg; 1061a0ee8cc6SDag-Erling Smørgrav Channel *c, *cc; 1062190cef3dSDag-Erling Smørgrav struct sshbuf *reply; 1063190cef3dSDag-Erling Smørgrav int r; 1064a0ee8cc6SDag-Erling Smørgrav 1065a0ee8cc6SDag-Erling Smørgrav if (cctx == NULL) 1066a0ee8cc6SDag-Erling Smørgrav fatal("%s: cctx == NULL", __func__); 10674f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 1068a0ee8cc6SDag-Erling Smørgrav fatal("%s: no channel for id %d", __func__, id); 10694f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 1070a0ee8cc6SDag-Erling Smørgrav fatal("%s: channel %d lacks control channel %d", __func__, 1071a0ee8cc6SDag-Erling Smørgrav id, c->ctl_chan); 1072190cef3dSDag-Erling Smørgrav if ((reply = sshbuf_new()) == NULL) 1073190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1074a0ee8cc6SDag-Erling Smørgrav 1075a0ee8cc6SDag-Erling Smørgrav if (!success) { 1076a0ee8cc6SDag-Erling Smørgrav debug3("%s: sending failure reply", __func__); 1077190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, cctx->rid, 1078190cef3dSDag-Erling Smørgrav "Session open refused by peer"); 1079a0ee8cc6SDag-Erling Smørgrav /* prepare reply */ 1080a0ee8cc6SDag-Erling Smørgrav goto done; 1081a0ee8cc6SDag-Erling Smørgrav } 1082a0ee8cc6SDag-Erling Smørgrav 1083a0ee8cc6SDag-Erling Smørgrav debug3("%s: sending success reply", __func__); 1084a0ee8cc6SDag-Erling Smørgrav /* prepare reply */ 1085190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || 1086190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || 1087190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, c->self)) != 0) 1088190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1089a0ee8cc6SDag-Erling Smørgrav 1090a0ee8cc6SDag-Erling Smørgrav done: 1091a0ee8cc6SDag-Erling Smørgrav /* Send reply */ 1092190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) 1093190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", __func__, ssh_err(r)); 1094190cef3dSDag-Erling Smørgrav sshbuf_free(reply); 1095a0ee8cc6SDag-Erling Smørgrav 1096a0ee8cc6SDag-Erling Smørgrav if (cc->mux_pause <= 0) 1097a0ee8cc6SDag-Erling Smørgrav fatal("%s: mux_pause %d", __func__, cc->mux_pause); 1098a0ee8cc6SDag-Erling Smørgrav cc->mux_pause = 0; /* start processing messages again */ 1099a0ee8cc6SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 1100a0ee8cc6SDag-Erling Smørgrav free(cctx); 1101a0ee8cc6SDag-Erling Smørgrav } 1102a0ee8cc6SDag-Erling Smørgrav 1103e146993eSDag-Erling Smørgrav static int 1104*2f513db7SEd Maste mux_master_process_stop_listening(struct ssh *ssh, u_int rid, 1105190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 1106e146993eSDag-Erling Smørgrav { 1107e146993eSDag-Erling Smørgrav debug("%s: channel %d: stop listening", __func__, c->self); 1108e146993eSDag-Erling Smørgrav 1109e146993eSDag-Erling Smørgrav if (options.control_master == SSHCTL_MASTER_ASK || 1110e146993eSDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_AUTO_ASK) { 1111e146993eSDag-Erling Smørgrav if (!ask_permission("Disable further multiplexing on shared " 1112e146993eSDag-Erling Smørgrav "connection to %s? ", host)) { 1113e146993eSDag-Erling Smørgrav debug2("%s: stop listen refused by user", __func__); 1114190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_PERMISSION_DENIED, rid, 1115190cef3dSDag-Erling Smørgrav "Permission denied"); 1116e146993eSDag-Erling Smørgrav return 0; 1117e146993eSDag-Erling Smørgrav } 1118e146993eSDag-Erling Smørgrav } 1119e146993eSDag-Erling Smørgrav 1120e146993eSDag-Erling Smørgrav if (mux_listener_channel != NULL) { 11214f52dfbbSDag-Erling Smørgrav channel_free(ssh, mux_listener_channel); 1122e146993eSDag-Erling Smørgrav client_stop_mux(); 1123e4a9863fSDag-Erling Smørgrav free(options.control_path); 1124e146993eSDag-Erling Smørgrav options.control_path = NULL; 1125e146993eSDag-Erling Smørgrav mux_listener_channel = NULL; 1126e146993eSDag-Erling Smørgrav muxserver_sock = -1; 1127e146993eSDag-Erling Smørgrav } 1128e146993eSDag-Erling Smørgrav 1129190cef3dSDag-Erling Smørgrav reply_ok(reply, rid); 1130e146993eSDag-Erling Smørgrav return 0; 1131e146993eSDag-Erling Smørgrav } 1132e146993eSDag-Erling Smørgrav 1133ca86bcf2SDag-Erling Smørgrav static int 1134*2f513db7SEd Maste mux_master_process_proxy(struct ssh *ssh, u_int rid, 1135190cef3dSDag-Erling Smørgrav Channel *c, struct sshbuf *m, struct sshbuf *reply) 1136ca86bcf2SDag-Erling Smørgrav { 1137190cef3dSDag-Erling Smørgrav int r; 1138190cef3dSDag-Erling Smørgrav 1139ca86bcf2SDag-Erling Smørgrav debug("%s: channel %d: proxy request", __func__, c->self); 1140ca86bcf2SDag-Erling Smørgrav 1141ca86bcf2SDag-Erling Smørgrav c->mux_rcb = channel_proxy_downstream; 1142190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 || 1143190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, rid)) != 0) 1144190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1145ca86bcf2SDag-Erling Smørgrav 1146ca86bcf2SDag-Erling Smørgrav return 0; 1147ca86bcf2SDag-Erling Smørgrav } 1148ca86bcf2SDag-Erling Smørgrav 1149b15c8340SDag-Erling Smørgrav /* Channel callbacks fired on read/write from mux slave fd */ 1150b15c8340SDag-Erling Smørgrav static int 11514f52dfbbSDag-Erling Smørgrav mux_master_read_cb(struct ssh *ssh, Channel *c) 1152b15c8340SDag-Erling Smørgrav { 1153b15c8340SDag-Erling Smørgrav struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 1154190cef3dSDag-Erling Smørgrav struct sshbuf *in = NULL, *out = NULL; 1155190cef3dSDag-Erling Smørgrav u_int type, rid, i; 1156190cef3dSDag-Erling Smørgrav int r, ret = -1; 1157190cef3dSDag-Erling Smørgrav 1158190cef3dSDag-Erling Smørgrav if ((out = sshbuf_new()) == NULL) 1159190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1160b15c8340SDag-Erling Smørgrav 1161b15c8340SDag-Erling Smørgrav /* Setup ctx and */ 1162b15c8340SDag-Erling Smørgrav if (c->mux_ctx == NULL) { 1163e2f6069cSDag-Erling Smørgrav state = xcalloc(1, sizeof(*state)); 1164b15c8340SDag-Erling Smørgrav c->mux_ctx = state; 11654f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, c->self, 1166b15c8340SDag-Erling Smørgrav mux_master_control_cleanup_cb, 0); 1167b15c8340SDag-Erling Smørgrav 1168b15c8340SDag-Erling Smørgrav /* Send hello */ 1169190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || 1170190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) 1171190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1172b15c8340SDag-Erling Smørgrav /* no extensions */ 1173190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(c->output, out)) != 0) 1174190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", 1175190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1176b15c8340SDag-Erling Smørgrav debug3("%s: channel %d: hello sent", __func__, c->self); 1177190cef3dSDag-Erling Smørgrav ret = 0; 1178190cef3dSDag-Erling Smørgrav goto out; 1179b15c8340SDag-Erling Smørgrav } 1180b15c8340SDag-Erling Smørgrav 1181b15c8340SDag-Erling Smørgrav /* Channel code ensures that we receive whole packets */ 1182190cef3dSDag-Erling Smørgrav if ((r = sshbuf_froms(c->input, &in)) != 0) { 1183b15c8340SDag-Erling Smørgrav malf: 1184b15c8340SDag-Erling Smørgrav error("%s: malformed message", __func__); 1185b15c8340SDag-Erling Smørgrav goto out; 1186b15c8340SDag-Erling Smørgrav } 1187b15c8340SDag-Erling Smørgrav 1188190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(in, &type)) != 0) 1189b15c8340SDag-Erling Smørgrav goto malf; 1190190cef3dSDag-Erling Smørgrav debug3("%s: channel %d packet type 0x%08x len %zu", 1191190cef3dSDag-Erling Smørgrav __func__, c->self, type, sshbuf_len(in)); 1192b15c8340SDag-Erling Smørgrav 1193b15c8340SDag-Erling Smørgrav if (type == MUX_MSG_HELLO) 1194b15c8340SDag-Erling Smørgrav rid = 0; 1195b15c8340SDag-Erling Smørgrav else { 1196b15c8340SDag-Erling Smørgrav if (!state->hello_rcvd) { 1197b15c8340SDag-Erling Smørgrav error("%s: expected MUX_MSG_HELLO(0x%08x), " 1198b15c8340SDag-Erling Smørgrav "received 0x%08x", __func__, MUX_MSG_HELLO, type); 1199b15c8340SDag-Erling Smørgrav goto out; 1200b15c8340SDag-Erling Smørgrav } 1201190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(in, &rid)) != 0) 1202b15c8340SDag-Erling Smørgrav goto malf; 1203b15c8340SDag-Erling Smørgrav } 1204b15c8340SDag-Erling Smørgrav 1205b15c8340SDag-Erling Smørgrav for (i = 0; mux_master_handlers[i].handler != NULL; i++) { 1206b15c8340SDag-Erling Smørgrav if (type == mux_master_handlers[i].type) { 12074f52dfbbSDag-Erling Smørgrav ret = mux_master_handlers[i].handler(ssh, rid, 1208190cef3dSDag-Erling Smørgrav c, in, out); 1209b15c8340SDag-Erling Smørgrav break; 1210b15c8340SDag-Erling Smørgrav } 1211b15c8340SDag-Erling Smørgrav } 1212b15c8340SDag-Erling Smørgrav if (mux_master_handlers[i].handler == NULL) { 1213b15c8340SDag-Erling Smørgrav error("%s: unsupported mux message 0x%08x", __func__, type); 1214190cef3dSDag-Erling Smørgrav reply_error(out, MUX_S_FAILURE, rid, "unsupported request"); 1215b15c8340SDag-Erling Smørgrav ret = 0; 1216b15c8340SDag-Erling Smørgrav } 1217b15c8340SDag-Erling Smørgrav /* Enqueue reply packet */ 1218190cef3dSDag-Erling Smørgrav if (sshbuf_len(out) != 0) { 1219190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(c->output, out)) != 0) 1220190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", 1221190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1222b15c8340SDag-Erling Smørgrav } 1223b15c8340SDag-Erling Smørgrav out: 1224190cef3dSDag-Erling Smørgrav sshbuf_free(in); 1225190cef3dSDag-Erling Smørgrav sshbuf_free(out); 1226b15c8340SDag-Erling Smørgrav return ret; 1227b15c8340SDag-Erling Smørgrav } 1228b15c8340SDag-Erling Smørgrav 1229b15c8340SDag-Erling Smørgrav void 12304f52dfbbSDag-Erling Smørgrav mux_exit_message(struct ssh *ssh, Channel *c, int exitval) 1231b15c8340SDag-Erling Smørgrav { 1232190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1233b15c8340SDag-Erling Smørgrav Channel *mux_chan; 1234190cef3dSDag-Erling Smørgrav int r; 1235b15c8340SDag-Erling Smørgrav 1236e4a9863fSDag-Erling Smørgrav debug3("%s: channel %d: exit message, exitval %d", __func__, c->self, 1237b15c8340SDag-Erling Smørgrav exitval); 1238b15c8340SDag-Erling Smørgrav 12394f52dfbbSDag-Erling Smørgrav if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 1240b15c8340SDag-Erling Smørgrav fatal("%s: channel %d missing mux channel %d", 1241b15c8340SDag-Erling Smørgrav __func__, c->self, c->ctl_chan); 1242b15c8340SDag-Erling Smørgrav 1243b15c8340SDag-Erling Smørgrav /* Append exit message packet to control socket output queue */ 1244190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1245190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1246190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 || 1247190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, c->self)) != 0 || 1248190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, exitval)) != 0 || 1249190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) 1250190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1251190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1252b15c8340SDag-Erling Smørgrav } 1253d4af9e69SDag-Erling Smørgrav 1254e146993eSDag-Erling Smørgrav void 12554f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(struct ssh *ssh, Channel *c) 1256e146993eSDag-Erling Smørgrav { 1257190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1258e146993eSDag-Erling Smørgrav Channel *mux_chan; 1259190cef3dSDag-Erling Smørgrav int r; 1260e146993eSDag-Erling Smørgrav 1261e146993eSDag-Erling Smørgrav debug3("%s: channel %d: TTY alloc failed", __func__, c->self); 1262e146993eSDag-Erling Smørgrav 12634f52dfbbSDag-Erling Smørgrav if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 1264e146993eSDag-Erling Smørgrav fatal("%s: channel %d missing mux channel %d", 1265e146993eSDag-Erling Smørgrav __func__, c->self, c->ctl_chan); 1266e146993eSDag-Erling Smørgrav 1267e146993eSDag-Erling Smørgrav /* Append exit message packet to control socket output queue */ 1268190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1269190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1270190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 || 1271190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, c->self)) != 0 || 1272190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) 1273190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1274190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1275e146993eSDag-Erling Smørgrav } 1276e146993eSDag-Erling Smørgrav 1277d4af9e69SDag-Erling Smørgrav /* Prepare a mux master to listen on a Unix domain socket. */ 1278d4af9e69SDag-Erling Smørgrav void 12794f52dfbbSDag-Erling Smørgrav muxserver_listen(struct ssh *ssh) 1280d4af9e69SDag-Erling Smørgrav { 1281d4af9e69SDag-Erling Smørgrav mode_t old_umask; 12824a421b63SDag-Erling Smørgrav char *orig_control_path = options.control_path; 12834a421b63SDag-Erling Smørgrav char rbuf[16+1]; 12844a421b63SDag-Erling Smørgrav u_int i, r; 1285a0ee8cc6SDag-Erling Smørgrav int oerrno; 1286d4af9e69SDag-Erling Smørgrav 1287d4af9e69SDag-Erling Smørgrav if (options.control_path == NULL || 1288d4af9e69SDag-Erling Smørgrav options.control_master == SSHCTL_MASTER_NO) 1289d4af9e69SDag-Erling Smørgrav return; 1290d4af9e69SDag-Erling Smørgrav 1291d4af9e69SDag-Erling Smørgrav debug("setting up multiplex master socket"); 1292d4af9e69SDag-Erling Smørgrav 12934a421b63SDag-Erling Smørgrav /* 12944a421b63SDag-Erling Smørgrav * Use a temporary path before listen so we can pseudo-atomically 12954a421b63SDag-Erling Smørgrav * establish the listening socket in its final location to avoid 12964a421b63SDag-Erling Smørgrav * other processes racing in between bind() and listen() and hitting 12974a421b63SDag-Erling Smørgrav * an unready socket. 12984a421b63SDag-Erling Smørgrav */ 12994a421b63SDag-Erling Smørgrav for (i = 0; i < sizeof(rbuf) - 1; i++) { 13004a421b63SDag-Erling Smørgrav r = arc4random_uniform(26+26+10); 13014a421b63SDag-Erling Smørgrav rbuf[i] = (r < 26) ? 'a' + r : 13024a421b63SDag-Erling Smørgrav (r < 26*2) ? 'A' + r - 26 : 13034a421b63SDag-Erling Smørgrav '0' + r - 26 - 26; 13044a421b63SDag-Erling Smørgrav } 13054a421b63SDag-Erling Smørgrav rbuf[sizeof(rbuf) - 1] = '\0'; 13064a421b63SDag-Erling Smørgrav options.control_path = NULL; 13074a421b63SDag-Erling Smørgrav xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); 13084a421b63SDag-Erling Smørgrav debug3("%s: temporary control path %s", __func__, options.control_path); 13094a421b63SDag-Erling Smørgrav 1310d4af9e69SDag-Erling Smørgrav old_umask = umask(0177); 1311a0ee8cc6SDag-Erling Smørgrav muxserver_sock = unix_listener(options.control_path, 64, 0); 1312a0ee8cc6SDag-Erling Smørgrav oerrno = errno; 1313a0ee8cc6SDag-Erling Smørgrav umask(old_umask); 1314a0ee8cc6SDag-Erling Smørgrav if (muxserver_sock < 0) { 1315a0ee8cc6SDag-Erling Smørgrav if (oerrno == EINVAL || oerrno == EADDRINUSE) { 1316d4af9e69SDag-Erling Smørgrav error("ControlSocket %s already exists, " 1317d4af9e69SDag-Erling Smørgrav "disabling multiplexing", options.control_path); 13184a421b63SDag-Erling Smørgrav disable_mux_master: 1319e146993eSDag-Erling Smørgrav if (muxserver_sock != -1) { 1320d4af9e69SDag-Erling Smørgrav close(muxserver_sock); 1321d4af9e69SDag-Erling Smørgrav muxserver_sock = -1; 1322e146993eSDag-Erling Smørgrav } 1323e4a9863fSDag-Erling Smørgrav free(orig_control_path); 1324e4a9863fSDag-Erling Smørgrav free(options.control_path); 1325d4af9e69SDag-Erling Smørgrav options.control_path = NULL; 1326d4af9e69SDag-Erling Smørgrav options.control_master = SSHCTL_MASTER_NO; 1327d4af9e69SDag-Erling Smørgrav return; 1328a0ee8cc6SDag-Erling Smørgrav } else { 1329a0ee8cc6SDag-Erling Smørgrav /* unix_listener() logs the error */ 1330a0ee8cc6SDag-Erling Smørgrav cleanup_exit(255); 1331d4af9e69SDag-Erling Smørgrav } 1332a0ee8cc6SDag-Erling Smørgrav } 1333d4af9e69SDag-Erling Smørgrav 13344a421b63SDag-Erling Smørgrav /* Now atomically "move" the mux socket into position */ 13354a421b63SDag-Erling Smørgrav if (link(options.control_path, orig_control_path) != 0) { 13364a421b63SDag-Erling Smørgrav if (errno != EEXIST) { 13374a421b63SDag-Erling Smørgrav fatal("%s: link mux listener %s => %s: %s", __func__, 13384a421b63SDag-Erling Smørgrav options.control_path, orig_control_path, 13394a421b63SDag-Erling Smørgrav strerror(errno)); 13404a421b63SDag-Erling Smørgrav } 13414a421b63SDag-Erling Smørgrav error("ControlSocket %s already exists, disabling multiplexing", 13424a421b63SDag-Erling Smørgrav orig_control_path); 13434a421b63SDag-Erling Smørgrav unlink(options.control_path); 13444a421b63SDag-Erling Smørgrav goto disable_mux_master; 13454a421b63SDag-Erling Smørgrav } 13464a421b63SDag-Erling Smørgrav unlink(options.control_path); 1347e4a9863fSDag-Erling Smørgrav free(options.control_path); 13484a421b63SDag-Erling Smørgrav options.control_path = orig_control_path; 13494a421b63SDag-Erling Smørgrav 1350d4af9e69SDag-Erling Smørgrav set_nonblock(muxserver_sock); 1351b15c8340SDag-Erling Smørgrav 13524f52dfbbSDag-Erling Smørgrav mux_listener_channel = channel_new(ssh, "mux listener", 1353b15c8340SDag-Erling Smørgrav SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, 1354b15c8340SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 13554a421b63SDag-Erling Smørgrav 0, options.control_path, 1); 1356b15c8340SDag-Erling Smørgrav mux_listener_channel->mux_rcb = mux_master_read_cb; 1357b15c8340SDag-Erling Smørgrav debug3("%s: mux listener channel %d fd %d", __func__, 1358b15c8340SDag-Erling Smørgrav mux_listener_channel->self, mux_listener_channel->sock); 1359d4af9e69SDag-Erling Smørgrav } 1360d4af9e69SDag-Erling Smørgrav 1361d4af9e69SDag-Erling Smørgrav /* Callback on open confirmation in mux master for a mux client session. */ 1362d4af9e69SDag-Erling Smørgrav static void 13634f52dfbbSDag-Erling Smørgrav mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) 1364d4af9e69SDag-Erling Smørgrav { 1365d4af9e69SDag-Erling Smørgrav struct mux_session_confirm_ctx *cctx = arg; 1366d4af9e69SDag-Erling Smørgrav const char *display; 1367e2f6069cSDag-Erling Smørgrav Channel *c, *cc; 1368190cef3dSDag-Erling Smørgrav int i, r; 1369190cef3dSDag-Erling Smørgrav struct sshbuf *reply; 1370d4af9e69SDag-Erling Smørgrav 1371d4af9e69SDag-Erling Smørgrav if (cctx == NULL) 1372d4af9e69SDag-Erling Smørgrav fatal("%s: cctx == NULL", __func__); 13734f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 1374d4af9e69SDag-Erling Smørgrav fatal("%s: no channel for id %d", __func__, id); 13754f52dfbbSDag-Erling Smørgrav if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 1376e2f6069cSDag-Erling Smørgrav fatal("%s: channel %d lacks control channel %d", __func__, 1377e2f6069cSDag-Erling Smørgrav id, c->ctl_chan); 1378190cef3dSDag-Erling Smørgrav if ((reply = sshbuf_new()) == NULL) 1379190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1380e2f6069cSDag-Erling Smørgrav 1381e2f6069cSDag-Erling Smørgrav if (!success) { 1382e2f6069cSDag-Erling Smørgrav debug3("%s: sending failure reply", __func__); 1383190cef3dSDag-Erling Smørgrav reply_error(reply, MUX_S_FAILURE, cctx->rid, 1384190cef3dSDag-Erling Smørgrav "Session open refused by peer"); 1385e2f6069cSDag-Erling Smørgrav goto done; 1386e2f6069cSDag-Erling Smørgrav } 1387d4af9e69SDag-Erling Smørgrav 1388d4af9e69SDag-Erling Smørgrav display = getenv("DISPLAY"); 1389d4af9e69SDag-Erling Smørgrav if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { 1390d4af9e69SDag-Erling Smørgrav char *proto, *data; 1391e2f6069cSDag-Erling Smørgrav 1392d4af9e69SDag-Erling Smørgrav /* Get reasonable local authentication information. */ 13934f52dfbbSDag-Erling Smørgrav if (client_x11_get_proto(ssh, display, options.xauth_location, 1394e2f6069cSDag-Erling Smørgrav options.forward_x11_trusted, options.forward_x11_timeout, 1395acc1a9efSDag-Erling Smørgrav &proto, &data) == 0) { 1396d4af9e69SDag-Erling Smørgrav /* Request forwarding with authentication spoofing. */ 1397e2f6069cSDag-Erling Smørgrav debug("Requesting X11 forwarding with authentication " 1398e2f6069cSDag-Erling Smørgrav "spoofing."); 13994f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(ssh, id, 14004f52dfbbSDag-Erling Smørgrav display, proto, data, 1); 1401e146993eSDag-Erling Smørgrav /* XXX exit_on_forward_failure */ 14024f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "X11 forwarding", 1403acc1a9efSDag-Erling Smørgrav CONFIRM_WARN); 1404acc1a9efSDag-Erling Smørgrav } 1405d4af9e69SDag-Erling Smørgrav } 1406d4af9e69SDag-Erling Smørgrav 1407d4af9e69SDag-Erling Smørgrav if (cctx->want_agent_fwd && options.forward_agent) { 1408d4af9e69SDag-Erling Smørgrav debug("Requesting authentication agent forwarding."); 14094f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); 1410d4af9e69SDag-Erling Smørgrav packet_send(); 1411d4af9e69SDag-Erling Smørgrav } 1412d4af9e69SDag-Erling Smørgrav 14134f52dfbbSDag-Erling Smørgrav client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, 1414190cef3dSDag-Erling Smørgrav cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env); 1415d4af9e69SDag-Erling Smørgrav 1416e2f6069cSDag-Erling Smørgrav debug3("%s: sending success reply", __func__); 1417e2f6069cSDag-Erling Smørgrav /* prepare reply */ 1418190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || 1419190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || 1420190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(reply, c->self)) != 0) 1421190cef3dSDag-Erling Smørgrav fatal("%s: reply: %s", __func__, ssh_err(r)); 1422e2f6069cSDag-Erling Smørgrav 1423e2f6069cSDag-Erling Smørgrav done: 1424e2f6069cSDag-Erling Smørgrav /* Send reply */ 1425190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) 1426190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", __func__, ssh_err(r)); 1427190cef3dSDag-Erling Smørgrav sshbuf_free(reply); 1428e2f6069cSDag-Erling Smørgrav 1429e2f6069cSDag-Erling Smørgrav if (cc->mux_pause <= 0) 1430e2f6069cSDag-Erling Smørgrav fatal("%s: mux_pause %d", __func__, cc->mux_pause); 1431e2f6069cSDag-Erling Smørgrav cc->mux_pause = 0; /* start processing messages again */ 1432d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 1433190cef3dSDag-Erling Smørgrav sshbuf_free(cctx->cmd); 1434e4a9863fSDag-Erling Smørgrav free(cctx->term); 1435d4af9e69SDag-Erling Smørgrav if (cctx->env != NULL) { 1436d4af9e69SDag-Erling Smørgrav for (i = 0; cctx->env[i] != NULL; i++) 1437e4a9863fSDag-Erling Smørgrav free(cctx->env[i]); 1438e4a9863fSDag-Erling Smørgrav free(cctx->env); 1439d4af9e69SDag-Erling Smørgrav } 1440e4a9863fSDag-Erling Smørgrav free(cctx); 1441d4af9e69SDag-Erling Smørgrav } 1442d4af9e69SDag-Erling Smørgrav 1443d4af9e69SDag-Erling Smørgrav /* ** Multiplexing client support */ 1444d4af9e69SDag-Erling Smørgrav 1445d4af9e69SDag-Erling Smørgrav /* Exit signal handler */ 1446d4af9e69SDag-Erling Smørgrav static void 1447d4af9e69SDag-Erling Smørgrav control_client_sighandler(int signo) 1448d4af9e69SDag-Erling Smørgrav { 1449d4af9e69SDag-Erling Smørgrav muxclient_terminate = signo; 1450d4af9e69SDag-Erling Smørgrav } 1451d4af9e69SDag-Erling Smørgrav 1452d4af9e69SDag-Erling Smørgrav /* 1453d4af9e69SDag-Erling Smørgrav * Relay signal handler - used to pass some signals from mux client to 1454d4af9e69SDag-Erling Smørgrav * mux master. 1455d4af9e69SDag-Erling Smørgrav */ 1456d4af9e69SDag-Erling Smørgrav static void 1457d4af9e69SDag-Erling Smørgrav control_client_sigrelay(int signo) 1458d4af9e69SDag-Erling Smørgrav { 1459d4af9e69SDag-Erling Smørgrav int save_errno = errno; 1460d4af9e69SDag-Erling Smørgrav 1461d4af9e69SDag-Erling Smørgrav if (muxserver_pid > 1) 1462d4af9e69SDag-Erling Smørgrav kill(muxserver_pid, signo); 1463d4af9e69SDag-Erling Smørgrav 1464d4af9e69SDag-Erling Smørgrav errno = save_errno; 1465d4af9e69SDag-Erling Smørgrav } 1466d4af9e69SDag-Erling Smørgrav 1467d4af9e69SDag-Erling Smørgrav static int 1468190cef3dSDag-Erling Smørgrav mux_client_read(int fd, struct sshbuf *b, size_t need) 1469d4af9e69SDag-Erling Smørgrav { 1470190cef3dSDag-Erling Smørgrav size_t have; 1471b15c8340SDag-Erling Smørgrav ssize_t len; 1472b15c8340SDag-Erling Smørgrav u_char *p; 1473b15c8340SDag-Erling Smørgrav struct pollfd pfd; 1474190cef3dSDag-Erling Smørgrav int r; 1475d4af9e69SDag-Erling Smørgrav 1476b15c8340SDag-Erling Smørgrav pfd.fd = fd; 1477b15c8340SDag-Erling Smørgrav pfd.events = POLLIN; 1478190cef3dSDag-Erling Smørgrav if ((r = sshbuf_reserve(b, need, &p)) != 0) 1479190cef3dSDag-Erling Smørgrav fatal("%s: reserve: %s", __func__, ssh_err(r)); 1480b15c8340SDag-Erling Smørgrav for (have = 0; have < need; ) { 1481b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 1482b15c8340SDag-Erling Smørgrav errno = EINTR; 1483b15c8340SDag-Erling Smørgrav return -1; 1484b15c8340SDag-Erling Smørgrav } 1485b15c8340SDag-Erling Smørgrav len = read(fd, p + have, need - have); 1486b15c8340SDag-Erling Smørgrav if (len < 0) { 1487b15c8340SDag-Erling Smørgrav switch (errno) { 1488b15c8340SDag-Erling Smørgrav #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1489b15c8340SDag-Erling Smørgrav case EWOULDBLOCK: 1490b15c8340SDag-Erling Smørgrav #endif 1491b15c8340SDag-Erling Smørgrav case EAGAIN: 1492b15c8340SDag-Erling Smørgrav (void)poll(&pfd, 1, -1); 1493b15c8340SDag-Erling Smørgrav /* FALLTHROUGH */ 1494b15c8340SDag-Erling Smørgrav case EINTR: 1495b15c8340SDag-Erling Smørgrav continue; 1496b15c8340SDag-Erling Smørgrav default: 1497b15c8340SDag-Erling Smørgrav return -1; 1498b15c8340SDag-Erling Smørgrav } 1499b15c8340SDag-Erling Smørgrav } 1500b15c8340SDag-Erling Smørgrav if (len == 0) { 1501b15c8340SDag-Erling Smørgrav errno = EPIPE; 1502b15c8340SDag-Erling Smørgrav return -1; 1503b15c8340SDag-Erling Smørgrav } 1504190cef3dSDag-Erling Smørgrav have += (size_t)len; 1505b15c8340SDag-Erling Smørgrav } 1506b15c8340SDag-Erling Smørgrav return 0; 1507b15c8340SDag-Erling Smørgrav } 1508d4af9e69SDag-Erling Smørgrav 1509b15c8340SDag-Erling Smørgrav static int 1510190cef3dSDag-Erling Smørgrav mux_client_write_packet(int fd, struct sshbuf *m) 1511b15c8340SDag-Erling Smørgrav { 1512190cef3dSDag-Erling Smørgrav struct sshbuf *queue; 1513b15c8340SDag-Erling Smørgrav u_int have, need; 1514190cef3dSDag-Erling Smørgrav int r, oerrno, len; 1515190cef3dSDag-Erling Smørgrav const u_char *ptr; 1516b15c8340SDag-Erling Smørgrav struct pollfd pfd; 1517d4af9e69SDag-Erling Smørgrav 1518b15c8340SDag-Erling Smørgrav pfd.fd = fd; 1519b15c8340SDag-Erling Smørgrav pfd.events = POLLOUT; 1520190cef3dSDag-Erling Smørgrav if ((queue = sshbuf_new()) == NULL) 1521190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1522190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(queue, m)) != 0) 1523190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_put_stringb: %s", __func__, ssh_err(r)); 1524b15c8340SDag-Erling Smørgrav 1525190cef3dSDag-Erling Smørgrav need = sshbuf_len(queue); 1526190cef3dSDag-Erling Smørgrav ptr = sshbuf_ptr(queue); 1527b15c8340SDag-Erling Smørgrav 1528b15c8340SDag-Erling Smørgrav for (have = 0; have < need; ) { 1529b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 1530190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1531b15c8340SDag-Erling Smørgrav errno = EINTR; 1532b15c8340SDag-Erling Smørgrav return -1; 1533b15c8340SDag-Erling Smørgrav } 1534b15c8340SDag-Erling Smørgrav len = write(fd, ptr + have, need - have); 1535b15c8340SDag-Erling Smørgrav if (len < 0) { 1536b15c8340SDag-Erling Smørgrav switch (errno) { 1537b15c8340SDag-Erling Smørgrav #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1538b15c8340SDag-Erling Smørgrav case EWOULDBLOCK: 1539b15c8340SDag-Erling Smørgrav #endif 1540b15c8340SDag-Erling Smørgrav case EAGAIN: 1541b15c8340SDag-Erling Smørgrav (void)poll(&pfd, 1, -1); 1542b15c8340SDag-Erling Smørgrav /* FALLTHROUGH */ 1543b15c8340SDag-Erling Smørgrav case EINTR: 1544b15c8340SDag-Erling Smørgrav continue; 1545b15c8340SDag-Erling Smørgrav default: 1546b15c8340SDag-Erling Smørgrav oerrno = errno; 1547190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1548b15c8340SDag-Erling Smørgrav errno = oerrno; 1549b15c8340SDag-Erling Smørgrav return -1; 1550b15c8340SDag-Erling Smørgrav } 1551b15c8340SDag-Erling Smørgrav } 1552b15c8340SDag-Erling Smørgrav if (len == 0) { 1553190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1554b15c8340SDag-Erling Smørgrav errno = EPIPE; 1555b15c8340SDag-Erling Smørgrav return -1; 1556b15c8340SDag-Erling Smørgrav } 1557b15c8340SDag-Erling Smørgrav have += (u_int)len; 1558b15c8340SDag-Erling Smørgrav } 1559190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1560b15c8340SDag-Erling Smørgrav return 0; 1561b15c8340SDag-Erling Smørgrav } 1562b15c8340SDag-Erling Smørgrav 1563b15c8340SDag-Erling Smørgrav static int 1564190cef3dSDag-Erling Smørgrav mux_client_read_packet(int fd, struct sshbuf *m) 1565b15c8340SDag-Erling Smørgrav { 1566190cef3dSDag-Erling Smørgrav struct sshbuf *queue; 1567190cef3dSDag-Erling Smørgrav size_t need, have; 1568a0ee8cc6SDag-Erling Smørgrav const u_char *ptr; 1569190cef3dSDag-Erling Smørgrav int r, oerrno; 1570b15c8340SDag-Erling Smørgrav 1571190cef3dSDag-Erling Smørgrav if ((queue = sshbuf_new()) == NULL) 1572190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1573190cef3dSDag-Erling Smørgrav if (mux_client_read(fd, queue, 4) != 0) { 1574b15c8340SDag-Erling Smørgrav if ((oerrno = errno) == EPIPE) 1575e4a9863fSDag-Erling Smørgrav debug3("%s: read header failed: %s", __func__, 1576e4a9863fSDag-Erling Smørgrav strerror(errno)); 1577190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1578b15c8340SDag-Erling Smørgrav errno = oerrno; 1579b15c8340SDag-Erling Smørgrav return -1; 1580b15c8340SDag-Erling Smørgrav } 1581190cef3dSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(queue)); 1582190cef3dSDag-Erling Smørgrav if (mux_client_read(fd, queue, need) != 0) { 1583b15c8340SDag-Erling Smørgrav oerrno = errno; 1584b15c8340SDag-Erling Smørgrav debug3("%s: read body failed: %s", __func__, strerror(errno)); 1585190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1586b15c8340SDag-Erling Smørgrav errno = oerrno; 1587b15c8340SDag-Erling Smørgrav return -1; 1588b15c8340SDag-Erling Smørgrav } 1589190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 || 1590190cef3dSDag-Erling Smørgrav (r = sshbuf_put(m, ptr, have)) != 0) 1591190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1592190cef3dSDag-Erling Smørgrav sshbuf_free(queue); 1593b15c8340SDag-Erling Smørgrav return 0; 1594b15c8340SDag-Erling Smørgrav } 1595b15c8340SDag-Erling Smørgrav 1596b15c8340SDag-Erling Smørgrav static int 1597b15c8340SDag-Erling Smørgrav mux_client_hello_exchange(int fd) 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) 1604190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 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) 1607190cef3dSDag-Erling Smørgrav fatal("%s: hello: %s", __func__, ssh_err(r)); 1608b15c8340SDag-Erling Smørgrav /* no extensions */ 1609b15c8340SDag-Erling Smørgrav 1610190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) { 16114f52dfbbSDag-Erling Smørgrav debug("%s: write packet: %s", __func__, 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 */ 1618190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 16194f52dfbbSDag-Erling Smørgrav debug("%s: read packet failed", __func__); 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) 1624190cef3dSDag-Erling Smørgrav fatal("%s: decode type: %s", __func__, ssh_err(r)); 16254f52dfbbSDag-Erling Smørgrav if (type != MUX_MSG_HELLO) { 16264f52dfbbSDag-Erling Smørgrav error("%s: expected HELLO (%u) received %u", 1627b15c8340SDag-Erling Smørgrav __func__, MUX_MSG_HELLO, type); 16284f52dfbbSDag-Erling Smørgrav goto out; 16294f52dfbbSDag-Erling Smørgrav } 1630190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &ver)) != 0) 1631190cef3dSDag-Erling Smørgrav fatal("%s: decode version: %s", __func__, ssh_err(r)); 16324f52dfbbSDag-Erling Smørgrav if (ver != SSHMUX_VER) { 16334f52dfbbSDag-Erling Smørgrav error("Unsupported multiplexing protocol version %d " 1634b15c8340SDag-Erling Smørgrav "(expected %d)", ver, SSHMUX_VER); 16354f52dfbbSDag-Erling Smørgrav goto out; 16364f52dfbbSDag-Erling Smørgrav } 1637b15c8340SDag-Erling Smørgrav debug2("%s: master version %u", __func__, ver); 1638b15c8340SDag-Erling Smørgrav /* No extensions are presently defined */ 1639190cef3dSDag-Erling Smørgrav while (sshbuf_len(m) > 0) { 1640190cef3dSDag-Erling Smørgrav char *name = NULL; 1641b15c8340SDag-Erling Smørgrav 1642190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || 1643190cef3dSDag-Erling Smørgrav (r = sshbuf_skip_string(m)) != 0) { /* value */ 1644190cef3dSDag-Erling Smørgrav error("%s: malformed extension: %s", 1645190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1646190cef3dSDag-Erling Smørgrav goto out; 1647190cef3dSDag-Erling Smørgrav } 1648b15c8340SDag-Erling Smørgrav debug2("Unrecognised master extension \"%s\"", name); 1649e4a9863fSDag-Erling Smørgrav free(name); 1650b15c8340SDag-Erling Smørgrav } 16514f52dfbbSDag-Erling Smørgrav /* success */ 16524f52dfbbSDag-Erling Smørgrav ret = 0; 16534f52dfbbSDag-Erling Smørgrav out: 1654190cef3dSDag-Erling Smørgrav sshbuf_free(m); 16554f52dfbbSDag-Erling Smørgrav return ret; 1656b15c8340SDag-Erling Smørgrav } 1657b15c8340SDag-Erling Smørgrav 1658b15c8340SDag-Erling Smørgrav static u_int 1659b15c8340SDag-Erling Smørgrav mux_client_request_alive(int fd) 1660b15c8340SDag-Erling Smørgrav { 1661190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1662b15c8340SDag-Erling Smørgrav char *e; 1663b15c8340SDag-Erling Smørgrav u_int pid, type, rid; 1664190cef3dSDag-Erling Smørgrav int r; 1665b15c8340SDag-Erling Smørgrav 1666b15c8340SDag-Erling Smørgrav debug3("%s: entering", __func__); 1667b15c8340SDag-Erling Smørgrav 1668190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1669190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1670190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 || 1671190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 1672190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1673b15c8340SDag-Erling Smørgrav 1674190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 1675b15c8340SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 1676b15c8340SDag-Erling Smørgrav 1677190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1678b15c8340SDag-Erling Smørgrav 1679b15c8340SDag-Erling Smørgrav /* Read their reply */ 1680190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1681190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1682b15c8340SDag-Erling Smørgrav return 0; 1683b15c8340SDag-Erling Smørgrav } 1684b15c8340SDag-Erling Smørgrav 1685190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0) 1686190cef3dSDag-Erling Smørgrav fatal("%s: decode type: %s", __func__, ssh_err(r)); 1687b15c8340SDag-Erling Smørgrav if (type != MUX_S_ALIVE) { 1688190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1689190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 1690b15c8340SDag-Erling Smørgrav fatal("%s: master returned error: %s", __func__, e); 1691b15c8340SDag-Erling Smørgrav } 1692b15c8340SDag-Erling Smørgrav 1693190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &rid)) != 0) 1694190cef3dSDag-Erling Smørgrav fatal("%s: decode remote ID: %s", __func__, ssh_err(r)); 1695190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 1696b15c8340SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 1697b15c8340SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 1698190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &pid)) != 0) 1699190cef3dSDag-Erling Smørgrav fatal("%s: decode PID: %s", __func__, ssh_err(r)); 1700190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1701b15c8340SDag-Erling Smørgrav 1702b15c8340SDag-Erling Smørgrav debug3("%s: done pid = %u", __func__, pid); 1703b15c8340SDag-Erling Smørgrav 1704b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1705b15c8340SDag-Erling Smørgrav 1706b15c8340SDag-Erling Smørgrav return pid; 1707b15c8340SDag-Erling Smørgrav } 1708b15c8340SDag-Erling Smørgrav 1709b15c8340SDag-Erling Smørgrav static void 1710b15c8340SDag-Erling Smørgrav mux_client_request_terminate(int fd) 1711b15c8340SDag-Erling Smørgrav { 1712190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1713b15c8340SDag-Erling Smørgrav char *e; 1714b15c8340SDag-Erling Smørgrav u_int type, rid; 1715190cef3dSDag-Erling Smørgrav int r; 1716b15c8340SDag-Erling Smørgrav 1717b15c8340SDag-Erling Smørgrav debug3("%s: entering", __func__); 1718b15c8340SDag-Erling Smørgrav 1719190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1720190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1721190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 || 1722190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 1723190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1724b15c8340SDag-Erling Smørgrav 1725190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 1726b15c8340SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 1727b15c8340SDag-Erling Smørgrav 1728190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1729b15c8340SDag-Erling Smørgrav 1730b15c8340SDag-Erling Smørgrav /* Read their reply */ 1731190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1732b15c8340SDag-Erling Smørgrav /* Remote end exited already */ 1733b15c8340SDag-Erling Smørgrav if (errno == EPIPE) { 1734190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1735b15c8340SDag-Erling Smørgrav return; 1736b15c8340SDag-Erling Smørgrav } 1737b15c8340SDag-Erling Smørgrav fatal("%s: read from master failed: %s", 1738b15c8340SDag-Erling Smørgrav __func__, strerror(errno)); 1739b15c8340SDag-Erling Smørgrav } 1740b15c8340SDag-Erling Smørgrav 1741190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1742190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 1743190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 1744190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 1745b15c8340SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 1746b15c8340SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 1747b15c8340SDag-Erling Smørgrav switch (type) { 1748b15c8340SDag-Erling Smørgrav case MUX_S_OK: 1749b15c8340SDag-Erling Smørgrav break; 1750b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1751190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1752190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 1753b15c8340SDag-Erling Smørgrav fatal("Master refused termination request: %s", e); 1754b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1755190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1756190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 1757b15c8340SDag-Erling Smørgrav fatal("%s: termination request failed: %s", __func__, e); 1758b15c8340SDag-Erling Smørgrav default: 1759b15c8340SDag-Erling Smørgrav fatal("%s: unexpected response from master 0x%08x", 1760b15c8340SDag-Erling Smørgrav __func__, type); 1761b15c8340SDag-Erling Smørgrav } 1762190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1763b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1764b15c8340SDag-Erling Smørgrav } 1765b15c8340SDag-Erling Smørgrav 1766b15c8340SDag-Erling Smørgrav static int 1767a0ee8cc6SDag-Erling Smørgrav mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) 1768b15c8340SDag-Erling Smørgrav { 1769190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1770b15c8340SDag-Erling Smørgrav char *e, *fwd_desc; 1771190cef3dSDag-Erling Smørgrav const char *lhost, *chost; 1772b15c8340SDag-Erling Smørgrav u_int type, rid; 1773190cef3dSDag-Erling Smørgrav int r; 1774b15c8340SDag-Erling Smørgrav 1775b15c8340SDag-Erling Smørgrav fwd_desc = format_forward(ftype, fwd); 1776462c32cbSDag-Erling Smørgrav debug("Requesting %s %s", 1777462c32cbSDag-Erling Smørgrav cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); 1778e4a9863fSDag-Erling Smørgrav free(fwd_desc); 1779b15c8340SDag-Erling Smørgrav 1780190cef3dSDag-Erling Smørgrav type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD; 1781190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 1782190cef3dSDag-Erling Smørgrav lhost = fwd->listen_path; 1783190cef3dSDag-Erling Smørgrav else if (fwd->listen_host == NULL) 1784190cef3dSDag-Erling Smørgrav lhost = ""; 1785190cef3dSDag-Erling Smørgrav else if (*fwd->listen_host == '\0') 1786190cef3dSDag-Erling Smørgrav lhost = "*"; 1787190cef3dSDag-Erling Smørgrav else 1788190cef3dSDag-Erling Smørgrav lhost = fwd->listen_host; 1789b15c8340SDag-Erling Smørgrav 1790190cef3dSDag-Erling Smørgrav if (fwd->connect_path != NULL) 1791190cef3dSDag-Erling Smørgrav chost = fwd->connect_path; 1792190cef3dSDag-Erling Smørgrav else if (fwd->connect_host == NULL) 1793190cef3dSDag-Erling Smørgrav chost = ""; 1794190cef3dSDag-Erling Smørgrav else 1795190cef3dSDag-Erling Smørgrav chost = fwd->connect_host; 1796190cef3dSDag-Erling Smørgrav 1797190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1798190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1799190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, type)) != 0 || 1800190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 1801190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, ftype)) != 0 || 1802190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, lhost)) != 0 || 1803190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 || 1804190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, chost)) != 0 || 1805190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, fwd->connect_port)) != 0) 1806190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1807190cef3dSDag-Erling Smørgrav 1808190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 1809b15c8340SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 1810b15c8340SDag-Erling Smørgrav 1811190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1812b15c8340SDag-Erling Smørgrav 1813b15c8340SDag-Erling Smørgrav /* Read their reply */ 1814190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1815190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1816b15c8340SDag-Erling Smørgrav return -1; 1817b15c8340SDag-Erling Smørgrav } 1818b15c8340SDag-Erling Smørgrav 1819190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1820190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 1821190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 1822190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 1823b15c8340SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 1824b15c8340SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 1825190cef3dSDag-Erling Smørgrav 1826b15c8340SDag-Erling Smørgrav switch (type) { 1827b15c8340SDag-Erling Smørgrav case MUX_S_OK: 1828b15c8340SDag-Erling Smørgrav break; 1829e2f6069cSDag-Erling Smørgrav case MUX_S_REMOTE_PORT: 1830462c32cbSDag-Erling Smørgrav if (cancel_flag) 1831462c32cbSDag-Erling Smørgrav fatal("%s: got MUX_S_REMOTE_PORT for cancel", __func__); 1832190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0) 1833190cef3dSDag-Erling Smørgrav fatal("%s: decode port: %s", __func__, ssh_err(r)); 1834557f75e5SDag-Erling Smørgrav verbose("Allocated port %u for remote forward to %s:%d", 1835e2f6069cSDag-Erling Smørgrav fwd->allocated_port, 1836e2f6069cSDag-Erling Smørgrav fwd->connect_host ? fwd->connect_host : "", 1837e2f6069cSDag-Erling Smørgrav fwd->connect_port); 1838e2f6069cSDag-Erling Smørgrav if (muxclient_command == SSHMUX_COMMAND_FORWARD) 1839acc1a9efSDag-Erling Smørgrav fprintf(stdout, "%i\n", fwd->allocated_port); 1840e2f6069cSDag-Erling Smørgrav break; 1841b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1842190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1843190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 1844190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1845b15c8340SDag-Erling Smørgrav error("Master refused forwarding request: %s", e); 1846b15c8340SDag-Erling Smørgrav return -1; 1847b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1848190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1849190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 1850190cef3dSDag-Erling Smørgrav sshbuf_free(m); 18514a421b63SDag-Erling Smørgrav error("%s: forwarding request failed: %s", __func__, e); 1852b15c8340SDag-Erling Smørgrav return -1; 1853b15c8340SDag-Erling Smørgrav default: 1854b15c8340SDag-Erling Smørgrav fatal("%s: unexpected response from master 0x%08x", 1855b15c8340SDag-Erling Smørgrav __func__, type); 1856b15c8340SDag-Erling Smørgrav } 1857190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1858b15c8340SDag-Erling Smørgrav 1859b15c8340SDag-Erling Smørgrav muxclient_request_id++; 1860b15c8340SDag-Erling Smørgrav return 0; 1861b15c8340SDag-Erling Smørgrav } 1862b15c8340SDag-Erling Smørgrav 1863b15c8340SDag-Erling Smørgrav static int 1864462c32cbSDag-Erling Smørgrav mux_client_forwards(int fd, int cancel_flag) 1865b15c8340SDag-Erling Smørgrav { 1866462c32cbSDag-Erling Smørgrav int i, ret = 0; 1867b15c8340SDag-Erling Smørgrav 1868462c32cbSDag-Erling Smørgrav debug3("%s: %s forwardings: %d local, %d remote", __func__, 1869462c32cbSDag-Erling Smørgrav cancel_flag ? "cancel" : "request", 1870b15c8340SDag-Erling Smørgrav options.num_local_forwards, options.num_remote_forwards); 1871b15c8340SDag-Erling Smørgrav 1872b15c8340SDag-Erling Smørgrav /* XXX ExitOnForwardingFailure */ 1873b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_local_forwards; i++) { 1874462c32cbSDag-Erling Smørgrav if (mux_client_forward(fd, cancel_flag, 1875b15c8340SDag-Erling Smørgrav options.local_forwards[i].connect_port == 0 ? 1876b15c8340SDag-Erling Smørgrav MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, 1877b15c8340SDag-Erling Smørgrav options.local_forwards + i) != 0) 1878462c32cbSDag-Erling Smørgrav ret = -1; 1879b15c8340SDag-Erling Smørgrav } 1880b15c8340SDag-Erling Smørgrav for (i = 0; i < options.num_remote_forwards; i++) { 1881462c32cbSDag-Erling Smørgrav if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, 1882b15c8340SDag-Erling Smørgrav options.remote_forwards + i) != 0) 1883462c32cbSDag-Erling Smørgrav ret = -1; 1884b15c8340SDag-Erling Smørgrav } 1885462c32cbSDag-Erling Smørgrav return ret; 1886b15c8340SDag-Erling Smørgrav } 1887b15c8340SDag-Erling Smørgrav 1888b15c8340SDag-Erling Smørgrav static int 1889b15c8340SDag-Erling Smørgrav mux_client_request_session(int fd) 1890b15c8340SDag-Erling Smørgrav { 1891190cef3dSDag-Erling Smørgrav struct sshbuf *m; 1892190cef3dSDag-Erling Smørgrav char *e; 1893190cef3dSDag-Erling Smørgrav const char *term; 1894190cef3dSDag-Erling Smørgrav u_int echar, rid, sid, esid, exitval, type, exitval_seen; 1895b15c8340SDag-Erling Smørgrav extern char **environ; 1896190cef3dSDag-Erling Smørgrav int r, i, devnull, rawmode; 1897b15c8340SDag-Erling Smørgrav 1898b15c8340SDag-Erling Smørgrav debug3("%s: entering", __func__); 1899b15c8340SDag-Erling Smørgrav 1900b15c8340SDag-Erling Smørgrav if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 1901b15c8340SDag-Erling Smørgrav error("%s: master alive request failed", __func__); 1902b15c8340SDag-Erling Smørgrav return -1; 1903b15c8340SDag-Erling Smørgrav } 1904b15c8340SDag-Erling Smørgrav 1905b15c8340SDag-Erling Smørgrav signal(SIGPIPE, SIG_IGN); 1906b15c8340SDag-Erling Smørgrav 1907b15c8340SDag-Erling Smørgrav if (stdin_null_flag) { 1908b15c8340SDag-Erling Smørgrav if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) 1909b15c8340SDag-Erling Smørgrav fatal("open(/dev/null): %s", strerror(errno)); 1910b15c8340SDag-Erling Smørgrav if (dup2(devnull, STDIN_FILENO) == -1) 1911b15c8340SDag-Erling Smørgrav fatal("dup2: %s", strerror(errno)); 1912b15c8340SDag-Erling Smørgrav if (devnull > STDERR_FILENO) 1913b15c8340SDag-Erling Smørgrav close(devnull); 1914b15c8340SDag-Erling Smørgrav } 1915b15c8340SDag-Erling Smørgrav 1916190cef3dSDag-Erling Smørgrav if ((term = getenv("TERM")) == NULL) 1917190cef3dSDag-Erling Smørgrav term = ""; 1918190cef3dSDag-Erling Smørgrav echar = 0xffffffff; 1919190cef3dSDag-Erling Smørgrav if (options.escape_char != SSH_ESCAPECHAR_NONE) 1920190cef3dSDag-Erling Smørgrav echar = (u_int)options.escape_char; 1921b15c8340SDag-Erling Smørgrav 1922190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 1923190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 1924190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 || 1925190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 1926190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ 1927190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, tty_flag)) != 0 || 1928190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.forward_x11)) != 0 || 1929190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.forward_agent)) != 0 || 1930190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, subsystem_flag)) != 0 || 1931190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, echar)) != 0 || 1932190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, term)) != 0 || 1933190cef3dSDag-Erling Smørgrav (r = sshbuf_put_stringb(m, command)) != 0) 1934190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1935b15c8340SDag-Erling Smørgrav 1936b15c8340SDag-Erling Smørgrav /* Pass environment */ 1937190cef3dSDag-Erling Smørgrav if (options.num_send_env > 0 && environ != NULL) { 1938b15c8340SDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 1939190cef3dSDag-Erling Smørgrav if (!env_permitted(environ[i])) 1940190cef3dSDag-Erling Smørgrav continue; 1941190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(m, environ[i])) != 0) 1942190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1943b15c8340SDag-Erling Smørgrav } 1944b15c8340SDag-Erling Smørgrav } 1945190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 1946190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0) 1947190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 1948b15c8340SDag-Erling Smørgrav } 1949b15c8340SDag-Erling Smørgrav 1950190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 1951b15c8340SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 1952b15c8340SDag-Erling Smørgrav 1953b15c8340SDag-Erling Smørgrav /* Send the stdio file descriptors */ 1954b15c8340SDag-Erling Smørgrav if (mm_send_fd(fd, STDIN_FILENO) == -1 || 1955b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDOUT_FILENO) == -1 || 1956b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDERR_FILENO) == -1) 1957b15c8340SDag-Erling Smørgrav fatal("%s: send fds failed", __func__); 1958b15c8340SDag-Erling Smørgrav 1959b15c8340SDag-Erling Smørgrav debug3("%s: session request sent", __func__); 1960b15c8340SDag-Erling Smørgrav 1961b15c8340SDag-Erling Smørgrav /* Read their reply */ 1962190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 1963190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 1964b15c8340SDag-Erling Smørgrav error("%s: read from master failed: %s", 1965b15c8340SDag-Erling Smørgrav __func__, strerror(errno)); 1966190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1967b15c8340SDag-Erling Smørgrav return -1; 1968b15c8340SDag-Erling Smørgrav } 1969b15c8340SDag-Erling Smørgrav 1970190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 1971190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 1972190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 1973190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 1974b15c8340SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 1975b15c8340SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 1976190cef3dSDag-Erling Smørgrav 1977b15c8340SDag-Erling Smørgrav switch (type) { 1978b15c8340SDag-Erling Smørgrav case MUX_S_SESSION_OPENED: 1979190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &sid)) != 0) 1980190cef3dSDag-Erling Smørgrav fatal("%s: decode ID: %s", __func__, ssh_err(r)); 1981b15c8340SDag-Erling Smørgrav break; 1982b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 1983190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1984190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 19854a421b63SDag-Erling Smørgrav error("Master refused session request: %s", e); 1986190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1987b15c8340SDag-Erling Smørgrav return -1; 1988b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 1989190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 1990190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 19914a421b63SDag-Erling Smørgrav error("%s: session request failed: %s", __func__, e); 1992190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1993b15c8340SDag-Erling Smørgrav return -1; 1994b15c8340SDag-Erling Smørgrav default: 1995190cef3dSDag-Erling Smørgrav sshbuf_free(m); 1996b15c8340SDag-Erling Smørgrav error("%s: unexpected response from master 0x%08x", 1997b15c8340SDag-Erling Smørgrav __func__, type); 1998b15c8340SDag-Erling Smørgrav return -1; 1999b15c8340SDag-Erling Smørgrav } 2000b15c8340SDag-Erling Smørgrav muxclient_request_id++; 2001b15c8340SDag-Erling Smørgrav 2002acc1a9efSDag-Erling Smørgrav if (pledge("stdio proc tty", NULL) == -1) 2003acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 2004acc1a9efSDag-Erling Smørgrav platform_pledge_mux(); 2005acc1a9efSDag-Erling Smørgrav 2006b15c8340SDag-Erling Smørgrav signal(SIGHUP, control_client_sighandler); 2007b15c8340SDag-Erling Smørgrav signal(SIGINT, control_client_sighandler); 2008b15c8340SDag-Erling Smørgrav signal(SIGTERM, control_client_sighandler); 2009b15c8340SDag-Erling Smørgrav signal(SIGWINCH, control_client_sigrelay); 2010b15c8340SDag-Erling Smørgrav 2011e146993eSDag-Erling Smørgrav rawmode = tty_flag; 2012b15c8340SDag-Erling Smørgrav if (tty_flag) 2013e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2014b15c8340SDag-Erling Smørgrav 2015b15c8340SDag-Erling Smørgrav /* 2016b15c8340SDag-Erling Smørgrav * Stick around until the controlee closes the client_fd. 2017b15c8340SDag-Erling Smørgrav * Before it does, it is expected to write an exit message. 2018b15c8340SDag-Erling Smørgrav * This process must read the value and wait for the closure of 2019b15c8340SDag-Erling Smørgrav * the client_fd; if this one closes early, the multiplex master will 2020b15c8340SDag-Erling Smørgrav * terminate early too (possibly losing data). 2021b15c8340SDag-Erling Smørgrav */ 2022b15c8340SDag-Erling Smørgrav for (exitval = 255, exitval_seen = 0;;) { 2023190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2024190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) 2025b15c8340SDag-Erling Smørgrav break; 2026190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0) 2027190cef3dSDag-Erling Smørgrav fatal("%s: decode type: %s", __func__, ssh_err(r)); 2028e146993eSDag-Erling Smørgrav switch (type) { 2029e146993eSDag-Erling Smørgrav case MUX_S_TTY_ALLOC_FAIL: 2030190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &esid)) != 0) 2031190cef3dSDag-Erling Smørgrav fatal("%s: decode ID: %s", 2032190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 2033190cef3dSDag-Erling Smørgrav if (esid != sid) 2034e146993eSDag-Erling Smørgrav fatal("%s: tty alloc fail on unknown session: " 2035e146993eSDag-Erling Smørgrav "my id %u theirs %u", 2036b15c8340SDag-Erling Smørgrav __func__, sid, esid); 2037e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == 2038e146993eSDag-Erling Smørgrav REQUEST_TTY_FORCE); 2039e146993eSDag-Erling Smørgrav rawmode = 0; 2040e146993eSDag-Erling Smørgrav continue; 2041e146993eSDag-Erling Smørgrav case MUX_S_EXIT_MESSAGE: 2042190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &esid)) != 0) 2043190cef3dSDag-Erling Smørgrav fatal("%s: decode ID: %s", 2044190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 2045190cef3dSDag-Erling Smørgrav if (esid != sid) 2046e146993eSDag-Erling Smørgrav fatal("%s: exit on unknown session: " 2047e146993eSDag-Erling Smørgrav "my id %u theirs %u", 2048e146993eSDag-Erling Smørgrav __func__, sid, esid); 2049b15c8340SDag-Erling Smørgrav if (exitval_seen) 2050b15c8340SDag-Erling Smørgrav fatal("%s: exitval sent twice", __func__); 2051190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &exitval)) != 0) 2052190cef3dSDag-Erling Smørgrav fatal("%s: decode exit value: %s", 2053190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 2054b15c8340SDag-Erling Smørgrav exitval_seen = 1; 2055e146993eSDag-Erling Smørgrav continue; 2056e146993eSDag-Erling Smørgrav default: 2057190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2058190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", 2059190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 2060e146993eSDag-Erling Smørgrav fatal("%s: master returned error: %s", __func__, e); 2061e146993eSDag-Erling Smørgrav } 2062b15c8340SDag-Erling Smørgrav } 2063b15c8340SDag-Erling Smørgrav 2064b15c8340SDag-Erling Smørgrav close(fd); 2065e146993eSDag-Erling Smørgrav if (rawmode) 2066e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2067b15c8340SDag-Erling Smørgrav 2068b15c8340SDag-Erling Smørgrav if (muxclient_terminate) { 20694f52dfbbSDag-Erling Smørgrav debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); 2070b15c8340SDag-Erling Smørgrav exitval = 255; 2071b15c8340SDag-Erling Smørgrav } else if (!exitval_seen) { 2072b15c8340SDag-Erling Smørgrav debug2("Control master terminated unexpectedly"); 2073b15c8340SDag-Erling Smørgrav exitval = 255; 2074b15c8340SDag-Erling Smørgrav } else 2075b15c8340SDag-Erling Smørgrav debug2("Received exit status from master %d", exitval); 2076b15c8340SDag-Erling Smørgrav 2077b15c8340SDag-Erling Smørgrav if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) 2078b15c8340SDag-Erling Smørgrav fprintf(stderr, "Shared connection to %s closed.\r\n", host); 2079b15c8340SDag-Erling Smørgrav 2080b15c8340SDag-Erling Smørgrav exit(exitval); 2081b15c8340SDag-Erling Smørgrav } 2082b15c8340SDag-Erling Smørgrav 2083b15c8340SDag-Erling Smørgrav static int 2084ca86bcf2SDag-Erling Smørgrav mux_client_proxy(int fd) 2085ca86bcf2SDag-Erling Smørgrav { 2086190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2087ca86bcf2SDag-Erling Smørgrav char *e; 2088ca86bcf2SDag-Erling Smørgrav u_int type, rid; 2089190cef3dSDag-Erling Smørgrav int r; 2090ca86bcf2SDag-Erling Smørgrav 2091190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 2092190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2093190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 || 2094190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 2095190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 2096190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 2097ca86bcf2SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 2098ca86bcf2SDag-Erling Smørgrav 2099190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2100ca86bcf2SDag-Erling Smørgrav 2101ca86bcf2SDag-Erling Smørgrav /* Read their reply */ 2102190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 2103190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2104ca86bcf2SDag-Erling Smørgrav return 0; 2105ca86bcf2SDag-Erling Smørgrav } 2106190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2107190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 2108190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 2109190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 2110ca86bcf2SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 2111ca86bcf2SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 2112190cef3dSDag-Erling Smørgrav if (type != MUX_S_PROXY) { 2113190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2114190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 2115190cef3dSDag-Erling Smørgrav fatal("%s: master returned error: %s", __func__, e); 2116190cef3dSDag-Erling Smørgrav } 2117190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2118ca86bcf2SDag-Erling Smørgrav 2119ca86bcf2SDag-Erling Smørgrav debug3("%s: done", __func__); 2120ca86bcf2SDag-Erling Smørgrav muxclient_request_id++; 2121ca86bcf2SDag-Erling Smørgrav return 0; 2122ca86bcf2SDag-Erling Smørgrav } 2123ca86bcf2SDag-Erling Smørgrav 2124ca86bcf2SDag-Erling Smørgrav static int 2125b15c8340SDag-Erling Smørgrav mux_client_request_stdio_fwd(int fd) 2126b15c8340SDag-Erling Smørgrav { 2127190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2128b15c8340SDag-Erling Smørgrav char *e; 2129b15c8340SDag-Erling Smørgrav u_int type, rid, sid; 2130190cef3dSDag-Erling Smørgrav int r, devnull; 2131b15c8340SDag-Erling Smørgrav 2132b15c8340SDag-Erling Smørgrav debug3("%s: entering", __func__); 2133b15c8340SDag-Erling Smørgrav 2134b15c8340SDag-Erling Smørgrav if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 2135b15c8340SDag-Erling Smørgrav error("%s: master alive request failed", __func__); 2136b15c8340SDag-Erling Smørgrav return -1; 2137b15c8340SDag-Erling Smørgrav } 2138b15c8340SDag-Erling Smørgrav 2139b15c8340SDag-Erling Smørgrav signal(SIGPIPE, SIG_IGN); 2140b15c8340SDag-Erling Smørgrav 2141b15c8340SDag-Erling Smørgrav if (stdin_null_flag) { 2142b15c8340SDag-Erling Smørgrav if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) 2143b15c8340SDag-Erling Smørgrav fatal("open(/dev/null): %s", strerror(errno)); 2144b15c8340SDag-Erling Smørgrav if (dup2(devnull, STDIN_FILENO) == -1) 2145b15c8340SDag-Erling Smørgrav fatal("dup2: %s", strerror(errno)); 2146b15c8340SDag-Erling Smørgrav if (devnull > STDERR_FILENO) 2147b15c8340SDag-Erling Smørgrav close(devnull); 2148b15c8340SDag-Erling Smørgrav } 2149b15c8340SDag-Erling Smørgrav 2150190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 2151190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2152190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 || 2153190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || 2154190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ 2155190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 || 2156190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0) 2157190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 2158b15c8340SDag-Erling Smørgrav 2159190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 2160b15c8340SDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 2161b15c8340SDag-Erling Smørgrav 2162b15c8340SDag-Erling Smørgrav /* Send the stdio file descriptors */ 2163b15c8340SDag-Erling Smørgrav if (mm_send_fd(fd, STDIN_FILENO) == -1 || 2164b15c8340SDag-Erling Smørgrav mm_send_fd(fd, STDOUT_FILENO) == -1) 2165b15c8340SDag-Erling Smørgrav fatal("%s: send fds failed", __func__); 2166b15c8340SDag-Erling Smørgrav 2167acc1a9efSDag-Erling Smørgrav if (pledge("stdio proc tty", NULL) == -1) 2168acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 2169acc1a9efSDag-Erling Smørgrav platform_pledge_mux(); 2170acc1a9efSDag-Erling Smørgrav 2171b15c8340SDag-Erling Smørgrav debug3("%s: stdio forward request sent", __func__); 2172b15c8340SDag-Erling Smørgrav 2173b15c8340SDag-Erling Smørgrav /* Read their reply */ 2174190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2175b15c8340SDag-Erling Smørgrav 2176190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 2177b15c8340SDag-Erling Smørgrav error("%s: read from master failed: %s", 2178b15c8340SDag-Erling Smørgrav __func__, strerror(errno)); 2179190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2180b15c8340SDag-Erling Smørgrav return -1; 2181b15c8340SDag-Erling Smørgrav } 2182b15c8340SDag-Erling Smørgrav 2183190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2184190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 2185190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 2186190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 2187b15c8340SDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 2188b15c8340SDag-Erling Smørgrav __func__, muxclient_request_id, rid); 2189b15c8340SDag-Erling Smørgrav switch (type) { 2190b15c8340SDag-Erling Smørgrav case MUX_S_SESSION_OPENED: 2191190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &sid)) != 0) 2192190cef3dSDag-Erling Smørgrav fatal("%s: decode ID: %s", __func__, ssh_err(r)); 2193b15c8340SDag-Erling Smørgrav debug("%s: master session id: %u", __func__, sid); 2194b15c8340SDag-Erling Smørgrav break; 2195b15c8340SDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 2196190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2197190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 2198190cef3dSDag-Erling Smørgrav sshbuf_free(m); 21994a421b63SDag-Erling Smørgrav fatal("Master refused stdio forwarding request: %s", e); 2200b15c8340SDag-Erling Smørgrav case MUX_S_FAILURE: 2201190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2202190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 2203190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2204a0ee8cc6SDag-Erling Smørgrav fatal("Stdio forwarding request failed: %s", e); 2205b15c8340SDag-Erling Smørgrav default: 2206190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2207b15c8340SDag-Erling Smørgrav error("%s: unexpected response from master 0x%08x", 2208b15c8340SDag-Erling Smørgrav __func__, type); 2209b15c8340SDag-Erling Smørgrav return -1; 2210b15c8340SDag-Erling Smørgrav } 2211b15c8340SDag-Erling Smørgrav muxclient_request_id++; 2212b15c8340SDag-Erling Smørgrav 2213b15c8340SDag-Erling Smørgrav signal(SIGHUP, control_client_sighandler); 2214b15c8340SDag-Erling Smørgrav signal(SIGINT, control_client_sighandler); 2215b15c8340SDag-Erling Smørgrav signal(SIGTERM, control_client_sighandler); 2216b15c8340SDag-Erling Smørgrav signal(SIGWINCH, control_client_sigrelay); 2217b15c8340SDag-Erling Smørgrav 2218b15c8340SDag-Erling Smørgrav /* 2219b15c8340SDag-Erling Smørgrav * Stick around until the controlee closes the client_fd. 2220b15c8340SDag-Erling Smørgrav */ 2221190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2222190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) { 2223b15c8340SDag-Erling Smørgrav if (errno == EPIPE || 2224b15c8340SDag-Erling Smørgrav (errno == EINTR && muxclient_terminate != 0)) 2225b15c8340SDag-Erling Smørgrav return 0; 2226b15c8340SDag-Erling Smørgrav fatal("%s: mux_client_read_packet: %s", 2227b15c8340SDag-Erling Smørgrav __func__, strerror(errno)); 2228b15c8340SDag-Erling Smørgrav } 2229b15c8340SDag-Erling Smørgrav fatal("%s: master returned unexpected message %u", __func__, type); 2230d4af9e69SDag-Erling Smørgrav } 2231d4af9e69SDag-Erling Smørgrav 2232e146993eSDag-Erling Smørgrav static void 2233e146993eSDag-Erling Smørgrav mux_client_request_stop_listening(int fd) 2234e146993eSDag-Erling Smørgrav { 2235190cef3dSDag-Erling Smørgrav struct sshbuf *m; 2236e146993eSDag-Erling Smørgrav char *e; 2237e146993eSDag-Erling Smørgrav u_int type, rid; 2238190cef3dSDag-Erling Smørgrav int r; 2239e146993eSDag-Erling Smørgrav 2240e146993eSDag-Erling Smørgrav debug3("%s: entering", __func__); 2241e146993eSDag-Erling Smørgrav 2242190cef3dSDag-Erling Smørgrav if ((m = sshbuf_new()) == NULL) 2243190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2244190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 || 2245190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) 2246190cef3dSDag-Erling Smørgrav fatal("%s: request: %s", __func__, ssh_err(r)); 2247e146993eSDag-Erling Smørgrav 2248190cef3dSDag-Erling Smørgrav if (mux_client_write_packet(fd, m) != 0) 2249e146993eSDag-Erling Smørgrav fatal("%s: write packet: %s", __func__, strerror(errno)); 2250e146993eSDag-Erling Smørgrav 2251190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 2252e146993eSDag-Erling Smørgrav 2253e146993eSDag-Erling Smørgrav /* Read their reply */ 2254190cef3dSDag-Erling Smørgrav if (mux_client_read_packet(fd, m) != 0) 2255e146993eSDag-Erling Smørgrav fatal("%s: read from master failed: %s", 2256e146993eSDag-Erling Smørgrav __func__, strerror(errno)); 2257e146993eSDag-Erling Smørgrav 2258190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &type)) != 0 || 2259190cef3dSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &rid)) != 0) 2260190cef3dSDag-Erling Smørgrav fatal("%s: decode: %s", __func__, ssh_err(r)); 2261190cef3dSDag-Erling Smørgrav if (rid != muxclient_request_id) 2262e146993eSDag-Erling Smørgrav fatal("%s: out of sequence reply: my id %u theirs %u", 2263e146993eSDag-Erling Smørgrav __func__, muxclient_request_id, rid); 2264190cef3dSDag-Erling Smørgrav 2265e146993eSDag-Erling Smørgrav switch (type) { 2266e146993eSDag-Erling Smørgrav case MUX_S_OK: 2267e146993eSDag-Erling Smørgrav break; 2268e146993eSDag-Erling Smørgrav case MUX_S_PERMISSION_DENIED: 2269190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2270190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 2271e146993eSDag-Erling Smørgrav fatal("Master refused stop listening request: %s", e); 2272e146993eSDag-Erling Smørgrav case MUX_S_FAILURE: 2273190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) 2274190cef3dSDag-Erling Smørgrav fatal("%s: decode error: %s", __func__, ssh_err(r)); 2275e146993eSDag-Erling Smørgrav fatal("%s: stop listening request failed: %s", __func__, e); 2276e146993eSDag-Erling Smørgrav default: 2277e146993eSDag-Erling Smørgrav fatal("%s: unexpected response from master 0x%08x", 2278e146993eSDag-Erling Smørgrav __func__, type); 2279e146993eSDag-Erling Smørgrav } 2280190cef3dSDag-Erling Smørgrav sshbuf_free(m); 2281e146993eSDag-Erling Smørgrav muxclient_request_id++; 2282e146993eSDag-Erling Smørgrav } 2283e146993eSDag-Erling Smørgrav 2284d4af9e69SDag-Erling Smørgrav /* Multiplex client main loop. */ 2285ca86bcf2SDag-Erling Smørgrav int 2286d4af9e69SDag-Erling Smørgrav muxclient(const char *path) 2287d4af9e69SDag-Erling Smørgrav { 2288d4af9e69SDag-Erling Smørgrav struct sockaddr_un addr; 2289b15c8340SDag-Erling Smørgrav int sock; 2290b15c8340SDag-Erling Smørgrav u_int pid; 2291d4af9e69SDag-Erling Smørgrav 2292b15c8340SDag-Erling Smørgrav if (muxclient_command == 0) { 2293076ad2f8SDag-Erling Smørgrav if (options.stdio_forward_host != NULL) 2294b15c8340SDag-Erling Smørgrav muxclient_command = SSHMUX_COMMAND_STDIO_FWD; 2295b15c8340SDag-Erling Smørgrav else 2296d4af9e69SDag-Erling Smørgrav muxclient_command = SSHMUX_COMMAND_OPEN; 2297b15c8340SDag-Erling Smørgrav } 2298d4af9e69SDag-Erling Smørgrav 2299d4af9e69SDag-Erling Smørgrav switch (options.control_master) { 2300d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_AUTO: 2301d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_AUTO_ASK: 2302d4af9e69SDag-Erling Smørgrav debug("auto-mux: Trying existing master"); 2303d4af9e69SDag-Erling Smørgrav /* FALLTHROUGH */ 2304d4af9e69SDag-Erling Smørgrav case SSHCTL_MASTER_NO: 2305d4af9e69SDag-Erling Smørgrav break; 2306d4af9e69SDag-Erling Smørgrav default: 2307ca86bcf2SDag-Erling Smørgrav return -1; 2308d4af9e69SDag-Erling Smørgrav } 2309d4af9e69SDag-Erling Smørgrav 2310d4af9e69SDag-Erling Smørgrav memset(&addr, '\0', sizeof(addr)); 2311d4af9e69SDag-Erling Smørgrav addr.sun_family = AF_UNIX; 2312d4af9e69SDag-Erling Smørgrav 2313d4af9e69SDag-Erling Smørgrav if (strlcpy(addr.sun_path, path, 2314d4af9e69SDag-Erling Smørgrav sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) 2315ca86bcf2SDag-Erling Smørgrav fatal("ControlPath too long ('%s' >= %u bytes)", path, 2316ca86bcf2SDag-Erling Smørgrav (unsigned int)sizeof(addr.sun_path)); 2317d4af9e69SDag-Erling Smørgrav 2318d4af9e69SDag-Erling Smørgrav if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) 2319d4af9e69SDag-Erling Smørgrav fatal("%s socket(): %s", __func__, strerror(errno)); 2320d4af9e69SDag-Erling Smørgrav 2321d93a896eSDag-Erling Smørgrav if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 2322b15c8340SDag-Erling Smørgrav switch (muxclient_command) { 2323b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_OPEN: 2324b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_STDIO_FWD: 2325b15c8340SDag-Erling Smørgrav break; 2326b15c8340SDag-Erling Smørgrav default: 2327d4af9e69SDag-Erling Smørgrav fatal("Control socket connect(%.100s): %s", path, 2328d4af9e69SDag-Erling Smørgrav strerror(errno)); 2329d4af9e69SDag-Erling Smørgrav } 23304a421b63SDag-Erling Smørgrav if (errno == ECONNREFUSED && 23314a421b63SDag-Erling Smørgrav options.control_master != SSHCTL_MASTER_NO) { 23324a421b63SDag-Erling Smørgrav debug("Stale control socket %.100s, unlinking", path); 23334a421b63SDag-Erling Smørgrav unlink(path); 23344a421b63SDag-Erling Smørgrav } else if (errno == ENOENT) { 2335d4af9e69SDag-Erling Smørgrav debug("Control socket \"%.100s\" does not exist", path); 23364a421b63SDag-Erling Smørgrav } else { 2337d4af9e69SDag-Erling Smørgrav error("Control socket connect(%.100s): %s", path, 2338d4af9e69SDag-Erling Smørgrav strerror(errno)); 2339d4af9e69SDag-Erling Smørgrav } 2340d4af9e69SDag-Erling Smørgrav close(sock); 2341ca86bcf2SDag-Erling Smørgrav return -1; 2342d4af9e69SDag-Erling Smørgrav } 2343b15c8340SDag-Erling Smørgrav set_nonblock(sock); 2344d4af9e69SDag-Erling Smørgrav 2345b15c8340SDag-Erling Smørgrav if (mux_client_hello_exchange(sock) != 0) { 2346b15c8340SDag-Erling Smørgrav error("%s: master hello exchange failed", __func__); 2347d4af9e69SDag-Erling Smørgrav close(sock); 2348ca86bcf2SDag-Erling Smørgrav return -1; 2349d4af9e69SDag-Erling Smørgrav } 2350d4af9e69SDag-Erling Smørgrav 2351d4af9e69SDag-Erling Smørgrav switch (muxclient_command) { 2352d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_ALIVE_CHECK: 2353b15c8340SDag-Erling Smørgrav if ((pid = mux_client_request_alive(sock)) == 0) 2354b15c8340SDag-Erling Smørgrav fatal("%s: master alive check failed", __func__); 2355acc1a9efSDag-Erling Smørgrav fprintf(stderr, "Master running (pid=%u)\r\n", pid); 2356d4af9e69SDag-Erling Smørgrav exit(0); 2357d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_TERMINATE: 2358b15c8340SDag-Erling Smørgrav mux_client_request_terminate(sock); 2359ca86bcf2SDag-Erling Smørgrav if (options.log_level != SYSLOG_LEVEL_QUIET) 2360d4af9e69SDag-Erling Smørgrav fprintf(stderr, "Exit request sent.\r\n"); 2361d4af9e69SDag-Erling Smørgrav exit(0); 2362e2f6069cSDag-Erling Smørgrav case SSHMUX_COMMAND_FORWARD: 2363462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 0) != 0) 2364e2f6069cSDag-Erling Smørgrav fatal("%s: master forward request failed", __func__); 2365e2f6069cSDag-Erling Smørgrav exit(0); 2366d4af9e69SDag-Erling Smørgrav case SSHMUX_COMMAND_OPEN: 2367462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 0) != 0) { 2368b15c8340SDag-Erling Smørgrav error("%s: master forward request failed", __func__); 2369ca86bcf2SDag-Erling Smørgrav return -1; 2370d4af9e69SDag-Erling Smørgrav } 2371b15c8340SDag-Erling Smørgrav mux_client_request_session(sock); 2372ca86bcf2SDag-Erling Smørgrav return -1; 2373b15c8340SDag-Erling Smørgrav case SSHMUX_COMMAND_STDIO_FWD: 2374b15c8340SDag-Erling Smørgrav mux_client_request_stdio_fwd(sock); 2375b15c8340SDag-Erling Smørgrav exit(0); 2376e146993eSDag-Erling Smørgrav case SSHMUX_COMMAND_STOP: 2377e146993eSDag-Erling Smørgrav mux_client_request_stop_listening(sock); 2378ca86bcf2SDag-Erling Smørgrav if (options.log_level != SYSLOG_LEVEL_QUIET) 2379e146993eSDag-Erling Smørgrav fprintf(stderr, "Stop listening request sent.\r\n"); 2380e146993eSDag-Erling Smørgrav exit(0); 2381462c32cbSDag-Erling Smørgrav case SSHMUX_COMMAND_CANCEL_FWD: 2382462c32cbSDag-Erling Smørgrav if (mux_client_forwards(sock, 1) != 0) 2383462c32cbSDag-Erling Smørgrav error("%s: master cancel forward request failed", 2384462c32cbSDag-Erling Smørgrav __func__); 2385462c32cbSDag-Erling Smørgrav exit(0); 2386ca86bcf2SDag-Erling Smørgrav case SSHMUX_COMMAND_PROXY: 2387ca86bcf2SDag-Erling Smørgrav mux_client_proxy(sock); 2388ca86bcf2SDag-Erling Smørgrav return (sock); 2389d4af9e69SDag-Erling Smørgrav default: 2390d4af9e69SDag-Erling Smørgrav fatal("unrecognised muxclient_command %d", muxclient_command); 2391d4af9e69SDag-Erling Smørgrav } 2392d4af9e69SDag-Erling Smørgrav } 2393