=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.210 retrieving revision 1.210.2.3 diff -u -r1.210 -r1.210.2.3 --- src/usr.bin/ssh/channels.c 2004/08/23 11:48:47 1.210 +++ src/usr.bin/ssh/channels.c 2005/09/02 03:44:59 1.210.2.3 @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.210 2004/08/23 11:48:47 djm Exp $"); +RCSID("$OpenBSD: channels.c,v 1.210.2.3 2005/09/02 03:44:59 brad Exp $"); #include "ssh.h" #include "ssh1.h" @@ -58,6 +58,8 @@ /* -- channel core */ +#define CHAN_RBUF 16*1024 + /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. @@ -109,6 +111,9 @@ /* 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; @@ -711,6 +716,9 @@ { u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); + /* check buffer limits */ + limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF)); + if (c->istate == CHAN_INPUT_OPEN && limit > 0 && buffer_len(&c->input) < limit) @@ -721,8 +729,8 @@ FD_SET(c->wfd, writeset); } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) - debug2("channel %d: obuf_empty delayed efd %d/(%d)", - c->self, c->efd, buffer_len(&c->extended)); + debug2("channel %d: obuf_empty delayed efd %d/(%d)", + c->self, c->efd, buffer_len(&c->extended)); else chan_obuf_empty(c); } @@ -888,7 +896,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) { char *p, *host; - int len, have, i, found; + u_int len, have, i, found; char username[256]; struct { u_int8_t version; @@ -973,7 +981,7 @@ } s5_req, s5_rsp; u_int16_t dest_port; u_char *p, dest_addr[255+1]; - int i, have, found, nmethods, addrlen, af; + u_int have, i, found, nmethods, addrlen, af; debug2("channel %d: decode socks5", c->self); p = buffer_ptr(&c->input); @@ -1017,7 +1025,7 @@ debug2("channel %d: only socks5 connect supported", c->self); return -1; } - switch(s5_req.atyp){ + switch (s5_req.atyp){ case SSH_SOCKS5_IPV4: addrlen = 4; af = AF_INET; @@ -1069,7 +1077,8 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) { u_char *p; - int have, ret; + u_int have; + int ret; have = buffer_len(&c->input); c->delayed = 0; @@ -1172,7 +1181,7 @@ int direct; char buf[1024]; char *remote_ipaddr = get_peer_ipaddr(c->sock); - u_short remote_port = get_peer_port(c->sock); + int remote_port = get_peer_port(c->sock); direct = (strcmp(rtype, "direct-tcpip") == 0); @@ -1202,7 +1211,7 @@ } /* originator host and port */ packet_put_cstring(remote_ipaddr); - packet_put_int(remote_port); + packet_put_int((u_int)remote_port); packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); @@ -1359,7 +1368,7 @@ static int channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) { - char buf[16*1024]; + char buf[CHAN_RBUF]; int len; if (c->rfd != -1 && @@ -1448,7 +1457,7 @@ static int channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) { - char buf[16*1024]; + char buf[CHAN_RBUF]; int len; /** XXX handle drain efd, too */ @@ -1798,8 +1807,8 @@ * hack for extended data: delay EOF if EFD still in use. */ if (CHANNEL_EFD_INPUT_ACTIVE(c)) - debug2("channel %d: ibuf_empty delayed efd %d/(%d)", - c->self, c->efd, buffer_len(&c->extended)); + debug2("channel %d: ibuf_empty delayed efd %d/(%d)", + c->self, c->efd, buffer_len(&c->extended)); else chan_ibuf_empty(c); } @@ -2173,35 +2182,77 @@ const char *host_to_connect, u_short port_to_connect, int gateway_ports) { Channel *c; - int success, sock, on = 1; + int sock, r, success = 0, on = 1, wildcard = 0, is_client; struct addrinfo hints, *ai, *aitop; - const char *host; + const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - success = 0; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? listen_addr : host_to_connect; + is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (host == NULL) { error("No forward host name."); - return success; + return 0; } if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { error("Forward host name too long."); - return success; + return 0; } /* + * Determine whether or not a port forward listens to loopback, + * specified address or wildcard. On the client, a specified bind + * address will always override gateway_ports. On the server, a + * gateway_ports of 1 (``yes'') will override the client's + * specification and force a wildcard bind, whereas a value of 2 + * (``clientspecified'') will bind to whatever address the client + * asked for. + * + * Special-case listen_addrs are: + * + * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR + * "" (empty string), "*" -> wildcard v4/v6 + * "localhost" -> loopback v4/v6 + */ + addr = NULL; + if (listen_addr == NULL) { + /* No address specified: default to gateway_ports setting */ + if (gateway_ports) + wildcard = 1; + } else if (gateway_ports || is_client) { + if (((datafellows & SSH_OLD_FORWARD_ADDR) && + strcmp(listen_addr, "0.0.0.0") == 0) || + *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || + (!is_client && gateway_ports == 1)) + wildcard = 1; + else if (strcmp(listen_addr, "localhost") != 0) + addr = listen_addr; + } + + debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", + type, wildcard, (addr == NULL) ? "NULL" : addr); + + /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; - hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; + hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", listen_port); - if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) - packet_disconnect("getaddrinfo: fatal error"); + if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { + if (addr == NULL) { + /* This really shouldn't happen */ + packet_disconnect("getaddrinfo: fatal error: %s", + gai_strerror(r)); + } else { + error("channel_setup_fwd_listener: " + "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); + } + return 0; + } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -2263,7 +2314,7 @@ u_int i; int found = 0; - for(i = 0; i < channels_alloc; i++) { + for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && @@ -2280,11 +2331,12 @@ /* protocol local port fwd, used by ssh (and sshd in v1) */ int -channel_setup_local_fwd_listener(u_short listen_port, +channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, - NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); + listen_host, listen_port, host_to_connect, port_to_connect, + gateway_ports); } /* protocol v2 remote port fwd, used by sshd */ @@ -2302,7 +2354,7 @@ */ void -channel_request_remote_forwarding(u_short listen_port, +channel_request_remote_forwarding(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect) { int type, success = 0; @@ -2313,7 +2365,14 @@ /* Send the forward request to the remote side. */ if (compat20) { - const char *address_to_bind = "0.0.0.0"; + const char *address_to_bind; + if (listen_host == NULL) + address_to_bind = "localhost"; + else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) + address_to_bind = ""; + else + address_to_bind = listen_host; + packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("tcpip-forward"); packet_put_char(1); /* boolean: want reply */ @@ -2359,10 +2418,9 @@ * local side. */ void -channel_request_rforward_cancel(u_short port) +channel_request_rforward_cancel(const char *host, u_short port) { int i; - const char *address_to_bind = "0.0.0.0"; if (!compat20) return; @@ -2379,7 +2437,7 @@ packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-tcpip-forward"); packet_put_char(0); - packet_put_cstring(address_to_bind); + packet_put_cstring(host == NULL ? "" : host); packet_put_int(port); packet_send(); @@ -2418,7 +2476,8 @@ packet_disconnect("Dynamic forwarding denied."); /* Initiate forwarding */ - channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); + channel_setup_local_fwd_listener(NULL, port, hostname, + host_port, gateway_ports); /* Free the argument string. */ xfree(hostname); @@ -2565,7 +2624,7 @@ struct winsize ws; for (i = 0; i < channels_alloc; i++) { - if (channels[i] == NULL || + if (channels[i] == NULL || !channels[i]->client_tty || channels[i]->type != SSH_CHANNEL_OPEN) continue; if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) @@ -2588,7 +2647,7 @@ */ int x11_create_display_inet(int x11_display_offset, int x11_use_localhost, - int single_connection, u_int *display_numberp) + int single_connection, u_int *display_numberp, int **chanids) { Channel *nc = NULL; int display_number, sock; @@ -2656,6 +2715,8 @@ } /* Allocate a channel for each socket. */ + if (chanids != NULL) + *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); for (n = 0; n < num_socks; n++) { sock = socks[n]; nc = channel_new("x11 listener", @@ -2663,7 +2724,11 @@ CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); nc->single_connection = single_connection; + if (*chanids != NULL) + (*chanids)[n] = nc->self; } + if (*chanids != NULL) + (*chanids)[n] = -1; /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; @@ -2861,53 +2926,59 @@ * This should be called in the client only. */ void -x11_request_forwarding_with_spoofing(int client_session_id, +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; - cp = getenv("DISPLAY"); + 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; + } + + cp = disp; + if (disp) + cp = strchr(disp, ':'); if (cp) - cp = strchr(cp, ':'); - if (cp) cp = strchr(cp, '.'); if (cp) screen_number = atoi(cp + 1); 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) {