diff options
-rw-r--r-- | auth-options.c | 11 | ||||
-rw-r--r-- | auth.c | 3 | ||||
-rw-r--r-- | channels.c | 3011 | ||||
-rw-r--r-- | channels.h | 180 | ||||
-rw-r--r-- | clientloop.c | 191 | ||||
-rw-r--r-- | clientloop.h | 31 | ||||
-rw-r--r-- | monitor.c | 5 | ||||
-rw-r--r-- | monitor_wrap.c | 4 | ||||
-rw-r--r-- | mux.c | 193 | ||||
-rw-r--r-- | nchan.c | 114 | ||||
-rw-r--r-- | packet.c | 68 | ||||
-rw-r--r-- | packet.h | 8 | ||||
-rw-r--r-- | servconf.c | 87 | ||||
-rw-r--r-- | servconf.h | 14 | ||||
-rw-r--r-- | serverloop.c | 105 | ||||
-rw-r--r-- | serverloop.h | 6 | ||||
-rw-r--r-- | session.c | 223 | ||||
-rw-r--r-- | session.h | 16 | ||||
-rw-r--r-- | ssh.c | 88 | ||||
-rw-r--r-- | sshbuf.h | 3 | ||||
-rw-r--r-- | sshconnect.c | 38 | ||||
-rw-r--r-- | sshconnect.h | 8 | ||||
-rw-r--r-- | sshd.c | 19 | ||||
-rw-r--r-- | ssherr.c | 4 | ||||
-rw-r--r-- | ssherr.h | 3 |
25 files changed, 2441 insertions, 1992 deletions
diff --git a/auth-options.c b/auth-options.c index 0a191dbb..bed00eef 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.73 2017/05/31 10:54:00 markus Exp $ */ +/* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -61,9 +61,13 @@ char *authorized_principals = NULL; extern ServerOptions options; +/* XXX refactor to be stateless */ + void auth_clear_options(void) { + struct ssh *ssh = active_state; /* XXX */ + no_agent_forwarding_flag = 0; no_port_forwarding_flag = 0; no_pty_flag = 0; @@ -81,7 +85,7 @@ auth_clear_options(void) free(authorized_principals); authorized_principals = NULL; forced_tun_device = -1; - channel_clear_permitted_opens(); + channel_clear_permitted_opens(ssh); } /* @@ -117,6 +121,7 @@ match_flag(const char *opt, int allow_negate, char **optsp, const char *msg) /* * return 1 if access is granted, 0 if not. * side effect: sets key option flags + * XXX remove side effects; fill structure instead. */ int auth_parse_options(struct passwd *pw, char *opts, const char *file, @@ -380,7 +385,7 @@ auth_parse_options(struct passwd *pw, char *opts, const char *file, goto bad_option; } if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) - channel_add_permitted_opens(host, port); + channel_add_permitted_opens(ssh, host, port); free(patterns); goto next_option; } @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.123 2017/08/18 05:36:45 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.124 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -567,6 +567,7 @@ getpwnamallow(const char *user) ci->user = user; parse_server_match_config(&options, ci); log_change_level(options.log_level); + process_permitopen(ssh, &options); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_setauthdb(user); @@ -55,26 +55,27 @@ #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <netdb.h> +#include <stdarg.h> #ifdef HAVE_STDINT_H -#include <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 "ssh2.h" #include "ssherr.h" +#include "sshbuf.h" #include "packet.h" #include "log.h" #include "misc.h" -#include "buffer.h" #include "channels.h" #include "compat.h" #include "canohost.h" @@ -82,28 +83,19 @@ #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; +/* -- agent forwarding */ +#define NUM_SOCKS 10 -/* - * Maximum file descriptor value used in any of the channels. This is - * updated in channel_new. - */ -static int channel_max_fd = 0; +/* -- tcp forwarding */ +/* 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 "*" -/* -- tcp forwarding */ +/* -- X11 forwarding */ +/* Maximum number of fake X11 displays to try. */ +#define MAX_DISPLAYS 1000 /* * Data structure for storing which hosts are permitted for forward requests. @@ -123,100 +115,150 @@ typedef struct { Channel *downstream; /* Downstream mux*/ } ForwardPermission; -/* List of all permitted host/port pairs to connect by the user. */ -static ForwardPermission *permitted_opens = NULL; +typedef void chan_fn(struct ssh *, Channel *c, + fd_set *readset, fd_set *writeset); -/* 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; +/* Master structure for channels state */ +struct ssh_channels { + /* + * Pointer to an array containing all allocated channels. The array + * is dynamically extended as needed. + */ + Channel **channels; -/* Number of permitted host/port pair in the array permitted by the admin. */ -static int num_adm_permitted_opens = 0; + /* + * Size of the channel array. All slots of the array must always be + * initialized (at least the type field); unused slots set to NULL + */ + u_int channels_alloc; -/* special-case port number meaning allow any port */ -#define FWD_PERMIT_ANY_PORT 0 + /* + * Maximum file descriptor value used in any of the channels. This is + * updated in channel_new. + */ + int channel_max_fd; -/* special-case wildcard meaning allow any host */ -#define FWD_PERMIT_ANY_HOST "*" + /* + * '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. + */ + chan_fn **channel_pre; + chan_fn **channel_post; -/* - * 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; + /* -- tcp forwarding */ + /* List of all permitted host/port pairs to connect by the user. */ + ForwardPermission *permitted_opens; -/* -- X11 forwarding */ + /* List of all permitted host/port pairs to connect by the admin. */ + ForwardPermission *permitted_adm_opens; -/* Maximum number of fake X11 displays to try. */ -#define MAX_DISPLAYS 1000 + /* + * Number of permitted host/port pairs in the array permitted by + * the user. + */ + u_int num_permitted_opens; -/* Saved X11 local (client) display. */ -static char *x11_saved_display = NULL; + /* + * Number of permitted host/port pair in the array permitted by + * the admin. + */ + u_int num_adm_permitted_opens; -/* Saved X11 authentication protocol name. */ -static char *x11_saved_proto = NULL; + /* + * 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. + */ + int all_opens_permitted; -/* Saved X11 authentication data. This is the real data. */ -static char *x11_saved_data = NULL; -static u_int x11_saved_data_len = 0; + /* -- X11 forwarding */ -/* Deadline after which all X11 connections are refused */ -static u_int x11_refuse_time; + /* Saved X11 local (client) display. */ + char *x11_saved_display; -/* - * 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; + /* Saved X11 authentication protocol name. */ + char *x11_saved_proto; + /* Saved X11 authentication data. This is the real data. */ + char *x11_saved_data; + u_int x11_saved_data_len; -/* -- agent forwarding */ + /* Deadline after which all X11 connections are refused */ + u_int x11_refuse_time; -#define NUM_SOCKS 10 + /* + * Fake X11 authentication data. This is what the server will be + * sending us; we should replace any occurrences of this by the + * real data. + */ + u_char *x11_fake_data; + u_int x11_fake_data_len; -/* AF_UNSPEC or AF_INET or AF_INET6 */ -static int IPv4or6 = AF_UNSPEC; + /* AF_UNSPEC or AF_INET or AF_INET6 */ + int IPv4or6; +}; /* helper */ -static void port_open_helper(Channel *c, char *rtype); +static void port_open_helper(struct ssh *ssh, 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 *); +/* Setup helper */ +static void channel_handler_init(struct ssh_channels *sc); + /* -- channel core */ +void +channel_init_channels(struct ssh *ssh) +{ + struct ssh_channels *sc; + + if ((sc = calloc(1, sizeof(*sc))) == NULL || + (sc->channel_pre = calloc(SSH_CHANNEL_MAX_TYPE, + sizeof(*sc->channel_pre))) == NULL || + (sc->channel_post = calloc(SSH_CHANNEL_MAX_TYPE, + sizeof(*sc->channel_post))) == NULL) + fatal("%s: allocation failed", __func__); + sc->channels_alloc = 10; + sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); + sc->IPv4or6 = AF_UNSPEC; + channel_handler_init(sc); + + ssh->chanctxt = sc; +} + Channel * -channel_by_id(int id) +channel_by_id(struct ssh *ssh, int id) { Channel *c; - if (id < 0 || (u_int)id >= channels_alloc) { - logit("channel_by_id: %d: bad id", id); + if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { + logit("%s: %d: bad id", __func__, id); return NULL; } - c = channels[id]; + c = ssh->chanctxt->channels[id]; if (c == NULL) { - logit("channel_by_id: %d: bad id: channel free", id); + logit("%s: %d: bad id: channel free", __func__, id); return NULL; } return c; } Channel * -channel_by_remote_id(int remote_id) +channel_by_remote_id(struct ssh *ssh, int remote_id) { Channel *c; u_int i; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; if (c != NULL && c->remote_id == remote_id) return c; } @@ -228,12 +270,12 @@ channel_by_remote_id(int remote_id) * Private channels, like listening sockets, may not receive messages. */ Channel * -channel_lookup(int id) +channel_lookup(struct ssh *ssh, int id) { Channel *c; - if ((c = channel_by_id(id)) == NULL) - return (NULL); + if ((c = channel_by_id(ssh, id)) == NULL) + return NULL; switch (c->type) { case SSH_CHANNEL_X11_OPEN: @@ -244,10 +286,10 @@ channel_lookup(int id) case SSH_CHANNEL_OPEN: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_PROXY: - return (c); + return c; } logit("Non-public channel %d, type %d.", id, c->type); - return (NULL); + return NULL; } /* @@ -255,13 +297,15 @@ channel_lookup(int id) * 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, +channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty) { + struct ssh_channels *sc = ssh->chanctxt; + /* 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); + sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, rfd); + sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, wfd); + sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, efd); if (rfd != -1) fcntl(rfd, F_SETFD, FD_CLOEXEC); @@ -299,190 +343,221 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, * remote_name to be freed. */ Channel * -channel_new(char *ctype, int type, int rfd, int wfd, int efd, +channel_new(struct ssh *ssh, 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; + struct ssh_channels *sc = ssh->chanctxt; + u_int i, found; 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) { + for (i = 0; i < sc->channels_alloc; i++) { + if (sc->channels[i] == NULL) { /* Found a free slot. */ - found = (int)i; + found = 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; + } + if (i >= sc->channels_alloc) { + /* + * There are no free slots. Take last+1 slot and expand + * the array. + */ + found = sc->channels_alloc; + if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) + fatal("%s: internal error: channels_alloc %d too big", + __func__, sc->channels_alloc); + sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, + sc->channels_alloc + 10, sizeof(*sc->channels)); + sc->channels_alloc += 10; + debug2("channel: expanding %d", sc->channels_alloc); } /* 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 = sc->channels[found] = xcalloc(1, sizeof(Channel)); + if ((c->input = sshbuf_new()) == NULL || + (c->output = sshbuf_new()) == NULL || + (c->extended = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); 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; + channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 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) +static void +channel_find_maxfd(struct ssh_channels *sc) { u_int i; int max = 0; Channel *c; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; + for (i = 0; i < sc->channels_alloc; i++) { + c = sc->channels[i]; if (c != NULL) { max = MAXIMUM(max, c->rfd); max = MAXIMUM(max, c->wfd); max = MAXIMUM(max, c->efd); } } - return max; + sc->channel_max_fd = max; } int -channel_close_fd(int *fdp) +channel_close_fd(struct ssh *ssh, int *fdp) { + struct ssh_channels *sc = ssh->chanctxt; int ret = 0, fd = *fdp; if (fd != -1) { ret = close(fd); *fdp = -1; - if (fd == channel_max_fd) - channel_max_fd = channel_find_maxfd(); + if (fd == sc->channel_max_fd) + channel_find_maxfd(sc); } return ret; } /* Close all channel fd/socket. */ static void -channel_close_fds(Channel *c) +channel_close_fds(struct ssh *ssh, Channel *c) +{ + channel_close_fd(ssh, &c->sock); + channel_close_fd(ssh, &c->rfd); + channel_close_fd(ssh, &c->wfd); + channel_close_fd(ssh, &c->efd); +} + +static void +fwd_perm_clear(ForwardPermission *fp) +{ + free(fp->host_to_connect); + free(fp->listen_host); + free(fp->listen_path); + bzero(fp, sizeof(*fp)); +} + +enum { FWDPERM_USER, FWDPERM_ADMIN }; + +static int +fwd_perm_list_add(struct ssh *ssh, int which, + const char *host_to_connect, int port_to_connect, + const char *listen_host, const char *listen_path, int listen_port, + Channel *downstream) +{ + ForwardPermission **fpl; + u_int n, *nfpl; + + switch (which) { + case FWDPERM_USER: + fpl = &ssh->chanctxt->permitted_opens; + nfpl = &ssh->chanctxt->num_permitted_opens; + break; + case FWDPERM_ADMIN: + fpl = &ssh->chanctxt->permitted_adm_opens; + nfpl = &ssh->chanctxt->num_adm_permitted_opens; + break; + default: + fatal("%s: invalid list %d", __func__, which); + } + + if (*nfpl >= INT_MAX) + fatal("%s: overflow", __func__); + + *fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl)); + n = (*nfpl)++; +#define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) + (*fpl)[n].host_to_connect = MAYBE_DUP(host_to_connect); + (*fpl)[n].port_to_connect = port_to_connect; + (*fpl)[n].listen_host = MAYBE_DUP(listen_host); + (*fpl)[n].listen_path = MAYBE_DUP(listen_path); + (*fpl)[n].listen_port = listen_port; + (*fpl)[n].downstream = downstream; +#undef MAYBE_DUP + return (int)n; +} + +static void +mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) { - channel_close_fd(&c->sock); - channel_close_fd(&c->rfd); - channel_close_fd(&c->wfd); - channel_close_fd(&c->efd); + struct ssh_channels *sc = ssh->chanctxt; + ForwardPermission *fp; + int r; + u_int i; + + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (fp->downstream != c) + continue; + + /* cancel on the server, since mux client is gone */ + debug("channel %d: cleanup remote forward for %s:%u", + c->self, fp->listen_host, fp->listen_port); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "cancel-tcpip-forward")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_put_cstring(ssh, + channel_rfwd_bind_host(fp->listen_host))) != 0 || + (r = sshpkt_put_u32(ssh, fp->listen_port)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: %s", __func__, + c->self, ssh_err(r)); + } + fwd_perm_clear(fp); /* unregister */ + } } /* Free the channel and close its fd/socket. */ void -channel_free(Channel *c) +channel_free(struct ssh *ssh, Channel *c) { + struct ssh_channels *sc = ssh->chanctxt; 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; - } + for (n = 0, i = 0; i < sc->channels_alloc; i++) { + if ((other = sc->channels[i]) == NULL) + continue; + 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; - } - } + if (c->type == SSH_CHANNEL_MUX_CLIENT) + mux_remove_remote_forwardings(ssh, c); - s = channel_open_message(); + s = channel_open_message(ssh); debug3("channel %d: status: %s", c->self, s); free(s); - channel_close_fds(c); - buffer_free(&c->input); - buffer_free(&c->output); - buffer_free(&c->extended); + channel_close_fds(ssh, c); + sshbuf_free(c->input); + sshbuf_free(c->output); + sshbuf_free(c->extended); + c->input = c->output = c->extended = NULL; free(c->remote_name); c->remote_name = NULL; free(c->path); @@ -491,25 +566,26 @@ channel_free(Channel *c) c->listening_addr = NULL; while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) - cc->abandon_cb(c, cc->ctx); + cc->abandon_cb(ssh, 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; + c->filter_cleanup(ssh, c->self, c->filter_ctx); + sc->channels[c->self] = NULL; + bzero(c, sizeof(*c)); free(c); } void -channel_free_all(void) +channel_free_all(struct ssh *ssh) { u_int i; - for (i = 0; i < channels_alloc; i++) - if (channels[i] != NULL) - channel_free(channels[i]); + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) + if (ssh->chanctxt->channels[i] != NULL) + channel_free(ssh, ssh->chanctxt->channels[i]); } /* @@ -517,26 +593,26 @@ channel_free_all(void) * descriptors after a fork. */ void -channel_close_all(void) +channel_close_all(struct ssh *ssh) { u_int i; - for (i = 0; i < channels_alloc; i++) - if (channels[i] != NULL) - channel_close_fds(channels[i]); + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) + if (ssh->chanctxt->channels[i] != NULL) + channel_close_fds(ssh, ssh->chanctxt->channels[i]); } /* * Stop listening to channels. */ void -channel_stop_listening(void) +channel_stop_listening(struct ssh *ssh) { u_int i; Channel *c; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; if (c != NULL) { switch (c->type) { case SSH_CHANNEL_AUTH_SOCKET: @@ -545,8 +621,8 @@ channel_stop_listening(void) case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: - channel_close_fd(&c->sock); - channel_free(c); + channel_close_fd(ssh, &c->sock); + channel_free(ssh, c); break; } } @@ -558,20 +634,20 @@ channel_stop_listening(void) * more channel is overfull. */ int -channel_not_very_much_buffered_data(void) +channel_not_very_much_buffered_data(struct ssh *ssh) { u_int i; + u_int maxsize = ssh_packet_get_maxsize(ssh); Channel *c; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; - if (c != NULL && c->type == SSH_CHANNEL_OPEN) { - 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; - } + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_OPEN) + continue; + if (sshbuf_len(c->output) > maxsize) { + debug2("channel %d: big output buffer %zu > %u", + c->self, sshbuf_len(c->output), maxsize); + return 0; } } return 1; @@ -579,13 +655,13 @@ channel_not_very_much_buffered_data(void) /* Returns true if any channel is still open. */ int -channel_still_open(void) +channel_still_open(struct ssh *ssh) { u_int i; Channel *c; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; if (c == NULL) continue; switch (c->type) { @@ -620,13 +696,13 @@ channel_still_open(void) /* Returns the id of an open channel suitable for keepaliving */ int -channel_find_open(void) +channel_find_open(struct ssh *ssh) { u_int i; Channel *c; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; if (c == NULL || c->remote_id < 0) continue; switch (c->type) { @@ -664,18 +740,21 @@ channel_find_open(void) * newlines. */ char * -channel_open_message(void) +channel_open_message(struct ssh *ssh) { - Buffer buffer; + struct sshbuf *buf; 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]; + int r; + char *ret; + + if ((buf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + if ((r = sshbuf_putf(buf, + "The following connections are open:\r\n")) != 0) + fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r)); + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; if (c == NULL) continue; switch (c->type) { @@ -698,69 +777,85 @@ channel_open_message(void) case SSH_CHANNEL_X11_OPEN: 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", + if ((r = sshbuf_putf(buf, " #%d %.300s " + "(t%d r%d i%u/%zu o%u/%zu 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)); + c->istate, sshbuf_len(c->input), + c->ostate, sshbuf_len(c->output), + c->rfd, c->wfd, c->ctl_chan)) != 0) + fatal("%s: sshbuf_putf: %s", + __func__, ssh_err(r)); continue; default: - fatal("channel_open_message: bad channel type %d", c->type); + fatal("%s: bad channel type %d", __func__, c->type); /* NOTREACHED */ } } - buffer_append(&buffer, "\0", 1); - cp = xstrdup((char *)buffer_ptr(&buffer)); - buffer_free(&buffer); - return cp; + if ((ret = sshbuf_dup_string(buf)) == NULL) + fatal("%s: sshbuf_dup_string", __func__); + sshbuf_free(buf); + return ret; +} + +static void +open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) +{ + int r; + + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL |