diff -u openssh-4.2p1/channels.c openssh-4.2p1/channels.c --- openssh-4.2p1/channels.c 2005-09-04 17:30:14.000000000 +0100 +++ openssh-4.2p1/channels.c 2005-09-07 12:50:17.000000000 +0100 @@ -78,6 +78,11 @@ */ static int channel_max_fd = 0; +/* + * Flag indicating that channel fds have been modified, used to restart + * preparations for select if necessary + */ +static int channel_fds_changed = 0; /* -- tcp forwarding */ @@ -111,23 +116,31 @@ /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 -/* Saved X11 local (client) display. */ -static char *x11_saved_display = NULL; +struct x11_fwd { + /* Saved X11 local (client) display. */ + char *saved_display; + + /* Saved X11 authentication protocol name. */ + char *saved_proto; + + /* Saved X11 authentication data. These are the real data. */ + u_char *saved_data; + u_int saved_data_len; -/* Saved X11 authentication protocol name. */ -static char *x11_saved_proto = NULL; + /* + * Fake X11 authentication data. This is what the server will be + sending us; we should replace any occurrences with the real data. + */ + u_char *fake_data; + u_int fake_data_len; + + u_int usecount; + struct x11_fwd *next; +}; -/* Saved X11 authentication data. This is the real data. */ -static char *x11_saved_data = NULL; -static u_int x11_saved_data_len = 0; - -/* - * Fake X11 authentication data. This is what the server will be sending us; - * we should replace any occurrences of this by the real data. - */ -static char *x11_fake_data = NULL; -static u_int x11_fake_data_len; +static struct x11_fwd *x11_fwds = NULL; +static void x11_put_fwd(struct x11_fwd *fwd); /* -- agent forwarding */ @@ -167,6 +180,16 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock) { + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->ctl_fd = -1; /* XXX: set elsewhere */ + c->efd = efd; + channel_fds_changed = 1; + + if (rfd == -1 && wfd == -1 && efd == -1) + return; + /* Update the maximum file descriptor value. */ channel_max_fd = MAX(channel_max_fd, rfd); channel_max_fd = MAX(channel_max_fd, wfd); @@ -174,11 +195,6 @@ /* XXX set close-on-exec -markus */ - c->rfd = rfd; - c->wfd = wfd; - c->sock = (rfd == wfd) ? rfd : -1; - c->ctl_fd = -1; /* XXX: set elsewhere */ - c->efd = efd; c->extended_usage = extusage; /* XXX ugly hack: nonblock is only set by the server */ @@ -354,6 +373,8 @@ c->remote_name = NULL; } channels[c->self] = NULL; + if (c->x11_fwd) + x11_put_fwd(c->x11_fwd); xfree(c); } @@ -773,6 +794,54 @@ FD_SET(c->sock, writeset); } +static struct x11_fwd * +x11_lookup_fwd_by_fakedata(unsigned char *proto, u_int protolen, unsigned char *data, u_int datalen) +{ + struct x11_fwd *fwd; + + for (fwd = x11_fwds; fwd; fwd = fwd->next) { + if (protolen == strlen(fwd->saved_proto) && + !memcmp(fwd->saved_proto, proto, protolen) && + fwd->fake_data_len == datalen && + !memcmp(fwd->fake_data, data, datalen)) + break; + } + return fwd; +} + +static struct x11_fwd * +x11_lookup_fwd_by_realdata(const char *display, const char *proto, const u_char *authdata, u_int datalen) +{ + struct x11_fwd *fwd; + + for (fwd = x11_fwds; fwd; fwd = fwd->next) { + if (!strcmp(display, fwd->saved_display) && + !strcmp(proto, fwd->saved_proto) && + datalen == fwd->saved_data_len && + !memcmp(authdata, fwd->saved_data, datalen)) + break; + } + return fwd; +} + +static void x11_put_fwd(struct x11_fwd *fwd) +{ + fwd->usecount--; + debug("Decrease usecount on X11 forward data for %s to %d.", fwd->saved_display, fwd->usecount); + if (!fwd->usecount) { + struct x11_fwd **tmp = &x11_fwds; + while (*tmp) { + if (*tmp == fwd) { + *tmp = fwd->next; + xfree(fwd); + return; + } + tmp = &(*tmp)->next; + } + fatal("X11 forward data not found."); + } +} + /* * This is a special state for X11 authentication spoofing. An opened X11 * connection (when authentication spoofing is being done) remains in this @@ -783,10 +852,13 @@ * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok */ static int -x11_open_helper(Buffer *b) +x11_open_helper(Channel *c) { + Buffer *b = &c->output; u_char *ucp; u_int proto_len, data_len; + struct x11_fwd *fwd; + int sock; /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(b) < 12) @@ -811,50 +883,54 @@ 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) return 0; - /* Check if authentication protocol matches. */ - if (proto_len != strlen(x11_saved_proto) || - memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { - debug2("X11 connection uses different authentication protocol."); - return -1; - } - /* Check if authentication data matches our fake data. */ - if (data_len != x11_fake_data_len || - memcmp(ucp + 12 + ((proto_len + 3) & ~3), - x11_fake_data, x11_fake_data_len) != 0) { - debug2("X11 auth data does not match fake data."); - return -1; - } - /* Check fake data length */ - if (x11_fake_data_len != x11_saved_data_len) { - error("X11 fake_data_len %d != saved_data_len %d", - x11_fake_data_len, x11_saved_data_len); + fwd = x11_lookup_fwd_by_fakedata(ucp+12, proto_len, + ucp + 12 + ((proto_len + 3) & ~3), data_len); + + if (!fwd) { + debug("No matching X11 forward found."); return -1; } + fwd->usecount++; + debug("Increase use count for X11 fwd for display %s to %d.", fwd->saved_display, fwd->usecount); + c->x11_fwd = fwd; /* - * Received authentication protocol and data match - * our fake data. Substitute the fake data with real - * data. + * We found a matching X11 forward. Substitute the fake data + * with real data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), - x11_saved_data, x11_saved_data_len); - return 1; + fwd->saved_data, fwd->saved_data_len); + + sock = x11_connect_display(fwd->saved_display); + if (sock == -1) + return -2; + channel_register_fds(c, sock, sock, -1, 0, 1); + c->type = SSH_CHANNEL_OPEN; + + /* Because we registered a new fd, the channel_pre handlers will be + called again immediately. And because we changed the channel type + to SSH_CHANNEL_OPEN, the x11 function won't be called next time. + It's just a normal channel now */ + return 0; } static void channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) { - int ret = x11_open_helper(&c->output); + int ret = x11_open_helper(c); - if (ret == 1) { - /* Start normal processing for the channel. */ - c->type = SSH_CHANNEL_OPEN; - channel_pre_open_13(c, readset, writeset); - } else if (ret == -1) { + if (ret < 0) { /* * We have received an X11 connection that has bad * authentication information. */ - logit("X11 connection rejected because of wrong authentication."); + switch (ret) { + case -1: + logit("X11 connection rejected because of wrong authentication."); + break; + case -2: + logit("X11 connection rejected due to failure to connect to local server."); + break; + } buffer_clear(&c->input); buffer_clear(&c->output); channel_close_fd(&c->sock); @@ -869,15 +945,17 @@ static void channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) { - int ret = x11_open_helper(&c->output); - - /* c->force_drain = 1; */ + int ret = x11_open_helper(c); - if (ret == 1) { - c->type = SSH_CHANNEL_OPEN; - channel_pre_open(c, readset, writeset); - } else if (ret == -1) { - logit("X11 connection rejected because of wrong authentication."); + if (ret < 0) { + switch (ret) { + case -1: + logit("X11 connection rejected because of wrong authentication."); + break; + case -2: + logit("X11 connection rejected due to failure to connect to local server."); + break; + } debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); chan_read_failed(c); buffer_clear(&c->input); @@ -1716,21 +1794,25 @@ { u_int n, sz; - n = MAX(*maxfdp, channel_max_fd); + for (channel_fds_changed = 1; channel_fds_changed;) { + channel_fds_changed = 0; + n = MAX(*maxfdp, channel_max_fd); + + sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); + /* perhaps check sz < nalloc/2 and shrink? */ + if (*readsetp == NULL || sz > *nallocp) { + *readsetp = xrealloc(*readsetp, sz); + *writesetp = xrealloc(*writesetp, sz); + *nallocp = sz; + } + *maxfdp = n; + memset(*readsetp, 0, sz); + memset(*writesetp, 0, sz); - sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); - /* perhaps check sz < nalloc/2 and shrink? */ - if (*readsetp == NULL || sz > *nallocp) { - *readsetp = xrealloc(*readsetp, sz); - *writesetp = xrealloc(*writesetp, sz); - *nallocp = sz; + if (!rekeying) + channel_handler(channel_pre, *readsetp, *writesetp); } - *maxfdp = n; - memset(*readsetp, 0, sz); - memset(*writesetp, 0, sz); - if (!rekeying) - channel_handler(channel_pre, *readsetp, *writesetp); } /* @@ -2790,23 +2872,16 @@ } int -x11_connect_display(void) +x11_connect_display(char *display) { int display_number, sock = 0; - const char *display; char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; - /* Try to open a socket for the local X server. */ - display = getenv("DISPLAY"); - if (!display) { - error("DISPLAY not set."); - return -1; - } /* - * Now we decode the value of the DISPLAY variable and make a + * Decode the value of the DISPLAY variable and make a * connection to the real X server. */ @@ -2894,7 +2969,7 @@ x11_input_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; - int remote_id, sock = 0; + int remote_id; char *remote_host; debug("Received X11 open request."); @@ -2908,27 +2983,20 @@ } packet_check_eom(); - /* Obtain a connection to the real X display. */ - sock = x11_connect_display(); - if (sock != -1) { - /* Allocate a channel for this connection. */ - c = channel_new("connected x11 socket", - SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, - remote_host, 1); - c->remote_id = remote_id; - c->force_drain = 1; - } + /* Allocate a channel for this connection. */ + c = channel_new("connected x11 socket", + SSH_CHANNEL_X11_OPEN, -1, -1, -1, 0, 0, 0, + remote_host, 1); + c->remote_id = remote_id; + c->force_drain = 1; + xfree(remote_host); - if (c == NULL) { - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_id); - } else { - /* Send a confirmation to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_id); - packet_put_int(c->self); - } + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_id); + packet_put_int(c->self); + packet_send(); } @@ -2964,21 +3032,62 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, const char *proto, const char *data) { + Channel *c = NULL; u_int data_len = (u_int) strlen(data) / 2; u_int i, value; char *new_data; + u_char *realdata; int screen_number; const char *cp; u_int32_t rnd = 0; + struct x11_fwd *fwd; - if (x11_saved_display == NULL) - x11_saved_display = xstrdup(disp); - else if (strcmp(disp, x11_saved_display) != 0) { - error("x11_request_forwarding_with_spoofing: different " - "$DISPLAY already forwarded"); - return; + if (compat20) { + c = channel_lookup(client_session_id); + if (!c) + error("%s: channel %d: unknown channel", __func__, client_session_id); + } + + /* Convert real authentication data to binary */ + realdata = 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); + realdata[i] = value; + } + + fwd = x11_lookup_fwd_by_realdata(disp, proto, realdata, data_len); + if (fwd) { + fwd->usecount++; + debug("Increase use count for X11 fwd for display %s to %d.", disp, fwd->usecount); + xfree(realdata); + } else { + /* No existing forward. Create a new one */ + debug("Create new X11 fake auth data for display %s.", disp); + fwd = xmalloc(sizeof(*fwd)); + fwd->saved_display = xstrdup(disp); + fwd->saved_proto = xstrdup(proto); + fwd->saved_data = realdata; + fwd->saved_data_len = data_len; + fwd->fake_data = xmalloc(data_len); + fwd->fake_data_len = data_len; + + for (i = 0; i < data_len; i++) { + if (i % 4 == 0) + rnd = arc4random(); + fwd->fake_data[i] = rnd & 0xff; + rnd >>= 8; + } + + fwd->usecount = 1; + fwd->next = x11_fwds; + x11_fwds = fwd; } + if (c) + c->x11_fwd = fwd; + cp = disp; if (disp) cp = strchr(disp, ':'); @@ -2989,31 +3098,8 @@ else screen_number = 0; - 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; - } - /* Convert the fake data into hex. */ - new_data = tohex(x11_fake_data, data_len); + new_data = tohex(fwd->fake_data, data_len); /* Send the request packet. */ if (compat20) { unchanged: --- openssh-4.2p1/channels.h~ 2005-07-17 08:19:25.000000000 +0100 +++ openssh-4.2p1/channels.h 2005-09-04 17:22:47.000000000 +0100 @@ -59,6 +59,7 @@ #define SSH_CHANNEL_PATH_LEN 256 +struct x11_fwd; struct Channel; typedef struct Channel Channel; @@ -111,6 +112,9 @@ struct Channel { channel_outfilter_fn *output_filter; int datagram; /* keep boundaries */ + + /* X11 forwarding data */ + struct x11_fwd *x11_fwd; }; #define CHAN_EXTENDED_IGNORE 0 @@ -213,7 +217,7 @@ int channel_cancel_rport_listener(const /* x11 forwarding */ -int x11_connect_display(void); +int x11_connect_display(char *); int x11_create_display_inet(int, int, int, u_int *, int **); void x11_input_open(int, u_int32_t, void *); void x11_request_forwarding_with_spoofing(int, const char *, const char *, unchanged: --- openssh-4.2p1/clientloop.c~ 2005-09-04 15:12:52.000000000 +0100 +++ openssh-4.2p1/clientloop.c 2005-09-04 16:45:58.000000000 +0100 @@ -1730,11 +1730,8 @@ client_request_x11(const char *request_t debug("client_request_x11: request from %s %d", originator, originator_port); xfree(originator); - sock = x11_connect_display(); - if (sock < 0) - return NULL; c = channel_new("x11", - SSH_CHANNEL_X11_OPEN, sock, sock, -1, + SSH_CHANNEL_X11_OPEN, -1, -1, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; return c;