1*ce74bacaSMatthew Dillon /* $OpenBSD: mux.c,v 1.69 2017/09/20 05:19:00 dtucker Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> 418de8d7fSPeter Avalos * 518de8d7fSPeter Avalos * Permission to use, copy, modify, and distribute this software for any 618de8d7fSPeter Avalos * purpose with or without fee is hereby granted, provided that the above 718de8d7fSPeter Avalos * copyright notice and this permission notice appear in all copies. 818de8d7fSPeter Avalos * 918de8d7fSPeter Avalos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1018de8d7fSPeter Avalos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1118de8d7fSPeter Avalos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1218de8d7fSPeter Avalos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1318de8d7fSPeter Avalos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1418de8d7fSPeter Avalos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1518de8d7fSPeter Avalos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1618de8d7fSPeter Avalos */ 1718de8d7fSPeter Avalos 1818de8d7fSPeter Avalos /* ssh session multiplexing support */ 1918de8d7fSPeter Avalos 2018de8d7fSPeter Avalos /* 2118de8d7fSPeter Avalos * TODO: 22856ea928SPeter Avalos * - Better signalling from master to slave, especially passing of 2318de8d7fSPeter Avalos * error messages 24856ea928SPeter Avalos * - Better fall-back from mux slave error to new connection. 25856ea928SPeter Avalos * - ExitOnForwardingFailure 26856ea928SPeter Avalos * - Maybe extension mechanisms for multi-X11/multi-agent forwarding 27856ea928SPeter Avalos * - Support ~^Z in mux slaves. 28856ea928SPeter Avalos * - Inspect or control sessions in master. 29856ea928SPeter Avalos * - If we ever support the "signal" channel request, send signals on 3018de8d7fSPeter Avalos * sessions in master. 3118de8d7fSPeter Avalos */ 3218de8d7fSPeter Avalos 33856ea928SPeter Avalos #include "includes.h" 34856ea928SPeter Avalos 3518de8d7fSPeter Avalos #include <sys/types.h> 3618de8d7fSPeter Avalos #include <sys/stat.h> 3718de8d7fSPeter Avalos #include <sys/socket.h> 3818de8d7fSPeter Avalos #include <sys/un.h> 3918de8d7fSPeter Avalos 4018de8d7fSPeter Avalos #include <errno.h> 4118de8d7fSPeter Avalos #include <fcntl.h> 4218de8d7fSPeter Avalos #include <signal.h> 4318de8d7fSPeter Avalos #include <stdarg.h> 4418de8d7fSPeter Avalos #include <stddef.h> 4518de8d7fSPeter Avalos #include <stdlib.h> 4618de8d7fSPeter Avalos #include <stdio.h> 4718de8d7fSPeter Avalos #include <string.h> 4818de8d7fSPeter Avalos #include <unistd.h> 4918de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 5018de8d7fSPeter Avalos #include <paths.h> 5118de8d7fSPeter Avalos #endif 5218de8d7fSPeter Avalos 53856ea928SPeter Avalos #ifdef HAVE_POLL_H 54856ea928SPeter Avalos #include <poll.h> 55856ea928SPeter Avalos #else 56856ea928SPeter Avalos # ifdef HAVE_SYS_POLL_H 57856ea928SPeter Avalos # include <sys/poll.h> 58856ea928SPeter Avalos # endif 59856ea928SPeter Avalos #endif 60856ea928SPeter Avalos 6118de8d7fSPeter Avalos #ifdef HAVE_UTIL_H 6218de8d7fSPeter Avalos # include <util.h> 6318de8d7fSPeter Avalos #endif 6418de8d7fSPeter Avalos 6518de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h" 6618de8d7fSPeter Avalos #include "xmalloc.h" 6718de8d7fSPeter Avalos #include "log.h" 6818de8d7fSPeter Avalos #include "ssh.h" 69856ea928SPeter Avalos #include "ssh2.h" 7018de8d7fSPeter Avalos #include "pathnames.h" 7118de8d7fSPeter Avalos #include "misc.h" 7218de8d7fSPeter Avalos #include "match.h" 7318de8d7fSPeter Avalos #include "buffer.h" 7418de8d7fSPeter Avalos #include "channels.h" 7518de8d7fSPeter Avalos #include "msg.h" 7618de8d7fSPeter Avalos #include "packet.h" 7718de8d7fSPeter Avalos #include "monitor_fdpass.h" 7818de8d7fSPeter Avalos #include "sshpty.h" 7918de8d7fSPeter Avalos #include "key.h" 8018de8d7fSPeter Avalos #include "readconf.h" 8118de8d7fSPeter Avalos #include "clientloop.h" 82*ce74bacaSMatthew Dillon #include "ssherr.h" 8318de8d7fSPeter Avalos 8418de8d7fSPeter Avalos /* from ssh.c */ 8518de8d7fSPeter Avalos extern int tty_flag; 8618de8d7fSPeter Avalos extern Options options; 8718de8d7fSPeter Avalos extern int stdin_null_flag; 8818de8d7fSPeter Avalos extern char *host; 89856ea928SPeter Avalos extern int subsystem_flag; 9018de8d7fSPeter Avalos extern Buffer command; 91856ea928SPeter Avalos extern volatile sig_atomic_t quit_pending; 9218de8d7fSPeter Avalos 9318de8d7fSPeter Avalos /* Context for session open confirmation callback */ 9418de8d7fSPeter Avalos struct mux_session_confirm_ctx { 95856ea928SPeter Avalos u_int want_tty; 96856ea928SPeter Avalos u_int want_subsys; 97856ea928SPeter Avalos u_int want_x_fwd; 98856ea928SPeter Avalos u_int want_agent_fwd; 9918de8d7fSPeter Avalos Buffer cmd; 10018de8d7fSPeter Avalos char *term; 10118de8d7fSPeter Avalos struct termios tio; 10218de8d7fSPeter Avalos char **env; 103856ea928SPeter Avalos u_int rid; 104856ea928SPeter Avalos }; 105856ea928SPeter Avalos 10636e94dc5SPeter Avalos /* Context for stdio fwd open confirmation callback */ 10736e94dc5SPeter Avalos struct mux_stdio_confirm_ctx { 10836e94dc5SPeter Avalos u_int rid; 10936e94dc5SPeter Avalos }; 11036e94dc5SPeter Avalos 111856ea928SPeter Avalos /* Context for global channel callback */ 112856ea928SPeter Avalos struct mux_channel_confirm_ctx { 113856ea928SPeter Avalos u_int cid; /* channel id */ 114856ea928SPeter Avalos u_int rid; /* request id */ 115856ea928SPeter Avalos int fid; /* forward id */ 11618de8d7fSPeter Avalos }; 11718de8d7fSPeter Avalos 11818de8d7fSPeter Avalos /* fd to control socket */ 11918de8d7fSPeter Avalos int muxserver_sock = -1; 12018de8d7fSPeter Avalos 121856ea928SPeter Avalos /* client request id */ 122856ea928SPeter Avalos u_int muxclient_request_id = 0; 123856ea928SPeter Avalos 12418de8d7fSPeter Avalos /* Multiplexing control command */ 12518de8d7fSPeter Avalos u_int muxclient_command = 0; 12618de8d7fSPeter Avalos 12718de8d7fSPeter Avalos /* Set when signalled. */ 12818de8d7fSPeter Avalos static volatile sig_atomic_t muxclient_terminate = 0; 12918de8d7fSPeter Avalos 13018de8d7fSPeter Avalos /* PID of multiplex server */ 13118de8d7fSPeter Avalos static u_int muxserver_pid = 0; 13218de8d7fSPeter Avalos 133856ea928SPeter Avalos static Channel *mux_listener_channel = NULL; 13418de8d7fSPeter Avalos 135856ea928SPeter Avalos struct mux_master_state { 136856ea928SPeter Avalos int hello_rcvd; 137856ea928SPeter Avalos }; 138856ea928SPeter Avalos 139856ea928SPeter Avalos /* mux protocol messages */ 140856ea928SPeter Avalos #define MUX_MSG_HELLO 0x00000001 141856ea928SPeter Avalos #define MUX_C_NEW_SESSION 0x10000002 142856ea928SPeter Avalos #define MUX_C_ALIVE_CHECK 0x10000004 143856ea928SPeter Avalos #define MUX_C_TERMINATE 0x10000005 144856ea928SPeter Avalos #define MUX_C_OPEN_FWD 0x10000006 145856ea928SPeter Avalos #define MUX_C_CLOSE_FWD 0x10000007 146856ea928SPeter Avalos #define MUX_C_NEW_STDIO_FWD 0x10000008 1471c188a7fSPeter Avalos #define MUX_C_STOP_LISTENING 0x10000009 148*ce74bacaSMatthew Dillon #define MUX_C_PROXY 0x1000000f 149856ea928SPeter Avalos #define MUX_S_OK 0x80000001 150856ea928SPeter Avalos #define MUX_S_PERMISSION_DENIED 0x80000002 151856ea928SPeter Avalos #define MUX_S_FAILURE 0x80000003 152856ea928SPeter Avalos #define MUX_S_EXIT_MESSAGE 0x80000004 153856ea928SPeter Avalos #define MUX_S_ALIVE 0x80000005 154856ea928SPeter Avalos #define MUX_S_SESSION_OPENED 0x80000006 155856ea928SPeter Avalos #define MUX_S_REMOTE_PORT 0x80000007 1561c188a7fSPeter Avalos #define MUX_S_TTY_ALLOC_FAIL 0x80000008 157*ce74bacaSMatthew Dillon #define MUX_S_PROXY 0x8000000f 158856ea928SPeter Avalos 159856ea928SPeter Avalos /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ 160856ea928SPeter Avalos #define MUX_FWD_LOCAL 1 161856ea928SPeter Avalos #define MUX_FWD_REMOTE 2 162856ea928SPeter Avalos #define MUX_FWD_DYNAMIC 3 163856ea928SPeter Avalos 164*ce74bacaSMatthew Dillon static void mux_session_confirm(struct ssh *, int, int, void *); 165*ce74bacaSMatthew Dillon static void mux_stdio_confirm(struct ssh *, int, int, void *); 166856ea928SPeter Avalos 167*ce74bacaSMatthew Dillon static int process_mux_master_hello(struct ssh *, u_int, 168*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 169*ce74bacaSMatthew Dillon static int process_mux_new_session(struct ssh *, u_int, 170*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 171*ce74bacaSMatthew Dillon static int process_mux_alive_check(struct ssh *, u_int, 172*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 173*ce74bacaSMatthew Dillon static int process_mux_terminate(struct ssh *, u_int, 174*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 175*ce74bacaSMatthew Dillon static int process_mux_open_fwd(struct ssh *, u_int, 176*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 177*ce74bacaSMatthew Dillon static int process_mux_close_fwd(struct ssh *, u_int, 178*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 179*ce74bacaSMatthew Dillon static int process_mux_stdio_fwd(struct ssh *, u_int, 180*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 181*ce74bacaSMatthew Dillon static int process_mux_stop_listening(struct ssh *, u_int, 182*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 183*ce74bacaSMatthew Dillon static int process_mux_proxy(struct ssh *, u_int, 184*ce74bacaSMatthew Dillon Channel *, struct sshbuf *, struct sshbuf *); 185856ea928SPeter Avalos 186856ea928SPeter Avalos static const struct { 187856ea928SPeter Avalos u_int type; 188*ce74bacaSMatthew Dillon int (*handler)(struct ssh *, u_int, Channel *, 189*ce74bacaSMatthew Dillon struct sshbuf *, struct sshbuf *); 190856ea928SPeter Avalos } mux_master_handlers[] = { 191856ea928SPeter Avalos { MUX_MSG_HELLO, process_mux_master_hello }, 192856ea928SPeter Avalos { MUX_C_NEW_SESSION, process_mux_new_session }, 193856ea928SPeter Avalos { MUX_C_ALIVE_CHECK, process_mux_alive_check }, 194856ea928SPeter Avalos { MUX_C_TERMINATE, process_mux_terminate }, 195856ea928SPeter Avalos { MUX_C_OPEN_FWD, process_mux_open_fwd }, 196856ea928SPeter Avalos { MUX_C_CLOSE_FWD, process_mux_close_fwd }, 197856ea928SPeter Avalos { MUX_C_NEW_STDIO_FWD, process_mux_stdio_fwd }, 1981c188a7fSPeter Avalos { MUX_C_STOP_LISTENING, process_mux_stop_listening }, 199*ce74bacaSMatthew Dillon { MUX_C_PROXY, process_mux_proxy }, 200856ea928SPeter Avalos { 0, NULL } 201856ea928SPeter Avalos }; 202856ea928SPeter Avalos 203856ea928SPeter Avalos /* Cleanup callback fired on closure of mux slave _session_ channel */ 204856ea928SPeter Avalos /* ARGSUSED */ 205856ea928SPeter Avalos static void 206*ce74bacaSMatthew Dillon mux_master_session_cleanup_cb(struct ssh *ssh, int cid, void *unused) 207856ea928SPeter Avalos { 208*ce74bacaSMatthew Dillon Channel *cc, *c = channel_by_id(ssh, cid); 209856ea928SPeter Avalos 210856ea928SPeter Avalos debug3("%s: entering for channel %d", __func__, cid); 211856ea928SPeter Avalos if (c == NULL) 212856ea928SPeter Avalos fatal("%s: channel_by_id(%i) == NULL", __func__, cid); 213856ea928SPeter Avalos if (c->ctl_chan != -1) { 214*ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 215856ea928SPeter Avalos fatal("%s: channel %d missing control channel %d", 216856ea928SPeter Avalos __func__, c->self, c->ctl_chan); 217856ea928SPeter Avalos c->ctl_chan = -1; 218*ce74bacaSMatthew Dillon cc->remote_id = 0; 219*ce74bacaSMatthew Dillon cc->have_remote_id = 0; 220*ce74bacaSMatthew Dillon chan_rcvd_oclose(ssh, cc); 221856ea928SPeter Avalos } 222*ce74bacaSMatthew Dillon channel_cancel_cleanup(ssh, c->self); 223856ea928SPeter Avalos } 224856ea928SPeter Avalos 225856ea928SPeter Avalos /* Cleanup callback fired on closure of mux slave _control_ channel */ 226856ea928SPeter Avalos /* ARGSUSED */ 227856ea928SPeter Avalos static void 228*ce74bacaSMatthew Dillon mux_master_control_cleanup_cb(struct ssh *ssh, int cid, void *unused) 229856ea928SPeter Avalos { 230*ce74bacaSMatthew Dillon Channel *sc, *c = channel_by_id(ssh, cid); 231856ea928SPeter Avalos 232856ea928SPeter Avalos debug3("%s: entering for channel %d", __func__, cid); 233856ea928SPeter Avalos if (c == NULL) 234856ea928SPeter Avalos fatal("%s: channel_by_id(%i) == NULL", __func__, cid); 235*ce74bacaSMatthew Dillon if (c->have_remote_id) { 236*ce74bacaSMatthew Dillon if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) 237*ce74bacaSMatthew Dillon fatal("%s: channel %d missing session channel %u", 238856ea928SPeter Avalos __func__, c->self, c->remote_id); 239*ce74bacaSMatthew Dillon c->remote_id = 0; 240*ce74bacaSMatthew Dillon c->have_remote_id = 0; 241856ea928SPeter Avalos sc->ctl_chan = -1; 24236e94dc5SPeter Avalos if (sc->type != SSH_CHANNEL_OPEN && 24336e94dc5SPeter Avalos sc->type != SSH_CHANNEL_OPENING) { 244856ea928SPeter Avalos debug2("%s: channel %d: not open", __func__, sc->self); 245*ce74bacaSMatthew Dillon chan_mark_dead(ssh, sc); 246856ea928SPeter Avalos } else { 247856ea928SPeter Avalos if (sc->istate == CHAN_INPUT_OPEN) 248*ce74bacaSMatthew Dillon chan_read_failed(ssh, sc); 249856ea928SPeter Avalos if (sc->ostate == CHAN_OUTPUT_OPEN) 250*ce74bacaSMatthew Dillon chan_write_failed(ssh, sc); 251856ea928SPeter Avalos } 252856ea928SPeter Avalos } 253*ce74bacaSMatthew Dillon channel_cancel_cleanup(ssh, c->self); 254856ea928SPeter Avalos } 255856ea928SPeter Avalos 256856ea928SPeter Avalos /* Check mux client environment variables before passing them to mux master. */ 257856ea928SPeter Avalos static int 258856ea928SPeter Avalos env_permitted(char *env) 259856ea928SPeter Avalos { 260856ea928SPeter Avalos int i, ret; 261856ea928SPeter Avalos char name[1024], *cp; 262856ea928SPeter Avalos 263856ea928SPeter Avalos if ((cp = strchr(env, '=')) == NULL || cp == env) 264856ea928SPeter Avalos return 0; 265856ea928SPeter Avalos ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); 266856ea928SPeter Avalos if (ret <= 0 || (size_t)ret >= sizeof(name)) { 267856ea928SPeter Avalos error("env_permitted: name '%.100s...' too long", env); 268856ea928SPeter Avalos return 0; 269856ea928SPeter Avalos } 270856ea928SPeter Avalos 271856ea928SPeter Avalos for (i = 0; i < options.num_send_env; i++) 272856ea928SPeter Avalos if (match_pattern(name, options.send_env[i])) 273856ea928SPeter Avalos return 1; 274856ea928SPeter Avalos 275856ea928SPeter Avalos return 0; 276856ea928SPeter Avalos } 277856ea928SPeter Avalos 278856ea928SPeter Avalos /* Mux master protocol message handlers */ 279856ea928SPeter Avalos 280856ea928SPeter Avalos static int 281*ce74bacaSMatthew Dillon process_mux_master_hello(struct ssh *ssh, u_int rid, 282*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 283856ea928SPeter Avalos { 284856ea928SPeter Avalos u_int ver; 285856ea928SPeter Avalos struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 286856ea928SPeter Avalos 287856ea928SPeter Avalos if (state == NULL) 288856ea928SPeter Avalos fatal("%s: channel %d: c->mux_ctx == NULL", __func__, c->self); 289856ea928SPeter Avalos if (state->hello_rcvd) { 290856ea928SPeter Avalos error("%s: HELLO received twice", __func__); 291856ea928SPeter Avalos return -1; 292856ea928SPeter Avalos } 293856ea928SPeter Avalos if (buffer_get_int_ret(&ver, m) != 0) { 294856ea928SPeter Avalos malf: 295856ea928SPeter Avalos error("%s: malformed message", __func__); 296856ea928SPeter Avalos return -1; 297856ea928SPeter Avalos } 298856ea928SPeter Avalos if (ver != SSHMUX_VER) { 299856ea928SPeter Avalos error("Unsupported multiplexing protocol version %d " 300856ea928SPeter Avalos "(expected %d)", ver, SSHMUX_VER); 301856ea928SPeter Avalos return -1; 302856ea928SPeter Avalos } 303856ea928SPeter Avalos debug2("%s: channel %d slave version %u", __func__, c->self, ver); 304856ea928SPeter Avalos 305856ea928SPeter Avalos /* No extensions are presently defined */ 306856ea928SPeter Avalos while (buffer_len(m) > 0) { 307856ea928SPeter Avalos char *name = buffer_get_string_ret(m, NULL); 308856ea928SPeter Avalos char *value = buffer_get_string_ret(m, NULL); 309856ea928SPeter Avalos 310856ea928SPeter Avalos if (name == NULL || value == NULL) { 31136e94dc5SPeter Avalos free(name); 31236e94dc5SPeter Avalos free(value); 313856ea928SPeter Avalos goto malf; 314856ea928SPeter Avalos } 315856ea928SPeter Avalos debug2("Unrecognised slave extension \"%s\"", name); 31636e94dc5SPeter Avalos free(name); 31736e94dc5SPeter Avalos free(value); 318856ea928SPeter Avalos } 319856ea928SPeter Avalos state->hello_rcvd = 1; 320856ea928SPeter Avalos return 0; 321856ea928SPeter Avalos } 322856ea928SPeter Avalos 323856ea928SPeter Avalos static int 324*ce74bacaSMatthew Dillon process_mux_new_session(struct ssh *ssh, u_int rid, 325*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 326856ea928SPeter Avalos { 327856ea928SPeter Avalos Channel *nc; 328856ea928SPeter Avalos struct mux_session_confirm_ctx *cctx; 329856ea928SPeter Avalos char *reserved, *cmd, *cp; 330856ea928SPeter Avalos u_int i, j, len, env_len, escape_char, window, packetmax; 331856ea928SPeter Avalos int new_fd[3]; 332856ea928SPeter Avalos 333856ea928SPeter Avalos /* Reply for SSHMUX_COMMAND_OPEN */ 334856ea928SPeter Avalos cctx = xcalloc(1, sizeof(*cctx)); 335856ea928SPeter Avalos cctx->term = NULL; 336856ea928SPeter Avalos cctx->rid = rid; 337856ea928SPeter Avalos cmd = reserved = NULL; 33899e85e0dSPeter Avalos cctx->env = NULL; 33999e85e0dSPeter Avalos env_len = 0; 340856ea928SPeter Avalos if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || 341856ea928SPeter Avalos buffer_get_int_ret(&cctx->want_tty, m) != 0 || 342856ea928SPeter Avalos buffer_get_int_ret(&cctx->want_x_fwd, m) != 0 || 343856ea928SPeter Avalos buffer_get_int_ret(&cctx->want_agent_fwd, m) != 0 || 344856ea928SPeter Avalos buffer_get_int_ret(&cctx->want_subsys, m) != 0 || 345856ea928SPeter Avalos buffer_get_int_ret(&escape_char, m) != 0 || 346856ea928SPeter Avalos (cctx->term = buffer_get_string_ret(m, &len)) == NULL || 347856ea928SPeter Avalos (cmd = buffer_get_string_ret(m, &len)) == NULL) { 348856ea928SPeter Avalos malf: 34936e94dc5SPeter Avalos free(cmd); 35036e94dc5SPeter Avalos free(reserved); 35199e85e0dSPeter Avalos for (j = 0; j < env_len; j++) 35236e94dc5SPeter Avalos free(cctx->env[j]); 35336e94dc5SPeter Avalos free(cctx->env); 35436e94dc5SPeter Avalos free(cctx->term); 35536e94dc5SPeter Avalos free(cctx); 356856ea928SPeter Avalos error("%s: malformed message", __func__); 357856ea928SPeter Avalos return -1; 358856ea928SPeter Avalos } 35936e94dc5SPeter Avalos free(reserved); 360856ea928SPeter Avalos reserved = NULL; 361856ea928SPeter Avalos 362856ea928SPeter Avalos while (buffer_len(m) > 0) { 363856ea928SPeter Avalos #define MUX_MAX_ENV_VARS 4096 36499e85e0dSPeter Avalos if ((cp = buffer_get_string_ret(m, &len)) == NULL) 365856ea928SPeter Avalos goto malf; 366856ea928SPeter Avalos if (!env_permitted(cp)) { 36736e94dc5SPeter Avalos free(cp); 368856ea928SPeter Avalos continue; 369856ea928SPeter Avalos } 370e9778795SPeter Avalos cctx->env = xreallocarray(cctx->env, env_len + 2, 371856ea928SPeter Avalos sizeof(*cctx->env)); 372856ea928SPeter Avalos cctx->env[env_len++] = cp; 373856ea928SPeter Avalos cctx->env[env_len] = NULL; 374856ea928SPeter Avalos if (env_len > MUX_MAX_ENV_VARS) { 375856ea928SPeter Avalos error(">%d environment variables received, ignoring " 376856ea928SPeter Avalos "additional", MUX_MAX_ENV_VARS); 377856ea928SPeter Avalos break; 378856ea928SPeter Avalos } 379856ea928SPeter Avalos } 380856ea928SPeter Avalos 381856ea928SPeter Avalos debug2("%s: channel %d: request tty %d, X %d, agent %d, subsys %d, " 382856ea928SPeter Avalos "term \"%s\", cmd \"%s\", env %u", __func__, c->self, 383856ea928SPeter Avalos cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, 384856ea928SPeter Avalos cctx->want_subsys, cctx->term, cmd, env_len); 385856ea928SPeter Avalos 386856ea928SPeter Avalos buffer_init(&cctx->cmd); 387856ea928SPeter Avalos buffer_append(&cctx->cmd, cmd, strlen(cmd)); 38836e94dc5SPeter Avalos free(cmd); 389856ea928SPeter Avalos cmd = NULL; 390856ea928SPeter Avalos 391856ea928SPeter Avalos /* Gather fds from client */ 392856ea928SPeter Avalos for(i = 0; i < 3; i++) { 393856ea928SPeter Avalos if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 394856ea928SPeter Avalos error("%s: failed to receive fd %d from slave", 395856ea928SPeter Avalos __func__, i); 396856ea928SPeter Avalos for (j = 0; j < i; j++) 397856ea928SPeter Avalos close(new_fd[j]); 398856ea928SPeter Avalos for (j = 0; j < env_len; j++) 39936e94dc5SPeter Avalos free(cctx->env[j]); 40036e94dc5SPeter Avalos free(cctx->env); 40136e94dc5SPeter Avalos free(cctx->term); 402856ea928SPeter Avalos buffer_free(&cctx->cmd); 40336e94dc5SPeter Avalos free(cctx); 404856ea928SPeter Avalos 405856ea928SPeter Avalos /* prepare reply */ 406856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 407856ea928SPeter Avalos buffer_put_int(r, rid); 408856ea928SPeter Avalos buffer_put_cstring(r, 409856ea928SPeter Avalos "did not receive file descriptors"); 410856ea928SPeter Avalos return -1; 411856ea928SPeter Avalos } 412856ea928SPeter Avalos } 413856ea928SPeter Avalos 414856ea928SPeter Avalos debug3("%s: got fds stdin %d, stdout %d, stderr %d", __func__, 415856ea928SPeter Avalos new_fd[0], new_fd[1], new_fd[2]); 416856ea928SPeter Avalos 417856ea928SPeter Avalos /* XXX support multiple child sessions in future */ 418*ce74bacaSMatthew Dillon if (c->have_remote_id) { 419856ea928SPeter Avalos debug2("%s: session already open", __func__); 420856ea928SPeter Avalos /* prepare reply */ 421856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 422856ea928SPeter Avalos buffer_put_int(r, rid); 423856ea928SPeter Avalos buffer_put_cstring(r, "Multiple sessions not supported"); 424856ea928SPeter Avalos cleanup: 425856ea928SPeter Avalos close(new_fd[0]); 426856ea928SPeter Avalos close(new_fd[1]); 427856ea928SPeter Avalos close(new_fd[2]); 42836e94dc5SPeter Avalos free(cctx->term); 429856ea928SPeter Avalos if (env_len != 0) { 430856ea928SPeter Avalos for (i = 0; i < env_len; i++) 43136e94dc5SPeter Avalos free(cctx->env[i]); 43236e94dc5SPeter Avalos free(cctx->env); 433856ea928SPeter Avalos } 434856ea928SPeter Avalos buffer_free(&cctx->cmd); 43536e94dc5SPeter Avalos free(cctx); 436856ea928SPeter Avalos return 0; 437856ea928SPeter Avalos } 438856ea928SPeter Avalos 439856ea928SPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK || 440856ea928SPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) { 441856ea928SPeter Avalos if (!ask_permission("Allow shared connection to %s? ", host)) { 442856ea928SPeter Avalos debug2("%s: session refused by user", __func__); 443856ea928SPeter Avalos /* prepare reply */ 444856ea928SPeter Avalos buffer_put_int(r, MUX_S_PERMISSION_DENIED); 445856ea928SPeter Avalos buffer_put_int(r, rid); 446856ea928SPeter Avalos buffer_put_cstring(r, "Permission denied"); 447856ea928SPeter Avalos goto cleanup; 448856ea928SPeter Avalos } 449856ea928SPeter Avalos } 450856ea928SPeter Avalos 451856ea928SPeter Avalos /* Try to pick up ttymodes from client before it goes raw */ 452856ea928SPeter Avalos if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) 453856ea928SPeter Avalos error("%s: tcgetattr: %s", __func__, strerror(errno)); 454856ea928SPeter Avalos 455856ea928SPeter Avalos /* enable nonblocking unless tty */ 456856ea928SPeter Avalos if (!isatty(new_fd[0])) 457856ea928SPeter Avalos set_nonblock(new_fd[0]); 458856ea928SPeter Avalos if (!isatty(new_fd[1])) 459856ea928SPeter Avalos set_nonblock(new_fd[1]); 460856ea928SPeter Avalos if (!isatty(new_fd[2])) 461856ea928SPeter Avalos set_nonblock(new_fd[2]); 462856ea928SPeter Avalos 463856ea928SPeter Avalos window = CHAN_SES_WINDOW_DEFAULT; 464856ea928SPeter Avalos packetmax = CHAN_SES_PACKET_DEFAULT; 465856ea928SPeter Avalos if (cctx->want_tty) { 466856ea928SPeter Avalos window >>= 1; 467856ea928SPeter Avalos packetmax >>= 1; 468856ea928SPeter Avalos } 469856ea928SPeter Avalos 470*ce74bacaSMatthew Dillon nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, 471856ea928SPeter Avalos new_fd[0], new_fd[1], new_fd[2], window, packetmax, 472856ea928SPeter Avalos CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); 473856ea928SPeter Avalos 474856ea928SPeter Avalos nc->ctl_chan = c->self; /* link session -> control channel */ 475856ea928SPeter Avalos c->remote_id = nc->self; /* link control -> session channel */ 476*ce74bacaSMatthew Dillon c->have_remote_id = 1; 477856ea928SPeter Avalos 478856ea928SPeter Avalos if (cctx->want_tty && escape_char != 0xffffffff) { 479*ce74bacaSMatthew Dillon channel_register_filter(ssh, nc->self, 480856ea928SPeter Avalos client_simple_escape_filter, NULL, 481856ea928SPeter Avalos client_filter_cleanup, 482856ea928SPeter Avalos client_new_escape_filter_ctx((int)escape_char)); 483856ea928SPeter Avalos } 484856ea928SPeter Avalos 485856ea928SPeter Avalos debug2("%s: channel_new: %d linked to control channel %d", 486856ea928SPeter Avalos __func__, nc->self, nc->ctl_chan); 487856ea928SPeter Avalos 488*ce74bacaSMatthew Dillon channel_send_open(ssh, nc->self); 489*ce74bacaSMatthew Dillon channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); 490856ea928SPeter Avalos c->mux_pause = 1; /* stop handling messages until open_confirm done */ 491*ce74bacaSMatthew Dillon channel_register_cleanup(ssh, nc->self, 492*ce74bacaSMatthew Dillon mux_master_session_cleanup_cb, 1); 493856ea928SPeter Avalos 494856ea928SPeter Avalos /* reply is deferred, sent by mux_session_confirm */ 495856ea928SPeter Avalos return 0; 496856ea928SPeter Avalos } 497856ea928SPeter Avalos 498856ea928SPeter Avalos static int 499*ce74bacaSMatthew Dillon process_mux_alive_check(struct ssh *ssh, u_int rid, 500*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 501856ea928SPeter Avalos { 502856ea928SPeter Avalos debug2("%s: channel %d: alive check", __func__, c->self); 503856ea928SPeter Avalos 504856ea928SPeter Avalos /* prepare reply */ 505856ea928SPeter Avalos buffer_put_int(r, MUX_S_ALIVE); 506856ea928SPeter Avalos buffer_put_int(r, rid); 507856ea928SPeter Avalos buffer_put_int(r, (u_int)getpid()); 508856ea928SPeter Avalos 509856ea928SPeter Avalos return 0; 510856ea928SPeter Avalos } 511856ea928SPeter Avalos 512856ea928SPeter Avalos static int 513*ce74bacaSMatthew Dillon process_mux_terminate(struct ssh *ssh, u_int rid, 514*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 515856ea928SPeter Avalos { 516856ea928SPeter Avalos debug2("%s: channel %d: terminate request", __func__, c->self); 517856ea928SPeter Avalos 518856ea928SPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK || 519856ea928SPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) { 520856ea928SPeter Avalos if (!ask_permission("Terminate shared connection to %s? ", 521856ea928SPeter Avalos host)) { 522856ea928SPeter Avalos debug2("%s: termination refused by user", __func__); 523856ea928SPeter Avalos buffer_put_int(r, MUX_S_PERMISSION_DENIED); 524856ea928SPeter Avalos buffer_put_int(r, rid); 525856ea928SPeter Avalos buffer_put_cstring(r, "Permission denied"); 526856ea928SPeter Avalos return 0; 527856ea928SPeter Avalos } 528856ea928SPeter Avalos } 529856ea928SPeter Avalos 530856ea928SPeter Avalos quit_pending = 1; 531856ea928SPeter Avalos buffer_put_int(r, MUX_S_OK); 532856ea928SPeter Avalos buffer_put_int(r, rid); 533856ea928SPeter Avalos /* XXX exit happens too soon - message never makes it to client */ 534856ea928SPeter Avalos return 0; 535856ea928SPeter Avalos } 536856ea928SPeter Avalos 537856ea928SPeter Avalos static char * 53836e94dc5SPeter Avalos format_forward(u_int ftype, struct Forward *fwd) 539856ea928SPeter Avalos { 540856ea928SPeter Avalos char *ret; 541856ea928SPeter Avalos 542856ea928SPeter Avalos switch (ftype) { 543856ea928SPeter Avalos case MUX_FWD_LOCAL: 544856ea928SPeter Avalos xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", 54536e94dc5SPeter Avalos (fwd->listen_path != NULL) ? fwd->listen_path : 546856ea928SPeter Avalos (fwd->listen_host == NULL) ? 54736e94dc5SPeter Avalos (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 548856ea928SPeter Avalos fwd->listen_host, fwd->listen_port, 54936e94dc5SPeter Avalos (fwd->connect_path != NULL) ? fwd->connect_path : 550856ea928SPeter Avalos fwd->connect_host, fwd->connect_port); 551856ea928SPeter Avalos break; 552856ea928SPeter Avalos case MUX_FWD_DYNAMIC: 553856ea928SPeter Avalos xasprintf(&ret, "dynamic forward %.200s:%d -> *", 554856ea928SPeter Avalos (fwd->listen_host == NULL) ? 55536e94dc5SPeter Avalos (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : 556856ea928SPeter Avalos fwd->listen_host, fwd->listen_port); 557856ea928SPeter Avalos break; 558856ea928SPeter Avalos case MUX_FWD_REMOTE: 559856ea928SPeter Avalos xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", 56036e94dc5SPeter Avalos (fwd->listen_path != NULL) ? fwd->listen_path : 561856ea928SPeter Avalos (fwd->listen_host == NULL) ? 562856ea928SPeter Avalos "LOCALHOST" : fwd->listen_host, 563856ea928SPeter Avalos fwd->listen_port, 56436e94dc5SPeter Avalos (fwd->connect_path != NULL) ? fwd->connect_path : 565856ea928SPeter Avalos fwd->connect_host, fwd->connect_port); 566856ea928SPeter Avalos break; 567856ea928SPeter Avalos default: 568856ea928SPeter Avalos fatal("%s: unknown forward type %u", __func__, ftype); 569856ea928SPeter Avalos } 570856ea928SPeter Avalos return ret; 571856ea928SPeter Avalos } 572856ea928SPeter Avalos 573856ea928SPeter Avalos static int 574856ea928SPeter Avalos compare_host(const char *a, const char *b) 575856ea928SPeter Avalos { 576856ea928SPeter Avalos if (a == NULL && b == NULL) 577856ea928SPeter Avalos return 1; 578856ea928SPeter Avalos if (a == NULL || b == NULL) 579856ea928SPeter Avalos return 0; 580856ea928SPeter Avalos return strcmp(a, b) == 0; 581856ea928SPeter Avalos } 582856ea928SPeter Avalos 583856ea928SPeter Avalos static int 58436e94dc5SPeter Avalos compare_forward(struct Forward *a, struct Forward *b) 585856ea928SPeter Avalos { 586856ea928SPeter Avalos if (!compare_host(a->listen_host, b->listen_host)) 587856ea928SPeter Avalos return 0; 58836e94dc5SPeter Avalos if (!compare_host(a->listen_path, b->listen_path)) 58936e94dc5SPeter Avalos return 0; 590856ea928SPeter Avalos if (a->listen_port != b->listen_port) 591856ea928SPeter Avalos return 0; 592856ea928SPeter Avalos if (!compare_host(a->connect_host, b->connect_host)) 593856ea928SPeter Avalos return 0; 59436e94dc5SPeter Avalos if (!compare_host(a->connect_path, b->connect_path)) 59536e94dc5SPeter Avalos return 0; 596856ea928SPeter Avalos if (a->connect_port != b->connect_port) 597856ea928SPeter Avalos return 0; 598856ea928SPeter Avalos 599856ea928SPeter Avalos return 1; 600856ea928SPeter Avalos } 601856ea928SPeter Avalos 602856ea928SPeter Avalos static void 603*ce74bacaSMatthew Dillon mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) 604856ea928SPeter Avalos { 605856ea928SPeter Avalos struct mux_channel_confirm_ctx *fctx = ctxt; 606856ea928SPeter Avalos char *failmsg = NULL; 60736e94dc5SPeter Avalos struct Forward *rfwd; 608856ea928SPeter Avalos Channel *c; 609856ea928SPeter Avalos Buffer out; 610856ea928SPeter Avalos 611*ce74bacaSMatthew Dillon if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { 612856ea928SPeter Avalos /* no channel for reply */ 613856ea928SPeter Avalos error("%s: unknown channel", __func__); 614856ea928SPeter Avalos return; 615856ea928SPeter Avalos } 616856ea928SPeter Avalos buffer_init(&out); 617e9778795SPeter Avalos if (fctx->fid >= options.num_remote_forwards || 618e9778795SPeter Avalos (options.remote_forwards[fctx->fid].connect_path == NULL && 619e9778795SPeter Avalos options.remote_forwards[fctx->fid].connect_host == NULL)) { 620856ea928SPeter Avalos xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); 621856ea928SPeter Avalos goto fail; 622856ea928SPeter Avalos } 623856ea928SPeter Avalos rfwd = &options.remote_forwards[fctx->fid]; 624856ea928SPeter Avalos debug("%s: %s for: listen %d, connect %s:%d", __func__, 625856ea928SPeter Avalos type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", 62636e94dc5SPeter Avalos rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : 62736e94dc5SPeter Avalos rfwd->connect_host, rfwd->connect_port); 628856ea928SPeter Avalos if (type == SSH2_MSG_REQUEST_SUCCESS) { 629856ea928SPeter Avalos if (rfwd->listen_port == 0) { 630856ea928SPeter Avalos rfwd->allocated_port = packet_get_int(); 631e9778795SPeter Avalos debug("Allocated port %u for mux remote forward" 632856ea928SPeter Avalos " to %s:%d", rfwd->allocated_port, 633856ea928SPeter Avalos rfwd->connect_host, rfwd->connect_port); 634856ea928SPeter Avalos buffer_put_int(&out, MUX_S_REMOTE_PORT); 635856ea928SPeter Avalos buffer_put_int(&out, fctx->rid); 636856ea928SPeter Avalos buffer_put_int(&out, rfwd->allocated_port); 637*ce74bacaSMatthew Dillon channel_update_permitted_opens(ssh, rfwd->handle, 63899e85e0dSPeter Avalos rfwd->allocated_port); 639856ea928SPeter Avalos } else { 640856ea928SPeter Avalos buffer_put_int(&out, MUX_S_OK); 641856ea928SPeter Avalos buffer_put_int(&out, fctx->rid); 642856ea928SPeter Avalos } 643856ea928SPeter Avalos goto out; 644856ea928SPeter Avalos } else { 64599e85e0dSPeter Avalos if (rfwd->listen_port == 0) 646*ce74bacaSMatthew Dillon channel_update_permitted_opens(ssh, rfwd->handle, -1); 64736e94dc5SPeter Avalos if (rfwd->listen_path != NULL) 64836e94dc5SPeter Avalos xasprintf(&failmsg, "remote port forwarding failed for " 64936e94dc5SPeter Avalos "listen path %s", rfwd->listen_path); 65036e94dc5SPeter Avalos else 651856ea928SPeter Avalos xasprintf(&failmsg, "remote port forwarding failed for " 652856ea928SPeter Avalos "listen port %d", rfwd->listen_port); 653e9778795SPeter Avalos 654e9778795SPeter Avalos debug2("%s: clearing registered forwarding for listen %d, " 655e9778795SPeter Avalos "connect %s:%d", __func__, rfwd->listen_port, 656e9778795SPeter Avalos rfwd->connect_path ? rfwd->connect_path : 657e9778795SPeter Avalos rfwd->connect_host, rfwd->connect_port); 658e9778795SPeter Avalos 659e9778795SPeter Avalos free(rfwd->listen_host); 660e9778795SPeter Avalos free(rfwd->listen_path); 661e9778795SPeter Avalos free(rfwd->connect_host); 662e9778795SPeter Avalos free(rfwd->connect_path); 663e9778795SPeter Avalos memset(rfwd, 0, sizeof(*rfwd)); 664856ea928SPeter Avalos } 665856ea928SPeter Avalos fail: 666856ea928SPeter Avalos error("%s: %s", __func__, failmsg); 667856ea928SPeter Avalos buffer_put_int(&out, MUX_S_FAILURE); 668856ea928SPeter Avalos buffer_put_int(&out, fctx->rid); 669856ea928SPeter Avalos buffer_put_cstring(&out, failmsg); 67036e94dc5SPeter Avalos free(failmsg); 671856ea928SPeter Avalos out: 672*ce74bacaSMatthew Dillon buffer_put_string(c->output, buffer_ptr(&out), buffer_len(&out)); 673856ea928SPeter Avalos buffer_free(&out); 674856ea928SPeter Avalos if (c->mux_pause <= 0) 675856ea928SPeter Avalos fatal("%s: mux_pause %d", __func__, c->mux_pause); 676856ea928SPeter Avalos c->mux_pause = 0; /* start processing messages again */ 677856ea928SPeter Avalos } 678856ea928SPeter Avalos 679856ea928SPeter Avalos static int 680*ce74bacaSMatthew Dillon process_mux_open_fwd(struct ssh *ssh, u_int rid, 681*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 682856ea928SPeter Avalos { 68336e94dc5SPeter Avalos struct Forward fwd; 684856ea928SPeter Avalos char *fwd_desc = NULL; 68536e94dc5SPeter Avalos char *listen_addr, *connect_addr; 686856ea928SPeter Avalos u_int ftype; 68736e94dc5SPeter Avalos u_int lport, cport; 688856ea928SPeter Avalos int i, ret = 0, freefwd = 1; 689856ea928SPeter Avalos 690e9778795SPeter Avalos memset(&fwd, 0, sizeof(fwd)); 691e9778795SPeter Avalos 69236e94dc5SPeter Avalos /* XXX - lport/cport check redundant */ 693856ea928SPeter Avalos if (buffer_get_int_ret(&ftype, m) != 0 || 69436e94dc5SPeter Avalos (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || 69536e94dc5SPeter Avalos buffer_get_int_ret(&lport, m) != 0 || 69636e94dc5SPeter Avalos (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || 69736e94dc5SPeter Avalos buffer_get_int_ret(&cport, m) != 0 || 69836e94dc5SPeter Avalos (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 69936e94dc5SPeter Avalos (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 700856ea928SPeter Avalos error("%s: malformed message", __func__); 701856ea928SPeter Avalos ret = -1; 702856ea928SPeter Avalos goto out; 703856ea928SPeter Avalos } 70436e94dc5SPeter Avalos if (*listen_addr == '\0') { 70536e94dc5SPeter Avalos free(listen_addr); 70636e94dc5SPeter Avalos listen_addr = NULL; 70736e94dc5SPeter Avalos } 70836e94dc5SPeter Avalos if (*connect_addr == '\0') { 70936e94dc5SPeter Avalos free(connect_addr); 71036e94dc5SPeter Avalos connect_addr = NULL; 71136e94dc5SPeter Avalos } 712856ea928SPeter Avalos 71336e94dc5SPeter Avalos memset(&fwd, 0, sizeof(fwd)); 71436e94dc5SPeter Avalos fwd.listen_port = lport; 71536e94dc5SPeter Avalos if (fwd.listen_port == PORT_STREAMLOCAL) 71636e94dc5SPeter Avalos fwd.listen_path = listen_addr; 71736e94dc5SPeter Avalos else 71836e94dc5SPeter Avalos fwd.listen_host = listen_addr; 71936e94dc5SPeter Avalos fwd.connect_port = cport; 72036e94dc5SPeter Avalos if (fwd.connect_port == PORT_STREAMLOCAL) 72136e94dc5SPeter Avalos fwd.connect_path = connect_addr; 72236e94dc5SPeter Avalos else 72336e94dc5SPeter Avalos fwd.connect_host = connect_addr; 724856ea928SPeter Avalos 725856ea928SPeter Avalos debug2("%s: channel %d: request %s", __func__, c->self, 726856ea928SPeter Avalos (fwd_desc = format_forward(ftype, &fwd))); 727856ea928SPeter Avalos 728856ea928SPeter Avalos if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && 729856ea928SPeter Avalos ftype != MUX_FWD_DYNAMIC) { 730856ea928SPeter Avalos logit("%s: invalid forwarding type %u", __func__, ftype); 731856ea928SPeter Avalos invalid: 73236e94dc5SPeter Avalos free(listen_addr); 73336e94dc5SPeter Avalos free(connect_addr); 734856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 735856ea928SPeter Avalos buffer_put_int(r, rid); 736856ea928SPeter Avalos buffer_put_cstring(r, "Invalid forwarding request"); 737856ea928SPeter Avalos return 0; 738856ea928SPeter Avalos } 73936e94dc5SPeter Avalos if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { 74036e94dc5SPeter Avalos logit("%s: streamlocal and dynamic forwards " 74136e94dc5SPeter Avalos "are mutually exclusive", __func__); 74236e94dc5SPeter Avalos goto invalid; 74336e94dc5SPeter Avalos } 74436e94dc5SPeter Avalos if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { 745856ea928SPeter Avalos logit("%s: invalid listen port %u", __func__, 746856ea928SPeter Avalos fwd.listen_port); 747856ea928SPeter Avalos goto invalid; 748856ea928SPeter Avalos } 749*ce74bacaSMatthew Dillon if ((fwd.connect_port != PORT_STREAMLOCAL && 750*ce74bacaSMatthew Dillon fwd.connect_port >= 65536) || 751*ce74bacaSMatthew Dillon (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && 752*ce74bacaSMatthew Dillon fwd.connect_port == 0)) { 753856ea928SPeter Avalos logit("%s: invalid connect port %u", __func__, 754856ea928SPeter Avalos fwd.connect_port); 755856ea928SPeter Avalos goto invalid; 756856ea928SPeter Avalos } 757*ce74bacaSMatthew Dillon if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && 758*ce74bacaSMatthew Dillon fwd.connect_path == NULL) { 759856ea928SPeter Avalos logit("%s: missing connect host", __func__); 760856ea928SPeter Avalos goto invalid; 761856ea928SPeter Avalos } 762856ea928SPeter Avalos 763856ea928SPeter Avalos /* Skip forwards that have already been requested */ 764856ea928SPeter Avalos switch (ftype) { 765856ea928SPeter Avalos case MUX_FWD_LOCAL: 766856ea928SPeter Avalos case MUX_FWD_DYNAMIC: 767856ea928SPeter Avalos for (i = 0; i < options.num_local_forwards; i++) { 768856ea928SPeter Avalos if (compare_forward(&fwd, 769856ea928SPeter Avalos options.local_forwards + i)) { 770856ea928SPeter Avalos exists: 771856ea928SPeter Avalos debug2("%s: found existing forwarding", 772856ea928SPeter Avalos __func__); 773856ea928SPeter Avalos buffer_put_int(r, MUX_S_OK); 774856ea928SPeter Avalos buffer_put_int(r, rid); 775856ea928SPeter Avalos goto out; 776856ea928SPeter Avalos } 777856ea928SPeter Avalos } 778856ea928SPeter Avalos break; 779856ea928SPeter Avalos case MUX_FWD_REMOTE: 780856ea928SPeter Avalos for (i = 0; i < options.num_remote_forwards; i++) { 781856ea928SPeter Avalos if (compare_forward(&fwd, 782856ea928SPeter Avalos options.remote_forwards + i)) { 783856ea928SPeter Avalos if (fwd.listen_port != 0) 784856ea928SPeter Avalos goto exists; 785856ea928SPeter Avalos debug2("%s: found allocated port", 786856ea928SPeter Avalos __func__); 787856ea928SPeter Avalos buffer_put_int(r, MUX_S_REMOTE_PORT); 788856ea928SPeter Avalos buffer_put_int(r, rid); 789856ea928SPeter Avalos buffer_put_int(r, 790856ea928SPeter Avalos options.remote_forwards[i].allocated_port); 791856ea928SPeter Avalos goto out; 792856ea928SPeter Avalos } 793856ea928SPeter Avalos } 794856ea928SPeter Avalos break; 795856ea928SPeter Avalos } 796856ea928SPeter Avalos 797856ea928SPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK || 798856ea928SPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) { 799856ea928SPeter Avalos if (!ask_permission("Open %s on %s?", fwd_desc, host)) { 800856ea928SPeter Avalos debug2("%s: forwarding refused by user", __func__); 801856ea928SPeter Avalos buffer_put_int(r, MUX_S_PERMISSION_DENIED); 802856ea928SPeter Avalos buffer_put_int(r, rid); 803856ea928SPeter Avalos buffer_put_cstring(r, "Permission denied"); 804856ea928SPeter Avalos goto out; 805856ea928SPeter Avalos } 806856ea928SPeter Avalos } 807856ea928SPeter Avalos 808856ea928SPeter Avalos if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { 809*ce74bacaSMatthew Dillon if (!channel_setup_local_fwd_listener(ssh, &fwd, 81036e94dc5SPeter Avalos &options.fwd_opts)) { 811856ea928SPeter Avalos fail: 812856ea928SPeter Avalos logit("slave-requested %s failed", fwd_desc); 813856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 814856ea928SPeter Avalos buffer_put_int(r, rid); 815856ea928SPeter Avalos buffer_put_cstring(r, "Port forwarding failed"); 816856ea928SPeter Avalos goto out; 817856ea928SPeter Avalos } 818856ea928SPeter Avalos add_local_forward(&options, &fwd); 819856ea928SPeter Avalos freefwd = 0; 820856ea928SPeter Avalos } else { 821856ea928SPeter Avalos struct mux_channel_confirm_ctx *fctx; 822856ea928SPeter Avalos 823*ce74bacaSMatthew Dillon fwd.handle = channel_request_remote_forwarding(ssh, &fwd); 82499e85e0dSPeter Avalos if (fwd.handle < 0) 825856ea928SPeter Avalos goto fail; 826856ea928SPeter Avalos add_remote_forward(&options, &fwd); 827856ea928SPeter Avalos fctx = xcalloc(1, sizeof(*fctx)); 828856ea928SPeter Avalos fctx->cid = c->self; 829856ea928SPeter Avalos fctx->rid = rid; 830856ea928SPeter Avalos fctx->fid = options.num_remote_forwards - 1; 831856ea928SPeter Avalos client_register_global_confirm(mux_confirm_remote_forward, 832856ea928SPeter Avalos fctx); 833856ea928SPeter Avalos freefwd = 0; 834856ea928SPeter Avalos c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ 835856ea928SPeter Avalos /* delayed reply in mux_confirm_remote_forward */ 836856ea928SPeter Avalos goto out; 837856ea928SPeter Avalos } 838856ea928SPeter Avalos buffer_put_int(r, MUX_S_OK); 839856ea928SPeter Avalos buffer_put_int(r, rid); 840856ea928SPeter Avalos out: 84136e94dc5SPeter Avalos free(fwd_desc); 842856ea928SPeter Avalos if (freefwd) { 84336e94dc5SPeter Avalos free(fwd.listen_host); 84436e94dc5SPeter Avalos free(fwd.listen_path); 84536e94dc5SPeter Avalos free(fwd.connect_host); 84636e94dc5SPeter Avalos free(fwd.connect_path); 847856ea928SPeter Avalos } 848856ea928SPeter Avalos return ret; 849856ea928SPeter Avalos } 850856ea928SPeter Avalos 851856ea928SPeter Avalos static int 852*ce74bacaSMatthew Dillon process_mux_close_fwd(struct ssh *ssh, u_int rid, 853*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 854856ea928SPeter Avalos { 85536e94dc5SPeter Avalos struct Forward fwd, *found_fwd; 856856ea928SPeter Avalos char *fwd_desc = NULL; 85799e85e0dSPeter Avalos const char *error_reason = NULL; 85836e94dc5SPeter Avalos char *listen_addr = NULL, *connect_addr = NULL; 859856ea928SPeter Avalos u_int ftype; 86036e94dc5SPeter Avalos int i, ret = 0; 86136e94dc5SPeter Avalos u_int lport, cport; 862856ea928SPeter Avalos 863e9778795SPeter Avalos memset(&fwd, 0, sizeof(fwd)); 864e9778795SPeter Avalos 865856ea928SPeter Avalos if (buffer_get_int_ret(&ftype, m) != 0 || 86636e94dc5SPeter Avalos (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || 86736e94dc5SPeter Avalos buffer_get_int_ret(&lport, m) != 0 || 86836e94dc5SPeter Avalos (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || 86936e94dc5SPeter Avalos buffer_get_int_ret(&cport, m) != 0 || 87036e94dc5SPeter Avalos (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || 87136e94dc5SPeter Avalos (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { 872856ea928SPeter Avalos error("%s: malformed message", __func__); 873856ea928SPeter Avalos ret = -1; 874856ea928SPeter Avalos goto out; 875856ea928SPeter Avalos } 876856ea928SPeter Avalos 87736e94dc5SPeter Avalos if (*listen_addr == '\0') { 87836e94dc5SPeter Avalos free(listen_addr); 87936e94dc5SPeter Avalos listen_addr = NULL; 880856ea928SPeter Avalos } 88136e94dc5SPeter Avalos if (*connect_addr == '\0') { 88236e94dc5SPeter Avalos free(connect_addr); 88336e94dc5SPeter Avalos connect_addr = NULL; 884856ea928SPeter Avalos } 885856ea928SPeter Avalos 88636e94dc5SPeter Avalos memset(&fwd, 0, sizeof(fwd)); 88736e94dc5SPeter Avalos fwd.listen_port = lport; 88836e94dc5SPeter Avalos if (fwd.listen_port == PORT_STREAMLOCAL) 88936e94dc5SPeter Avalos fwd.listen_path = listen_addr; 89036e94dc5SPeter Avalos else 89136e94dc5SPeter Avalos fwd.listen_host = listen_addr; 89236e94dc5SPeter Avalos fwd.connect_port = cport; 89336e94dc5SPeter Avalos if (fwd.connect_port == PORT_STREAMLOCAL) 89436e94dc5SPeter Avalos fwd.connect_path = connect_addr; 89536e94dc5SPeter Avalos else 89636e94dc5SPeter Avalos fwd.connect_host = connect_addr; 89736e94dc5SPeter Avalos 89899e85e0dSPeter Avalos debug2("%s: channel %d: request cancel %s", __func__, c->self, 899856ea928SPeter Avalos (fwd_desc = format_forward(ftype, &fwd))); 900856ea928SPeter Avalos 90199e85e0dSPeter Avalos /* make sure this has been requested */ 90299e85e0dSPeter Avalos found_fwd = NULL; 90399e85e0dSPeter Avalos switch (ftype) { 90499e85e0dSPeter Avalos case MUX_FWD_LOCAL: 90599e85e0dSPeter Avalos case MUX_FWD_DYNAMIC: 90699e85e0dSPeter Avalos for (i = 0; i < options.num_local_forwards; i++) { 90799e85e0dSPeter Avalos if (compare_forward(&fwd, 90899e85e0dSPeter Avalos options.local_forwards + i)) { 90999e85e0dSPeter Avalos found_fwd = options.local_forwards + i; 91099e85e0dSPeter Avalos break; 91199e85e0dSPeter Avalos } 91299e85e0dSPeter Avalos } 91399e85e0dSPeter Avalos break; 91499e85e0dSPeter Avalos case MUX_FWD_REMOTE: 91599e85e0dSPeter Avalos for (i = 0; i < options.num_remote_forwards; i++) { 91699e85e0dSPeter Avalos if (compare_forward(&fwd, 91799e85e0dSPeter Avalos options.remote_forwards + i)) { 91899e85e0dSPeter Avalos found_fwd = options.remote_forwards + i; 91999e85e0dSPeter Avalos break; 92099e85e0dSPeter Avalos } 92199e85e0dSPeter Avalos } 92299e85e0dSPeter Avalos break; 92399e85e0dSPeter Avalos } 92499e85e0dSPeter Avalos 92599e85e0dSPeter Avalos if (found_fwd == NULL) 92699e85e0dSPeter Avalos error_reason = "port not forwarded"; 92799e85e0dSPeter Avalos else if (ftype == MUX_FWD_REMOTE) { 92899e85e0dSPeter Avalos /* 92999e85e0dSPeter Avalos * This shouldn't fail unless we confused the host/port 93099e85e0dSPeter Avalos * between options.remote_forwards and permitted_opens. 93199e85e0dSPeter Avalos * However, for dynamic allocated listen ports we need 93236e94dc5SPeter Avalos * to use the actual listen port. 93399e85e0dSPeter Avalos */ 934*ce74bacaSMatthew Dillon if (channel_request_rforward_cancel(ssh, found_fwd) == -1) 93599e85e0dSPeter Avalos error_reason = "port not in permitted opens"; 93699e85e0dSPeter Avalos } else { /* local and dynamic forwards */ 93799e85e0dSPeter Avalos /* Ditto */ 938*ce74bacaSMatthew Dillon if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, 93936e94dc5SPeter Avalos &options.fwd_opts) == -1) 94099e85e0dSPeter Avalos error_reason = "port not found"; 94199e85e0dSPeter Avalos } 94299e85e0dSPeter Avalos 94399e85e0dSPeter Avalos if (error_reason == NULL) { 94499e85e0dSPeter Avalos buffer_put_int(r, MUX_S_OK); 94599e85e0dSPeter Avalos buffer_put_int(r, rid); 94699e85e0dSPeter Avalos 94736e94dc5SPeter Avalos free(found_fwd->listen_host); 94836e94dc5SPeter Avalos free(found_fwd->listen_path); 94936e94dc5SPeter Avalos free(found_fwd->connect_host); 95036e94dc5SPeter Avalos free(found_fwd->connect_path); 95199e85e0dSPeter Avalos found_fwd->listen_host = found_fwd->connect_host = NULL; 95236e94dc5SPeter Avalos found_fwd->listen_path = found_fwd->connect_path = NULL; 95399e85e0dSPeter Avalos found_fwd->listen_port = found_fwd->connect_port = 0; 95499e85e0dSPeter Avalos } else { 955856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 956856ea928SPeter Avalos buffer_put_int(r, rid); 95799e85e0dSPeter Avalos buffer_put_cstring(r, error_reason); 95899e85e0dSPeter Avalos } 959856ea928SPeter Avalos out: 96036e94dc5SPeter Avalos free(fwd_desc); 96136e94dc5SPeter Avalos free(listen_addr); 96236e94dc5SPeter Avalos free(connect_addr); 963856ea928SPeter Avalos 964856ea928SPeter Avalos return ret; 965856ea928SPeter Avalos } 966856ea928SPeter Avalos 967856ea928SPeter Avalos static int 968*ce74bacaSMatthew Dillon process_mux_stdio_fwd(struct ssh *ssh, u_int rid, 969*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 970856ea928SPeter Avalos { 971856ea928SPeter Avalos Channel *nc; 972856ea928SPeter Avalos char *reserved, *chost; 973856ea928SPeter Avalos u_int cport, i, j; 974856ea928SPeter Avalos int new_fd[2]; 97536e94dc5SPeter Avalos struct mux_stdio_confirm_ctx *cctx; 976856ea928SPeter Avalos 977856ea928SPeter Avalos chost = reserved = NULL; 978856ea928SPeter Avalos if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || 979856ea928SPeter Avalos (chost = buffer_get_string_ret(m, NULL)) == NULL || 980856ea928SPeter Avalos buffer_get_int_ret(&cport, m) != 0) { 98136e94dc5SPeter Avalos free(reserved); 98236e94dc5SPeter Avalos free(chost); 983856ea928SPeter Avalos error("%s: malformed message", __func__); 984856ea928SPeter Avalos return -1; 985856ea928SPeter Avalos } 98636e94dc5SPeter Avalos free(reserved); 987856ea928SPeter Avalos 988856ea928SPeter Avalos debug2("%s: channel %d: request stdio fwd to %s:%u", 989856ea928SPeter Avalos __func__, c->self, chost, cport); 990856ea928SPeter Avalos 991856ea928SPeter Avalos /* Gather fds from client */ 992856ea928SPeter Avalos for(i = 0; i < 2; i++) { 993856ea928SPeter Avalos if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { 994856ea928SPeter Avalos error("%s: failed to receive fd %d from slave", 995856ea928SPeter Avalos __func__, i); 996856ea928SPeter Avalos for (j = 0; j < i; j++) 997856ea928SPeter Avalos close(new_fd[j]); 99836e94dc5SPeter Avalos free(chost); 999856ea928SPeter Avalos 1000856ea928SPeter Avalos /* prepare reply */ 1001856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 1002856ea928SPeter Avalos buffer_put_int(r, rid); 1003856ea928SPeter Avalos buffer_put_cstring(r, 1004856ea928SPeter Avalos "did not receive file descriptors"); 1005856ea928SPeter Avalos return -1; 1006856ea928SPeter Avalos } 1007856ea928SPeter Avalos } 1008856ea928SPeter Avalos 1009856ea928SPeter Avalos debug3("%s: got fds stdin %d, stdout %d", __func__, 1010856ea928SPeter Avalos new_fd[0], new_fd[1]); 1011856ea928SPeter Avalos 1012856ea928SPeter Avalos /* XXX support multiple child sessions in future */ 1013*ce74bacaSMatthew Dillon if (c->have_remote_id) { 1014856ea928SPeter Avalos debug2("%s: session already open", __func__); 1015856ea928SPeter Avalos /* prepare reply */ 1016856ea928SPeter Avalos buffer_put_int(r, MUX_S_FAILURE); 1017856ea928SPeter Avalos buffer_put_int(r, rid); 1018856ea928SPeter Avalos buffer_put_cstring(r, "Multiple sessions not supported"); 1019856ea928SPeter Avalos cleanup: 1020856ea928SPeter Avalos close(new_fd[0]); 1021856ea928SPeter Avalos close(new_fd[1]); 102236e94dc5SPeter Avalos free(chost); 1023856ea928SPeter Avalos return 0; 1024856ea928SPeter Avalos } 1025856ea928SPeter Avalos 1026856ea928SPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK || 1027856ea928SPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) { 10289f304aafSPeter Avalos if (!ask_permission("Allow forward to %s:%u? ", 1029856ea928SPeter Avalos chost, cport)) { 1030856ea928SPeter Avalos debug2("%s: stdio fwd refused by user", __func__); 1031856ea928SPeter Avalos /* prepare reply */ 1032856ea928SPeter Avalos buffer_put_int(r, MUX_S_PERMISSION_DENIED); 1033856ea928SPeter Avalos buffer_put_int(r, rid); 1034856ea928SPeter Avalos buffer_put_cstring(r, "Permission denied"); 1035856ea928SPeter Avalos goto cleanup; 1036856ea928SPeter Avalos } 1037856ea928SPeter Avalos } 1038856ea928SPeter Avalos 1039856ea928SPeter Avalos /* enable nonblocking unless tty */ 1040856ea928SPeter Avalos if (!isatty(new_fd[0])) 1041856ea928SPeter Avalos set_nonblock(new_fd[0]); 1042856ea928SPeter Avalos if (!isatty(new_fd[1])) 1043856ea928SPeter Avalos set_nonblock(new_fd[1]); 1044856ea928SPeter Avalos 1045*ce74bacaSMatthew Dillon nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1]); 1046856ea928SPeter Avalos 1047856ea928SPeter Avalos nc->ctl_chan = c->self; /* link session -> control channel */ 1048856ea928SPeter Avalos c->remote_id = nc->self; /* link control -> session channel */ 1049*ce74bacaSMatthew Dillon c->have_remote_id = 1; 1050856ea928SPeter Avalos 1051856ea928SPeter Avalos debug2("%s: channel_new: %d linked to control channel %d", 1052856ea928SPeter Avalos __func__, nc->self, nc->ctl_chan); 1053856ea928SPeter Avalos 1054*ce74bacaSMatthew Dillon channel_register_cleanup(ssh, nc->self, 1055*ce74bacaSMatthew Dillon mux_master_session_cleanup_cb, 1); 1056856ea928SPeter Avalos 105736e94dc5SPeter Avalos cctx = xcalloc(1, sizeof(*cctx)); 105836e94dc5SPeter Avalos cctx->rid = rid; 1059*ce74bacaSMatthew Dillon channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); 106036e94dc5SPeter Avalos c->mux_pause = 1; /* stop handling messages until open_confirm done */ 1061856ea928SPeter Avalos 106236e94dc5SPeter Avalos /* reply is deferred, sent by mux_session_confirm */ 1063856ea928SPeter Avalos return 0; 1064856ea928SPeter Avalos } 1065856ea928SPeter Avalos 106636e94dc5SPeter Avalos /* Callback on open confirmation in mux master for a mux stdio fwd session. */ 106736e94dc5SPeter Avalos static void 1068*ce74bacaSMatthew Dillon mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) 106936e94dc5SPeter Avalos { 107036e94dc5SPeter Avalos struct mux_stdio_confirm_ctx *cctx = arg; 107136e94dc5SPeter Avalos Channel *c, *cc; 107236e94dc5SPeter Avalos Buffer reply; 107336e94dc5SPeter Avalos 107436e94dc5SPeter Avalos if (cctx == NULL) 107536e94dc5SPeter Avalos fatal("%s: cctx == NULL", __func__); 1076*ce74bacaSMatthew Dillon if ((c = channel_by_id(ssh, id)) == NULL) 107736e94dc5SPeter Avalos fatal("%s: no channel for id %d", __func__, id); 1078*ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 107936e94dc5SPeter Avalos fatal("%s: channel %d lacks control channel %d", __func__, 108036e94dc5SPeter Avalos id, c->ctl_chan); 108136e94dc5SPeter Avalos 108236e94dc5SPeter Avalos if (!success) { 108336e94dc5SPeter Avalos debug3("%s: sending failure reply", __func__); 108436e94dc5SPeter Avalos /* prepare reply */ 108536e94dc5SPeter Avalos buffer_init(&reply); 108636e94dc5SPeter Avalos buffer_put_int(&reply, MUX_S_FAILURE); 108736e94dc5SPeter Avalos buffer_put_int(&reply, cctx->rid); 108836e94dc5SPeter Avalos buffer_put_cstring(&reply, "Session open refused by peer"); 108936e94dc5SPeter Avalos goto done; 109036e94dc5SPeter Avalos } 109136e94dc5SPeter Avalos 109236e94dc5SPeter Avalos debug3("%s: sending success reply", __func__); 109336e94dc5SPeter Avalos /* prepare reply */ 109436e94dc5SPeter Avalos buffer_init(&reply); 109536e94dc5SPeter Avalos buffer_put_int(&reply, MUX_S_SESSION_OPENED); 109636e94dc5SPeter Avalos buffer_put_int(&reply, cctx->rid); 109736e94dc5SPeter Avalos buffer_put_int(&reply, c->self); 109836e94dc5SPeter Avalos 109936e94dc5SPeter Avalos done: 110036e94dc5SPeter Avalos /* Send reply */ 1101*ce74bacaSMatthew Dillon buffer_put_string(cc->output, buffer_ptr(&reply), buffer_len(&reply)); 110236e94dc5SPeter Avalos buffer_free(&reply); 110336e94dc5SPeter Avalos 110436e94dc5SPeter Avalos if (cc->mux_pause <= 0) 110536e94dc5SPeter Avalos fatal("%s: mux_pause %d", __func__, cc->mux_pause); 110636e94dc5SPeter Avalos cc->mux_pause = 0; /* start processing messages again */ 110736e94dc5SPeter Avalos c->open_confirm_ctx = NULL; 110836e94dc5SPeter Avalos free(cctx); 110936e94dc5SPeter Avalos } 111036e94dc5SPeter Avalos 11111c188a7fSPeter Avalos static int 1112*ce74bacaSMatthew Dillon process_mux_stop_listening(struct ssh *ssh, u_int rid, 1113*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 11141c188a7fSPeter Avalos { 11151c188a7fSPeter Avalos debug("%s: channel %d: stop listening", __func__, c->self); 11161c188a7fSPeter Avalos 11171c188a7fSPeter Avalos if (options.control_master == SSHCTL_MASTER_ASK || 11181c188a7fSPeter Avalos options.control_master == SSHCTL_MASTER_AUTO_ASK) { 11191c188a7fSPeter Avalos if (!ask_permission("Disable further multiplexing on shared " 11201c188a7fSPeter Avalos "connection to %s? ", host)) { 11211c188a7fSPeter Avalos debug2("%s: stop listen refused by user", __func__); 11221c188a7fSPeter Avalos buffer_put_int(r, MUX_S_PERMISSION_DENIED); 11231c188a7fSPeter Avalos buffer_put_int(r, rid); 11241c188a7fSPeter Avalos buffer_put_cstring(r, "Permission denied"); 11251c188a7fSPeter Avalos return 0; 11261c188a7fSPeter Avalos } 11271c188a7fSPeter Avalos } 11281c188a7fSPeter Avalos 11291c188a7fSPeter Avalos if (mux_listener_channel != NULL) { 1130*ce74bacaSMatthew Dillon channel_free(ssh, mux_listener_channel); 11311c188a7fSPeter Avalos client_stop_mux(); 113236e94dc5SPeter Avalos free(options.control_path); 11331c188a7fSPeter Avalos options.control_path = NULL; 11341c188a7fSPeter Avalos mux_listener_channel = NULL; 11351c188a7fSPeter Avalos muxserver_sock = -1; 11361c188a7fSPeter Avalos } 11371c188a7fSPeter Avalos 11381c188a7fSPeter Avalos /* prepare reply */ 11391c188a7fSPeter Avalos buffer_put_int(r, MUX_S_OK); 11401c188a7fSPeter Avalos buffer_put_int(r, rid); 11411c188a7fSPeter Avalos 11421c188a7fSPeter Avalos return 0; 11431c188a7fSPeter Avalos } 11441c188a7fSPeter Avalos 1145*ce74bacaSMatthew Dillon static int 1146*ce74bacaSMatthew Dillon process_mux_proxy(struct ssh *ssh, u_int rid, 1147*ce74bacaSMatthew Dillon Channel *c, Buffer *m, Buffer *r) 1148*ce74bacaSMatthew Dillon { 1149*ce74bacaSMatthew Dillon debug("%s: channel %d: proxy request", __func__, c->self); 1150*ce74bacaSMatthew Dillon 1151*ce74bacaSMatthew Dillon c->mux_rcb = channel_proxy_downstream; 1152*ce74bacaSMatthew Dillon buffer_put_int(r, MUX_S_PROXY); 1153*ce74bacaSMatthew Dillon buffer_put_int(r, rid); 1154*ce74bacaSMatthew Dillon 1155*ce74bacaSMatthew Dillon return 0; 1156*ce74bacaSMatthew Dillon } 1157*ce74bacaSMatthew Dillon 1158856ea928SPeter Avalos /* Channel callbacks fired on read/write from mux slave fd */ 1159856ea928SPeter Avalos static int 1160*ce74bacaSMatthew Dillon mux_master_read_cb(struct ssh *ssh, Channel *c) 1161856ea928SPeter Avalos { 1162856ea928SPeter Avalos struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; 1163856ea928SPeter Avalos Buffer in, out; 116436e94dc5SPeter Avalos const u_char *ptr; 1165856ea928SPeter Avalos u_int type, rid, have, i; 1166856ea928SPeter Avalos int ret = -1; 1167856ea928SPeter Avalos 1168856ea928SPeter Avalos /* Setup ctx and */ 1169856ea928SPeter Avalos if (c->mux_ctx == NULL) { 1170856ea928SPeter Avalos state = xcalloc(1, sizeof(*state)); 1171856ea928SPeter Avalos c->mux_ctx = state; 1172*ce74bacaSMatthew Dillon channel_register_cleanup(ssh, c->self, 1173856ea928SPeter Avalos mux_master_control_cleanup_cb, 0); 1174856ea928SPeter Avalos 1175856ea928SPeter Avalos /* Send hello */ 1176856ea928SPeter Avalos buffer_init(&out); 1177856ea928SPeter Avalos buffer_put_int(&out, MUX_MSG_HELLO); 1178856ea928SPeter Avalos buffer_put_int(&out, SSHMUX_VER); 1179856ea928SPeter Avalos /* no extensions */ 1180*ce74bacaSMatthew Dillon buffer_put_string(c->output, buffer_ptr(&out), 1181856ea928SPeter Avalos buffer_len(&out)); 1182856ea928SPeter Avalos buffer_free(&out); 1183856ea928SPeter Avalos debug3("%s: channel %d: hello sent", __func__, c->self); 1184856ea928SPeter Avalos return 0; 1185856ea928SPeter Avalos } 1186856ea928SPeter Avalos 1187856ea928SPeter Avalos buffer_init(&in); 1188856ea928SPeter Avalos buffer_init(&out); 1189856ea928SPeter Avalos 1190856ea928SPeter Avalos /* Channel code ensures that we receive whole packets */ 1191*ce74bacaSMatthew Dillon if ((ptr = buffer_get_string_ptr_ret(c->input, &have)) == NULL) { 1192856ea928SPeter Avalos malf: 1193856ea928SPeter Avalos error("%s: malformed message", __func__); 1194856ea928SPeter Avalos goto out; 1195856ea928SPeter Avalos } 1196856ea928SPeter Avalos buffer_append(&in, ptr, have); 1197856ea928SPeter Avalos 1198856ea928SPeter Avalos if (buffer_get_int_ret(&type, &in) != 0) 1199856ea928SPeter Avalos goto malf; 1200856ea928SPeter Avalos debug3("%s: channel %d packet type 0x%08x len %u", 1201856ea928SPeter Avalos __func__, c->self, type, buffer_len(&in)); 1202856ea928SPeter Avalos 1203856ea928SPeter Avalos if (type == MUX_MSG_HELLO) 1204856ea928SPeter Avalos rid = 0; 1205856ea928SPeter Avalos else { 1206856ea928SPeter Avalos if (!state->hello_rcvd) { 1207856ea928SPeter Avalos error("%s: expected MUX_MSG_HELLO(0x%08x), " 1208856ea928SPeter Avalos "received 0x%08x", __func__, MUX_MSG_HELLO, type); 1209856ea928SPeter Avalos goto out; 1210856ea928SPeter Avalos } 1211856ea928SPeter Avalos if (buffer_get_int_ret(&rid, &in) != 0) 1212856ea928SPeter Avalos goto malf; 1213856ea928SPeter Avalos } 1214856ea928SPeter Avalos 1215856ea928SPeter Avalos for (i = 0; mux_master_handlers[i].handler != NULL; i++) { 1216856ea928SPeter Avalos if (type == mux_master_handlers[i].type) { 1217*ce74bacaSMatthew Dillon ret = mux_master_handlers[i].handler(ssh, rid, 1218*ce74bacaSMatthew Dillon c, &in, &out); 1219856ea928SPeter Avalos break; 1220856ea928SPeter Avalos } 1221856ea928SPeter Avalos } 1222856ea928SPeter Avalos if (mux_master_handlers[i].handler == NULL) { 1223856ea928SPeter Avalos error("%s: unsupported mux message 0x%08x", __func__, type); 1224856ea928SPeter Avalos buffer_put_int(&out, MUX_S_FAILURE); 1225856ea928SPeter Avalos buffer_put_int(&out, rid); 1226856ea928SPeter Avalos buffer_put_cstring(&out, "unsupported request"); 1227856ea928SPeter Avalos ret = 0; 1228856ea928SPeter Avalos } 1229856ea928SPeter Avalos /* Enqueue reply packet */ 1230856ea928SPeter Avalos if (buffer_len(&out) != 0) { 1231*ce74bacaSMatthew Dillon buffer_put_string(c->output, buffer_ptr(&out), 1232856ea928SPeter Avalos buffer_len(&out)); 1233856ea928SPeter Avalos } 1234856ea928SPeter Avalos out: 1235856ea928SPeter Avalos buffer_free(&in); 1236856ea928SPeter Avalos buffer_free(&out); 1237856ea928SPeter Avalos return ret; 1238856ea928SPeter Avalos } 1239856ea928SPeter Avalos 1240856ea928SPeter Avalos void 1241*ce74bacaSMatthew Dillon mux_exit_message(struct ssh *ssh, Channel *c, int exitval) 1242856ea928SPeter Avalos { 1243856ea928SPeter Avalos Buffer m; 1244856ea928SPeter Avalos Channel *mux_chan; 1245856ea928SPeter Avalos 124636e94dc5SPeter Avalos debug3("%s: channel %d: exit message, exitval %d", __func__, c->self, 1247856ea928SPeter Avalos exitval); 1248856ea928SPeter Avalos 1249*ce74bacaSMatthew Dillon if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 1250856ea928SPeter Avalos fatal("%s: channel %d missing mux channel %d", 1251856ea928SPeter Avalos __func__, c->self, c->ctl_chan); 1252856ea928SPeter Avalos 1253856ea928SPeter Avalos /* Append exit message packet to control socket output queue */ 1254856ea928SPeter Avalos buffer_init(&m); 1255856ea928SPeter Avalos buffer_put_int(&m, MUX_S_EXIT_MESSAGE); 1256856ea928SPeter Avalos buffer_put_int(&m, c->self); 1257856ea928SPeter Avalos buffer_put_int(&m, exitval); 1258856ea928SPeter Avalos 1259*ce74bacaSMatthew Dillon buffer_put_string(mux_chan->output, buffer_ptr(&m), buffer_len(&m)); 1260856ea928SPeter Avalos buffer_free(&m); 1261856ea928SPeter Avalos } 126218de8d7fSPeter Avalos 12631c188a7fSPeter Avalos void 1264*ce74bacaSMatthew Dillon mux_tty_alloc_failed(struct ssh *ssh, Channel *c) 12651c188a7fSPeter Avalos { 12661c188a7fSPeter Avalos Buffer m; 12671c188a7fSPeter Avalos Channel *mux_chan; 12681c188a7fSPeter Avalos 12691c188a7fSPeter Avalos debug3("%s: channel %d: TTY alloc failed", __func__, c->self); 12701c188a7fSPeter Avalos 1271*ce74bacaSMatthew Dillon if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) 12721c188a7fSPeter Avalos fatal("%s: channel %d missing mux channel %d", 12731c188a7fSPeter Avalos __func__, c->self, c->ctl_chan); 12741c188a7fSPeter Avalos 12751c188a7fSPeter Avalos /* Append exit message packet to control socket output queue */ 12761c188a7fSPeter Avalos buffer_init(&m); 12771c188a7fSPeter Avalos buffer_put_int(&m, MUX_S_TTY_ALLOC_FAIL); 12781c188a7fSPeter Avalos buffer_put_int(&m, c->self); 12791c188a7fSPeter Avalos 1280*ce74bacaSMatthew Dillon buffer_put_string(mux_chan->output, buffer_ptr(&m), buffer_len(&m)); 12811c188a7fSPeter Avalos buffer_free(&m); 12821c188a7fSPeter Avalos } 12831c188a7fSPeter Avalos 128418de8d7fSPeter Avalos /* Prepare a mux master to listen on a Unix domain socket. */ 128518de8d7fSPeter Avalos void 1286*ce74bacaSMatthew Dillon muxserver_listen(struct ssh *ssh) 128718de8d7fSPeter Avalos { 128818de8d7fSPeter Avalos mode_t old_umask; 12899f304aafSPeter Avalos char *orig_control_path = options.control_path; 12909f304aafSPeter Avalos char rbuf[16+1]; 12919f304aafSPeter Avalos u_int i, r; 129236e94dc5SPeter Avalos int oerrno; 129318de8d7fSPeter Avalos 129418de8d7fSPeter Avalos if (options.control_path == NULL || 129518de8d7fSPeter Avalos options.control_master == SSHCTL_MASTER_NO) 129618de8d7fSPeter Avalos return; 129718de8d7fSPeter Avalos 129818de8d7fSPeter Avalos debug("setting up multiplex master socket"); 129918de8d7fSPeter Avalos 13009f304aafSPeter Avalos /* 13019f304aafSPeter Avalos * Use a temporary path before listen so we can pseudo-atomically 13029f304aafSPeter Avalos * establish the listening socket in its final location to avoid 13039f304aafSPeter Avalos * other processes racing in between bind() and listen() and hitting 13049f304aafSPeter Avalos * an unready socket. 13059f304aafSPeter Avalos */ 13069f304aafSPeter Avalos for (i = 0; i < sizeof(rbuf) - 1; i++) { 13079f304aafSPeter Avalos r = arc4random_uniform(26+26+10); 13089f304aafSPeter Avalos rbuf[i] = (r < 26) ? 'a' + r : 13099f304aafSPeter Avalos (r < 26*2) ? 'A' + r - 26 : 13109f304aafSPeter Avalos '0' + r - 26 - 26; 13119f304aafSPeter Avalos } 13129f304aafSPeter Avalos rbuf[sizeof(rbuf) - 1] = '\0'; 13139f304aafSPeter Avalos options.control_path = NULL; 13149f304aafSPeter Avalos xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); 13159f304aafSPeter Avalos debug3("%s: temporary control path %s", __func__, options.control_path); 13169f304aafSPeter Avalos 131718de8d7fSPeter Avalos old_umask = umask(0177); 131836e94dc5SPeter Avalos muxserver_sock = unix_listener(options.control_path, 64, 0); 131936e94dc5SPeter Avalos oerrno = errno; 132036e94dc5SPeter Avalos umask(old_umask); 132136e94dc5SPeter Avalos if (muxserver_sock < 0) { 132236e94dc5SPeter Avalos if (oerrno == EINVAL || oerrno == EADDRINUSE) { 132318de8d7fSPeter Avalos error("ControlSocket %s already exists, " 132418de8d7fSPeter Avalos "disabling multiplexing", options.control_path); 13259f304aafSPeter Avalos disable_mux_master: 13261c188a7fSPeter Avalos if (muxserver_sock != -1) { 132718de8d7fSPeter Avalos close(muxserver_sock); 132818de8d7fSPeter Avalos muxserver_sock = -1; 13291c188a7fSPeter Avalos } 133036e94dc5SPeter Avalos free(orig_control_path); 133136e94dc5SPeter Avalos free(options.control_path); 133218de8d7fSPeter Avalos options.control_path = NULL; 133318de8d7fSPeter Avalos options.control_master = SSHCTL_MASTER_NO; 133418de8d7fSPeter Avalos return; 133536e94dc5SPeter Avalos } else { 133636e94dc5SPeter Avalos /* unix_listener() logs the error */ 133736e94dc5SPeter Avalos cleanup_exit(255); 133818de8d7fSPeter Avalos } 133936e94dc5SPeter Avalos } 134018de8d7fSPeter Avalos 13419f304aafSPeter Avalos /* Now atomically "move" the mux socket into position */ 13429f304aafSPeter Avalos if (link(options.control_path, orig_control_path) != 0) { 13439f304aafSPeter Avalos if (errno != EEXIST) { 13449f304aafSPeter Avalos fatal("%s: link mux listener %s => %s: %s", __func__, 13459f304aafSPeter Avalos options.control_path, orig_control_path, 13469f304aafSPeter Avalos strerror(errno)); 13479f304aafSPeter Avalos } 13489f304aafSPeter Avalos error("ControlSocket %s already exists, disabling multiplexing", 13499f304aafSPeter Avalos orig_control_path); 13509f304aafSPeter Avalos unlink(options.control_path); 13519f304aafSPeter Avalos goto disable_mux_master; 13529f304aafSPeter Avalos } 13539f304aafSPeter Avalos unlink(options.control_path); 135436e94dc5SPeter Avalos free(options.control_path); 13559f304aafSPeter Avalos options.control_path = orig_control_path; 13569f304aafSPeter Avalos 135718de8d7fSPeter Avalos set_nonblock(muxserver_sock); 1358856ea928SPeter Avalos 1359*ce74bacaSMatthew Dillon mux_listener_channel = channel_new(ssh, "mux listener", 1360856ea928SPeter Avalos SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, 1361856ea928SPeter Avalos CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 13629f304aafSPeter Avalos 0, options.control_path, 1); 1363856ea928SPeter Avalos mux_listener_channel->mux_rcb = mux_master_read_cb; 1364856ea928SPeter Avalos debug3("%s: mux listener channel %d fd %d", __func__, 1365856ea928SPeter Avalos mux_listener_channel->self, mux_listener_channel->sock); 136618de8d7fSPeter Avalos } 136718de8d7fSPeter Avalos 136818de8d7fSPeter Avalos /* Callback on open confirmation in mux master for a mux client session. */ 136918de8d7fSPeter Avalos static void 1370*ce74bacaSMatthew Dillon mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) 137118de8d7fSPeter Avalos { 137218de8d7fSPeter Avalos struct mux_session_confirm_ctx *cctx = arg; 137318de8d7fSPeter Avalos const char *display; 1374856ea928SPeter Avalos Channel *c, *cc; 137518de8d7fSPeter Avalos int i; 1376856ea928SPeter Avalos Buffer reply; 137718de8d7fSPeter Avalos 137818de8d7fSPeter Avalos if (cctx == NULL) 137918de8d7fSPeter Avalos fatal("%s: cctx == NULL", __func__); 1380*ce74bacaSMatthew Dillon if ((c = channel_by_id(ssh, id)) == NULL) 138118de8d7fSPeter Avalos fatal("%s: no channel for id %d", __func__, id); 1382*ce74bacaSMatthew Dillon if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) 1383856ea928SPeter Avalos fatal("%s: channel %d lacks control channel %d", __func__, 1384856ea928SPeter Avalos id, c->ctl_chan); 1385856ea928SPeter Avalos 1386856ea928SPeter Avalos if (!success) { 1387856ea928SPeter Avalos debug3("%s: sending failure reply", __func__); 1388856ea928SPeter Avalos /* prepare reply */ 1389856ea928SPeter Avalos buffer_init(&reply); 1390856ea928SPeter Avalos buffer_put_int(&reply, MUX_S_FAILURE); 1391856ea928SPeter Avalos buffer_put_int(&reply, cctx->rid); 1392856ea928SPeter Avalos buffer_put_cstring(&reply, "Session open refused by peer"); 1393856ea928SPeter Avalos goto done; 1394856ea928SPeter Avalos } 139518de8d7fSPeter Avalos 139618de8d7fSPeter Avalos display = getenv("DISPLAY"); 139718de8d7fSPeter Avalos if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { 139818de8d7fSPeter Avalos char *proto, *data; 1399856ea928SPeter Avalos 140018de8d7fSPeter Avalos /* Get reasonable local authentication information. */ 1401*ce74bacaSMatthew Dillon if (client_x11_get_proto(ssh, display, options.xauth_location, 1402856ea928SPeter Avalos options.forward_x11_trusted, options.forward_x11_timeout, 1403e9778795SPeter Avalos &proto, &data) == 0) { 140418de8d7fSPeter Avalos /* Request forwarding with authentication spoofing. */ 1405856ea928SPeter Avalos debug("Requesting X11 forwarding with authentication " 1406856ea928SPeter Avalos "spoofing."); 1407*ce74bacaSMatthew Dillon x11_request_forwarding_with_spoofing(ssh, id, 1408*ce74bacaSMatthew Dillon display, proto, data, 1); 14091c188a7fSPeter Avalos /* XXX exit_on_forward_failure */ 1410*ce74bacaSMatthew Dillon client_expect_confirm(ssh, id, "X11 forwarding", 1411e9778795SPeter Avalos CONFIRM_WARN); 1412e9778795SPeter Avalos } 141318de8d7fSPeter Avalos } 141418de8d7fSPeter Avalos 141518de8d7fSPeter Avalos if (cctx->want_agent_fwd && options.forward_agent) { 141618de8d7fSPeter Avalos debug("Requesting authentication agent forwarding."); 1417*ce74bacaSMatthew Dillon channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); 141818de8d7fSPeter Avalos packet_send(); 141918de8d7fSPeter Avalos } 142018de8d7fSPeter Avalos 1421*ce74bacaSMatthew Dillon client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, 142218de8d7fSPeter Avalos cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); 142318de8d7fSPeter Avalos 1424856ea928SPeter Avalos debug3("%s: sending success reply", __func__); 1425856ea928SPeter Avalos /* prepare reply */ 1426856ea928SPeter Avalos buffer_init(&reply); 1427856ea928SPeter Avalos buffer_put_int(&reply, MUX_S_SESSION_OPENED); 1428856ea928SPeter Avalos buffer_put_int(&reply, cctx->rid); 1429856ea928SPeter Avalos buffer_put_int(&reply, c->self); 1430856ea928SPeter Avalos 1431856ea928SPeter Avalos done: 1432856ea928SPeter Avalos /* Send reply */ 1433*ce74bacaSMatthew Dillon buffer_put_string(cc->output, buffer_ptr(&reply), buffer_len(&reply)); 1434856ea928SPeter Avalos buffer_free(&reply); 1435856ea928SPeter Avalos 1436856ea928SPeter Avalos if (cc->mux_pause <= 0) 1437856ea928SPeter Avalos fatal("%s: mux_pause %d", __func__, cc->mux_pause); 1438856ea928SPeter Avalos cc->mux_pause = 0; /* start processing messages again */ 143918de8d7fSPeter Avalos c->open_confirm_ctx = NULL; 144018de8d7fSPeter Avalos buffer_free(&cctx->cmd); 144136e94dc5SPeter Avalos free(cctx->term); 144218de8d7fSPeter Avalos if (cctx->env != NULL) { 144318de8d7fSPeter Avalos for (i = 0; cctx->env[i] != NULL; i++) 144436e94dc5SPeter Avalos free(cctx->env[i]); 144536e94dc5SPeter Avalos free(cctx->env); 144618de8d7fSPeter Avalos } 144736e94dc5SPeter Avalos free(cctx); 144818de8d7fSPeter Avalos } 144918de8d7fSPeter Avalos 145018de8d7fSPeter Avalos /* ** Multiplexing client support */ 145118de8d7fSPeter Avalos 145218de8d7fSPeter Avalos /* Exit signal handler */ 145318de8d7fSPeter Avalos static void 145418de8d7fSPeter Avalos control_client_sighandler(int signo) 145518de8d7fSPeter Avalos { 145618de8d7fSPeter Avalos muxclient_terminate = signo; 145718de8d7fSPeter Avalos } 145818de8d7fSPeter Avalos 145918de8d7fSPeter Avalos /* 146018de8d7fSPeter Avalos * Relay signal handler - used to pass some signals from mux client to 146118de8d7fSPeter Avalos * mux master. 146218de8d7fSPeter Avalos */ 146318de8d7fSPeter Avalos static void 146418de8d7fSPeter Avalos control_client_sigrelay(int signo) 146518de8d7fSPeter Avalos { 146618de8d7fSPeter Avalos int save_errno = errno; 146718de8d7fSPeter Avalos 146818de8d7fSPeter Avalos if (muxserver_pid > 1) 146918de8d7fSPeter Avalos kill(muxserver_pid, signo); 147018de8d7fSPeter Avalos 147118de8d7fSPeter Avalos errno = save_errno; 147218de8d7fSPeter Avalos } 147318de8d7fSPeter Avalos 147418de8d7fSPeter Avalos static int 1475856ea928SPeter Avalos mux_client_read(int fd, Buffer *b, u_int need) 147618de8d7fSPeter Avalos { 1477856ea928SPeter Avalos u_int have; 1478856ea928SPeter Avalos ssize_t len; 1479856ea928SPeter Avalos u_char *p; 1480856ea928SPeter Avalos struct pollfd pfd; 148118de8d7fSPeter Avalos 1482856ea928SPeter Avalos pfd.fd = fd; 1483856ea928SPeter Avalos pfd.events = POLLIN; 1484856ea928SPeter Avalos p = buffer_append_space(b, need); 1485856ea928SPeter Avalos for (have = 0; have < need; ) { 1486856ea928SPeter Avalos if (muxclient_terminate) { 1487856ea928SPeter Avalos errno = EINTR; 1488856ea928SPeter Avalos return -1; 1489856ea928SPeter Avalos } 1490856ea928SPeter Avalos len = read(fd, p + have, need - have); 1491856ea928SPeter Avalos if (len < 0) { 1492856ea928SPeter Avalos switch (errno) { 1493856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1494856ea928SPeter Avalos case EWOULDBLOCK: 1495856ea928SPeter Avalos #endif 1496856ea928SPeter Avalos case EAGAIN: 1497856ea928SPeter Avalos (void)poll(&pfd, 1, -1); 1498856ea928SPeter Avalos /* FALLTHROUGH */ 1499856ea928SPeter Avalos case EINTR: 1500856ea928SPeter Avalos continue; 1501856ea928SPeter Avalos default: 1502856ea928SPeter Avalos return -1; 1503856ea928SPeter Avalos } 1504856ea928SPeter Avalos } 1505856ea928SPeter Avalos if (len == 0) { 1506856ea928SPeter Avalos errno = EPIPE; 1507856ea928SPeter Avalos return -1; 1508856ea928SPeter Avalos } 1509856ea928SPeter Avalos have += (u_int)len; 1510856ea928SPeter Avalos } 1511856ea928SPeter Avalos return 0; 1512856ea928SPeter Avalos } 151318de8d7fSPeter Avalos 1514856ea928SPeter Avalos static int 1515856ea928SPeter Avalos mux_client_write_packet(int fd, Buffer *m) 1516856ea928SPeter Avalos { 1517856ea928SPeter Avalos Buffer queue; 1518856ea928SPeter Avalos u_int have, need; 1519856ea928SPeter Avalos int oerrno, len; 1520856ea928SPeter Avalos u_char *ptr; 1521856ea928SPeter Avalos struct pollfd pfd; 152218de8d7fSPeter Avalos 1523856ea928SPeter Avalos pfd.fd = fd; 1524856ea928SPeter Avalos pfd.events = POLLOUT; 1525856ea928SPeter Avalos buffer_init(&queue); 1526856ea928SPeter Avalos buffer_put_string(&queue, buffer_ptr(m), buffer_len(m)); 1527856ea928SPeter Avalos 1528856ea928SPeter Avalos need = buffer_len(&queue); 1529856ea928SPeter Avalos ptr = buffer_ptr(&queue); 1530856ea928SPeter Avalos 1531856ea928SPeter Avalos for (have = 0; have < need; ) { 1532856ea928SPeter Avalos if (muxclient_terminate) { 1533856ea928SPeter Avalos buffer_free(&queue); 1534856ea928SPeter Avalos errno = EINTR; 1535856ea928SPeter Avalos return -1; 1536856ea928SPeter Avalos } 1537856ea928SPeter Avalos len = write(fd, ptr + have, need - have); 1538856ea928SPeter Avalos if (len < 0) { 1539856ea928SPeter Avalos switch (errno) { 1540856ea928SPeter Avalos #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1541856ea928SPeter Avalos case EWOULDBLOCK: 1542856ea928SPeter Avalos #endif 1543856ea928SPeter Avalos case EAGAIN: 1544856ea928SPeter Avalos (void)poll(&pfd, 1, -1); 1545856ea928SPeter Avalos /* FALLTHROUGH */ 1546856ea928SPeter Avalos case EINTR: 1547856ea928SPeter Avalos continue; 1548856ea928SPeter Avalos default: 1549856ea928SPeter Avalos oerrno = errno; 1550856ea928SPeter Avalos buffer_free(&queue); 1551856ea928SPeter Avalos errno = oerrno; 1552856ea928SPeter Avalos return -1; 1553856ea928SPeter Avalos } 1554856ea928SPeter Avalos } 1555856ea928SPeter Avalos if (len == 0) { 1556856ea928SPeter Avalos buffer_free(&queue); 1557856ea928SPeter Avalos errno = EPIPE; 1558856ea928SPeter Avalos return -1; 1559856ea928SPeter Avalos } 1560856ea928SPeter Avalos have += (u_int)len; 1561856ea928SPeter Avalos } 1562856ea928SPeter Avalos buffer_free(&queue); 1563856ea928SPeter Avalos return 0; 1564856ea928SPeter Avalos } 1565856ea928SPeter Avalos 1566856ea928SPeter Avalos static int 1567856ea928SPeter Avalos mux_client_read_packet(int fd, Buffer *m) 1568856ea928SPeter Avalos { 1569856ea928SPeter Avalos Buffer queue; 1570856ea928SPeter Avalos u_int need, have; 157136e94dc5SPeter Avalos const u_char *ptr; 1572856ea928SPeter Avalos int oerrno; 1573856ea928SPeter Avalos 1574856ea928SPeter Avalos buffer_init(&queue); 1575856ea928SPeter Avalos if (mux_client_read(fd, &queue, 4) != 0) { 1576856ea928SPeter Avalos if ((oerrno = errno) == EPIPE) 157736e94dc5SPeter Avalos debug3("%s: read header failed: %s", __func__, 157836e94dc5SPeter Avalos strerror(errno)); 157936e94dc5SPeter Avalos buffer_free(&queue); 1580856ea928SPeter Avalos errno = oerrno; 1581856ea928SPeter Avalos return -1; 1582856ea928SPeter Avalos } 1583856ea928SPeter Avalos need = get_u32(buffer_ptr(&queue)); 1584856ea928SPeter Avalos if (mux_client_read(fd, &queue, need) != 0) { 1585856ea928SPeter Avalos oerrno = errno; 1586856ea928SPeter Avalos debug3("%s: read body failed: %s", __func__, strerror(errno)); 158736e94dc5SPeter Avalos buffer_free(&queue); 1588856ea928SPeter Avalos errno = oerrno; 1589856ea928SPeter Avalos return -1; 1590856ea928SPeter Avalos } 1591856ea928SPeter Avalos ptr = buffer_get_string_ptr(&queue, &have); 1592856ea928SPeter Avalos buffer_append(m, ptr, have); 1593856ea928SPeter Avalos buffer_free(&queue); 1594856ea928SPeter Avalos return 0; 1595856ea928SPeter Avalos } 1596856ea928SPeter Avalos 1597856ea928SPeter Avalos static int 1598856ea928SPeter Avalos mux_client_hello_exchange(int fd) 1599856ea928SPeter Avalos { 1600856ea928SPeter Avalos Buffer m; 1601856ea928SPeter Avalos u_int type, ver; 1602*ce74bacaSMatthew Dillon int ret = -1; 1603856ea928SPeter Avalos 1604856ea928SPeter Avalos buffer_init(&m); 1605856ea928SPeter Avalos buffer_put_int(&m, MUX_MSG_HELLO); 1606856ea928SPeter Avalos buffer_put_int(&m, SSHMUX_VER); 1607856ea928SPeter Avalos /* no extensions */ 1608856ea928SPeter Avalos 1609*ce74bacaSMatthew Dillon if (mux_client_write_packet(fd, &m) != 0) { 1610*ce74bacaSMatthew Dillon debug("%s: write packet: %s", __func__, strerror(errno)); 1611*ce74bacaSMatthew Dillon goto out; 1612*ce74bacaSMatthew Dillon } 1613856ea928SPeter Avalos 1614856ea928SPeter Avalos buffer_clear(&m); 1615856ea928SPeter Avalos 1616856ea928SPeter Avalos /* Read their HELLO */ 1617856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 1618*ce74bacaSMatthew Dillon debug("%s: read packet failed", __func__); 1619*ce74bacaSMatthew Dillon goto out; 1620856ea928SPeter Avalos } 1621856ea928SPeter Avalos 1622856ea928SPeter Avalos type = buffer_get_int(&m); 1623*ce74bacaSMatthew Dillon if (type != MUX_MSG_HELLO) { 1624*ce74bacaSMatthew Dillon error("%s: expected HELLO (%u) received %u", 1625856ea928SPeter Avalos __func__, MUX_MSG_HELLO, type); 1626*ce74bacaSMatthew Dillon goto out; 1627*ce74bacaSMatthew Dillon } 1628856ea928SPeter Avalos ver = buffer_get_int(&m); 1629*ce74bacaSMatthew Dillon if (ver != SSHMUX_VER) { 1630*ce74bacaSMatthew Dillon error("Unsupported multiplexing protocol version %d " 1631856ea928SPeter Avalos "(expected %d)", ver, SSHMUX_VER); 1632*ce74bacaSMatthew Dillon goto out; 1633*ce74bacaSMatthew Dillon } 1634856ea928SPeter Avalos debug2("%s: master version %u", __func__, ver); 1635856ea928SPeter Avalos /* No extensions are presently defined */ 1636856ea928SPeter Avalos while (buffer_len(&m) > 0) { 1637856ea928SPeter Avalos char *name = buffer_get_string(&m, NULL); 1638856ea928SPeter Avalos char *value = buffer_get_string(&m, NULL); 1639856ea928SPeter Avalos 1640856ea928SPeter Avalos debug2("Unrecognised master extension \"%s\"", name); 164136e94dc5SPeter Avalos free(name); 164236e94dc5SPeter Avalos free(value); 1643856ea928SPeter Avalos } 1644*ce74bacaSMatthew Dillon /* success */ 1645*ce74bacaSMatthew Dillon ret = 0; 1646*ce74bacaSMatthew Dillon out: 1647856ea928SPeter Avalos buffer_free(&m); 1648*ce74bacaSMatthew Dillon return ret; 1649856ea928SPeter Avalos } 1650856ea928SPeter Avalos 1651856ea928SPeter Avalos static u_int 1652856ea928SPeter Avalos mux_client_request_alive(int fd) 1653856ea928SPeter Avalos { 1654856ea928SPeter Avalos Buffer m; 1655856ea928SPeter Avalos char *e; 1656856ea928SPeter Avalos u_int pid, type, rid; 1657856ea928SPeter Avalos 1658856ea928SPeter Avalos debug3("%s: entering", __func__); 1659856ea928SPeter Avalos 1660856ea928SPeter Avalos buffer_init(&m); 1661856ea928SPeter Avalos buffer_put_int(&m, MUX_C_ALIVE_CHECK); 1662856ea928SPeter Avalos buffer_put_int(&m, muxclient_request_id); 1663856ea928SPeter Avalos 1664856ea928SPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 1665856ea928SPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 1666856ea928SPeter Avalos 1667856ea928SPeter Avalos buffer_clear(&m); 1668856ea928SPeter Avalos 1669856ea928SPeter Avalos /* Read their reply */ 1670856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 1671856ea928SPeter Avalos buffer_free(&m); 1672856ea928SPeter Avalos return 0; 1673856ea928SPeter Avalos } 1674856ea928SPeter Avalos 1675856ea928SPeter Avalos type = buffer_get_int(&m); 1676856ea928SPeter Avalos if (type != MUX_S_ALIVE) { 1677856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1678856ea928SPeter Avalos fatal("%s: master returned error: %s", __func__, e); 1679856ea928SPeter Avalos } 1680856ea928SPeter Avalos 1681856ea928SPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 1682856ea928SPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 1683856ea928SPeter Avalos __func__, muxclient_request_id, rid); 1684856ea928SPeter Avalos pid = buffer_get_int(&m); 1685856ea928SPeter Avalos buffer_free(&m); 1686856ea928SPeter Avalos 1687856ea928SPeter Avalos debug3("%s: done pid = %u", __func__, pid); 1688856ea928SPeter Avalos 1689856ea928SPeter Avalos muxclient_request_id++; 1690856ea928SPeter Avalos 1691856ea928SPeter Avalos return pid; 1692856ea928SPeter Avalos } 1693856ea928SPeter Avalos 1694856ea928SPeter Avalos static void 1695856ea928SPeter Avalos mux_client_request_terminate(int fd) 1696856ea928SPeter Avalos { 1697856ea928SPeter Avalos Buffer m; 1698856ea928SPeter Avalos char *e; 1699856ea928SPeter Avalos u_int type, rid; 1700856ea928SPeter Avalos 1701856ea928SPeter Avalos debug3("%s: entering", __func__); 1702856ea928SPeter Avalos 1703856ea928SPeter Avalos buffer_init(&m); 1704856ea928SPeter Avalos buffer_put_int(&m, MUX_C_TERMINATE); 1705856ea928SPeter Avalos buffer_put_int(&m, muxclient_request_id); 1706856ea928SPeter Avalos 1707856ea928SPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 1708856ea928SPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 1709856ea928SPeter Avalos 1710856ea928SPeter Avalos buffer_clear(&m); 1711856ea928SPeter Avalos 1712856ea928SPeter Avalos /* Read their reply */ 1713856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 1714856ea928SPeter Avalos /* Remote end exited already */ 1715856ea928SPeter Avalos if (errno == EPIPE) { 1716856ea928SPeter Avalos buffer_free(&m); 1717856ea928SPeter Avalos return; 1718856ea928SPeter Avalos } 1719856ea928SPeter Avalos fatal("%s: read from master failed: %s", 1720856ea928SPeter Avalos __func__, strerror(errno)); 1721856ea928SPeter Avalos } 1722856ea928SPeter Avalos 1723856ea928SPeter Avalos type = buffer_get_int(&m); 1724856ea928SPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 1725856ea928SPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 1726856ea928SPeter Avalos __func__, muxclient_request_id, rid); 1727856ea928SPeter Avalos switch (type) { 1728856ea928SPeter Avalos case MUX_S_OK: 1729856ea928SPeter Avalos break; 1730856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED: 1731856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1732856ea928SPeter Avalos fatal("Master refused termination request: %s", e); 1733856ea928SPeter Avalos case MUX_S_FAILURE: 1734856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1735856ea928SPeter Avalos fatal("%s: termination request failed: %s", __func__, e); 1736856ea928SPeter Avalos default: 1737856ea928SPeter Avalos fatal("%s: unexpected response from master 0x%08x", 1738856ea928SPeter Avalos __func__, type); 1739856ea928SPeter Avalos } 1740856ea928SPeter Avalos buffer_free(&m); 1741856ea928SPeter Avalos muxclient_request_id++; 1742856ea928SPeter Avalos } 1743856ea928SPeter Avalos 1744856ea928SPeter Avalos static int 174536e94dc5SPeter Avalos mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) 1746856ea928SPeter Avalos { 1747856ea928SPeter Avalos Buffer m; 1748856ea928SPeter Avalos char *e, *fwd_desc; 1749856ea928SPeter Avalos u_int type, rid; 1750856ea928SPeter Avalos 1751856ea928SPeter Avalos fwd_desc = format_forward(ftype, fwd); 175299e85e0dSPeter Avalos debug("Requesting %s %s", 175399e85e0dSPeter Avalos cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); 175436e94dc5SPeter Avalos free(fwd_desc); 1755856ea928SPeter Avalos 1756856ea928SPeter Avalos buffer_init(&m); 175799e85e0dSPeter Avalos buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD); 1758856ea928SPeter Avalos buffer_put_int(&m, muxclient_request_id); 1759856ea928SPeter Avalos buffer_put_int(&m, ftype); 176036e94dc5SPeter Avalos if (fwd->listen_path != NULL) { 176136e94dc5SPeter Avalos buffer_put_cstring(&m, fwd->listen_path); 176236e94dc5SPeter Avalos } else { 1763856ea928SPeter Avalos buffer_put_cstring(&m, 1764e9778795SPeter Avalos fwd->listen_host == NULL ? "" : 1765e9778795SPeter Avalos (*fwd->listen_host == '\0' ? "*" : fwd->listen_host)); 176636e94dc5SPeter Avalos } 1767856ea928SPeter Avalos buffer_put_int(&m, fwd->listen_port); 176836e94dc5SPeter Avalos if (fwd->connect_path != NULL) { 176936e94dc5SPeter Avalos buffer_put_cstring(&m, fwd->connect_path); 177036e94dc5SPeter Avalos } else { 1771856ea928SPeter Avalos buffer_put_cstring(&m, 1772856ea928SPeter Avalos fwd->connect_host == NULL ? "" : fwd->connect_host); 177336e94dc5SPeter Avalos } 1774856ea928SPeter Avalos buffer_put_int(&m, fwd->connect_port); 1775856ea928SPeter Avalos 1776856ea928SPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 1777856ea928SPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 1778856ea928SPeter Avalos 1779856ea928SPeter Avalos buffer_clear(&m); 1780856ea928SPeter Avalos 1781856ea928SPeter Avalos /* Read their reply */ 1782856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 1783856ea928SPeter Avalos buffer_free(&m); 1784856ea928SPeter Avalos return -1; 1785856ea928SPeter Avalos } 1786856ea928SPeter Avalos 1787856ea928SPeter Avalos type = buffer_get_int(&m); 1788856ea928SPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 1789856ea928SPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 1790856ea928SPeter Avalos __func__, muxclient_request_id, rid); 1791856ea928SPeter Avalos switch (type) { 1792856ea928SPeter Avalos case MUX_S_OK: 1793856ea928SPeter Avalos break; 1794856ea928SPeter Avalos case MUX_S_REMOTE_PORT: 179599e85e0dSPeter Avalos if (cancel_flag) 179699e85e0dSPeter Avalos fatal("%s: got MUX_S_REMOTE_PORT for cancel", __func__); 1797856ea928SPeter Avalos fwd->allocated_port = buffer_get_int(&m); 1798e9778795SPeter Avalos verbose("Allocated port %u for remote forward to %s:%d", 1799856ea928SPeter Avalos fwd->allocated_port, 1800856ea928SPeter Avalos fwd->connect_host ? fwd->connect_host : "", 1801856ea928SPeter Avalos fwd->connect_port); 1802856ea928SPeter Avalos if (muxclient_command == SSHMUX_COMMAND_FORWARD) 1803e9778795SPeter Avalos fprintf(stdout, "%i\n", fwd->allocated_port); 1804856ea928SPeter Avalos break; 1805856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED: 1806856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1807856ea928SPeter Avalos buffer_free(&m); 1808856ea928SPeter Avalos error("Master refused forwarding request: %s", e); 1809856ea928SPeter Avalos return -1; 1810856ea928SPeter Avalos case MUX_S_FAILURE: 1811856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1812856ea928SPeter Avalos buffer_free(&m); 18139f304aafSPeter Avalos error("%s: forwarding request failed: %s", __func__, e); 1814856ea928SPeter Avalos return -1; 1815856ea928SPeter Avalos default: 1816856ea928SPeter Avalos fatal("%s: unexpected response from master 0x%08x", 1817856ea928SPeter Avalos __func__, type); 1818856ea928SPeter Avalos } 1819856ea928SPeter Avalos buffer_free(&m); 1820856ea928SPeter Avalos 1821856ea928SPeter Avalos muxclient_request_id++; 1822856ea928SPeter Avalos return 0; 1823856ea928SPeter Avalos } 1824856ea928SPeter Avalos 1825856ea928SPeter Avalos static int 182699e85e0dSPeter Avalos mux_client_forwards(int fd, int cancel_flag) 1827856ea928SPeter Avalos { 182899e85e0dSPeter Avalos int i, ret = 0; 1829856ea928SPeter Avalos 183099e85e0dSPeter Avalos debug3("%s: %s forwardings: %d local, %d remote", __func__, 183199e85e0dSPeter Avalos cancel_flag ? "cancel" : "request", 1832856ea928SPeter Avalos options.num_local_forwards, options.num_remote_forwards); 1833856ea928SPeter Avalos 1834856ea928SPeter Avalos /* XXX ExitOnForwardingFailure */ 1835856ea928SPeter Avalos for (i = 0; i < options.num_local_forwards; i++) { 183699e85e0dSPeter Avalos if (mux_client_forward(fd, cancel_flag, 1837856ea928SPeter Avalos options.local_forwards[i].connect_port == 0 ? 1838856ea928SPeter Avalos MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, 1839856ea928SPeter Avalos options.local_forwards + i) != 0) 184099e85e0dSPeter Avalos ret = -1; 1841856ea928SPeter Avalos } 1842856ea928SPeter Avalos for (i = 0; i < options.num_remote_forwards; i++) { 184399e85e0dSPeter Avalos if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, 1844856ea928SPeter Avalos options.remote_forwards + i) != 0) 184599e85e0dSPeter Avalos ret = -1; 1846856ea928SPeter Avalos } 184799e85e0dSPeter Avalos return ret; 1848856ea928SPeter Avalos } 1849856ea928SPeter Avalos 1850856ea928SPeter Avalos static int 1851856ea928SPeter Avalos mux_client_request_session(int fd) 1852856ea928SPeter Avalos { 1853856ea928SPeter Avalos Buffer m; 1854856ea928SPeter Avalos char *e, *term; 1855856ea928SPeter Avalos u_int i, rid, sid, esid, exitval, type, exitval_seen; 1856856ea928SPeter Avalos extern char **environ; 18571c188a7fSPeter Avalos int devnull, rawmode; 1858856ea928SPeter Avalos 1859856ea928SPeter Avalos debug3("%s: entering", __func__); 1860856ea928SPeter Avalos 1861856ea928SPeter Avalos if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 1862856ea928SPeter Avalos error("%s: master alive request failed", __func__); 1863856ea928SPeter Avalos return -1; 1864856ea928SPeter Avalos } 1865856ea928SPeter Avalos 1866856ea928SPeter Avalos signal(SIGPIPE, SIG_IGN); 1867856ea928SPeter Avalos 1868856ea928SPeter Avalos if (stdin_null_flag) { 1869856ea928SPeter Avalos if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) 1870856ea928SPeter Avalos fatal("open(/dev/null): %s", strerror(errno)); 1871856ea928SPeter Avalos if (dup2(devnull, STDIN_FILENO) == -1) 1872856ea928SPeter Avalos fatal("dup2: %s", strerror(errno)); 1873856ea928SPeter Avalos if (devnull > STDERR_FILENO) 1874856ea928SPeter Avalos close(devnull); 1875856ea928SPeter Avalos } 1876856ea928SPeter Avalos 1877856ea928SPeter Avalos term = getenv("TERM"); 1878856ea928SPeter Avalos 1879856ea928SPeter Avalos buffer_init(&m); 1880856ea928SPeter Avalos buffer_put_int(&m, MUX_C_NEW_SESSION); 1881856ea928SPeter Avalos buffer_put_int(&m, muxclient_request_id); 1882856ea928SPeter Avalos buffer_put_cstring(&m, ""); /* reserved */ 1883856ea928SPeter Avalos buffer_put_int(&m, tty_flag); 1884856ea928SPeter Avalos buffer_put_int(&m, options.forward_x11); 1885856ea928SPeter Avalos buffer_put_int(&m, options.forward_agent); 1886856ea928SPeter Avalos buffer_put_int(&m, subsystem_flag); 1887856ea928SPeter Avalos buffer_put_int(&m, options.escape_char == SSH_ESCAPECHAR_NONE ? 1888856ea928SPeter Avalos 0xffffffff : (u_int)options.escape_char); 1889856ea928SPeter Avalos buffer_put_cstring(&m, term == NULL ? "" : term); 1890856ea928SPeter Avalos buffer_put_string(&m, buffer_ptr(&command), buffer_len(&command)); 1891856ea928SPeter Avalos 1892856ea928SPeter Avalos if (options.num_send_env > 0 && environ != NULL) { 1893856ea928SPeter Avalos /* Pass environment */ 1894856ea928SPeter Avalos for (i = 0; environ[i] != NULL; i++) { 1895856ea928SPeter Avalos if (env_permitted(environ[i])) { 1896856ea928SPeter Avalos buffer_put_cstring(&m, environ[i]); 1897856ea928SPeter Avalos } 1898856ea928SPeter Avalos } 1899856ea928SPeter Avalos } 1900856ea928SPeter Avalos 1901856ea928SPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 1902856ea928SPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 1903856ea928SPeter Avalos 1904856ea928SPeter Avalos /* Send the stdio file descriptors */ 1905856ea928SPeter Avalos if (mm_send_fd(fd, STDIN_FILENO) == -1 || 1906856ea928SPeter Avalos mm_send_fd(fd, STDOUT_FILENO) == -1 || 1907856ea928SPeter Avalos mm_send_fd(fd, STDERR_FILENO) == -1) 1908856ea928SPeter Avalos fatal("%s: send fds failed", __func__); 1909856ea928SPeter Avalos 1910856ea928SPeter Avalos debug3("%s: session request sent", __func__); 1911856ea928SPeter Avalos 1912856ea928SPeter Avalos /* Read their reply */ 1913856ea928SPeter Avalos buffer_clear(&m); 1914856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 1915856ea928SPeter Avalos error("%s: read from master failed: %s", 1916856ea928SPeter Avalos __func__, strerror(errno)); 1917856ea928SPeter Avalos buffer_free(&m); 1918856ea928SPeter Avalos return -1; 1919856ea928SPeter Avalos } 1920856ea928SPeter Avalos 1921856ea928SPeter Avalos type = buffer_get_int(&m); 1922856ea928SPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 1923856ea928SPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 1924856ea928SPeter Avalos __func__, muxclient_request_id, rid); 1925856ea928SPeter Avalos switch (type) { 1926856ea928SPeter Avalos case MUX_S_SESSION_OPENED: 1927856ea928SPeter Avalos sid = buffer_get_int(&m); 1928856ea928SPeter Avalos debug("%s: master session id: %u", __func__, sid); 1929856ea928SPeter Avalos break; 1930856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED: 1931856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1932856ea928SPeter Avalos buffer_free(&m); 19339f304aafSPeter Avalos error("Master refused session request: %s", e); 1934856ea928SPeter Avalos return -1; 1935856ea928SPeter Avalos case MUX_S_FAILURE: 1936856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 1937856ea928SPeter Avalos buffer_free(&m); 19389f304aafSPeter Avalos error("%s: session request failed: %s", __func__, e); 1939856ea928SPeter Avalos return -1; 1940856ea928SPeter Avalos default: 1941856ea928SPeter Avalos buffer_free(&m); 1942856ea928SPeter Avalos error("%s: unexpected response from master 0x%08x", 1943856ea928SPeter Avalos __func__, type); 1944856ea928SPeter Avalos return -1; 1945856ea928SPeter Avalos } 1946856ea928SPeter Avalos muxclient_request_id++; 1947856ea928SPeter Avalos 1948e9778795SPeter Avalos if (pledge("stdio proc tty", NULL) == -1) 1949e9778795SPeter Avalos fatal("%s pledge(): %s", __func__, strerror(errno)); 1950e9778795SPeter Avalos platform_pledge_mux(); 1951e9778795SPeter Avalos 1952856ea928SPeter Avalos signal(SIGHUP, control_client_sighandler); 1953856ea928SPeter Avalos signal(SIGINT, control_client_sighandler); 1954856ea928SPeter Avalos signal(SIGTERM, control_client_sighandler); 1955856ea928SPeter Avalos signal(SIGWINCH, control_client_sigrelay); 1956856ea928SPeter Avalos 19571c188a7fSPeter Avalos rawmode = tty_flag; 1958856ea928SPeter Avalos if (tty_flag) 19591c188a7fSPeter Avalos enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1960856ea928SPeter Avalos 1961856ea928SPeter Avalos /* 1962856ea928SPeter Avalos * Stick around until the controlee closes the client_fd. 1963856ea928SPeter Avalos * Before it does, it is expected to write an exit message. 1964856ea928SPeter Avalos * This process must read the value and wait for the closure of 1965856ea928SPeter Avalos * the client_fd; if this one closes early, the multiplex master will 1966856ea928SPeter Avalos * terminate early too (possibly losing data). 1967856ea928SPeter Avalos */ 1968856ea928SPeter Avalos for (exitval = 255, exitval_seen = 0;;) { 1969856ea928SPeter Avalos buffer_clear(&m); 1970856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) 1971856ea928SPeter Avalos break; 1972856ea928SPeter Avalos type = buffer_get_int(&m); 19731c188a7fSPeter Avalos switch (type) { 19741c188a7fSPeter Avalos case MUX_S_TTY_ALLOC_FAIL: 1975856ea928SPeter Avalos if ((esid = buffer_get_int(&m)) != sid) 19761c188a7fSPeter Avalos fatal("%s: tty alloc fail on unknown session: " 19771c188a7fSPeter Avalos "my id %u theirs %u", 1978856ea928SPeter Avalos __func__, sid, esid); 19791c188a7fSPeter Avalos leave_raw_mode(options.request_tty == 19801c188a7fSPeter Avalos REQUEST_TTY_FORCE); 19811c188a7fSPeter Avalos rawmode = 0; 19821c188a7fSPeter Avalos continue; 19831c188a7fSPeter Avalos case MUX_S_EXIT_MESSAGE: 19841c188a7fSPeter Avalos if ((esid = buffer_get_int(&m)) != sid) 19851c188a7fSPeter Avalos fatal("%s: exit on unknown session: " 19861c188a7fSPeter Avalos "my id %u theirs %u", 19871c188a7fSPeter Avalos __func__, sid, esid); 1988856ea928SPeter Avalos if (exitval_seen) 1989856ea928SPeter Avalos fatal("%s: exitval sent twice", __func__); 1990856ea928SPeter Avalos exitval = buffer_get_int(&m); 1991856ea928SPeter Avalos exitval_seen = 1; 19921c188a7fSPeter Avalos continue; 19931c188a7fSPeter Avalos default: 19941c188a7fSPeter Avalos e = buffer_get_string(&m, NULL); 19951c188a7fSPeter Avalos fatal("%s: master returned error: %s", __func__, e); 19961c188a7fSPeter Avalos } 1997856ea928SPeter Avalos } 1998856ea928SPeter Avalos 1999856ea928SPeter Avalos close(fd); 20001c188a7fSPeter Avalos if (rawmode) 20011c188a7fSPeter Avalos leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2002856ea928SPeter Avalos 2003856ea928SPeter Avalos if (muxclient_terminate) { 2004*ce74bacaSMatthew Dillon debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); 2005856ea928SPeter Avalos exitval = 255; 2006856ea928SPeter Avalos } else if (!exitval_seen) { 2007856ea928SPeter Avalos debug2("Control master terminated unexpectedly"); 2008856ea928SPeter Avalos exitval = 255; 2009856ea928SPeter Avalos } else 2010856ea928SPeter Avalos debug2("Received exit status from master %d", exitval); 2011856ea928SPeter Avalos 2012856ea928SPeter Avalos if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) 2013856ea928SPeter Avalos fprintf(stderr, "Shared connection to %s closed.\r\n", host); 2014856ea928SPeter Avalos 2015856ea928SPeter Avalos exit(exitval); 2016856ea928SPeter Avalos } 2017856ea928SPeter Avalos 2018856ea928SPeter Avalos static int 2019*ce74bacaSMatthew Dillon mux_client_proxy(int fd) 2020*ce74bacaSMatthew Dillon { 2021*ce74bacaSMatthew Dillon Buffer m; 2022*ce74bacaSMatthew Dillon char *e; 2023*ce74bacaSMatthew Dillon u_int type, rid; 2024*ce74bacaSMatthew Dillon 2025*ce74bacaSMatthew Dillon buffer_init(&m); 2026*ce74bacaSMatthew Dillon buffer_put_int(&m, MUX_C_PROXY); 2027*ce74bacaSMatthew Dillon buffer_put_int(&m, muxclient_request_id); 2028*ce74bacaSMatthew Dillon if (mux_client_write_packet(fd, &m) != 0) 2029*ce74bacaSMatthew Dillon fatal("%s: write packet: %s", __func__, strerror(errno)); 2030*ce74bacaSMatthew Dillon 2031*ce74bacaSMatthew Dillon buffer_clear(&m); 2032*ce74bacaSMatthew Dillon 2033*ce74bacaSMatthew Dillon /* Read their reply */ 2034*ce74bacaSMatthew Dillon if (mux_client_read_packet(fd, &m) != 0) { 2035*ce74bacaSMatthew Dillon buffer_free(&m); 2036*ce74bacaSMatthew Dillon return 0; 2037*ce74bacaSMatthew Dillon } 2038*ce74bacaSMatthew Dillon type = buffer_get_int(&m); 2039*ce74bacaSMatthew Dillon if (type != MUX_S_PROXY) { 2040*ce74bacaSMatthew Dillon e = buffer_get_string(&m, NULL); 2041*ce74bacaSMatthew Dillon fatal("%s: master returned error: %s", __func__, e); 2042*ce74bacaSMatthew Dillon } 2043*ce74bacaSMatthew Dillon if ((rid = buffer_get_int(&m)) != muxclient_request_id) 2044*ce74bacaSMatthew Dillon fatal("%s: out of sequence reply: my id %u theirs %u", 2045*ce74bacaSMatthew Dillon __func__, muxclient_request_id, rid); 2046*ce74bacaSMatthew Dillon buffer_free(&m); 2047*ce74bacaSMatthew Dillon 2048*ce74bacaSMatthew Dillon debug3("%s: done", __func__); 2049*ce74bacaSMatthew Dillon muxclient_request_id++; 2050*ce74bacaSMatthew Dillon return 0; 2051*ce74bacaSMatthew Dillon } 2052*ce74bacaSMatthew Dillon 2053*ce74bacaSMatthew Dillon static int 2054856ea928SPeter Avalos mux_client_request_stdio_fwd(int fd) 2055856ea928SPeter Avalos { 2056856ea928SPeter Avalos Buffer m; 2057856ea928SPeter Avalos char *e; 2058856ea928SPeter Avalos u_int type, rid, sid; 2059856ea928SPeter Avalos int devnull; 2060856ea928SPeter Avalos 2061856ea928SPeter Avalos debug3("%s: entering", __func__); 2062856ea928SPeter Avalos 2063856ea928SPeter Avalos if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { 2064856ea928SPeter Avalos error("%s: master alive request failed", __func__); 2065856ea928SPeter Avalos return -1; 2066856ea928SPeter Avalos } 2067856ea928SPeter Avalos 2068856ea928SPeter Avalos signal(SIGPIPE, SIG_IGN); 2069856ea928SPeter Avalos 2070856ea928SPeter Avalos if (stdin_null_flag) { 2071856ea928SPeter Avalos if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) 2072856ea928SPeter Avalos fatal("open(/dev/null): %s", strerror(errno)); 2073856ea928SPeter Avalos if (dup2(devnull, STDIN_FILENO) == -1) 2074856ea928SPeter Avalos fatal("dup2: %s", strerror(errno)); 2075856ea928SPeter Avalos if (devnull > STDERR_FILENO) 2076856ea928SPeter Avalos close(devnull); 2077856ea928SPeter Avalos } 2078856ea928SPeter Avalos 2079856ea928SPeter Avalos buffer_init(&m); 2080856ea928SPeter Avalos buffer_put_int(&m, MUX_C_NEW_STDIO_FWD); 2081856ea928SPeter Avalos buffer_put_int(&m, muxclient_request_id); 2082856ea928SPeter Avalos buffer_put_cstring(&m, ""); /* reserved */ 2083e9778795SPeter Avalos buffer_put_cstring(&m, options.stdio_forward_host); 2084e9778795SPeter Avalos buffer_put_int(&m, options.stdio_forward_port); 2085856ea928SPeter Avalos 2086856ea928SPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 2087856ea928SPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 2088856ea928SPeter Avalos 2089856ea928SPeter Avalos /* Send the stdio file descriptors */ 2090856ea928SPeter Avalos if (mm_send_fd(fd, STDIN_FILENO) == -1 || 2091856ea928SPeter Avalos mm_send_fd(fd, STDOUT_FILENO) == -1) 2092856ea928SPeter Avalos fatal("%s: send fds failed", __func__); 2093856ea928SPeter Avalos 2094e9778795SPeter Avalos if (pledge("stdio proc tty", NULL) == -1) 2095e9778795SPeter Avalos fatal("%s pledge(): %s", __func__, strerror(errno)); 2096e9778795SPeter Avalos platform_pledge_mux(); 2097e9778795SPeter Avalos 2098856ea928SPeter Avalos debug3("%s: stdio forward request sent", __func__); 2099856ea928SPeter Avalos 2100856ea928SPeter Avalos /* Read their reply */ 2101856ea928SPeter Avalos buffer_clear(&m); 2102856ea928SPeter Avalos 2103856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 2104856ea928SPeter Avalos error("%s: read from master failed: %s", 2105856ea928SPeter Avalos __func__, strerror(errno)); 2106856ea928SPeter Avalos buffer_free(&m); 2107856ea928SPeter Avalos return -1; 2108856ea928SPeter Avalos } 2109856ea928SPeter Avalos 2110856ea928SPeter Avalos type = buffer_get_int(&m); 2111856ea928SPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 2112856ea928SPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 2113856ea928SPeter Avalos __func__, muxclient_request_id, rid); 2114856ea928SPeter Avalos switch (type) { 2115856ea928SPeter Avalos case MUX_S_SESSION_OPENED: 2116856ea928SPeter Avalos sid = buffer_get_int(&m); 2117856ea928SPeter Avalos debug("%s: master session id: %u", __func__, sid); 2118856ea928SPeter Avalos break; 2119856ea928SPeter Avalos case MUX_S_PERMISSION_DENIED: 2120856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 2121856ea928SPeter Avalos buffer_free(&m); 21229f304aafSPeter Avalos fatal("Master refused stdio forwarding request: %s", e); 2123856ea928SPeter Avalos case MUX_S_FAILURE: 2124856ea928SPeter Avalos e = buffer_get_string(&m, NULL); 2125856ea928SPeter Avalos buffer_free(&m); 212636e94dc5SPeter Avalos fatal("Stdio forwarding request failed: %s", e); 2127856ea928SPeter Avalos default: 2128856ea928SPeter Avalos buffer_free(&m); 2129856ea928SPeter Avalos error("%s: unexpected response from master 0x%08x", 2130856ea928SPeter Avalos __func__, type); 2131856ea928SPeter Avalos return -1; 2132856ea928SPeter Avalos } 2133856ea928SPeter Avalos muxclient_request_id++; 2134856ea928SPeter Avalos 2135856ea928SPeter Avalos signal(SIGHUP, control_client_sighandler); 2136856ea928SPeter Avalos signal(SIGINT, control_client_sighandler); 2137856ea928SPeter Avalos signal(SIGTERM, control_client_sighandler); 2138856ea928SPeter Avalos signal(SIGWINCH, control_client_sigrelay); 2139856ea928SPeter Avalos 2140856ea928SPeter Avalos /* 2141856ea928SPeter Avalos * Stick around until the controlee closes the client_fd. 2142856ea928SPeter Avalos */ 2143856ea928SPeter Avalos buffer_clear(&m); 2144856ea928SPeter Avalos if (mux_client_read_packet(fd, &m) != 0) { 2145856ea928SPeter Avalos if (errno == EPIPE || 2146856ea928SPeter Avalos (errno == EINTR && muxclient_terminate != 0)) 2147856ea928SPeter Avalos return 0; 2148856ea928SPeter Avalos fatal("%s: mux_client_read_packet: %s", 2149856ea928SPeter Avalos __func__, strerror(errno)); 2150856ea928SPeter Avalos } 2151856ea928SPeter Avalos fatal("%s: master returned unexpected message %u", __func__, type); 215218de8d7fSPeter Avalos } 215318de8d7fSPeter Avalos 21541c188a7fSPeter Avalos static void 21551c188a7fSPeter Avalos mux_client_request_stop_listening(int fd) 21561c188a7fSPeter Avalos { 21571c188a7fSPeter Avalos Buffer m; 21581c188a7fSPeter Avalos char *e; 21591c188a7fSPeter Avalos u_int type, rid; 21601c188a7fSPeter Avalos 21611c188a7fSPeter Avalos debug3("%s: entering", __func__); 21621c188a7fSPeter Avalos 21631c188a7fSPeter Avalos buffer_init(&m); 21641c188a7fSPeter Avalos buffer_put_int(&m, MUX_C_STOP_LISTENING); 21651c188a7fSPeter Avalos buffer_put_int(&m, muxclient_request_id); 21661c188a7fSPeter Avalos 21671c188a7fSPeter Avalos if (mux_client_write_packet(fd, &m) != 0) 21681c188a7fSPeter Avalos fatal("%s: write packet: %s", __func__, strerror(errno)); 21691c188a7fSPeter Avalos 21701c188a7fSPeter Avalos buffer_clear(&m); 21711c188a7fSPeter Avalos 21721c188a7fSPeter Avalos /* Read their reply */ 21731c188a7fSPeter Avalos if (mux_client_read_packet(fd, &m) != 0) 21741c188a7fSPeter Avalos fatal("%s: read from master failed: %s", 21751c188a7fSPeter Avalos __func__, strerror(errno)); 21761c188a7fSPeter Avalos 21771c188a7fSPeter Avalos type = buffer_get_int(&m); 21781c188a7fSPeter Avalos if ((rid = buffer_get_int(&m)) != muxclient_request_id) 21791c188a7fSPeter Avalos fatal("%s: out of sequence reply: my id %u theirs %u", 21801c188a7fSPeter Avalos __func__, muxclient_request_id, rid); 21811c188a7fSPeter Avalos switch (type) { 21821c188a7fSPeter Avalos case MUX_S_OK: 21831c188a7fSPeter Avalos break; 21841c188a7fSPeter Avalos case MUX_S_PERMISSION_DENIED: 21851c188a7fSPeter Avalos e = buffer_get_string(&m, NULL); 21861c188a7fSPeter Avalos fatal("Master refused stop listening request: %s", e); 21871c188a7fSPeter Avalos case MUX_S_FAILURE: 21881c188a7fSPeter Avalos e = buffer_get_string(&m, NULL); 21891c188a7fSPeter Avalos fatal("%s: stop listening request failed: %s", __func__, e); 21901c188a7fSPeter Avalos default: 21911c188a7fSPeter Avalos fatal("%s: unexpected response from master 0x%08x", 21921c188a7fSPeter Avalos __func__, type); 21931c188a7fSPeter Avalos } 21941c188a7fSPeter Avalos buffer_free(&m); 21951c188a7fSPeter Avalos muxclient_request_id++; 21961c188a7fSPeter Avalos } 21971c188a7fSPeter Avalos 219818de8d7fSPeter Avalos /* Multiplex client main loop. */ 2199*ce74bacaSMatthew Dillon int 220018de8d7fSPeter Avalos muxclient(const char *path) 220118de8d7fSPeter Avalos { 220218de8d7fSPeter Avalos struct sockaddr_un addr; 2203856ea928SPeter Avalos int sock; 2204856ea928SPeter Avalos u_int pid; 220518de8d7fSPeter Avalos 2206856ea928SPeter Avalos if (muxclient_command == 0) { 2207e9778795SPeter Avalos if (options.stdio_forward_host != NULL) 2208856ea928SPeter Avalos muxclient_command = SSHMUX_COMMAND_STDIO_FWD; 2209856ea928SPeter Avalos else 221018de8d7fSPeter Avalos muxclient_command = SSHMUX_COMMAND_OPEN; 2211856ea928SPeter Avalos } 221218de8d7fSPeter Avalos 221318de8d7fSPeter Avalos switch (options.control_master) { 221418de8d7fSPeter Avalos case SSHCTL_MASTER_AUTO: 221518de8d7fSPeter Avalos case SSHCTL_MASTER_AUTO_ASK: 221618de8d7fSPeter Avalos debug("auto-mux: Trying existing master"); 221718de8d7fSPeter Avalos /* FALLTHROUGH */ 221818de8d7fSPeter Avalos case SSHCTL_MASTER_NO: 221918de8d7fSPeter Avalos break; 222018de8d7fSPeter Avalos default: 2221*ce74bacaSMatthew Dillon return -1; 222218de8d7fSPeter Avalos } 222318de8d7fSPeter Avalos 222418de8d7fSPeter Avalos memset(&addr, '\0', sizeof(addr)); 222518de8d7fSPeter Avalos addr.sun_family = AF_UNIX; 222618de8d7fSPeter Avalos 222718de8d7fSPeter Avalos if (strlcpy(addr.sun_path, path, 222818de8d7fSPeter Avalos sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) 2229*ce74bacaSMatthew Dillon fatal("ControlPath too long ('%s' >= %u bytes)", path, 2230*ce74bacaSMatthew Dillon (unsigned int)sizeof(addr.sun_path)); 223118de8d7fSPeter Avalos 223218de8d7fSPeter Avalos if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) 223318de8d7fSPeter Avalos fatal("%s socket(): %s", __func__, strerror(errno)); 223418de8d7fSPeter Avalos 2235*ce74bacaSMatthew Dillon if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 2236856ea928SPeter Avalos switch (muxclient_command) { 2237856ea928SPeter Avalos case SSHMUX_COMMAND_OPEN: 2238856ea928SPeter Avalos case SSHMUX_COMMAND_STDIO_FWD: 2239856ea928SPeter Avalos break; 2240856ea928SPeter Avalos default: 224118de8d7fSPeter Avalos fatal("Control socket connect(%.100s): %s", path, 224218de8d7fSPeter Avalos strerror(errno)); 224318de8d7fSPeter Avalos } 22449f304aafSPeter Avalos if (errno == ECONNREFUSED && 22459f304aafSPeter Avalos options.control_master != SSHCTL_MASTER_NO) { 22469f304aafSPeter Avalos debug("Stale control socket %.100s, unlinking", path); 22479f304aafSPeter Avalos unlink(path); 22489f304aafSPeter Avalos } else if (errno == ENOENT) { 224918de8d7fSPeter Avalos debug("Control socket \"%.100s\" does not exist", path); 22509f304aafSPeter Avalos } else { 225118de8d7fSPeter Avalos error("Control socket connect(%.100s): %s", path, 225218de8d7fSPeter Avalos strerror(errno)); 225318de8d7fSPeter Avalos } 225418de8d7fSPeter Avalos close(sock); 2255*ce74bacaSMatthew Dillon return -1; 225618de8d7fSPeter Avalos } 2257856ea928SPeter Avalos set_nonblock(sock); 225818de8d7fSPeter Avalos 2259856ea928SPeter Avalos if (mux_client_hello_exchange(sock) != 0) { 2260856ea928SPeter Avalos error("%s: master hello exchange failed", __func__); 226118de8d7fSPeter Avalos close(sock); 2262*ce74bacaSMatthew Dillon return -1; 226318de8d7fSPeter Avalos } 226418de8d7fSPeter Avalos 226518de8d7fSPeter Avalos switch (muxclient_command) { 226618de8d7fSPeter Avalos case SSHMUX_COMMAND_ALIVE_CHECK: 2267856ea928SPeter Avalos if ((pid = mux_client_request_alive(sock)) == 0) 2268856ea928SPeter Avalos fatal("%s: master alive check failed", __func__); 2269e9778795SPeter Avalos fprintf(stderr, "Master running (pid=%u)\r\n", pid); 227018de8d7fSPeter Avalos exit(0); 227118de8d7fSPeter Avalos case SSHMUX_COMMAND_TERMINATE: 2272856ea928SPeter Avalos mux_client_request_terminate(sock); 2273*ce74bacaSMatthew Dillon if (options.log_level != SYSLOG_LEVEL_QUIET) 227418de8d7fSPeter Avalos fprintf(stderr, "Exit request sent.\r\n"); 227518de8d7fSPeter Avalos exit(0); 2276856ea928SPeter Avalos case SSHMUX_COMMAND_FORWARD: 227799e85e0dSPeter Avalos if (mux_client_forwards(sock, 0) != 0) 2278856ea928SPeter Avalos fatal("%s: master forward request failed", __func__); 2279856ea928SPeter Avalos exit(0); 228018de8d7fSPeter Avalos case SSHMUX_COMMAND_OPEN: 228199e85e0dSPeter Avalos if (mux_client_forwards(sock, 0) != 0) { 2282856ea928SPeter Avalos error("%s: master forward request failed", __func__); 2283*ce74bacaSMatthew Dillon return -1; 228418de8d7fSPeter Avalos } 2285856ea928SPeter Avalos mux_client_request_session(sock); 2286*ce74bacaSMatthew Dillon return -1; 2287856ea928SPeter Avalos case SSHMUX_COMMAND_STDIO_FWD: 2288856ea928SPeter Avalos mux_client_request_stdio_fwd(sock); 2289856ea928SPeter Avalos exit(0); 22901c188a7fSPeter Avalos case SSHMUX_COMMAND_STOP: 22911c188a7fSPeter Avalos mux_client_request_stop_listening(sock); 2292*ce74bacaSMatthew Dillon if (options.log_level != SYSLOG_LEVEL_QUIET) 22931c188a7fSPeter Avalos fprintf(stderr, "Stop listening request sent.\r\n"); 22941c188a7fSPeter Avalos exit(0); 229599e85e0dSPeter Avalos case SSHMUX_COMMAND_CANCEL_FWD: 229699e85e0dSPeter Avalos if (mux_client_forwards(sock, 1) != 0) 229799e85e0dSPeter Avalos error("%s: master cancel forward request failed", 229899e85e0dSPeter Avalos __func__); 229999e85e0dSPeter Avalos exit(0); 2300*ce74bacaSMatthew Dillon case SSHMUX_COMMAND_PROXY: 2301*ce74bacaSMatthew Dillon mux_client_proxy(sock); 2302*ce74bacaSMatthew Dillon return (sock); 230318de8d7fSPeter Avalos default: 230418de8d7fSPeter Avalos fatal("unrecognised muxclient_command %d", muxclient_command); 230518de8d7fSPeter Avalos } 230618de8d7fSPeter Avalos } 2307