diff options
author | dtucker@openbsd.org <dtucker@openbsd.org> | 2016-10-18 17:32:54 +0000 |
---|---|---|
committer | Darren Tucker <dtucker@zip.com.au> | 2016-10-19 07:16:01 +1100 |
commit | 246aa842a4ad368d8ce030495e657ef3a0e1f95c (patch) | |
tree | c6c82ee881b08ce78672f2e8bda687f24a3fef4a | |
parent | 2c6697c443d2c9c908260eed73eb9143223e3ec9 (diff) |
upstream commit
Remove channel_input_port_forward_request(); the only caller
was the recently-removed SSH1 server code so it's now dead code. ok markus@
Upstream-ID: 05453983230a1f439562535fec2818f63f297af9
-rw-r--r-- | channels.c | 41 | ||||
-rw-r--r-- | channels.c.orig | 4656 | ||||
-rw-r--r-- | channels.c.rej | 8 | ||||
-rw-r--r-- | channels.h | 3 | ||||
-rw-r--r-- | channels.h.orig | 323 | ||||
-rw-r--r-- | channels.h.rej | 16 |
6 files changed, 5005 insertions, 42 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.355 2016/09/30 20:24:46 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.356 2016/10/18 17:32:54 dtucker Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -3806,45 +3806,6 @@ channel_request_rforward_cancel(struct Forward *fwd) } /* - * 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). - */ -int -channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) -{ - int success = 0; - struct Forward fwd; - - /* Get arguments from the packet. */ - memset(&fwd, 0, sizeof(fwd)); - fwd.listen_port = packet_get_int(); - fwd.connect_host = packet_get_string(NULL); - fwd.connect_port = packet_get_int(); - -#ifndef HAVE_CYGWIN - /* - * Check that an unprivileged user is not trying to forward a - * privileged port. - */ - if (fwd.listen_port < IPPORT_RESERVED && !is_root) - packet_disconnect( - "Requested forwarding of port %d but user is not root.", - fwd.listen_port); - if (fwd.connect_port == 0) - packet_disconnect("Dynamic forwarding denied."); -#endif - - /* Initiate forwarding */ - success = channel_setup_local_fwd_listener(&fwd, fwd_opts); - - /* Free the argument string. */ - free(fwd.connect_host); - - return (success ? 0 : -1); -} - -/* * Permits opening to any host/port if permitted_opens[] is empty. 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. diff --git a/channels.c.orig b/channels.c.orig new file mode 100644 index 00000000..ab2d9988 --- /dev/null +++ b/channels.c.orig @@ -0,0 +1,4656 @@ +/* $OpenBSD: channels.c,v 1.355 2016/09/30 20:24:46 djm Exp $ */ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * This file contains functions for generic socket connection forwarding. + * There is also code for initiating connection forwarding for X11 connections, + * arbitrary tcp/ip connections, and the authentication agent connection. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support added by Markus Friedl. + * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. + * Copyright (c) 1999 Dug Song. All rights reserved. + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/un.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <stdarg.h> + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" +#include "ssherr.h" +#include "packet.h" +#include "log.h" +#include "misc.h" +#include "buffer.h" +#include "channels.h" +#include "compat.h" +#include "canohost.h" +#include "key.h" +#include "authfd.h" +#include "pathnames.h" + +/* -- channel core */ + +/* + * Pointer to an array containing all allocated channels. The array is + * dynamically extended as needed. + */ +static Channel **channels = NULL; + +/* + * 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 u_int channels_alloc = 0; + +/* + * Maximum file descriptor value used in any of the channels. This is + * updated in channel_new. + */ +static int channel_max_fd = 0; + + +/* -- tcp forwarding */ + +/* + * Data structure for storing which hosts are permitted for forward requests. + * The local sides of any remote forwards are stored in this array to prevent + * a corrupt remote server from accessing arbitrary TCP/IP ports on our local + * network (which might be behind a firewall). + */ +/* XXX: streamlocal wants a path instead of host:port */ +/* Overload host_to_connect; we could just make this match Forward */ +/* XXX - can we use listen_host instead of listen_path? */ +typedef struct { + char *host_to_connect; /* Connect to 'host'. */ + int port_to_connect; /* Connect to 'port'. */ + char *listen_host; /* Remote side should listen address. */ + char *listen_path; /* Remote side should listen path. */ + int listen_port; /* Remote side should listen port. */ + Channel *downstream; /* Downstream mux*/ +} ForwardPermission; + +/* List of all permitted host/port pairs to connect by the user. */ +static ForwardPermission *permitted_opens = NULL; + +/* List of all permitted host/port pairs to connect by the admin. */ +static ForwardPermission *permitted_adm_opens = NULL; + +/* Number of permitted host/port pairs in the array permitted by the user. */ +static int num_permitted_opens = 0; + +/* Number of permitted host/port pair in the array permitted by the admin. */ +static int num_adm_permitted_opens = 0; + +/* special-case port number meaning allow any port */ +#define FWD_PERMIT_ANY_PORT 0 + +/* special-case wildcard meaning allow any host */ +#define FWD_PERMIT_ANY_HOST "*" + +/* + * If this is true, all opens are permitted. This is the case on the server + * on which we have to trust the client anyway, and the user could do + * anything after logging in anyway. + */ +static int all_opens_permitted = 0; + + +/* -- X11 forwarding */ + +/* 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; + +/* Saved X11 authentication data. This is the real data. */ +static char *x11_saved_data = NULL; +static u_int x11_saved_data_len = 0; + +/* Deadline after which all X11 connections are refused */ +static u_int x11_refuse_time; + +/* + * Fake X11 authentication data. This is what the server will be sending us; + * we should replace any occurrences of this by the real data. + */ +static u_char *x11_fake_data = NULL; +static u_int x11_fake_data_len; + + +/* -- agent forwarding */ + +#define NUM_SOCKS 10 + +/* AF_UNSPEC or AF_INET or AF_INET6 */ +static int IPv4or6 = AF_UNSPEC; + +/* helper */ +static void port_open_helper(Channel *c, char *rtype); +static const char *channel_rfwd_bind_host(const char *listen_host); + +/* non-blocking connect helpers */ +static int connect_next(struct channel_connect *); +static void channel_connect_ctx_free(struct channel_connect *); + +/* -- channel core */ + +Channel * +channel_by_id(int id) +{ + Channel *c; + + if (id < 0 || (u_int)id >= channels_alloc) { + logit("channel_by_id: %d: bad id", id); + return NULL; + } + c = channels[id]; + if (c == NULL) { + logit("channel_by_id: %d: bad id: channel free", id); + return NULL; + } + return c; +} + +Channel * +channel_by_remote_id(int remote_id) +{ + Channel *c; + u_int i; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c != NULL && c->remote_id == remote_id) + return c; + } + return NULL; +} + +/* + * 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: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_MUX_PROXY: + return (c); + } + 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 + */ +static void +channel_register_fds(Channel *c, int rfd, int wfd, int efd, + int extusage, int nonblock, int is_tty) +{ + /* Update the maximum file descriptor value. */ + channel_max_fd = MAXIMUM(channel_max_fd, rfd); + channel_max_fd = MAXIMUM(channel_max_fd, wfd); + channel_max_fd = MAXIMUM(channel_max_fd, efd); + + if (rfd != -1) + fcntl(rfd, F_SETFD, FD_CLOEXEC); + if (wfd != -1 && wfd != rfd) + fcntl(wfd, F_SETFD, FD_CLOEXEC); + if (efd != -1 && efd != rfd && efd != wfd) + fcntl(efd, F_SETFD, FD_CLOEXEC); + + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->efd = efd; + c->extended_usage = extusage; + + if ((c->isatty = is_tty) != 0) + debug2("channel %d: rfd %d isatty", c->self, c->rfd); +#ifdef _AIX + /* XXX: Later AIX versions can't push as much data to tty */ + c->wfd_isatty = is_tty || isatty(c->wfd); +#endif + + /* enable nonblocking mode */ + if (nonblock) { + if (rfd != -1) + set_nonblock(rfd); + if (wfd != -1) + set_nonblock(wfd); + if (efd != -1) + set_nonblock(efd); + } +} + +/* + * Allocate a new channel object and set its type and socket. This will cause + * remote_name to be freed. + */ +Channel * +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 found; + u_int i; + Channel *c; + + /* Do initial allocation if this is the first call. */ + if (channels_alloc == 0) { + channels_alloc = 10; + channels = xcalloc(channels_alloc, sizeof(Channel *)); + for (i = 0; i < channels_alloc; i++) + channels[i] = 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 = (int)i; + break; + } + if (found < 0) { + /* There are no free slots. Take last+1 slot and expand the array. */ + found = channels_alloc; + if (channels_alloc > 10000) + fatal("channel_new: internal error: channels_alloc %d " + "too big.", channels_alloc); + channels = xreallocarray(channels, channels_alloc + 10, + sizeof(Channel *)); + channels_alloc += 10; + debug2("channel: expanding %d", channels_alloc); + for (i = found; i < channels_alloc; i++) + channels[i] = NULL; + } + /* Initialize and return new channel. */ + c = channels[found] = xcalloc(1, sizeof(Channel)); + buffer_init(&c->input); + buffer_init(&c->output); + buffer_init(&c->extended); + c->path = NULL; + c->listening_addr = NULL; + c->listening_port = 0; + c->ostate = CHAN_OUTPUT_OPEN; + c->istate = CHAN_INPUT_OPEN; + c->flags = 0; + channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); + c->notbefore = 0; + c->self = found; + c->type = type; + c->ctype = ctype; + c->local_window = window; + c->local_window_max = window; + c->local_consumed = 0; + c->local_maxpacket = maxpack; + c->remote_id = -1; + c->remote_name = xstrdup(remote_name); + c->remote_window = 0; + c->remote_maxpacket = 0; + c->force_drain = 0; + c->single_connection = 0; + c->detach_user = NULL; + c->detach_close = 0; + c->open_confirm = NULL; + c->open_confirm_ctx = NULL; + c->input_filter = NULL; + c->output_filter = NULL; + c->filter_ctx = NULL; + c->filter_cleanup = NULL; + c->ctl_chan = -1; + c->mux_rcb = NULL; + c->mux_ctx = NULL; + c->mux_pause = 0; + c->delayed = 1; /* prevent call to channel_post handler */ + TAILQ_INIT(&c->status_confirms); + debug("channel %d: new [%s]", found, remote_name); + return c; +} + +static int +channel_find_maxfd(void) +{ + u_int i; + int max = 0; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c != NULL) { + max = MAXIMUM(max, c->rfd); + max = MAXIMUM(max, c->wfd); + max = MAXIMUM(max, c->efd); + } + } + return max; +} + +int +channel_close_fd(int *fdp) +{ + int ret = 0, fd = *fdp; + + if (fd != -1) { + ret = close(fd); + *fdp = -1; + if (fd == channel_max_fd) + channel_max_fd = channel_find_maxfd(); + } + return ret; +} + +/* Close all channel fd/socket. */ +static void +channel_close_fds(Channel *c) +{ + channel_close_fd(&c->sock); + channel_close_fd(&c->rfd); + channel_close_fd(&c->wfd); + channel_close_fd(&c->efd); +} + +/* Free the channel and close its fd/socket. */ +void +channel_free(Channel *c) +{ + char *s; + u_int i, n; + Channel *other; + struct channel_confirm *cc; + + for (n = 0, i = 0; i < channels_alloc; i++) { + if ((other = channels[i]) != NULL) { + n++; + + /* detach from mux client and prepare for closing */ + if (c->type == SSH_CHANNEL_MUX_CLIENT && + other->type == SSH_CHANNEL_MUX_PROXY && + other->mux_ctx == c) { + other->mux_ctx = NULL; + other->type = SSH_CHANNEL_OPEN; + other->istate = CHAN_INPUT_CLOSED; + other->ostate = CHAN_OUTPUT_CLOSED; + } + } + } + debug("channel %d: free: %s, nchannels %u", c->self, + c->remote_name ? c->remote_name : "???", n); + + /* XXX more MUX cleanup: remove remote forwardings */ + if (c->type == SSH_CHANNEL_MUX_CLIENT) { + for (i = 0; i < (u_int)num_permitted_opens; i++) { + if (permitted_opens[i].downstream != c) + continue; + /* cancel on the server, since mux client is gone */ + debug("channel %d: cleanup remote forward for %s:%u", + c->self, + permitted_opens[i].listen_host, + permitted_opens[i].listen_port); + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-tcpip-forward"); + packet_put_char(0); + packet_put_cstring(channel_rfwd_bind_host( + permitted_opens[i].listen_host)); + packet_put_int(permitted_opens[i].listen_port); + packet_send(); + /* unregister */ + 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; + free(permitted_opens[i].listen_host); + permitted_opens[i].listen_host = NULL; + permitted_opens[i].listen_path = NULL; + permitted_opens[i].downstream = NULL; + } + } + + s = channel_open_message(); + debug3("channel %d: status: %s", c->self, s); + free(s); + + if (c->sock != -1) + shutdown(c->sock, SHUT_RDWR); + channel_close_fds(c); + buffer_free(&c->input); + buffer_free(&c->output); + buffer_free(&c->extended); + free(c->remote_name); + c->remote_name = NULL; + free(c->path); + c->path = NULL; + free(c->listening_addr); + c->listening_addr = NULL; + while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { + if (cc->abandon_cb != NULL) + cc->abandon_cb(c, cc->ctx); + TAILQ_REMOVE(&c->status_confirms, cc, entry); + explicit_bzero(cc, sizeof(*cc)); + free(cc); + } + if (c->filter_cleanup != NULL && c->filter_ctx != NULL) + c->filter_cleanup(c->self, c->filter_ctx); + channels[c->self] = NULL; + free(c); +} + +void +channel_free_all(void) +{ + u_int i; + + for (i = 0; i < channels_alloc; i++) + if (channels[i] != NULL) + channel_free(channels[i]); +} + +/* + * Closes the sockets/fds of all channels. This is used to close extra file + * descriptors after a fork. + */ +void +channel_close_all(void) +{ + u_int i; + + for (i = 0; i < channels_alloc; i++) + if (channels[i] != NULL) + channel_close_fds(channels[i]); +} + +/* + * Stop listening to channels. + */ +void +channel_stop_listening(void) +{ + u_int i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c != NULL) { + switch (c->type) { + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + channel_close_fd(&c->sock); + channel_free(c); + break; + } + } + } +} + +/* + * Returns true if no channel has too much buffered data, and false if one or + * more channel is overfull. + */ +int +channel_not_very_much_buffered_data(void) +{ + u_int i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c != NULL && c->type == SSH_CHANNEL_OPEN) { +#if 0 + if (!compat20 && + buffer_len(&c->input) > packet_get_maxsize()) { + debug2("channel %d: big input buffer %d", + c->self, buffer_len(&c->input)); + return 0; + } +#endif + if (buffer_len(&c->output) > packet_get_maxsize()) { + debug2("channel %d: big output buffer %u > %u", + c->self, buffer_len(&c->output), + packet_get_maxsize()); + return 0; + } + } + } + return 1; +} + +/* Returns true if any channel is still open. */ +int +channel_still_open(void) +{ + u_int i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c == NULL) + continue; + switch (c->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + if (!compat20) + fatal("cannot happen: SSH_CHANNEL_LARVAL"); + continue; + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_PROXY: + return 1; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return 1; + default: + fatal("channel_still_open: bad channel type %d", c->type); + /* NOTREACHED */ + } + } + return 0; +} + +/* Returns the id of an open channel suitable for keepaliving */ +int +channel_find_open(void) +{ + u_int i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c == NULL || c->remote_id < 0) + continue; + switch (c->type) { + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_PROXY: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return i; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return i; + default: + fatal("channel_find_open: bad channel type %d", c->type); + /* NOTREACHED */ + } + } + return -1; +} + +/* + * Returns a message describing the currently open forwarded connections, + * suitable for sending to the client. The message contains crlf pairs for + * newlines. + */ +char * +channel_open_message(void) +{ + Buffer buffer; + Channel *c; + char buf[1024], *cp; + u_int i; + + buffer_init(&buffer); + snprintf(buf, sizeof buf, "The following connections are open:\r\n"); + buffer_append(&buffer, buf, strlen(buf)); + for (i = 0; i < channels_alloc; i++) { + c = channels[i]; + if (c == NULL) + continue; + switch (c->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + case SSH_CHANNEL_MUX_PROXY: + case SSH_CHANNEL_MUX_CLIENT: + snprintf(buf, sizeof buf, + " #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %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->ctl_chan); + buffer_append(&buffer, buf, strlen(buf)); + continue; + default: + fatal("channel_open_message: bad channel type %d", c->type); + /* NOTREACHED */ + } + } + buffer_append(&buffer, "\0", 1); + cp = xstrdup((char *)buffer_ptr(&buffer)); + buffer_free(&buffer); + return cp; +} + +void +channel_send_open(int id) +{ + Channel *c = channel_lookup(id); + + if (c == NULL) { + logit("channel_send_open: %d: bad id", id); + return; + } + debug2("channel %d: send open", id); + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring(c->ctype); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); +} + +void +channel_request_start(int id, char *service, int wantconfirm) +{ + Channel *c = channel_lookup(id); + + if (c == NULL) { + logit("channel_request_start: %d: unknown channel id", id); + return; + } + 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_status_confirm(int id, channel_confirm_cb *cb, + channel_confirm_abandon_cb *abandon_cb, void *ctx) +{ + struct channel_confirm *cc; + Channel *c; + + if ((c = channel_lookup(id)) == NULL) + fatal("channel_register_expect: %d: bad id", id); + + cc = xcalloc(1, sizeof(*cc)); + cc->cb = cb; + cc->abandon_cb = abandon_cb; + cc->ctx = ctx; + TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); +} + +void +channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) +{ + Channel *c = channel_lookup(id); + + if (c == NULL) { + logit("channel_register_open_confirm: %d: bad id", id); + return; + } + c->open_confirm = fn; + c->open_confirm_ctx = ctx; +} + +void +channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) +{ + Channel *c = channel_by_id(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); + + 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_filter_cleanup_fn *cfn, void *ctx) +{ + Channel *c = channel_lookup(id); + + if (c == NULL) { + logit("channel_register_filter: %d: bad id", id); + return; + } + c->input_filter = ifn; + c->output_filter = ofn; + c->filter_ctx = ctx; + c->filter_cleanup = cfn; +} + +void +channel_set_fds(int id, int rfd, int wfd, int efd, + int extusage, int nonblock, int is_tty, u_int window_max) +{ + Channel *c = channel_lookup(id); + + if (c == NULL || c->type != SSH_CHANNEL_LARVAL) + fatal("channel_activate for non-larval channel %d.", id); + channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); + c->type = SSH_CHANNEL_OPEN; + c->local_window = c->local_window_max = window_max; + packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_put_int(c->remote_id); + packet_put_int(c->local_window); + packet_send(); +} + +/* + * '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]; + +/* ARGSUSED */ +static void +channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) +{ + FD_SET(c->sock, readset); +} + +/* ARGSUSED */ +static void +channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) +{ + debug3("channel %d: waiting for connection", c->self); + FD_SET(c->sock, writeset); +} + +static 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); +} + +static void +channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) +{ + u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); + + if (c->istate == CHAN_INPUT_OPEN && + limit > 0 && + buffer_len(&c->input) < limit && + buffer_check_alloc(&c->input, CHAN_RBUF)) + FD_SET(c->rfd, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + 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)); + else + chan_obuf_empty(c); + } + } + /** XXX check close conditions, too */ + if (compat20 && c->efd != -1 && + !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + buffer_len(&c->extended) > 0) + FD_SET(c->efd, writeset); + else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && + (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) && + buffer_len(&c->extended) < c->remote_window) + FD_SET(c->efd, readset); + } + /* XXX: What about efd? races? */ +} + +/* ARGSUSED */ +static 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; + debug2("channel %d: closing after input drain.", c->self); + } +} + +/* ARGSUSED */ +static void +channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) +{ + if (buffer_len(&c->output) == 0) + chan_mark_dead(c); + else + FD_SET(c->sock, writeset); +} + +/* + * 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. + * XXX All this happens at the client side. + * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok + */ +static int +x11_open_helper(Buffer *b) +{ + u_char *ucp; + u_int proto_len, data_len; + + /* Is this being called after the refusal deadline? */ + if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return -1; + } + + /* Check if the fixed size part of the packet is in buffer. */ + if (buffer_len(b) < 12) + return 0; + + /* Parse the lengths of variable-length fields. */ + ucp = buffer_ptr(b); + 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 { + debug2("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(b) < + 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) + return 0; + + /* Check if authentication protocol matches. */ + if (proto_len != strlen(x11_saved_proto) || + memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { + debug2("X11 connection uses different authentication protocol."); + return -1; |