=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.214.2.2 retrieving revision 1.215 diff -u -r1.214.2.2 -r1.215 --- src/usr.bin/ssh/channels.c 2006/02/03 02:53:44 1.214.2.2 +++ src/usr.bin/ssh/channels.c 2005/06/16 03:38:36 1.215 @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.214.2.2 2006/02/03 02:53:44 brad Exp $"); +RCSID("$OpenBSD: channels.c,v 1.215 2005/06/16 03:38:36 djm 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,9 +111,6 @@ /* 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; @@ -140,51 +139,23 @@ /* -- channel core */ Channel * -channel_by_id(int id) +channel_lookup(int id) { Channel *c; if (id < 0 || (u_int)id >= channels_alloc) { - logit("channel_by_id: %d: bad id", id); + logit("channel_lookup: %d: bad id", id); return NULL; } c = channels[id]; if (c == NULL) { - logit("channel_by_id: %d: bad id: channel free", id); + logit("channel_lookup: %d: bad id: channel free", id); return NULL; } return c; } /* - * Returns the channel if it is allowed to receive protocol messages. - * Private channels, like listening sockets, may not receive messages. - */ -Channel * -channel_lookup(int id) -{ - Channel *c; - - if ((c = channel_by_id(id)) == NULL) - return (NULL); - - switch(c->type) { - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_LARVAL: - case SSH_CHANNEL_CONNECTING: - case SSH_CHANNEL_DYNAMIC: - case SSH_CHANNEL_OPENING: - case SSH_CHANNEL_OPEN: - case SSH_CHANNEL_INPUT_DRAINING: - case SSH_CHANNEL_OUTPUT_DRAINING: - return (c); - break; - } - logit("Non-public channel %d, type %d.", id, c->type); - return (NULL); -} - -/* * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */ @@ -294,11 +265,9 @@ c->force_drain = 0; c->single_connection = 0; c->detach_user = NULL; - c->detach_close = 0; c->confirm = NULL; c->confirm_ctx = NULL; c->input_filter = NULL; - c->output_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c; } @@ -655,32 +624,29 @@ c->confirm_ctx = ctx; } void -channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) +channel_register_cleanup(int id, channel_callback_fn *fn) { - Channel *c = channel_by_id(id); + Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_register_cleanup: %d: bad id", id); return; } c->detach_user = fn; - c->detach_close = do_close; } void channel_cancel_cleanup(int id) { - Channel *c = channel_by_id(id); + Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_cancel_cleanup: %d: bad id", id); return; } c->detach_user = NULL; - c->detach_close = 0; } void -channel_register_filter(int id, channel_infilter_fn *ifn, - channel_outfilter_fn *ofn) +channel_register_filter(int id, channel_filter_fn *fn) { Channel *c = channel_lookup(id); @@ -688,8 +654,7 @@ logit("channel_register_filter: %d: bad id", id); return; } - c->input_filter = ifn; - c->output_filter = ofn; + c->input_filter = fn; } void @@ -761,8 +726,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); } @@ -928,7 +893,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) { char *p, *host; - u_int len, have, i, found; + int len, have, i, found; char username[256]; struct { u_int8_t version; @@ -1013,7 +978,7 @@ } s5_req, s5_rsp; u_int16_t dest_port; u_char *p, dest_addr[255+1]; - u_int have, i, found, nmethods, addrlen, af; + int i, have, found, nmethods, addrlen, af; debug2("channel %d: decode socks5", c->self); p = buffer_ptr(&c->input); @@ -1109,8 +1074,7 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) { u_char *p; - u_int have; - int ret; + int have, ret; have = buffer_len(&c->input); c->delayed = 0; @@ -1213,7 +1177,7 @@ int direct; char buf[1024]; char *remote_ipaddr = get_peer_ipaddr(c->sock); - int remote_port = get_peer_port(c->sock); + u_short remote_port = get_peer_port(c->sock); direct = (strcmp(rtype, "direct-tcpip") == 0); @@ -1243,7 +1207,7 @@ } /* originator host and port */ packet_put_cstring(remote_ipaddr); - packet_put_int((u_int)remote_port); + packet_put_int(remote_port); packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); @@ -1258,19 +1222,6 @@ xfree(remote_ipaddr); } -static void -channel_set_reuseaddr(int fd) -{ - int on = 1; - - /* - * Set socket options. - * Allow local port reuse in TIME_WAIT. - */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) - error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); -} - /* * This socket is listening for connections to a forwarded TCP/IP port. */ @@ -1442,8 +1393,6 @@ debug2("channel %d: filter stops", c->self); chan_read_failed(c); } - } else if (c->datagram) { - buffer_put_string(&c->input, buf, len); } else { buffer_append(&c->input, buf, len); } @@ -1454,7 +1403,7 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) { struct termios tio; - u_char *data = NULL, *buf; + u_char *data; u_int dlen; int len; @@ -1462,40 +1411,9 @@ if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { - if (c->output_filter != NULL) { - if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { - debug2("channel %d: filter stops", c->self); - if (c->type != SSH_CHANNEL_OPEN) - chan_mark_dead(c); - else - chan_write_failed(c); - return -1; - } - } else if (c->datagram) { - buf = data = buffer_get_string(&c->output, &dlen); - } else { - buf = data = buffer_ptr(&c->output); - dlen = buffer_len(&c->output); - } - - if (c->datagram) { - /* ignore truncated writes, datagrams might get lost */ - c->local_consumed += dlen + 4; - len = write(c->wfd, buf, dlen); - xfree(data); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) - return 1; - if (len <= 0) { - if (c->type != SSH_CHANNEL_OPEN) - chan_mark_dead(c); - else - chan_write_failed(c); - return -1; - } - return 1; - } - - len = write(c->wfd, buf, dlen); + data = buffer_ptr(&c->output); + dlen = buffer_len(&c->output); + len = write(c->wfd, data, dlen); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; if (len <= 0) { @@ -1512,14 +1430,14 @@ } return -1; } - if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { + if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { /* * Simulate echo to reduce the impact of * traffic analysis. We need to match the * size of a SSH2_MSG_CHANNEL_DATA message - * (4 byte channel id + buf) + * (4 byte channel id + data) */ packet_send_ignore(4 + len); packet_send(); @@ -1738,7 +1656,7 @@ if (c == NULL) return; if (c->detach_user != NULL) { - if (!chan_is_dead(c, c->detach_close)) + if (!chan_is_dead(c, 0)) return; debug2("channel %d: gc: notify user", c->self); c->detach_user(c->self, NULL); @@ -1848,22 +1766,6 @@ if ((c->istate == CHAN_INPUT_OPEN || c->istate == CHAN_INPUT_WAIT_DRAIN) && (len = buffer_len(&c->input)) > 0) { - if (c->datagram) { - if (len > 0) { - u_char *data; - u_int dlen; - - data = buffer_get_string(&c->input, - &dlen); - packet_start(SSH2_MSG_CHANNEL_DATA); - packet_put_int(c->remote_id); - packet_put_string(data, dlen); - packet_send(); - c->remote_window -= dlen + 4; - xfree(data); - } - continue; - } /* * Send some data for the other side over the secure * connection. @@ -1901,8 +1803,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); } @@ -1986,10 +1888,7 @@ c->local_window -= data_len; } packet_check_eom(); - if (c->datagram) - buffer_put_string(&c->output, data, data_len); - else - buffer_append(&c->output, data, data_len); + buffer_append(&c->output, data, data_len); xfree(data); } @@ -2220,8 +2119,9 @@ id = packet_get_int(); c = channel_lookup(id); - if (c == NULL) { - logit("Received window adjust for non-open channel %d.", id); + if (c == NULL || c->type != SSH_CHANNEL_OPEN) { + logit("Received window adjust for " + "non-open channel %d.", id); return; } adjust = packet_get_int(); @@ -2278,7 +2178,7 @@ const char *host_to_connect, u_short port_to_connect, int gateway_ports) { Channel *c; - int sock, r, success = 0, wildcard = 0, is_client; + int sock, r, success = 0, on = 1, wildcard = 0, is_client; struct addrinfo hints, *ai, *aitop; const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; @@ -2289,11 +2189,11 @@ if (host == NULL) { error("No forward host name."); - return 0; + return success; } if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { error("Forward host name too long."); - return 0; + return success; } /* @@ -2344,10 +2244,12 @@ packet_disconnect("getaddrinfo: fatal error: %s", gai_strerror(r)); } else { - error("channel_setup_fwd_listener: " + verbose("channel_setup_fwd_listener: " "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); + packet_send_debug("channel_setup_fwd_listener: " + "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); } - return 0; + aitop = NULL; } for (ai = aitop; ai; ai = ai->ai_next) { @@ -2365,9 +2267,14 @@ verbose("socket: %.100s", strerror(errno)); continue; } + /* + * Set socket options. + * Allow local port reuse in TIME_WAIT. + */ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)) == -1) + error("setsockopt SO_REUSEADDR: %s", strerror(errno)); - channel_set_reuseaddr(sock); - debug("Local forwarding listening on %s port %s.", ntop, strport); /* Bind the socket to the address. */ @@ -2534,7 +2441,7 @@ permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; - xfree(permitted_opens[i].host_to_connect); + free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; } @@ -2738,7 +2645,7 @@ */ int x11_create_display_inet(int x11_display_offset, int x11_use_localhost, - int single_connection, u_int *display_numberp, int **chanids) + int single_connection, u_int *display_numberp) { Channel *nc = NULL; int display_number, sock; @@ -2747,9 +2654,6 @@ char strport[NI_MAXSERV]; int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; - if (chanids == NULL) - return -1; - for (display_number = x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { @@ -2773,7 +2677,6 @@ freeaddrinfo(aitop); return -1; } - channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("bind port %d: %.100s", port, strerror(errno)); close(sock); @@ -2810,7 +2713,6 @@ } /* Allocate a channel for each socket. */ - *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); for (n = 0; n < num_socks; n++) { sock = socks[n]; nc = channel_new("x11 listener", @@ -2818,9 +2720,7 @@ CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); nc->single_connection = single_connection; - (*chanids)[n] = nc->self; } - (*chanids)[n] = -1; /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; @@ -3006,7 +2906,7 @@ error("deny_input_open: type %d", type); break; } - error("Warning: this is probably a break-in attempt by a malicious server."); + error("Warning: this is probably a break in attempt by a malicious server."); packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_send(); @@ -3022,20 +2922,12 @@ const char *proto, const char *data) { u_int data_len = (u_int) strlen(data) / 2; - u_int i, value; + u_int i, value, len; char *new_data; int screen_number; const char *cp; u_int32_t rnd = 0; - 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, ':'); @@ -3046,31 +2938,33 @@ 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; + /* 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); + 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]); /* Send the request packet. */ if (compat20) {