summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2017-09-12 06:32:07 +0000
committerDamien Miller <djm@mindrot.org>2017-09-12 17:37:02 +1000
commitdbee4119b502e3f8b6cd3282c69c537fd01d8e16 (patch)
treeb8a3263a79e0920e8d08f188654f1ccb7c254406
parentabd59663df37a42152e37980113ccaa405b9a282 (diff)
upstream commit
refactor channels.c Move static state to a "struct ssh_channels" that is allocated at runtime and tracked as a member of struct ssh. Explicitly pass "struct ssh" to all channels functions. Replace use of the legacy packet APIs in channels.c. Rework sshd_config PermitOpen handling: previously the configuration parser would call directly into the channels layer. After the refactor this is not possible, as the channels structures are allocated at connection time and aren't available when the configuration is parsed. The server config parser now tracks PermitOpen itself and explicitly configures the channels code later. ok markus@ Upstream-ID: 11828f161656b965cc306576422613614bea2d8f
-rw-r--r--auth-options.c11
-rw-r--r--auth.c3
-rw-r--r--channels.c3011
-rw-r--r--channels.h180
-rw-r--r--clientloop.c191
-rw-r--r--clientloop.h31
-rw-r--r--monitor.c5
-rw-r--r--monitor_wrap.c4
-rw-r--r--mux.c193
-rw-r--r--nchan.c114
-rw-r--r--packet.c68
-rw-r--r--packet.h8
-rw-r--r--servconf.c87
-rw-r--r--servconf.h14
-rw-r--r--serverloop.c105
-rw-r--r--serverloop.h6
-rw-r--r--session.c223
-rw-r--r--session.h16
-rw-r--r--ssh.c88
-rw-r--r--sshbuf.h3
-rw-r--r--sshconnect.c38
-rw-r--r--sshconnect.h8
-rw-r--r--sshd.c19
-rw-r--r--ssherr.c4
-rw-r--r--ssherr.h3
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;
}
diff --git a/auth.c b/auth.c
index 7f073e0f..a4490617 100644
--- a/auth.c
+++ b/auth.c
@@ -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);
diff --git a/channels.c b/channels.c
index d9e81b5f..935625c7 100644
--- a/channels.c
+++ b/channels.c
@@ -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];