From 1339002e8b05d89b10767849d9ee9be55e460f4c Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 6 Jul 2005 09:44:19 +1000 Subject: [PATCH] - djm@cvs.openbsd.org 2005/07/04 00:58:43 [channels.c clientloop.c clientloop.h misc.c misc.h ssh.c ssh_config.5] implement support for X11 and agent forwarding over multiplex slave connections. Because of protocol limitations, the slave connections inherit the master's DISPLAY and SSH_AUTH_SOCK rather than distinctly forwarding their own. ok dtucker@ "put it in" deraadt@ --- ChangeLog | 9 +++++++- channels.c | 59 +++++++++++++++++++++++++++++----------------------- clientloop.c | 35 +++++++++++++++++++++++++------ clientloop.h | 7 ++++++- misc.c | 19 ++++++++++++++++- misc.h | 3 ++- ssh.c | 45 ++++++++++++++++++++------------------- ssh_config.5 | 8 ++++++- 8 files changed, 127 insertions(+), 58 deletions(-) diff --git a/ChangeLog b/ChangeLog index 85d4e91c9..58607022a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,13 @@ - markus@cvs.openbsd.org 2005/07/01 13:19:47 [channels.c] don't free() if getaddrinfo() fails; report mpech@ + - djm@cvs.openbsd.org 2005/07/04 00:58:43 + [channels.c clientloop.c clientloop.h misc.c misc.h ssh.c ssh_config.5] + implement support for X11 and agent forwarding over multiplex slave + connections. Because of protocol limitations, the slave connections inherit + the master's DISPLAY and SSH_AUTH_SOCK rather than distinctly forwarding + their own. + ok dtucker@ "put it in" deraadt@ 20050626 - (djm) OpenBSD CVS Sync @@ -2769,4 +2776,4 @@ - (djm) Trim deprecated options from INSTALL. Mention UsePAM - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu -$Id: ChangeLog,v 1.3832 2005/07/05 23:36:05 djm Exp $ +$Id: ChangeLog,v 1.3833 2005/07/05 23:44:19 djm Exp $ diff --git a/channels.c b/channels.c index b58902328..14ff166ae 100644 --- a/channels.c +++ b/channels.c @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.218 2005/07/01 13:19:47 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.219 2005/07/04 00:58:42 djm Exp $"); #include "ssh.h" #include "ssh1.h" @@ -111,6 +111,9 @@ static int all_opens_permitted = 0; /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 +/* Saved X11 local (client) display. */ +static char *x11_saved_display = NULL; + /* Saved X11 authentication protocol name. */ static char *x11_saved_proto = NULL; @@ -2955,12 +2958,18 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, const char *proto, const char *data) { u_int data_len = (u_int) strlen(data) / 2; - u_int i, value, len; + u_int i, value; char *new_data; int screen_number; const char *cp; u_int32_t rnd = 0; + if (x11_saved_display && strcmp(disp, x11_saved_display) != 0) { + error("x11_request_forwarding_with_spoofing: different " + "$DISPLAY already forwarded"); + return; + } + cp = disp; if (disp) cp = strchr(disp, ':'); @@ -2971,33 +2980,31 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, else screen_number = 0; - /* Save protocol name. */ - x11_saved_proto = xstrdup(proto); - - /* - * Extract real authentication data and generate fake data of the - * same length. - */ - x11_saved_data = xmalloc(data_len); - x11_fake_data = xmalloc(data_len); - for (i = 0; i < data_len; i++) { - if (sscanf(data + 2 * i, "%2x", &value) != 1) - fatal("x11_request_forwarding: bad authentication data: %.100s", data); - if (i % 4 == 0) - rnd = arc4random(); - x11_saved_data[i] = value; - x11_fake_data[i] = rnd & 0xff; - rnd >>= 8; + if (x11_saved_proto == NULL) { + /* Save protocol name. */ + x11_saved_proto = xstrdup(proto); + /* + * Extract real authentication data and generate fake data + * of the same length. + */ + x11_saved_data = xmalloc(data_len); + x11_fake_data = xmalloc(data_len); + for (i = 0; i < data_len; i++) { + if (sscanf(data + 2 * i, "%2x", &value) != 1) + fatal("x11_request_forwarding: bad " + "authentication data: %.100s", data); + if (i % 4 == 0) + rnd = arc4random(); + x11_saved_data[i] = value; + x11_fake_data[i] = rnd & 0xff; + rnd >>= 8; + } + x11_saved_data_len = data_len; + x11_fake_data_len = data_len; } - x11_saved_data_len = data_len; - x11_fake_data_len = data_len; /* Convert the fake data into hex. */ - len = 2 * data_len + 1; - new_data = xmalloc(len); - for (i = 0; i < data_len; i++) - snprintf(new_data + 2 * i, len - 2 * i, - "%02x", (u_char) x11_fake_data[i]); + new_data = tohex(x11_fake_data, data_len); /* Send the request packet. */ if (compat20) { diff --git a/clientloop.c b/clientloop.c index a030cf6e4..9611a5e3e 100644 --- a/clientloop.c +++ b/clientloop.c @@ -59,7 +59,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: clientloop.c,v 1.139 2005/06/17 02:44:32 djm Exp $"); +RCSID("$OpenBSD: clientloop.c,v 1.140 2005/07/04 00:58:43 djm Exp $"); #include "ssh.h" #include "ssh1.h" @@ -140,6 +140,8 @@ int session_ident = -1; struct confirm_ctx { int want_tty; int want_subsys; + int want_x_fwd; + int want_agent_fwd; Buffer cmd; char *term; struct termios tio; @@ -631,6 +633,7 @@ static void client_extra_session2_setup(int id, void *arg) { struct confirm_ctx *cctx = arg; + const char *display; Channel *c; int i; @@ -639,6 +642,24 @@ client_extra_session2_setup(int id, void *arg) if ((c = channel_lookup(id)) == NULL) fatal("%s: no channel for id %d", __func__, id); + display = getenv("DISPLAY"); + if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { + char *proto, *data; + /* Get reasonable local authentication information. */ + client_x11_get_proto(display, options.xauth_location, + options.forward_x11_trusted, &proto, &data); + /* Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication spoofing."); + x11_request_forwarding_with_spoofing(id, display, proto, data); + /* XXX wait for reply */ + } + + if (cctx->want_agent_fwd && options.forward_agent) { + debug("Requesting authentication agent forwarding."); + channel_request_start(id, "auth-agent-req@openssh.com", 0); + packet_send(); + } + client_session2_setup(id, cctx->want_tty, cctx->want_subsys, cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env, client_subsystem_reply); @@ -704,7 +725,7 @@ client_process_control(fd_set * readset) buffer_free(&m); return; } - if ((ver = buffer_get_char(&m)) != 1) { + if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { error("%s: wrong client version %d", __func__, ver); buffer_free(&m); close(client_fd); @@ -738,7 +759,7 @@ client_process_control(fd_set * readset) buffer_clear(&m); buffer_put_int(&m, allowed); buffer_put_int(&m, getpid()); - if (ssh_msg_send(client_fd, /* version */1, &m) == -1) { + if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { error("%s: client msg_send failed", __func__); close(client_fd); buffer_free(&m); @@ -758,7 +779,7 @@ client_process_control(fd_set * readset) buffer_clear(&m); buffer_put_int(&m, allowed); buffer_put_int(&m, getpid()); - if (ssh_msg_send(client_fd, /* version */1, &m) == -1) { + if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { error("%s: client msg_send failed", __func__); close(client_fd); buffer_free(&m); @@ -779,7 +800,7 @@ client_process_control(fd_set * readset) buffer_free(&m); return; } - if ((ver = buffer_get_char(&m)) != 1) { + if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { error("%s: wrong client version %d", __func__, ver); buffer_free(&m); close(client_fd); @@ -790,6 +811,8 @@ client_process_control(fd_set * readset) memset(cctx, 0, sizeof(*cctx)); cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; + 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); cmd = buffer_get_string(&m, &len); @@ -823,7 +846,7 @@ client_process_control(fd_set * readset) /* This roundtrip is just for synchronisation of ttymodes */ buffer_clear(&m); - if (ssh_msg_send(client_fd, /* version */1, &m) == -1) { + if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { error("%s: client msg_send failed", __func__); close(client_fd); close(new_fd[0]); diff --git a/clientloop.h b/clientloop.h index 71c61b5d2..aed2d918b 100644 --- a/clientloop.h +++ b/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.13 2005/06/16 03:38:36 djm Exp $ */ +/* $OpenBSD: clientloop.h,v 1.14 2005/07/04 00:58:43 djm Exp $ */ /* * Author: Tatu Ylonen @@ -43,6 +43,9 @@ void client_global_request_reply_fwd(int, u_int32_t, void *); void client_session2_setup(int, int, int, const char *, struct termios *, int, Buffer *, char **, dispatch_fn *); +/* Multiplexing protocol version */ +#define SSHMUX_VER 1 + /* Multiplexing control protocol flags */ #define SSHMUX_COMMAND_OPEN 1 /* Open new connection */ #define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */ @@ -50,3 +53,5 @@ void client_session2_setup(int, int, int, const char *, struct termios *, #define SSHMUX_FLAG_TTY (1) /* Request tty on open */ #define SSHMUX_FLAG_SUBSYS (1<<1) /* Subsystem request on open */ +#define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */ +#define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */ diff --git a/misc.c b/misc.c index c5ca0ce38..808b7ba27 100644 --- a/misc.c +++ b/misc.c @@ -24,7 +24,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: misc.c,v 1.32 2005/06/17 02:44:32 djm Exp $"); +RCSID("$OpenBSD: misc.c,v 1.33 2005/07/04 00:58:43 djm Exp $"); #include "misc.h" #include "log.h" @@ -506,3 +506,20 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, } return -1; } + +char * +tohex(const u_char *d, u_int l) +{ + char b[3], *r; + u_int i, hl; + + hl = l * 2 + 1; + r = xmalloc(hl); + *r = '\0'; + for (i = 0; i < l; i++) { + snprintf(b, sizeof(b), "%02x", d[i]); + strlcat(r, b, hl); + } + return (r); +} + diff --git a/misc.h b/misc.h index a85fcd134..92848b28e 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.23 2005/06/06 11:20:36 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.24 2005/07/04 00:58:43 djm Exp $ */ /* * Author: Tatu Ylonen @@ -26,6 +26,7 @@ char *colon(char *); long convtime(const char *); char *tilde_expand_filename(const char *, uid_t); char *percent_expand(const char *, ...) __attribute__((sentinel)); +char *tohex(const u_char *, u_int); struct passwd *pwcopy(struct passwd *); diff --git a/ssh.c b/ssh.c index 67af53e69..43d97abcc 100644 --- a/ssh.c +++ b/ssh.c @@ -40,7 +40,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh.c,v 1.246 2005/06/25 22:47:49 djm Exp $"); +RCSID("$OpenBSD: ssh.c,v 1.247 2005/07/04 00:58:43 djm Exp $"); #include #include @@ -1250,41 +1250,44 @@ control_client(const char *path) error("Control socket connect(%.100s): %s", path, strerror(errno)); } - close(sock); - return; - } - - if (stdin_null_flag) { - if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) - fatal("open(/dev/null): %s", strerror(errno)); - if (dup2(fd, STDIN_FILENO) == -1) - fatal("dup2: %s", strerror(errno)); - if (fd > STDERR_FILENO) - close(fd); - } - - if ((term = getenv("TERM")) == NULL) - term = ""; + close(sock); + return; + } + + if (stdin_null_flag) { + if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal("open(/dev/null): %s", strerror(errno)); + if (dup2(fd, STDIN_FILENO) == -1) + fatal("dup2: %s", strerror(errno)); + if (fd > STDERR_FILENO) + close(fd); + } + + term = getenv("TERM"); flags = 0; if (tty_flag) flags |= SSHMUX_FLAG_TTY; if (subsystem_flag) flags |= SSHMUX_FLAG_SUBSYS; + if (options.forward_x11) + flags |= SSHMUX_FLAG_X11_FWD; + if (options.forward_agent) + flags |= SSHMUX_FLAG_AGENT_FWD; buffer_init(&m); /* Send our command to server */ buffer_put_int(&m, mux_command); buffer_put_int(&m, flags); - if (ssh_msg_send(sock, /* version */1, &m) == -1) + if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) fatal("%s: msg_send", __func__); 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) != 1) + if (buffer_get_char(&m) != SSHMUX_VER) fatal("%s: wrong version", __func__); if (buffer_get_int(&m) != 1) fatal("Connection to master denied"); @@ -1308,7 +1311,7 @@ control_client(const char *path) } /* SSHMUX_COMMAND_OPEN */ - buffer_put_cstring(&m, term); + buffer_put_cstring(&m, term ? term : ""); buffer_append(&command, "\0", 1); buffer_put_cstring(&m, buffer_ptr(&command)); @@ -1330,7 +1333,7 @@ control_client(const char *path) } } - if (ssh_msg_send(sock, /* version */1, &m) == -1) + if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) fatal("%s: msg_send", __func__); mm_send_fd(sock, STDIN_FILENO); @@ -1341,7 +1344,7 @@ control_client(const char *path) buffer_clear(&m); if (ssh_msg_recv(sock, &m) == -1) fatal("%s: msg_recv", __func__); - if (buffer_get_char(&m) != 1) + if (buffer_get_char(&m) != SSHMUX_VER) fatal("%s: wrong version", __func__); buffer_free(&m); diff --git a/ssh_config.5 b/ssh_config.5 index 3e7ca8f28..40774297c 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.57 2005/06/18 04:30:36 djm Exp $ +.\" $OpenBSD: ssh_config.5,v 1.58 2005/07/04 00:58:43 djm Exp $ .Dd September 25, 1999 .Dt SSH_CONFIG 5 .Os @@ -279,6 +279,12 @@ can not be opened, .Nm ssh will continue without connecting to a master instance. .Pp +X11 and +.Xr ssh-agent 4 +forwarding is supported over these multiplexed connections, however the +display and agent fowarded will be the one belonging to the master +connection. I.e. it is not possible to forward multiple displays or agents. +.Pp Two additional options allow for opportunistic multiplexing: try to use a master connection but fall back to creating a new one if one does not already exist.