=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.195 retrieving revision 1.195.2.2 diff -u -r1.195 -r1.195.2.2 --- src/usr.bin/ssh/channels.c 2003/09/16 21:02:40 1.195 +++ src/usr.bin/ssh/channels.c 2004/08/19 22:37:30 1.195.2.2 @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.195 2003/09/16 21:02:40 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.195.2.2 2004/08/19 22:37:30 brad Exp $"); #include "ssh.h" #include "ssh1.h" @@ -68,7 +68,7 @@ * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots set to NULL */ -static int channels_alloc = 0; +static u_int channels_alloc = 0; /* * Maximum file descriptor value used in any of the channels. This is @@ -141,7 +141,7 @@ { Channel *c; - if (id < 0 || id >= channels_alloc) { + if (id < 0 || (u_int)id >= channels_alloc) { logit("channel_lookup: %d: bad id", id); return NULL; } @@ -172,6 +172,7 @@ 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; @@ -207,7 +208,8 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) { - int i, found; + int found; + u_int i; Channel *c; /* Do initial allocation if this is the first call. */ @@ -216,16 +218,15 @@ channels = xmalloc(channels_alloc * sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = NULL; - fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); } /* Try to find a free slot where to put the new channel. */ for (found = -1, i = 0; i < channels_alloc; i++) if (channels[i] == NULL) { /* Found a free slot. */ - found = i; + found = (int)i; break; } - if (found == -1) { + if (found < 0) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; if (channels_alloc > 10000) @@ -263,6 +264,7 @@ c->single_connection = 0; c->detach_user = NULL; c->confirm = NULL; + c->confirm_ctx = NULL; c->input_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c; @@ -271,7 +273,8 @@ static int channel_find_maxfd(void) { - int i, max = 0; + u_int i; + int max = 0; Channel *c; for (i = 0; i < channels_alloc; i++) { @@ -304,10 +307,11 @@ static void channel_close_fds(Channel *c) { - debug3("channel %d: close_fds r %d w %d e %d", - c->self, c->rfd, c->wfd, c->efd); + debug3("channel %d: close_fds r %d w %d e %d c %d", + c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); channel_close_fd(&c->sock); + channel_close_fd(&c->ctl_fd); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); @@ -319,12 +323,12 @@ channel_free(Channel *c) { char *s; - int i, n; + u_int i, n; for (n = 0, i = 0; i < channels_alloc; i++) if (channels[i]) n++; - debug("channel %d: free: %s, nchannels %d", c->self, + debug("channel %d: free: %s, nchannels %u", c->self, c->remote_name ? c->remote_name : "???", n); s = channel_open_message(); @@ -333,6 +337,8 @@ if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); + if (c->ctl_fd != -1) + shutdown(c->ctl_fd, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); @@ -348,7 +354,7 @@ void channel_free_all(void) { - int i; + u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) @@ -363,7 +369,7 @@ void channel_close_all(void) { - int i; + u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) @@ -377,7 +383,7 @@ void channel_stop_listening(void) { - int i; + u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { @@ -434,7 +440,7 @@ int channel_still_open(void) { - int i; + u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { @@ -477,12 +483,12 @@ int channel_find_open(void) { - int i; + u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; - if (c == NULL) + if (c == NULL || c->remote_id < 0) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: @@ -525,7 +531,7 @@ Buffer buffer; Channel *c; char buf[1024], *cp; - int i; + u_int i; buffer_init(&buffer); snprintf(buf, sizeof buf, "The following connections are open:\r\n"); @@ -550,12 +556,13 @@ case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: - snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", + snprintf(buf, sizeof buf, + " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), - c->rfd, c->wfd); + c->rfd, c->wfd, c->ctl_fd); buffer_append(&buffer, buf, strlen(buf)); continue; default: @@ -596,14 +603,14 @@ logit("channel_request_start: %d: unknown channel id", id); return; } - debug2("channel %d: request %s", id, service) ; + debug2("channel %d: request %s confirm %d", id, service, wantconfirm); packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring(service); packet_put_char(wantconfirm); } void -channel_register_confirm(int id, channel_callback_fn *fn) +channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) { Channel *c = channel_lookup(id); @@ -612,6 +619,7 @@ return; } c->confirm = fn; + c->confirm_ctx = ctx; } void channel_register_cleanup(int id, channel_callback_fn *fn) @@ -729,6 +737,10 @@ buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } + /* XXX: What about efd? races? */ + if (compat20 && c->ctl_fd != -1 && + c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) + FD_SET(c->ctl_fd, readset); } static void @@ -970,7 +982,7 @@ have = buffer_len(&c->input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ - if (have < 2) + if (have < 2) return 0; nmethods = p[1]; if (have < nmethods + 2) @@ -1031,11 +1043,11 @@ buffer_get(&c->input, (char *)&dest_port, 2); dest_addr[addrlen] = '\0'; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) - strlcpy(c->path, dest_addr, sizeof(c->path)); + strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) return -1; c->host_port = ntohs(dest_port); - + debug2("channel %d: dynamic request: socks5 host %s port %u command %u", c->self, c->path, c->host_port, s5_req.command); @@ -1477,6 +1489,33 @@ return 1; } static int +channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16]; + int len; + + /* Monitor control fd to detect if the slave client exits */ + if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { + len = read(c->ctl_fd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug2("channel %d: ctl read<=0", c->self); + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(c); + return -1; + } else { + chan_read_failed(c); + chan_write_failed(c); + } + return -1; + } else + fatal("%s: unexpected data on ctl fd", __func__); + } + return 1; +} +static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && @@ -1506,6 +1545,7 @@ if (!compat20) return; channel_handle_efd(c, readset, writeset); + channel_handle_ctl(c, readset, writeset); channel_check_window(c); } @@ -1630,7 +1670,7 @@ channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) { static int did_init = 0; - int i; + u_int i; Channel *c; if (!did_init) { @@ -1653,10 +1693,9 @@ */ void channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, - int *nallocp, int rekeying) + u_int *nallocp, int rekeying) { - int n; - u_int sz; + u_int n, sz; n = MAX(*maxfdp, channel_max_fd); @@ -1692,8 +1731,7 @@ channel_output_poll(void) { Channel *c; - int i; - u_int len; + u_int i, len; for (i = 0; i < channels_alloc; i++) { c = channels[i]; @@ -1812,13 +1850,25 @@ c->type != SSH_CHANNEL_X11_OPEN) return; - /* same for protocol 1.5 if output end is no longer open */ - if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) - return; - /* Get the data. */ data = packet_get_string(&data_len); + /* + * Ignore data for protocol > 1.3 if output end is no longer open. + * For protocol 2 the sending side is reducing its window as it sends + * data, so we must 'fake' consumption of the data in order to ensure + * that window updates are sent back. Otherwise the connection might + * deadlock. + */ + if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { + if (compat20) { + c->local_window -= data_len; + c->local_consumed += data_len; + } + xfree(data); + return; + } + if (compat20) { if (data_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %d, maxpack %d", @@ -1994,7 +2044,7 @@ c->remote_maxpacket = packet_get_int(); if (c->confirm) { debug2("callback start"); - c->confirm(c->self, NULL); + c->confirm(c->self, c->confirm_ctx); debug2("callback done"); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, @@ -2186,7 +2236,7 @@ continue; } /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) { + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); continue; @@ -2207,6 +2257,27 @@ return success; } +int +channel_cancel_rport_listener(const char *host, u_short port) +{ + u_int i; + int found = 0; + + for(i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + + if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && + strncmp(c->path, host, sizeof(c->path)) == 0 && + c->listening_port == port) { + debug2("%s: close clannel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int channel_setup_local_fwd_listener(u_short listen_port, @@ -2284,6 +2355,41 @@ } /* + * Request cancellation of remote forwarding of connection host:port from + * local side. + */ +void +channel_request_rforward_cancel(u_short port) +{ + int i; + const char *address_to_bind = "0.0.0.0"; + + if (!compat20) + return; + + for (i = 0; i < num_permitted_opens; i++) { + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == port) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return; + } + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-tcpip-forward"); + packet_put_char(0); + packet_put_cstring(address_to_bind); + packet_put_int(port); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + free(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; +} + +/* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect * message if there was an error). This never returns if there was an error. @@ -2350,7 +2456,8 @@ int i; for (i = 0; i < num_permitted_opens; i++) - xfree(permitted_opens[i].host_to_connect); + if (permitted_opens[i].host_to_connect != NULL) + xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; } @@ -2390,8 +2497,8 @@ verbose("socket: %.100s", strerror(errno)); continue; } - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) - fatal("connect_to: F_SETFL: %s", strerror(errno)); + if (set_nonblock(sock) == -1) + fatal("%s: set_nonblock(%d)", __func__, sock); if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS) { error("connect_to %.100s port %s: %.100s", ntop, strport, @@ -2418,7 +2525,8 @@ int i; for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].listen_port == listen_port) + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == listen_port) return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect); @@ -2436,7 +2544,8 @@ permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].port_to_connect == port && + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].port_to_connect == port && strcmp(permitted_opens[i].host_to_connect, host) == 0) permit = 1; @@ -2449,6 +2558,27 @@ return connect_to(host, port); } +void +channel_send_window_changes(void) +{ + u_int i; + struct winsize ws; + + for (i = 0; i < channels_alloc; i++) { + if (channels[i] == NULL || + channels[i]->type != SSH_CHANNEL_OPEN) + continue; + if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) + continue; + channel_request_start(i, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } +} + /* -- X11 forwarding */ /* @@ -2487,6 +2617,7 @@ ai->ai_protocol); if (sock < 0) { error("socket: %.100s", strerror(errno)); + freeaddrinfo(aitop); return -1; } if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { @@ -2517,7 +2648,7 @@ /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; - if (listen(sock, 5) < 0) { + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); return -1; @@ -2738,7 +2869,7 @@ char *new_data; int screen_number; const char *cp; - u_int32_t rand = 0; + u_int32_t rnd = 0; cp = getenv("DISPLAY"); if (cp) @@ -2763,10 +2894,10 @@ if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad authentication data: %.100s", data); if (i % 4 == 0) - rand = arc4random(); + rnd = arc4random(); x11_saved_data[i] = value; - x11_fake_data[i] = rand & 0xff; - rand >>= 8; + x11_fake_data[i] = rnd & 0xff; + rnd >>= 8; } x11_saved_data_len = data_len; x11_fake_data_len = data_len; @@ -2804,47 +2935,4 @@ packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); packet_send(); packet_write_wait(); -} - -/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ - -void -auth_input_open_request(int type, u_int32_t seq, void *ctxt) -{ - Channel *c = NULL; - int remote_id, sock; - - /* Read the remote channel number from the message. */ - remote_id = packet_get_int(); - packet_check_eom(); - - /* - * Get a connection to the local authentication agent (this may again - * get forwarded). - */ - sock = ssh_get_authentication_socket(); - - /* - * If we could not connect the agent, send an error message back to - * the server. This should never happen unless the agent dies, - * because authentication forwarding is only enabled if we have an - * agent. - */ - if (sock >= 0) { - c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, - -1, 0, 0, 0, "authentication agent connection", 1); - c->remote_id = remote_id; - c->force_drain = 1; - } - if (c == NULL) { - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_id); - } else { - /* Send a confirmation to the remote host. */ - debug("Forwarding authentication connection."); - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_id); - packet_put_int(c->self); - } - packet_send(); }