diff options
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/channels.c | 1184 | ||||
-rw-r--r-- | usr.bin/ssh/channels.h | 194 | ||||
-rw-r--r-- | usr.bin/ssh/clientloop.c | 205 | ||||
-rw-r--r-- | usr.bin/ssh/lib/Makefile | 2 | ||||
-rw-r--r-- | usr.bin/ssh/nchan.c | 5 | ||||
-rw-r--r-- | usr.bin/ssh/nchan.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/packet.c | 3 | ||||
-rw-r--r-- | usr.bin/ssh/readconf.c | 3 | ||||
-rw-r--r-- | usr.bin/ssh/serverloop.c | 191 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.c | 3 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.h | 171 | ||||
-rw-r--r-- | usr.bin/ssh/sshd.c | 4 |
12 files changed, 1071 insertions, 898 deletions
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c index ef151267c8f..8fc85fa8171 100644 --- a/usr.bin/ssh/channels.c +++ b/usr.bin/ssh/channels.c @@ -16,7 +16,7 @@ */ #include "includes.h" -RCSID("$Id: channels.c,v 1.40 2000/03/27 17:41:50 markus Exp $"); +RCSID("$Id: channels.c,v 1.41 2000/03/28 20:31:25 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -37,6 +37,10 @@ RCSID("$Id: channels.c,v 1.40 2000/03/27 17:41:50 markus Exp $"); /* Max len of agent socket */ #define MAX_SOCKET_NAME 100 +/* default buffer for tcp-fwd-channel */ +#define CHAN_WINDOW_DEFAULT (8*1024) +#define CHAN_PACKET_DEFAULT (CHAN_WINDOW_DEFAULT/2) + /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. @@ -81,8 +85,9 @@ unsigned int x11_fake_data_len; * network (which might be behind a firewall). */ typedef struct { - char *host; /* Host name. */ - u_short port; /* Port number. */ + char *host_to_connect; /* Connect to 'host'. */ + u_short port_to_connect; /* Connect to 'port'. */ + u_short listen_port; /* Remote side should listen port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect. */ @@ -119,20 +124,43 @@ channel_permit_all_opens() all_opens_permitted = 1; } +/* lookup channel by id */ + +Channel * +channel_lookup(int id) +{ + Channel *c; + if (id < 0 && id > channels_alloc) { + log("channel_lookup: %d: bad id", id); + return NULL; + } + c = &channels[id]; + if (c->type == SSH_CHANNEL_FREE) { + log("channel_lookup: %d: bad id: channel free", id); + return NULL; + } + return c; +} + /* * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */ int -channel_allocate(int type, int sock, char *remote_name) +channel_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extended_usage, char *remote_name) { int i, found; Channel *c; /* Update the maximum file descriptor value. */ - if (sock > channel_max_fd_value) - channel_max_fd_value = sock; + if (rfd > channel_max_fd_value) + channel_max_fd_value = rfd; + if (wfd > channel_max_fd_value) + channel_max_fd_value = wfd; + if (efd > channel_max_fd_value) + channel_max_fd_value = efd; /* XXX set close-on-exec -markus */ /* Do initial allocation if this is the first call. */ @@ -167,388 +195,511 @@ channel_allocate(int type, int sock, char *remote_name) c = &channels[found]; buffer_init(&c->input); buffer_init(&c->output); + buffer_init(&c->extended); chan_init_iostates(c); c->self = found; c->type = type; - c->sock = sock; + c->ctype = ctype; + c->local_window = window; + c->local_window_max = window; + c->local_consumed = 0; + c->local_maxpacket = maxpack; + c->remote_window = 0; + c->remote_maxpacket = 0; + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->efd = efd; + c->extended_usage = extended_usage; c->remote_id = -1; c->remote_name = remote_name; + c->remote_window = 0; + c->remote_maxpacket = 0; + c->cb_fn = NULL; + c->cb_arg = NULL; + c->cb_event = 0; + c->dettach_user = NULL; debug("channel %d: new [%s]", found, remote_name); return found; } +int +channel_allocate(int type, int sock, char *remote_name) +{ + return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name); +} /* Free the channel and close its socket. */ void -channel_free(int channel) +channel_free(int id) { - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("channel free: bad local channel %d", channel); - + Channel *c = channel_lookup(id); + if (c == NULL) + packet_disconnect("channel free: bad local channel %d", id); + debug("channel_free: channel %d: status: %s", id, channel_open_message()); if (compat13) - shutdown(channels[channel].sock, SHUT_RDWR); - close(channels[channel].sock); - buffer_free(&channels[channel].input); - buffer_free(&channels[channel].output); - channels[channel].type = SSH_CHANNEL_FREE; - if (channels[channel].remote_name) { - xfree(channels[channel].remote_name); - channels[channel].remote_name = NULL; + shutdown(c->sock, SHUT_RDWR); + buffer_free(&c->input); + buffer_free(&c->output); + buffer_free(&c->extended); + c->type = SSH_CHANNEL_FREE; + if (c->remote_name) { + xfree(c->remote_name); + c->remote_name = NULL; } } /* - * This is called just before select() to add any bits relevant to channels - * in the select bitmasks. + * 'channel_pre*' are called just before select() to add any bits relevant to + * channels in the select bitmasks. + */ +/* + * 'channel_post*': perform any appropriate operations for channels which + * have events pending. */ +typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); +chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; +chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; -void -channel_prepare_select(fd_set * readset, fd_set * writeset) +void +channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) { - int i; - Channel *ch; - unsigned char *ucp; - unsigned int proto_len, data_len; + FD_SET(c->sock, readset); +} - for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; -redo: - switch (ch->type) { - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_AUTH_SOCKET: - FD_SET(ch->sock, readset); - break; +void +channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (buffer_len(&c->output) > 0) + FD_SET(c->sock, writeset); +} - case SSH_CHANNEL_OPEN: - if (compat13) { - if (buffer_len(&ch->input) < packet_get_maxsize()) - FD_SET(ch->sock, readset); - if (buffer_len(&ch->output) > 0) - FD_SET(ch->sock, writeset); - break; - } - /* test whether sockets are 'alive' for read/write */ - if (ch->istate == CHAN_INPUT_OPEN) - if (buffer_len(&ch->input) < packet_get_maxsize()) - FD_SET(ch->sock, readset); - if (ch->ostate == CHAN_OUTPUT_OPEN || - ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - if (buffer_len(&ch->output) > 0) { - FD_SET(ch->sock, writeset); - } else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - chan_obuf_empty(ch); - } - } - break; +void +channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) +{ + /* test whether sockets are 'alive' for read/write */ + if (c->istate == CHAN_INPUT_OPEN) + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + FD_SET(c->sock, writeset); + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(c); + } + } +} - case SSH_CHANNEL_INPUT_DRAINING: - if (!compat13) - fatal("cannot happen: IN_DRAIN"); - if (buffer_len(&ch->input) == 0) { - packet_start(SSH_MSG_CHANNEL_CLOSE); - packet_put_int(ch->remote_id); - packet_send(); - ch->type = SSH_CHANNEL_CLOSED; - debug("Closing channel %d after input drain.", ch->self); - break; - } - break; +void +channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) == 0) { + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + c->type = SSH_CHANNEL_CLOSED; + debug("Closing channel %d after input drain.", c->self); + } +} - case SSH_CHANNEL_OUTPUT_DRAINING: - if (!compat13) - fatal("cannot happen: OUT_DRAIN"); - if (buffer_len(&ch->output) == 0) { - channel_free(i); - break; - } - FD_SET(ch->sock, writeset); - break; +void +channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->output) == 0) + channel_free(c->self); + else + FD_SET(c->sock, writeset); +} - case SSH_CHANNEL_X11_OPEN: - /* - * This is a special state for X11 authentication - * spoofing. An opened X11 connection (when - * authentication spoofing is being done) remains in - * this state until the first packet has been - * completely read. The authentication data in that - * packet is then substituted by the real data if it - * matches the fake data, and the channel is put into - * normal mode. - */ - /* Check if the fixed size part of the packet is in buffer. */ - if (buffer_len(&ch->output) < 12) - break; +/* + * This is a special state for X11 authentication spoofing. An opened X11 + * connection (when authentication spoofing is being done) remains in this + * state until the first packet has been completely read. The authentication + * data in that packet is then substituted by the real data if it matches the + * fake data, and the channel is put into normal mode. + */ +int +x11_open_helper(Channel *c) +{ + unsigned char *ucp; + unsigned int proto_len, data_len; - /* Parse the lengths of variable-length fields. */ - ucp = (unsigned char *) buffer_ptr(&ch->output); - if (ucp[0] == 0x42) { /* Byte order MSB first. */ - proto_len = 256 * ucp[6] + ucp[7]; - data_len = 256 * ucp[8] + ucp[9]; - } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ - proto_len = ucp[6] + 256 * ucp[7]; - data_len = ucp[8] + 256 * ucp[9]; - } else { - debug("Initial X11 packet contains bad byte order byte: 0x%x", - ucp[0]); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } + /* Check if the fixed size part of the packet is in buffer. */ + if (buffer_len(&c->output) < 12) + return 0; + + /* Parse the lengths of variable-length fields. */ + ucp = (unsigned char *) buffer_ptr(&c->output); + if (ucp[0] == 0x42) { /* Byte order MSB first. */ + proto_len = 256 * ucp[6] + ucp[7]; + data_len = 256 * ucp[8] + ucp[9]; + } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ + proto_len = ucp[6] + 256 * ucp[7]; + data_len = ucp[8] + 256 * ucp[9]; + } else { + debug("Initial X11 packet contains bad byte order byte: 0x%x", + ucp[0]); + return -1; + } - /* Check if the whole packet is in buffer. */ - if (buffer_len(&ch->output) < - 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) - break; + /* Check if the whole packet is in buffer. */ + if (buffer_len(&c->output) < + 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) { - debug("X11 connection uses different authentication protocol."); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - /* 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) { - debug("X11 auth data does not match fake data."); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - /* 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); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - /* - * Received authentication protocol and data match - * our fake data. Substitute the fake data with real - * data. - */ - memcpy(ucp + 12 + ((proto_len + 3) & ~3), - x11_saved_data, x11_saved_data_len); + /* Check if authentication protocol matches. */ + if (proto_len != strlen(x11_saved_proto) || + memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { + debug("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) { + debug("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); + return -1; + } + /* + * Received authentication protocol and data match + * our fake data. Substitute the fake data with real + * data. + */ + memcpy(ucp + 12 + ((proto_len + 3) & ~3), + x11_saved_data, x11_saved_data_len); + return 1; +} - /* Start normal processing for the channel. */ - ch->type = SSH_CHANNEL_OPEN; - goto redo; +void +channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + /* Start normal processing for the channel. */ + c->type = SSH_CHANNEL_OPEN; + } else if (ret == -1) { + /* + * We have received an X11 connection that has bad + * authentication information. + */ + log("X11 connection rejected because of wrong authentication.\r\n"); + buffer_clear(&c->input); + buffer_clear(&c->output); + close(c->sock); + c->sock = -1; + c->type = SSH_CHANNEL_CLOSED; + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + } +} - reject: - /* - * We have received an X11 connection that has bad - * authentication information. - */ - log("X11 connection rejected because of wrong authentication.\r\n"); - buffer_clear(&ch->input); - buffer_clear(&ch->output); - if (compat13) { - close(ch->sock); - ch->sock = -1; - ch->type = SSH_CHANNEL_CLOSED; - packet_start(SSH_MSG_CHANNEL_CLOSE); - packet_put_int(ch->remote_id); - packet_send(); - } else { - debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); - chan_read_failed(ch); - chan_write_failed(ch); - debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); - } - break; +void +channel_pre_x11_open_15(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + c->type = SSH_CHANNEL_OPEN; + } else if (ret == -1) { + debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); + chan_read_failed(c); + chan_write_failed(c); + debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); + } +} - case SSH_CHANNEL_FREE: - default: - continue; +/* This is our fake X11 server socket. */ +void +channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + char buf[16384], *remote_hostname; + + if (FD_ISSET(c->sock, readset)) { + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; } + remote_hostname = get_remote_hostname(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_hostname, get_peer_port(newsock)); + xfree(remote_hostname); + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup(buf)); + packet_start(SSH_SMSG_X11_OPEN); + packet_put_int(newch); + if (have_hostname_in_open) + packet_put_string(buf, strlen(buf)); + packet_send(); } } /* - * After select, perform any appropriate operations for channels which have - * events pending. + * This socket is listening for connections to a forwarded TCP/IP port. */ +void +channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + char buf[1024], *remote_hostname; + int remote_port; + + if (FD_ISSET(c->sock, readset)) { + debug("Connection to port %d forwarding " + "to %.100s port %d requested.", + c->listening_port, c->path, c->host_port); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; + } + remote_hostname = get_remote_hostname(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, + "listen port %d for %.100s port %d, " + "connect from %.200s port %d", + c->listening_port, c->path, c->host_port, + remote_hostname, remote_port); + newch = channel_new("direct-tcpip", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup(buf)); + + packet_start(SSH_MSG_PORT_OPEN); + packet_put_int(newch); + packet_put_string(c->path, strlen(c->path)); + packet_put_int(c->host_port); + if (have_hostname_in_open) { + packet_put_string(buf, strlen(buf)); + } + packet_send(); + xfree(remote_hostname); + } +} -void -channel_after_select(fd_set * readset, fd_set * writeset) +/* + * This is the authentication agent socket listening for connections from + * clients. + */ +void +channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) { struct sockaddr addr; - int newsock, i, newch, len; + int newsock, newch; socklen_t addrlen; - Channel *ch; - char buf[16384], *remote_hostname; - /* Loop over all channels... */ - for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; - switch (ch->type) { - case SSH_CHANNEL_X11_LISTENER: - /* This is our fake X11 server socket. */ - if (FD_ISSET(ch->sock, readset)) { - debug("X11 connection requested."); - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) { - error("accept: %.100s", strerror(errno)); - break; - } - remote_hostname = get_remote_hostname(newsock); - snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", - remote_hostname, get_peer_port(newsock)); - xfree(remote_hostname); - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup(buf)); - packet_start(SSH_SMSG_X11_OPEN); - packet_put_int(newch); - if (have_hostname_in_open) - packet_put_string(buf, strlen(buf)); - packet_send(); - } - break; + if (FD_ISSET(c->sock, readset)) { + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept from auth socket: %.100s", strerror(errno)); + return; + } + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup("accepted auth socket")); + packet_start(SSH_SMSG_AGENT_OPEN); + packet_put_int(newch); + packet_send(); + } +} - case SSH_CHANNEL_PORT_LISTENER: - /* - * This socket is listening for connections to a - * forwarded TCP/IP port. - */ - if (FD_ISSET(ch->sock, readset)) { - debug("Connection to port %d forwarding to %.100s port %d requested.", - ch->listening_port, ch->path, ch->host_port); - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) { - error("accept: %.100s", strerror(errno)); - break; - } - remote_hostname = get_remote_hostname(newsock); - snprintf(buf, sizeof buf, "listen port %d for %.100s port %d, connect from %.200s port %d", - ch->listening_port, ch->path, ch->host_port, - remote_hostname, get_peer_port(newsock)); - xfree(remote_hostname); - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup(buf)); - packet_start(SSH_MSG_PORT_OPEN); - packet_put_int(newch); - packet_put_string(ch->path, strlen(ch->path)); - packet_put_int(ch->host_port); - if (have_hostname_in_open) - packet_put_string(buf, strlen(buf)); - packet_send(); +int +channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16*1024]; + int len; + + if (c->rfd != -1 && + FD_ISSET(c->rfd, readset)) { + len = read(c->rfd, buf, sizeof(buf)); + if (len <= 0) { + debug("channel %d: read<0 rfd %d len %d", + c->self, c->rfd, len); + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + c->type = SSH_CHANNEL_INPUT_DRAINING; + debug("Channel %d status set to input draining.", c->self); + } else { + chan_read_failed(c); } - break; - - case SSH_CHANNEL_AUTH_SOCKET: - /* - * This is the authentication agent socket listening - * for connections from clients. - */ - if (FD_ISSET(ch->sock, readset)) { - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) { - error("accept from auth socket: %.100s", strerror(errno)); - break; - } - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup("accepted auth socket")); - packet_start(SSH_SMSG_AGENT_OPEN); - packet_put_int(newch); - packet_send(); + return -1; + } + buffer_append(&c->input, buf, len); + } + return 1; +} +int +channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + + /* Send buffered output data to the socket. */ + if (c->wfd != -1 && + FD_ISSET(c->wfd, writeset) && + buffer_len(&c->output) > 0) { + len = write(c->wfd, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len <= 0) { + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + debug("Channel %d status set to input draining.", c->self); + c->type = SSH_CHANNEL_INPUT_DRAINING; + } else { + chan_write_failed(c); } - break; + return -1; + } + buffer_consume(&c->output, len); + } + return 1; +} - case SSH_CHANNEL_OPEN: - /* - * This is an open two-way communication channel. It - * is not of interest to us at this point what kind - * of data is being transmitted. - */ +void +channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) +{ + channel_handle_rfd(c, readset, writeset); + channel_handle_wfd(c, readset, writeset); +} - /* - * Read available incoming data and append it to - * buffer; shutdown socket, if read or write failes - */ - if (FD_ISSET(ch->sock, readset)) { - len = read(ch->sock, buf, sizeof(buf)); - if (len <= 0) { - if (compat13) { - buffer_consume(&ch->output, buffer_len(&ch->output)); - ch->type = SSH_CHANNEL_INPUT_DRAINING; - debug("Channel %d status set to input draining.", i); - } else { - chan_read_failed(ch); - } - break; - } - buffer_append(&ch->input, buf, len); - } - /* Send buffered output data to the socket. */ - if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { - len = write(ch->sock, buffer_ptr(&ch->output), - buffer_len(&ch->output)); - if (len <= 0) { - if (compat13) { - buffer_consume(&ch->output, buffer_len(&ch->output)); - debug("Channel %d status set to input draining.", i); - ch->type = SSH_CHANNEL_INPUT_DRAINING; - } else { - chan_write_failed(ch); - } - break; - } - buffer_consume(&ch->output, len); - } - break; +void +channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + /* Send buffered output data to the socket. */ + if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { + len = write(c->sock, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len <= 0) + buffer_consume(&c->output, buffer_len(&c->output)); + else + buffer_consume(&c->output, len); + } +} - case SSH_CHANNEL_OUTPUT_DRAINING: - if (!compat13) - fatal("cannot happen: OUT_DRAIN"); - /* Send buffered output data to the socket. */ - if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { - len = write(ch->sock, buffer_ptr(&ch->output), - buffer_len(&ch->output)); - if (len <= 0) - buffer_consume(&ch->output, buffer_len(&ch->output)); - else - buffer_consume(&ch->output, len); - } - break; +void +channel_handler_init_13(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; + channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; + + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; +} - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_FREE: - default: +void +channel_handler_init_15(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_15; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; +} + +void +channel_handler_init(void) +{ + int i; + for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { + channel_pre[i] = NULL; + channel_post[i] = NULL; + } + if (compat13) + channel_handler_init_13(); + else + channel_handler_init_15(); +} + +void +channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) +{ + static int did_init = 0; + int i; + Channel *c; + + if (!did_init) { + channel_handler_init(); + did_init = 1; + } + for (i = 0; i < channels_alloc; i++) { + c = &channels[i]; + if (c->type == SSH_CHANNEL_FREE) continue; - } + if (ftab[c->type] == NULL) + continue; + (*ftab[c->type])(c, readset, writeset); + chan_delete_if_full_closed(c); } } +void +channel_prepare_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_pre, readset, writeset); +} + +void +channel_after_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_post, readset, writeset); +} + /* If there is data to send to the connection, send some of it now. */ void channel_output_poll() { int len, i; - Channel *ch; + Channel *c; for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; + c = &channels[i]; /* We are only interested in channels that can have buffered incoming data. */ if (compat13) { - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_INPUT_DRAINING) + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_INPUT_DRAINING) continue; } else { - if (ch->type != SSH_CHANNEL_OPEN) + if (c->type != SSH_CHANNEL_OPEN) continue; - if (ch->istate != CHAN_INPUT_OPEN && - ch->istate != CHAN_INPUT_WAIT_DRAIN) + if (c->istate != CHAN_INPUT_OPEN && + c->istate != CHAN_INPUT_WAIT_DRAIN) continue; } /* Get the amount of buffered data for this channel. */ - len = buffer_len(&ch->input); + len = buffer_len(&c->input); if (len > 0) { /* Send some data for the other side over the secure connection. */ if (packet_is_interactive()) { @@ -559,19 +710,23 @@ channel_output_poll() if (len > packet_get_maxsize()) len = packet_get_maxsize()/2; } - packet_start(SSH_MSG_CHANNEL_DATA); - packet_put_int(ch->remote_id); - packet_put_string(buffer_ptr(&ch->input), len); - packet_send(); - buffer_consume(&ch->input, len); - } else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) { + if (len > 0) { + packet_start(SSH_MSG_CHANNEL_DATA); + packet_put_int(c->remote_id); + packet_put_string(buffer_ptr(&c->input), len); + packet_send(); + buffer_consume(&c->input, len); + c->remote_window -= len; +debug("channel %d: send data len %d", c->self, len); + } + } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { if (compat13) fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* * input-buffer is empty and read-socket shutdown: * tell peer, that we will not send more data: send IEOF */ - chan_ibuf_empty(ch); + chan_ibuf_empty(c); } } } @@ -583,35 +738,33 @@ channel_output_poll() */ void -channel_input_data(int payload_len) +channel_input_data(int type, int plen) { int id; char *data; unsigned int data_len; - Channel *ch; + Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); - if (id < 0 || id >= channels_alloc) + c = channel_lookup(id); + if (c == NULL) packet_disconnect("Received data for nonexistent channel %d.", id); - ch = &channels[id]; - - if (ch->type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for free channel %d.", ch->self); /* Ignore any data for non-open channels (might happen on close) */ - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_X11_OPEN) + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_X11_OPEN) return; /* same for protocol 1.5 if output end is no longer open */ - if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN) + if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) return; /* Get the data. */ data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); - buffer_append(&ch->output, data, data_len); + + packet_integrity_check(plen, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); + buffer_append(&c->output, data, data_len); xfree(data); } @@ -624,45 +777,60 @@ int channel_not_very_much_buffered_data() { unsigned int i; - Channel *ch; + Channel *c; for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; - if (ch->type == SSH_CHANNEL_OPEN) { - if (buffer_len(&ch->input) > packet_get_maxsize()) + c = &channels[i]; + if (c->type == SSH_CHANNEL_OPEN) { + if (buffer_len(&c->input) > packet_get_maxsize()) { + debug("channel %d: big input buffer %d", + c->self, buffer_len(&c->input)); return 0; - if (buffer_len(&ch->output) > packet_get_maxsize()) + } + if (buffer_len(&c->output) > packet_get_maxsize()) { + debug("channel %d: big output buffer %d", + c->self, buffer_len(&c->output)); return 0; + } } } return 1; } -/* This is called after receiving CHANNEL_CLOSE/IEOF. */ +void +channel_input_ieof(int type, int plen) +{ + int id; + Channel *c; + + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received ieof for nonexistent channel %d.", id); + chan_rcvd_ieof(c); +} void -channel_input_close() +channel_input_close(int type, int plen) { - int channel; + int id; + Channel *c; - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for nonexistent channel %d.", channel); - - if (!compat13) { - /* proto version 1.5 overloads CLOSE with IEOF */ - chan_rcvd_ieof(&channels[channel]); - return; - } + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received close for nonexistent channel %d.", id); /* * Send a confirmation that we have closed the channel and no more * data is coming for it. */ packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); - packet_put_int(channels[channel].remote_id); + packet_put_int(c->remote_id); packet_send(); /* @@ -672,81 +840,80 @@ channel_input_close() * no-one to receive the confirmation. The channel gets freed when * the confirmation arrives. */ - if (channels[channel].type != SSH_CHANNEL_CLOSED) { + if (c->type != SSH_CHANNEL_CLOSED) { /* * Not a closed channel - mark it as draining, which will * cause it to be freed later. */ - buffer_consume(&channels[channel].input, - buffer_len(&channels[channel].input)); - channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; + buffer_consume(&c->input, buffer_len(&c->input)); + c->type = SSH_CHANNEL_OUTPUT_DRAINING; } } -/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ - +/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ void -channel_input_close_confirmation() +channel_input_oclose(int type, int plen) { - int channel; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc) - packet_disconnect("Received close confirmation for out-of-range channel %d.", - channel); - - if (!compat13) { - /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ - chan_rcvd_oclose(&channels[channel]); - return; - } - if (channels[channel].type != SSH_CHANNEL_CLOSED) - packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", - channel, channels[channel].type); - - /* Free the channel. */ - channel_free(channel); + int id = packet_get_int(); + Channel *c = channel_lookup(id); + packet_integrity_check(plen, 4, type); + if (c == NULL) + packet_disconnect("Received oclose for nonexistent channel %d.", id); + chan_rcvd_oclose(c); } -/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ +void +channel_input_close_confirmation(int type, int plen) +{ + int id = packet_get_int(); + Channel *c = channel_lookup(id); + + if (c == NULL) + packet_disconnect("Received close confirmation for " + "out-of-range channel %d.", id); + if (c->type != SSH_CHANNEL_CLOSED) + packet_disconnect("Received close confirmation for " + "non-closed channel %d (type %d).", id, c->type); + channel_free(c->self); +} void -channel_input_open_confirmation() +channel_input_open_confirmation(int type, int plen) { - int channel, remote_channel; + int id, remote_id; + Channel *c; - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type != SSH_CHANNEL_OPENING) - packet_disconnect("Received open confirmation for non-opening channel %d.", - channel); + packet_integrity_check(plen, 4 + 4, type); - /* Get remote side's id for this channel. */ - remote_channel = packet_get_int(); + id = packet_get_int(); + c = channel_lookup(id); + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open confirmation for " + "non-opening channel %d.", id); + remote_id = packet_get_int(); /* Record the remote channel number and mark that the channel is now open. */ - channels[channel].remote_id = remote_channel; - channels[channel].type = SSH_CHANNEL_OPEN; + c->remote_id = remote_id; + c->type = SSH_CHANNEL_OPEN; } -/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ - void -channel_input_open_failure() +channel_input_open_failure(int type, int plen) { - int channel; + int id; + Channel *c; - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type != SSH_CHANNEL_OPENING) - packet_disconnect("Received open failure for non-opening channel %d.", - channel); + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open failure for " + "non-opening channel %d.", id); /* Free the channel. This will also close the socket. */ - channel_free(channel); + channel_free(id); } /* @@ -859,15 +1026,16 @@ channel_open_message() 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)\r\n", + snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%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->ostate, buffer_len(&c->output), + c->rfd, c->wfd); buffer_append(&buffer, buf, strlen(buf)); continue; default: - fatal("channel_still_open: bad channel type %d", c->type); + fatal("channel_open_message: bad channel type %d", c->type); /* NOTREACHED */ } } @@ -946,8 +1114,11 @@ channel_request_local_forwarding(u_short port, const char *host, continue; } /* Allocate a channel number for the socket. */ - ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, - xstrdup("port listener")); + ch = channel_new( + "port listener", SSH_CHANNEL_PORT_LISTENER, + sock, sock, -1, + CHAN_WINDOW_DEFAULT, CHAN_PACKET_DEFAULT, + 0, xstrdup("port listener")); strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); channels[ch].host_port = host_port; channels[ch].listening_port = port; @@ -964,26 +1135,26 @@ channel_request_local_forwarding(u_short port, const char *host, */ void -channel_request_remote_forwarding(u_short port, const char *host, - u_short remote_port) +channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, + u_short port_to_connect) { int payload_len; /* Record locally that connection to this host/port is permitted. */ if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_request_remote_forwarding: too many forwards"); - permitted_opens[num_permitted_opens].host = xstrdup(host); - permitted_opens[num_permitted_opens].port = remote_port; + permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); + permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; + permitted_opens[num_permitted_opens].listen_port = listen_port; num_permitted_opens++; /* Send the forward request to the remote side. */ packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); - packet_put_int(port); - packet_put_string(host, strlen(host)); - packet_put_int(remote_port); + packet_put_int(port_to_connect); + packet_put_string(host_to_connect, strlen(host_to_connect)); + packet_put_int(listen_port); packet_send(); packet_write_wait(); - /* * Wait for response from the remote side. It will send a disconnect * message on failure, and we will never see it here. @@ -1025,61 +1196,14 @@ channel_input_port_forward_request(int is_root) xfree(hostname); } -/* - * This is called after receiving PORT_OPEN message. This attempts to - * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION - * or CHANNEL_OPEN_FAILURE. - */ - -void -channel_input_port_open(int payload_len) +/* XXX move to aux.c */ +int +channel_connect_to(const char *host, u_short host_port) { - int remote_channel, sock = 0, newch, i; - u_short host_port; - char *host, *originator_string; - unsigned int host_len, originator_len; struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int gaierr; - - /* Get remote channel number. */ - remote_channel = packet_get_int(); - - /* Get host name to connect to. */ - host = packet_get_string(&host_len); - - /* Get port to connect to. */ - host_port = packet_get_int(); - - /* Get remote originator name. */ - if (have_hostname_in_open) { - originator_string = packet_get_string(&originator_len); - originator_len += 4; /* size of packet_int */ - } else { - originator_string = xstrdup("unknown (remote did not supply name)"); - originator_len = 0; /* no originator supplied */ - } - - packet_integrity_check(payload_len, - 4 + 4 + host_len + 4 + originator_len, - SSH_MSG_PORT_OPEN); - - /* Check if opening that port is permitted. */ - if (!all_opens_permitted) { - /* Go trough all permitted ports. */ - for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].port == host_port && - strcmp(permitted_opens[i].host, host) == 0) - break; - - /* Check if we found the requested port among those permitted. */ - if (i >= num_permitted_opens) { - /* The port is not permitted. */ - log("Received request to connect to %.100s:%d, but the request was denied.", - host, host_port); - goto fail; - } - } + int sock = -1; memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; @@ -1087,15 +1211,14 @@ channel_input_port_open(int payload_len) snprintf(strport, sizeof strport, "%d", host_port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); - goto fail; + return -1; } - for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("channel_input_port_open: getnameinfo failed"); + error("channel_connect_to: getnameinfo failed"); continue; } /* Create the socket. */ @@ -1115,37 +1238,81 @@ channel_input_port_open(int payload_len) } freeaddrinfo(aitop); - if (!ai) { error("connect %.100s port %d: failed.", host, host_port); - goto fail; + return -1; } + /* success */ + return sock; +} +/* + * This is called after receiving PORT_OPEN message. This attempts to + * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION + * or CHANNEL_OPEN_FAILURE. + */ - /* Successful connection. */ +void +channel_input_port_open(int type, int plen) +{ + u_short host_port; + char *host, *originator_string; + int remote_channel, sock = -1, newch, i, denied; + unsigned int host_len, originator_len; - /* Allocate a channel for this connection. */ - newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); - channels[newch].remote_id = remote_channel; + /* Get remote channel number. */ + remote_channel = packet_get_int(); - /* Send a confirmation to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_channel); - packet_put_int(newch); - packet_send(); + /* Get host name to connect to. */ + host = packet_get_string(&host_len); - /* Free the argument string. */ - xfree(host); + /* Get port to connect to. */ + host_port = packet_get_int(); - return; + /* Get remote originator name. */ + if (have_hostname_in_open) { + originator_string = packet_get_string(&originator_len); + originator_len += 4; /* size of packet_int */ + } else { + originator_string = xstrdup("unknown (remote did not supply name)"); + originator_len = 0; /* no originator supplied */ + } -fail: - /* Free the argument string. */ - xfree(host); + packet_integrity_check(plen, + 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); + /* Check if opening that port is permitted. */ + denied = 0; + if (!all_opens_permitted) { + /* Go trough all permitted ports. */ + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].port_to_connect == host_port && + strcmp(permitted_opens[i].host_to_connect, host) == 0) + break; + + /* Check if we found the requested port among those permitted. */ + if (i >= num_permitted_opens) { + /* The port is not permitted. */ + log("Received request to connect to %.100s:%d, but the request was denied.", + host, host_port); + denied = 1; + } + } + sock = denied ? -1 : channel_connect_to(host, host_port); + if (sock > 0) { + /* Allocate a channel for this connection. */ + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); + channels[newch].remote_id = remote_channel; + + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + } else { + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } + xfree(host); } /* @@ -1279,7 +1446,7 @@ connect_local_xsocket(unsigned int dnr) */ void -x11_input_open(int payload_len) +x11_input_open(int type, int plen) { int remote_channel, display_number, sock = 0, newch; const char *display; @@ -1302,7 +1469,7 @@ x11_input_open(int payload_len) } debug("Received X11 open request."); - packet_integrity_check(payload_len, 4 + remote_len, SSH_SMSG_X11_OPEN); + packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN); /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); @@ -1368,19 +1535,18 @@ x11_input_open(int payload_len) sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { debug("socket: %.100s", strerror(errno)); - continue; - } - /* Connect it to the display. */ - if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, - strerror(errno)); - close(sock); - continue; + continue; + } + /* Connect it to the display. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("connect %.100s port %d: %.100s", buf, + 6000 + display_number, strerror(errno)); + close(sock); + continue; + } + /* Success */ + break; } - /* Success */ - break; - - } /* (ai = aitop, ai; ai = ai->ai_next) */ freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %d: %.100s", buf, 6000 + display_number, @@ -1568,11 +1734,13 @@ auth_input_request_forwarding(struct passwd * pw) /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ void -auth_input_open_request() +auth_input_open_request(int type, int plen) { int remch, sock, newch; char *dummyname; + packet_integrity_check(plen, 4, type); + /* Read the remote channel number from the message. */ remch = packet_get_int(); diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h index 93a7b539b76..7d639035820 100644 --- a/usr.bin/ssh/channels.h +++ b/usr.bin/ssh/channels.h @@ -1,4 +1,4 @@ -/* RCSID("$Id: channels.h,v 1.6 1999/11/24 19:53:45 markus Exp $"); */ +/* RCSID("$Id: channels.h,v 1.7 2000/03/28 20:31:26 markus Exp $"); */ #ifndef CHANNELS_H #define CHANNELS_H @@ -10,17 +10,18 @@ #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ -/* SSH_CHANNEL_AUTH_FD 6 authentication fd */ -#define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ -/* SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ -#define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ -#define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to conn */ -#define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to app */ +#define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ +#define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ +#define SSH_CHANNEL_INPUT_DRAINING 8 /* sending remaining data to conn */ +#define SSH_CHANNEL_OUTPUT_DRAINING 9 /* sending remaining data to app */ +#define SSH_CHANNEL_LARVAL 10 /* larval session */ +#define SSH_CHANNEL_MAX_TYPE 11 /* * Data structure for channel data. This is iniailized in channel_allocate * and cleared in channel_free. */ +typedef void channel_callback_fn(int id, void *arg); typedef struct Channel { int type; /* channel type/state */ @@ -29,15 +30,192 @@ typedef struct Channel { /* peer can be reached over encrypted connection, via packet-sent */ int istate; /* input from channel (state of receive half) */ int ostate; /* output to channel (state of transmit half) */ - int sock; /* data socket, linked to this channel */ + int rfd; /* read fd */ + int wfd; /* write fd */ + int efd; /* extended fd */ + int sock; /* sock fd */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for * send on socket */ + Buffer extended; char path[200]; /* path for unix domain sockets, or host name * for forwards */ int listening_port; /* port being listened for forwards */ int host_port; /* remote port to connect for forwards */ char *remote_name; /* remote hostname */ + + int remote_window; + int remote_maxpacket; + int local_window; + int local_window_max; + int local_consumed; + int local_maxpacket; + int extended_usage; + + char *ctype; /* type */ + + // callback + channel_callback_fn *cb_fn; + void *cb_arg; + int cb_event; + channel_callback_fn *dettach_user; } Channel; + +#define CHAN_EXTENDED_IGNORE 0 +#define CHAN_EXTENDED_READ 1 +#define CHAN_EXTENDED_WRITE 2 + +void channel_open(int id); +Channel *channel_lookup(int id); + +int +channel_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extended_usage, char *remote_name); + +void channel_input_close(int type, int plen); +void channel_input_close_confirmation(int type, int plen); +void channel_input_data(int type, int plen); +void channel_input_ieof(int type, int plen); +void channel_input_oclose(int type, int plen); +void channel_input_open_confirmation(int type, int plen); +void channel_input_open_failure(int type, int plen); +void channel_input_port_open(int type, int plen); +void channel_input_open(int type, int plen); + +/* Sets specific protocol options. */ +void channel_set_options(int hostname_in_open); + +/* + * Allocate a new channel object and set its type and socket. Remote_name + * must have been allocated with xmalloc; this will free it when the channel + * is freed. + */ +int channel_allocate(int type, int sock, char *remote_name); + +/* Free the channel and close its socket. */ +void channel_free(int channel); + +/* Add any bits relevant to channels in select bitmasks. */ +void channel_prepare_select(fd_set * readset, fd_set * writeset); + +/* + * After select, perform any appropriate operations for channels which have + * events pending. + */ +void channel_after_select(fd_set * readset, fd_set * writeset); + +/* If there is data to send to the connection, send some of it now. */ +void channel_output_poll(void); + +/* Returns true if no channel has too much buffered data. */ +int channel_not_very_much_buffered_data(void); + +/* This closes any sockets that are listening for connections; this removes + any unix domain sockets. */ +void channel_stop_listening(void); + +/* + * Closes the sockets of all channels. This is used to close extra file + * descriptors after a fork. + */ +void channel_close_all(void); + +/* Returns the maximum file descriptor number used by the channels. */ +int channel_max_fd(void); + +/* Returns true if there is still an open channel over the connection. */ +int channel_still_open(void); + +/* + * Returns a string containing a list of all open channels. The list is + * suitable for displaying to the user. It uses crlf instead of newlines. + * The caller should free the string with xfree. + */ +char *channel_open_message(void); + +/* + * Initiate forwarding of connections to local port "port" through the secure + * channel to host:port from remote side. This never returns if there was an + * error. + */ +void +channel_request_local_forwarding(u_short port, const char *host, + u_short remote_port, int gateway_ports); + +/* + * Initiate forwarding of connections to port "port" on remote host through + * the secure channel to host:port from local side. This never returns if + * there was an error. This registers that open requests for that port are + * permitted. + */ +void +channel_request_remote_forwarding(u_short port, const char *host, + u_short remote_port); + +/* + * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually + * called by the server, because the user could connect to any port anyway, + * and the server has no way to know but to trust the client anyway. + */ +void channel_permit_all_opens(void); + +/* + * 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. + */ +void channel_input_port_forward_request(int is_root); + +/* + * Creates a port for X11 connections, and starts listening for it. Returns + * the display name, or NULL if an error was encountered. + */ +char *x11_create_display(int screen); + +/* + * Creates an internet domain socket for listening for X11 connections. + * Returns a suitable value for the DISPLAY variable, or NULL if an error + * occurs. + */ +char *x11_create_display_inet(int screen, int x11_display_offset); + +/* + * This is called when SSH_SMSG_X11_OPEN is received. The packet contains + * the remote channel number. We should do whatever we want, and respond + * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. + */ +void x11_input_open(int type, int plen); + +/* + * Requests forwarding of X11 connections. This should be called on the + * client only. + */ +void x11_request_forwarding(void); + +/* + * Requests forwarding for X11 connections, with authentication spoofing. + * This should be called in the client only. + */ +void x11_request_forwarding_with_spoofing(const char *proto, const char *data); + +/* Sends a message to the server to request authentication fd forwarding. */ +void auth_request_forwarding(void); + +/* + * Returns the name of the forwarded authentication socket. Returns NULL if + * there is no forwarded authentication socket. The returned value points to + * a static buffer. + */ +char *auth_get_socket_name(void); + +/* + * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. + * This starts forwarding authentication requests. + */ +void auth_input_request_forwarding(struct passwd * pw); + +/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ +void auth_input_open_request(int type, int plen); + #endif diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c index 59dad3d7d30..a5bfa0a5b49 100644 --- a/usr.bin/ssh/clientloop.c +++ b/usr.bin/ssh/clientloop.c @@ -15,7 +15,7 @@ */ #include "includes.h" -RCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $"); +RCSID("$Id: clientloop.c,v 1.15 2000/03/28 20:31:26 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -24,6 +24,11 @@ RCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $"); #include "authfd.h" #include "readconf.h" +#include "compat.h" +#include "channels.h" +#include "dispatch.h" + + /* Flag indicating that stdin should be redirected from /dev/null. */ extern int stdin_null_flag; @@ -228,108 +233,6 @@ client_check_initial_eof_on_stdin() } } -/* - * Get packets from the connection input buffer, and process them as long as - * there are packets available. - */ - -void -client_process_buffered_input_packets() -{ - int type; - char *data; - unsigned int data_len; - int payload_len; - - /* Process any buffered packets from the server. */ - while (!quit_pending && - (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { - switch (type) { - - case SSH_SMSG_STDOUT_DATA: - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + data_len, type); - buffer_append(&stdout_buffer, data, data_len); - stdout_bytes += data_len; - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_SMSG_STDERR_DATA: - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + data_len, type); - buffer_append(&stderr_buffer, data, data_len); - stdout_bytes += data_len; - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_SMSG_EXITSTATUS: - packet_integrity_check(payload_len, 4, type); - exit_status = packet_get_int(); - /* Acknowledge the exit. */ - packet_start(SSH_CMSG_EXIT_CONFIRMATION); - packet_send(); - /* - * Must wait for packet to be sent since we are - * exiting the loop. - */ - packet_write_wait(); - /* Flag that we want to exit. */ - quit_pending = 1; - break; - - case SSH_SMSG_X11_OPEN: - x11_input_open(payload_len); - break; - - case SSH_MSG_PORT_OPEN: - channel_input_port_open(payload_len); - break; - - case SSH_SMSG_AGENT_OPEN: - packet_integrity_check(payload_len, 4, type); - auth_input_open_request(); - break; - - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - packet_integrity_check(payload_len, 4 + 4, type); - channel_input_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - packet_integrity_check(payload_len, 4, type); - channel_input_open_failure(); - break; - - case SSH_MSG_CHANNEL_DATA: - channel_input_data(payload_len); - break; - - case SSH_MSG_CHANNEL_CLOSE: - packet_integrity_check(payload_len, 4, type); - channel_input_close(); - break; - - case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: - packet_integrity_check(payload_len, 4, type); - channel_input_close_confirmation(); - break; - - default: - /* - * Any unknown packets received during the actual - * session cause the session to terminate. This is - * intended to make debugging easier since no - * confirmations are sent. Any compatible protocol - * extensions must be negotiated during the - * preparatory phase. - */ - packet_disconnect("Protocol error during session: type %d", - type); - } - } -} /* * Make packets from buffered stdin data, and buffer them for sending to the @@ -776,12 +679,32 @@ client_process_output(fd_set * writeset) } /* + * Get packets from the connection input buffer, and process them as long as + * there are packets available. + * + * Any unknown packets received during the actual + * session cause the session to terminate. This is + * intended to make debugging easier since no + * confirmations are sent. Any compatible protocol + * extensions must be negotiated during the + * preparatory phase. + */ + +void +client_process_buffered_input_packets() +{ + dispatch_run(DISPATCH_NONBLOCK, &quit_pending); +} + +/* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the * remote host. If escape_char != -1, it is the character used as an escape * character for terminating or suspending the session. */ +void client_init_dispatch(void); + int client_loop(int have_pty, int escape_char_arg) { @@ -816,6 +739,8 @@ client_loop(int have_pty, int escape_char_arg) buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); + client_init_dispatch(); + /* Set signal handlers to restore non-blocking mode. */ signal(SIGINT, signal_handler); signal(SIGQUIT, signal_handler); @@ -950,3 +875,77 @@ client_loop(int have_pty, int escape_char_arg) debug("Exit status %d", exit_status); return exit_status; } + +/*********/ + +void +client_input_stdout_data(int type, int plen) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stdout_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +void +client_input_stderr_data(int type, int plen) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stderr_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +void +client_input_exit_status(int type, int plen) +{ + packet_integrity_check(plen, 4, type); + exit_status = packet_get_int(); + /* Acknowledge the exit. */ + packet_start(SSH_CMSG_EXIT_CONFIRMATION); + packet_send(); + /* + * Must wait for packet to be sent since we are + * exiting the loop. + */ + packet_write_wait(); + /* Flag that we want to exit. */ + quit_pending = 1; +} + +void +client_init_dispatch_13() +{ + dispatch_init(NULL); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); + dispatch_set(SSH_SMSG_AGENT_OPEN, &auth_input_open_request); + dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); + dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); + dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); + dispatch_set(SSH_SMSG_X11_OPEN, &x11_input_open); +} +void +client_init_dispatch_15() +{ + client_init_dispatch_13(); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); +} +void +client_init_dispatch() +{ + if (compat13) + client_init_dispatch_13(); + else + client_init_dispatch_15(); +} diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index 4a9ce1cb0ae..262273cc35c 100644 --- a/usr.bin/ssh/lib/Makefile +++ b/usr.bin/ssh/lib/Makefile @@ -5,7 +5,7 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ cipher.c compat.c compress.c crc32.c deattack.c fingerprint.c \ hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ - key.c + key.c dispatch.c NOPROFILE= yes NOPIC= yes diff --git a/usr.bin/ssh/nchan.c b/usr.bin/ssh/nchan.c index 1f30a7cf2c6..48b61ef541d 100644 --- a/usr.bin/ssh/nchan.c +++ b/usr.bin/ssh/nchan.c @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: nchan.c,v 1.10 2000/01/10 10:15:28 markus Exp $"); +RCSID("$Id: nchan.c,v 1.11 2000/03/28 20:31:27 markus Exp $"); #include "ssh.h" @@ -41,7 +41,6 @@ static void chan_send_ieof(Channel *c); static void chan_send_oclose(Channel *c); static void chan_shutdown_write(Channel *c); static void chan_shutdown_read(Channel *c); -static void chan_delete_if_full_closed(Channel *c); /* * EVENTS update channel input/output states execute ACTIONS @@ -222,7 +221,7 @@ chan_shutdown_read(Channel *c) error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s", c->self, c->sock, c->istate, c->ostate, strerror(errno)); } -static void +void chan_delete_if_full_closed(Channel *c) { if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { diff --git a/usr.bin/ssh/nchan.h b/usr.bin/ssh/nchan.h index 3397be8a54b..85528c850a5 100644 --- a/usr.bin/ssh/nchan.h +++ b/usr.bin/ssh/nchan.h @@ -27,7 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$Id: nchan.h,v 1.5 1999/11/24 16:15:25 markus Exp $"); */ +/* RCSID("$Id: nchan.h,v 1.6 2000/03/28 20:31:27 markus Exp $"); */ #ifndef NCHAN_H #define NCHAN_H @@ -83,4 +83,6 @@ void chan_write_failed(Channel * c); void chan_obuf_empty(Channel * c); void chan_init_iostates(Channel * c); + +void chan_delete_if_full_closed(Channel *c); #endif diff --git a/usr.bin/ssh/packet.c b/usr.bin/ssh/packet.c index af54759233b..23aee637a6c 100644 --- a/usr.bin/ssh/packet.c +++ b/usr.bin/ssh/packet.c @@ -15,7 +15,7 @@ */ #include "includes.h" -RCSID("$Id: packet.c,v 1.22 2000/02/05 10:13:11 markus Exp $"); +RCSID("$Id: packet.c,v 1.23 2000/03/28 20:31:27 markus Exp $"); #include "xmalloc.h" #include "buffer.h" @@ -28,6 +28,7 @@ RCSID("$Id: packet.c,v 1.22 2000/02/05 10:13:11 markus Exp $"); #include "compress.h" #include "deattack.h" +#include "channels.h" /* * This variable contains the file descriptors used for communicating with diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index e9c8455bf10..e3133521924 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/readconf.c @@ -14,11 +14,12 @@ */ #include "includes.h" -RCSID("$Id: readconf.c,v 1.23 2000/02/28 19:51:58 markus Exp $"); +RCSID("$Id: readconf.c,v 1.24 2000/03/28 20:31:28 markus Exp $"); #include "ssh.h" #include "cipher.h" #include "readconf.h" +#include "match.h" #include "xmalloc.h" /* Format of the configuration file: diff --git a/usr.bin/ssh/serverloop.c b/usr.bin/ssh/serverloop.c index d787894b0a9..fb3903cdf14 100644 --- a/usr.bin/ssh/serverloop.c +++ b/usr.bin/ssh/serverloop.c @@ -13,6 +13,10 @@ #include "buffer.h" #include "servconf.h" #include "pty.h" +#include "channels.h" + +#include "compat.h" +#include "dispatch.h" static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ @@ -42,6 +46,8 @@ static int child_pid; /* Pid of the child. */ static volatile int child_terminated; /* The child has terminated. */ static volatile int child_wait_status; /* Status from wait(). */ +void server_init_dispatch(void); + void sigchld_handler(int sig) { @@ -62,104 +68,6 @@ sigchld_handler(int sig) } /* - * Process any buffered packets that have been received from the client. - */ -void -process_buffered_input_packets() -{ - int type; - char *data; - unsigned int data_len; - int row, col, xpixel, ypixel; - int payload_len; - - /* Process buffered packets from the client. */ - while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { - switch (type) { - case SSH_CMSG_STDIN_DATA: - /* Stdin data from the client. Append it to the buffer. */ - /* Ignore any data if the client has closed stdin. */ - if (fdin == -1) - break; - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, (4 + data_len), type); - buffer_append(&stdin_buffer, data, data_len); - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_CMSG_EOF: - /* - * Eof from the client. The stdin descriptor to the - * program will be closed when all buffered data has - * drained. - */ - debug("EOF received for stdin."); - packet_integrity_check(payload_len, 0, type); - stdin_eof = 1; - break; - - case SSH_CMSG_WINDOW_SIZE: - debug("Window change received."); - packet_integrity_check(payload_len, 4 * 4, type); - row = packet_get_int(); - col = packet_get_int(); - xpixel = packet_get_int(); - ypixel = packet_get_int(); - if (fdin != -1) - pty_change_window_size(fdin, row, col, xpixel, ypixel); - break; - - case SSH_MSG_PORT_OPEN: - debug("Received port open request."); - channel_input_port_open(payload_len); - break; - - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - debug("Received channel open confirmation."); - packet_integrity_check(payload_len, 4 + 4, type); - channel_input_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - debug("Received channel open failure."); - packet_integrity_check(payload_len, 4, type); - channel_input_open_failure(); - break; - - case SSH_MSG_CHANNEL_DATA: - channel_input_data(payload_len); - break; - - case SSH_MSG_CHANNEL_CLOSE: - debug("Received channel close."); - packet_integrity_check(payload_len, 4, type); - channel_input_close(); - break; - - case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: - debug("Received channel close confirmation."); - packet_integrity_check(payload_len, 4, type); - channel_input_close_confirmation(); - break; - - default: - /* - * In this phase, any unexpected messages cause a - * protocol error. This is to ease debugging; also, - * since no confirmations are sent messages, - * unprocessed unknown messages could cause strange - * problems. Any compatible protocol extensions must - * be negotiated before entering the interactive - * session. - */ - packet_disconnect("Protocol error during session: type %d", - type); - } - } -} - -/* * Make packets from buffered stderr data, and buffer it for sending * to the client. */ @@ -416,6 +324,12 @@ drain_output() packet_write_wait(); } +void +process_buffered_input_packets() +{ + dispatch_run(DISPATCH_NONBLOCK, NULL); +} + /* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and @@ -480,6 +394,8 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) if (fderr == -1) fderr_eof = 1; + server_init_dispatch(); + /* Main loop of the server for the interactive session mode. */ for (;;) { fd_set readset, writeset; @@ -536,7 +452,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) if (fdout_eof && fderr_eof && !packet_have_data_to_write() && buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { if (!channel_still_open()) - goto quit; + break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate...\r\n"; char *cp; @@ -563,7 +479,6 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) process_output(&writeset); } -quit: /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ @@ -649,3 +564,79 @@ quit: packet_disconnect("wait returned status %04x.", wait_status); /* NOTREACHED */ } + +void +server_input_stdin_data(int type, int plen) +{ + char *data; + unsigned int data_len; + + /* Stdin data from the client. Append it to the buffer. */ + /* Ignore any data if the client has closed stdin. */ + if (fdin == -1) + return; + data = packet_get_string(&data_len); + packet_integrity_check(plen, (4 + data_len), type); + buffer_append(&stdin_buffer, data, data_len); + memset(data, 0, data_len); + xfree(data); +} + +void +server_input_eof(int type, int plen) +{ + /* + * Eof from the client. The stdin descriptor to the + * program will be closed when all buffered data has + * drained. + */ + debug("EOF received for stdin."); + packet_integrity_check(plen, 0, type); + stdin_eof = 1; +} + +void +server_input_window_size(int type, int plen) +{ + int row = packet_get_int(); + int col = packet_get_int(); + int xpixel = packet_get_int(); + int ypixel = packet_get_int(); + + debug("Window change received."); + packet_integrity_check(plen, 4 * 4, type); + if (fdin != -1) + pty_change_window_size(fdin, row, col, xpixel, ypixel); +} + +void +server_init_dispatch_13() +{ + debug("server_init_dispatch_13"); + dispatch_init(NULL); + dispatch_set(SSH_CMSG_EOF, &server_input_eof); + dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); + dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); +} +void +server_init_dispatch_15() +{ + server_init_dispatch_13(); + debug("server_init_dispatch_15"); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); +} +void +server_init_dispatch() +{ + if (compat13) + server_init_dispatch_13(); + else + server_init_dispatch_15(); +} diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index c5652eaa67e..bbfb9b50928 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -11,7 +11,7 @@ */ #include "includes.h" -RCSID("$Id: ssh.c,v 1.43 2000/03/23 21:52:02 markus Exp $"); +RCSID("$Id: ssh.c,v 1.44 2000/03/28 20:31:28 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -20,6 +20,7 @@ RCSID("$Id: ssh.c,v 1.43 2000/03/23 21:52:02 markus Exp $"); #include "authfd.h" #include "readconf.h" #include "uidswap.h" +#include "channels.h" /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. Default value is AF_UNSPEC means both IPv4 and IPv6. */ diff --git a/usr.bin/ssh/ssh.h b/usr.bin/ssh/ssh.h index 78f95f89d85..0c55ef0faaf 100644 --- a/usr.bin/ssh/ssh.h +++ b/usr.bin/ssh/ssh.h @@ -13,7 +13,7 @@ * */ -/* RCSID("$Id: ssh.h,v 1.34 2000/03/23 22:15:33 markus Exp $"); */ +/* RCSID("$Id: ssh.h,v 1.35 2000/03/28 20:31:29 markus Exp $"); */ #ifndef SSH_H #define SSH_H @@ -461,175 +461,6 @@ void fatal_add_cleanup(void (*proc) (void *context), void *context); /* Removes a cleanup function to be called at fatal(). */ void fatal_remove_cleanup(void (*proc) (void *context), void *context); -/*---------------- definitions for channels ------------------*/ - -/* Sets specific protocol options. */ -void channel_set_options(int hostname_in_open); - -/* - * Allocate a new channel object and set its type and socket. Remote_name - * must have been allocated with xmalloc; this will free it when the channel - * is freed. - */ -int channel_allocate(int type, int sock, char *remote_name); - -/* Free the channel and close its socket. */ -void channel_free(int channel); - -/* Add any bits relevant to channels in select bitmasks. */ -void channel_prepare_select(fd_set * readset, fd_set * writeset); - -/* - * After select, perform any appropriate operations for channels which have - * events pending. - */ -void channel_after_select(fd_set * readset, fd_set * writeset); - -/* If there is data to send to the connection, send some of it now. */ -void channel_output_poll(void); - -/* - * This is called when a packet of type CHANNEL_DATA has just been received. - * The message type has already been consumed, but channel number and data is - * still there. - */ -void channel_input_data(int payload_len); - -/* Returns true if no channel has too much buffered data. */ -int channel_not_very_much_buffered_data(void); - -/* This is called after receiving CHANNEL_CLOSE. */ -void channel_input_close(void); - -/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */ -void channel_input_close_confirmation(void); - -/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ -void channel_input_open_confirmation(void); - -/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ -void channel_input_open_failure(void); - -/* This closes any sockets that are listening for connections; this removes - any unix domain sockets. */ -void channel_stop_listening(void); - -/* - * Closes the sockets of all channels. This is used to close extra file - * descriptors after a fork. - */ -void channel_close_all(void); - -/* Returns the maximum file descriptor number used by the channels. */ -int channel_max_fd(void); - -/* Returns true if there is still an open channel over the connection. */ -int channel_still_open(void); - -/* - * Returns a string containing a list of all open channels. The list is - * suitable for displaying to the user. It uses crlf instead of newlines. - * The caller should free the string with xfree. - */ -char *channel_open_message(void); - -/* - * Initiate forwarding of connections to local port "port" through the secure - * channel to host:port from remote side. This never returns if there was an - * error. - */ -void -channel_request_local_forwarding(u_short port, const char *host, - u_short remote_port, int gateway_ports); - -/* - * Initiate forwarding of connections to port "port" on remote host through - * the secure channel to host:port from local side. This never returns if - * there was an error. This registers that open requests for that port are - * permitted. - */ -void -channel_request_remote_forwarding(u_short port, const char *host, - u_short remote_port); - -/* - * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually - * called by the server, because the user could connect to any port anyway, - * and the server has no way to know but to trust the client anyway. - */ -void channel_permit_all_opens(void); - -/* - * 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. - */ -void channel_input_port_forward_request(int is_root); - -/* - * This is called after receiving PORT_OPEN message. This attempts to - * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION - * or CHANNEL_OPEN_FAILURE. - */ -void channel_input_port_open(int payload_len); - -/* - * Creates a port for X11 connections, and starts listening for it. Returns - * the display name, or NULL if an error was encountered. - */ -char *x11_create_display(int screen); - -/* - * Creates an internet domain socket for listening for X11 connections. - * Returns a suitable value for the DISPLAY variable, or NULL if an error - * occurs. - */ -char *x11_create_display_inet(int screen, int x11_display_offset); - -/* - * This is called when SSH_SMSG_X11_OPEN is received. The packet contains - * the remote channel number. We should do whatever we want, and respond - * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. - */ -void x11_input_open(int payload_len); - -/* - * Requests forwarding of X11 connections. This should be called on the - * client only. - */ -void x11_request_forwarding(void); - -/* - * Requests forwarding for X11 connections, with authentication spoofing. - * This should be called in the client only. - */ -void x11_request_forwarding_with_spoofing(const char *proto, const char *data); - -/* Sends a message to the server to request authentication fd forwarding. */ -void auth_request_forwarding(void); - -/* - * Returns the name of the forwarded authentication socket. Returns NULL if - * there is no forwarded authentication socket. The returned value points to - * a static buffer. - */ -char *auth_get_socket_name(void); - -/* - * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. - * This starts forwarding authentication requests. - */ -void auth_input_request_forwarding(struct passwd * pw); - -/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ -void auth_input_open_request(void); - -/* - * Returns true if the given string matches the pattern (which may contain ? - * and * as wildcards), and zero if it does not match. - */ -int match_pattern(const char *s, const char *pattern); - /* * Expands tildes in the file name. Returns data allocated by xmalloc. * Warning: this calls getpw*. diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index 6a5625394ff..282444a12c9 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -11,7 +11,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.94 2000/03/23 22:15:34 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.95 2000/03/28 20:31:29 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -23,6 +23,8 @@ RCSID("$OpenBSD: sshd.c,v 1.94 2000/03/23 22:15:34 markus Exp $"); #include "mpaux.h" #include "servconf.h" #include "uidswap.h" +#include "match.h" +#include "channels.h" #include "compat.h" #ifdef LIBWRAP |