X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/3bcced4c9a1f6c605179089cff82e0aae8d16898..5deb8b6e1fa1e779d04da2ddb6cafcedf257b641:/mux.c diff --git a/mux.c b/mux.c index 78ba6315..239edd5f 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.1 2008/05/09 14:18:44 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.9 2010/01/09 05:04:24 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -17,6 +17,25 @@ /* ssh session multiplexing support */ +#include "includes.h" + +/* + * TODO: + * 1. partial reads in muxserver_accept_control (maybe make channels + * from accepted connections) + * 2. Better signalling from master to slave, especially passing of + * error messages + * 3. Better fall-back from mux slave error to new connection. + * 3. Add/delete forwardings via slave + * 4. ExitOnForwardingFailure (after #3 obviously) + * 5. Maybe extension mechanisms for multi-X11/multi-agent forwarding + * 6. Document the mux mini-protocol somewhere. + * 7. Support ~^Z in mux slaves. + * 8. Inspect or control sessions in master. + * 9. If we ever support the "signal" channel request, send signals on + * sessions in master. + */ + #include #include #include @@ -32,8 +51,17 @@ #include #include #include -#include +#ifdef HAVE_PATHS_H #include +#endif + +#ifdef HAVE_UTIL_H +# include +#endif + +#ifdef HAVE_LIBUTIL_H +# include +#endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" @@ -54,12 +82,25 @@ /* from ssh.c */ extern int tty_flag; +extern int force_tty_flag; extern Options options; extern int stdin_null_flag; extern char *host; -int subsystem_flag; +extern int subsystem_flag; extern Buffer command; +/* Context for session open confirmation callback */ +struct mux_session_confirm_ctx { + int want_tty; + int want_subsys; + int want_x_fwd; + int want_agent_fwd; + Buffer cmd; + char *term; + struct termios tio; + char **env; +}; + /* fd to control socket */ int muxserver_sock = -1; @@ -104,10 +145,16 @@ muxserver_listen(void) old_umask = umask(0177); if (bind(muxserver_sock, (struct sockaddr *)&addr, addr_len) == -1) { muxserver_sock = -1; - if (errno == EINVAL || errno == EADDRINUSE) - fatal("ControlSocket %s already exists", - options.control_path); - else + if (errno == EINVAL || errno == EADDRINUSE) { + error("ControlSocket %s already exists, " + "disabling multiplexing", options.control_path); + close(muxserver_sock); + muxserver_sock = -1; + xfree(options.control_path); + options.control_path = NULL; + options.control_master = SSHCTL_MASTER_NO; + return; + } else fatal("%s bind(): %s", __func__, strerror(errno)); } umask(old_umask); @@ -120,7 +167,7 @@ muxserver_listen(void) /* Callback on open confirmation in mux master for a mux client session. */ static void -client_extra_session2_setup(int id, void *arg) +mux_session_confirm(int id, void *arg) { struct mux_session_confirm_ctx *cctx = arg; const char *display; @@ -179,7 +226,7 @@ muxserver_accept_control(void) struct sockaddr_storage addr; struct mux_session_confirm_ctx *cctx; char *cmd; - u_int i, j, len, env_len, mux_command, flags; + u_int i, j, len, env_len, mux_command, flags, escape_char; uid_t euid; gid_t egid; int start_close = 0; @@ -306,6 +353,7 @@ muxserver_accept_control(void) cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; cctx->term = buffer_get_string(&m, &len); + escape_char = buffer_get_int(&m); cmd = buffer_get_string(&m, &len); buffer_init(&cctx->cmd); @@ -391,14 +439,18 @@ muxserver_accept_control(void) new_fd[0], new_fd[1], new_fd[2], window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); - /* XXX */ c->ctl_fd = client_fd; + if (cctx->want_tty && escape_char != 0xffffffff) { + channel_register_filter(c->self, + client_simple_escape_filter, NULL, + client_filter_cleanup, + client_new_escape_filter_ctx((int)escape_char)); + } debug3("%s: channel_new: %d", __func__, c->self); channel_send_open(c->self); - channel_register_open_confirm(c->self, - client_extra_session2_setup, cctx); + channel_register_open_confirm(c->self, mux_session_confirm, cctx); return 0; } @@ -455,7 +507,7 @@ muxclient(const char *path) Buffer m; char *term; extern char **environ; - u_int flags; + u_int allowed, flags; if (muxclient_command == 0) muxclient_command = SSHMUX_COMMAND_OPEN; @@ -526,17 +578,38 @@ muxclient(const char *path) /* Send our command to server */ buffer_put_int(&m, muxclient_command); buffer_put_int(&m, flags); - if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) - fatal("%s: msg_send", __func__); + if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) { + error("%s: msg_send", __func__); + muxerr: + close(sock); + buffer_free(&m); + if (muxclient_command != SSHMUX_COMMAND_OPEN) + cleanup_exit(255); + logit("Falling back to non-multiplexed connection"); + xfree(options.control_path); + options.control_path = NULL; + options.control_master = SSHCTL_MASTER_NO; + return; + } buffer_clear(&m); /* Get authorisation status and PID of controlee */ - if (ssh_msg_recv(sock, &m) == -1) - fatal("%s: msg_recv", __func__); - if (buffer_get_char(&m) != SSHMUX_VER) - fatal("%s: wrong version", __func__); - if (buffer_get_int(&m) != 1) - fatal("Connection to master denied"); + if (ssh_msg_recv(sock, &m) == -1) { + error("%s: Did not receive reply from master", __func__); + goto muxerr; + } + if (buffer_get_char(&m) != SSHMUX_VER) { + error("%s: Master replied with wrong version", __func__); + goto muxerr; + } + if (buffer_get_int_ret(&allowed, &m) != 0) { + error("%s: bad server reply", __func__); + goto muxerr; + } + if (allowed != 1) { + error("Connection to master denied"); + goto muxerr; + } muxserver_pid = buffer_get_int(&m); buffer_clear(&m); @@ -550,42 +623,52 @@ muxclient(const char *path) fprintf(stderr, "Exit request sent.\r\n"); exit(0); case SSHMUX_COMMAND_OPEN: - /* continue below */ + buffer_put_cstring(&m, term ? term : ""); + if (options.escape_char == SSH_ESCAPECHAR_NONE) + buffer_put_int(&m, 0xffffffff); + else + buffer_put_int(&m, options.escape_char); + buffer_append(&command, "\0", 1); + buffer_put_cstring(&m, buffer_ptr(&command)); + + if (options.num_send_env == 0 || environ == NULL) { + buffer_put_int(&m, 0); + } else { + /* Pass environment */ + num_env = 0; + for (i = 0; environ[i] != NULL; i++) { + if (env_permitted(environ[i])) + num_env++; /* Count */ + } + buffer_put_int(&m, num_env); + for (i = 0; environ[i] != NULL && num_env >= 0; i++) { + if (env_permitted(environ[i])) { + num_env--; + buffer_put_cstring(&m, environ[i]); + } + } + } break; default: - fatal("silly muxclient_command %d", muxclient_command); + fatal("unrecognised muxclient_command %d", muxclient_command); } - /* SSHMUX_COMMAND_OPEN */ - buffer_put_cstring(&m, term ? term : ""); - buffer_append(&command, "\0", 1); - buffer_put_cstring(&m, buffer_ptr(&command)); - - if (options.num_send_env == 0 || environ == NULL) { - buffer_put_int(&m, 0); - } else { - /* Pass environment */ - num_env = 0; - for (i = 0; environ[i] != NULL; i++) - if (env_permitted(environ[i])) - num_env++; /* Count */ - - buffer_put_int(&m, num_env); - - for (i = 0; environ[i] != NULL && num_env >= 0; i++) - if (env_permitted(environ[i])) { - num_env--; - buffer_put_cstring(&m, environ[i]); - } + if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) { + error("%s: msg_send", __func__); + goto muxerr; } - if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) - fatal("%s: msg_send", __func__); - if (mm_send_fd(sock, STDIN_FILENO) == -1 || mm_send_fd(sock, STDOUT_FILENO) == -1 || - mm_send_fd(sock, STDERR_FILENO) == -1) - fatal("%s: send fds failed", __func__); + mm_send_fd(sock, STDERR_FILENO) == -1) { + error("%s: send fds failed", __func__); + goto muxerr; + } + + /* + * Mux errors are non-recoverable from this point as the master + * has ownership of the session now. + */ /* Wait for reply, so master has a chance to gather ttymodes */ buffer_clear(&m); @@ -601,7 +684,7 @@ muxclient(const char *path) signal(SIGWINCH, control_client_sigrelay); if (tty_flag) - enter_raw_mode(); + enter_raw_mode(force_tty_flag); /* * Stick around until the controlee closes the client_fd. @@ -626,10 +709,10 @@ muxclient(const char *path) } close(sock); - leave_raw_mode(); + leave_raw_mode(force_tty_flag); if (i > (int)sizeof(int)) fatal("%s: master returned too much data (%d > %lu)", - __func__, i, sizeof(int)); + __func__, i, (u_long)sizeof(int)); if (muxclient_terminate) { debug2("Exiting on signal %d", muxclient_terminate); exitval[0] = 255;