diff --git a/auth-options.c b/auth-options.c index 0a191dbba..bed00eef0 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 * Copyright (c) 1995 Tatu Ylonen , 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 7f073e0f3..a44906174 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 d9e81b5fa..935625c74 100644 --- a/channels.c +++ b/channels.c @@ -55,26 +55,27 @@ #include #include +#include #include +#include #ifdef HAVE_STDINT_H -#include + #include #endif #include #include #include #include #include -#include #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; - -/* - * Maximum file descriptor value used in any of the channels. This is - * updated in channel_new. - */ -static int channel_max_fd = 0; - +/* -- agent forwarding */ +#define NUM_SOCKS 10 /* -- 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 "*" + +/* -- 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; +/* 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 pairs in the array permitted by the user. */ -static int num_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; -/* Number of permitted host/port pair in the array permitted by the admin. */ -static int num_adm_permitted_opens = 0; + /* + * Maximum file descriptor value used in any of the channels. This is + * updated in channel_new. + */ + int channel_max_fd; -/* special-case port number meaning allow any port */ -#define FWD_PERMIT_ANY_PORT 0 + /* + * '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; -/* special-case wildcard meaning allow any host */ -#define FWD_PERMIT_ANY_HOST "*" + /* -- tcp forwarding */ -/* - * 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; + /* List of all permitted host/port pairs to connect by the user. */ + ForwardPermission *permitted_opens; + /* List of all permitted host/port pairs to connect by the admin. */ + ForwardPermission *permitted_adm_opens; -/* -- X11 forwarding */ + /* + * Number of permitted host/port pairs in the array permitted by + * the user. + */ + u_int num_permitted_opens; -/* Maximum number of fake X11 displays to try. */ -#define MAX_DISPLAYS 1000 + /* + * Number of permitted host/port pair in the array permitted by + * the admin. + */ + u_int num_adm_permitted_opens; -/* Saved X11 local (client) display. */ -static char *x11_saved_display = 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 protocol name. */ -static char *x11_saved_proto = NULL; + /* -- X11 forwarding */ -/* Saved X11 authentication data. This is the real data. */ -static char *x11_saved_data = NULL; -static u_int x11_saved_data_len = 0; + /* Saved X11 local (client) display. */ + char *x11_saved_display; -/* Deadline after which all X11 connections are refused */ -static u_int x11_refuse_time; + /* Saved X11 authentication protocol name. */ + char *x11_saved_proto; -/* - * 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 data. This is the real data. */ + char *x11_saved_data; + u_int x11_saved_data_len; + /* Deadline after which all X11 connections are refused */ + u_int x11_refuse_time; -/* -- agent forwarding */ + /* + * 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; -#define NUM_SOCKS 10 - -/* 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(&c->sock); - channel_close_fd(&c->rfd); - channel_close_fd(&c->wfd); - channel_close_fd(&c->efd); + 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) +{ + 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; + int r; + char *ret; - 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 ((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_OPEN)) != 0 || + (r = sshpkt_put_cstring(ssh, type)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { + fatal("%s: channel %i: open: %s", where, c->self, ssh_err(r)); + } } void -channel_send_open(int id) +channel_send_open(struct ssh *ssh, int id) { - Channel *c = channel_lookup(id); + Channel *c = channel_lookup(ssh, id); + int r; 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(); + open_preamble(ssh, __func__, c, c->ctype); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); } void -channel_request_start(int id, char *service, int wantconfirm) +channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) { - Channel *c = channel_lookup(id); + Channel *c = channel_lookup(ssh, id); + int r; if (c == NULL) { - logit("channel_request_start: %d: unknown channel id", id); + logit("%s: %d: unknown channel id", __func__, 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); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_cstring(ssh, service)) != 0 || + (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { + fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); + } } void -channel_register_status_confirm(int id, channel_confirm_cb *cb, - channel_confirm_abandon_cb *abandon_cb, void *ctx) +channel_register_status_confirm(struct ssh *ssh, 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); + if ((c = channel_lookup(ssh, id)) == NULL) + fatal("%s: %d: bad id", __func__, id); cc = xcalloc(1, sizeof(*cc)); cc->cb = cb; @@ -770,12 +865,13 @@ channel_register_status_confirm(int id, channel_confirm_cb *cb, } void -channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) +channel_register_open_confirm(struct ssh *ssh, int id, + channel_open_fn *fn, void *ctx) { - Channel *c = channel_lookup(id); + Channel *c = channel_lookup(ssh, id); if (c == NULL) { - logit("channel_register_open_confirm: %d: bad id", id); + logit("%s: %d: bad id", __func__, id); return; } c->open_confirm = fn; @@ -783,12 +879,13 @@ channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) } void -channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) +channel_register_cleanup(struct ssh *ssh, int id, + channel_callback_fn *fn, int do_close) { - Channel *c = channel_by_id(id); + Channel *c = channel_by_id(ssh, id); if (c == NULL) { - logit("channel_register_cleanup: %d: bad id", id); + logit("%s: %d: bad id", __func__, id); return; } c->detach_user = fn; @@ -796,12 +893,12 @@ channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) } void -channel_cancel_cleanup(int id) +channel_cancel_cleanup(struct ssh *ssh, int id) { - Channel *c = channel_by_id(id); + Channel *c = channel_by_id(ssh, id); if (c == NULL) { - logit("channel_cancel_cleanup: %d: bad id", id); + logit("%s: %d: bad id", __func__, id); return; } c->detach_user = NULL; @@ -809,13 +906,13 @@ channel_cancel_cleanup(int id) } void -channel_register_filter(int id, channel_infilter_fn *ifn, +channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) { - Channel *c = channel_lookup(id); + Channel *c = channel_lookup(ssh, id); if (c == NULL) { - logit("channel_register_filter: %d: bad id", id); + logit("%s: %d: bad id", __func__, id); return; } c->input_filter = ifn; @@ -825,79 +922,72 @@ channel_register_filter(int id, channel_infilter_fn *ifn, } void -channel_set_fds(int id, int rfd, int wfd, int efd, +channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty, u_int window_max) { - Channel *c = channel_lookup(id); + Channel *c = channel_lookup(ssh, id); + int r; 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); + channel_register_fds(ssh, 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(); + + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); } -/* - * '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) +channel_pre_listener(struct ssh *ssh, 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) +channel_pre_connecting(struct ssh *ssh, 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(Channel *c, fd_set *readset, fd_set *writeset) +channel_pre_open(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { if (c->istate == CHAN_INPUT_OPEN && c->remote_window > 0 && - buffer_len(&c->input) < c->remote_window && - buffer_check_alloc(&c->input, CHAN_RBUF)) + sshbuf_len(c->input) < c->remote_window && + sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) FD_SET(c->rfd, readset); if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - if (buffer_len(&c->output) > 0) { + if (sshbuf_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)); + debug2("channel %d: " + "obuf_empty delayed efd %d/(%zu)", c->self, + c->efd, sshbuf_len(c->extended)); else - chan_obuf_empty(c); + chan_obuf_empty(ssh, c); } } /** XXX check close conditions, too */ if (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) + sshbuf_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) + sshbuf_len(c->extended) < c->remote_window) FD_SET(c->efd, readset); } /* XXX: What about efd? races? */ @@ -913,24 +1003,26 @@ channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok */ static int -x11_open_helper(Buffer *b) +x11_open_helper(struct ssh *ssh, struct sshbuf *b) { + struct ssh_channels *sc = ssh->chanctxt; 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) { + if (sc->x11_refuse_time != 0 && + (u_int)monotime() >= sc->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) + if (sshbuf_len(b) < 12) return 0; /* Parse the lengths of variable-length fields. */ - ucp = buffer_ptr(b); + ucp = sshbuf_mutable_ptr(b); if (ucp[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * ucp[6] + ucp[7]; data_len = 256 * ucp[8] + ucp[9]; @@ -944,27 +1036,27 @@ x11_open_helper(Buffer *b) } /* Check if the whole packet is in buffer. */ - if (buffer_len(b) < + if (sshbuf_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) { + if (proto_len != strlen(sc->x11_saved_proto) || + memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { debug2("X11 connection uses different authentication protocol."); return -1; } /* Check if authentication data matches our fake data. */ - if (data_len != x11_fake_data_len || + if (data_len != sc->x11_fake_data_len || timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), - x11_fake_data, x11_fake_data_len) != 0) { + sc->x11_fake_data, sc->x11_fake_data_len) != 0) { debug2("X11 auth data does not match fake data."); return -1; } /* Check fake data length */ - if (x11_fake_data_len != x11_saved_data_len) { + if (sc->x11_fake_data_len != sc->x11_saved_data_len) { error("X11 fake_data_len %d != saved_data_len %d", - x11_fake_data_len, x11_saved_data_len); + sc->x11_fake_data_len, sc->x11_saved_data_len); return -1; } /* @@ -973,60 +1065,64 @@ x11_open_helper(Buffer *b) * data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), - x11_saved_data, x11_saved_data_len); + sc->x11_saved_data, sc->x11_saved_data_len); return 1; } static void -channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) +channel_pre_x11_open(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { - int ret = x11_open_helper(&c->output); + int ret = x11_open_helper(ssh, c->output); /* c->force_drain = 1; */ if (ret == 1) { c->type = SSH_CHANNEL_OPEN; - channel_pre_open(c, readset, writeset); + channel_pre_open(ssh, c, readset, writeset); } else if (ret == -1) { logit("X11 connection rejected because of wrong authentication."); - debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); - chan_read_failed(c); - buffer_clear(&c->input); - chan_ibuf_empty(c); - buffer_clear(&c->output); - chan_write_failed(c); + debug2("X11 rejected %d i%d/o%d", + c->self, c->istate, c->ostate); + chan_read_failed(ssh, c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + sshbuf_reset(c->output); + chan_write_failed(ssh, c); debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); } } static void -channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +channel_pre_mux_client(struct ssh *ssh, + Channel *c, fd_set *readset, fd_set *writeset) { if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && - buffer_check_alloc(&c->input, CHAN_RBUF)) + sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) FD_SET(c->rfd, readset); if (c->istate == CHAN_INPUT_WAIT_DRAIN) { /* clear buffer immediately (discard any partial packet) */ - buffer_clear(&c->input); - chan_ibuf_empty(c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); /* Start output drain. XXX just kill chan? */ - chan_rcvd_oclose(c); + chan_rcvd_oclose(ssh, c); } if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - if (buffer_len(&c->output) > 0) + if (sshbuf_len(c->output) > 0) FD_SET(c->wfd, writeset); else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) - chan_obuf_empty(c); + chan_obuf_empty(ssh, c); } } /* try to decode a socks4 header */ -/* ARGSUSED */ static int -channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) +channel_decode_socks4(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { - char *p, *host; + const u_char *p; + char *host; u_int len, have, i, found, need; char username[256]; struct { @@ -1035,14 +1131,15 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) u_int16_t dest_port; struct in_addr dest_addr; } s4_req, s4_rsp; + int r; debug2("channel %d: decode socks4", c->self); - have = buffer_len(&c->input); + have = sshbuf_len(c->input); len = sizeof(s4_req); if (have < len) return 0; - p = (char *)buffer_ptr(&c->input); + p = sshbuf_ptr(c->input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ @@ -1067,12 +1164,15 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) } if (found < need) return 0; - buffer_get(&c->input, (char *)&s4_req.version, 1); - buffer_get(&c->input, (char *)&s4_req.command, 1); - buffer_get(&c->input, (char *)&s4_req.dest_port, 2); - buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); - have = buffer_len(&c->input); - p = (char *)buffer_ptr(&c->input); + if ((r = sshbuf_get(c->input, &s4_req.version, 1)) != 0 || + (r = sshbuf_get(c->input, &s4_req.command, 1)) != 0 || + (r = sshbuf_get(c->input, &s4_req.dest_port, 2)) != 0 || + (r = sshbuf_get(c->input, &s4_req.dest_addr, 4)) != 0) { + debug("channels %d: decode socks4: %s", c->self, ssh_err(r)); + return -1; + } + have = sshbuf_len(c->input); + p = sshbuf_ptr(c->input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4: user not nul terminated", c->self); @@ -1080,21 +1180,20 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) } len = strlen(p); debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); - len++; /* trailing '\0' */ - if (len > have) - fatal("channel %d: decode socks4: len %d > have %d", - c->self, len, have); + len++; /* trailing '\0' */ strlcpy(username, p, sizeof(username)); - buffer_consume(&c->input, len); - + if ((r = sshbuf_consume(c->input, len)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } free(c->path); c->path = NULL; if (need == 1) { /* SOCKS4: one string */ host = inet_ntoa(s4_req.dest_addr); c->path = xstrdup(host); } else { /* SOCKS4A: two strings */ - have = buffer_len(&c->input); - p = (char *)buffer_ptr(&c->input); + have = sshbuf_len(c->input); + p = sshbuf_ptr(c->input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4a: host not nul " "terminated", c->self); @@ -1110,7 +1209,10 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) return -1; } c->path = xstrdup(p); - buffer_consume(&c->input, len); + if ((r = sshbuf_consume(c->input, len)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } } c->host_port = ntohs(s4_req.dest_port); @@ -1126,7 +1228,10 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) s4_rsp.command = 90; /* cd: req granted */ s4_rsp.dest_port = 0; /* ignored */ s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ - buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); + if ((r = sshbuf_put(c->output, &s4_rsp, sizeof(s4_rsp))) != 0) { + fatal("%s: channel %d: append reply: %s", __func__, + c->self, ssh_err(r)); + } return 1; } @@ -1139,10 +1244,11 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) #define SSH_SOCKS5_CONNECT 0x01 #define SSH_SOCKS5_SUCCESS 0x00 -/* ARGSUSED */ static int -channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) +channel_decode_socks5(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { + /* XXX use get/put_u8 instead of trusting struct padding */ struct { u_int8_t version; u_int8_t command; @@ -1151,14 +1257,15 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) } s5_req, s5_rsp; u_int16_t dest_port; char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; - u_char *p; + const u_char *p; u_int have, need, i, found, nmethods, addrlen, af; + int r; debug2("channel %d: decode socks5", c->self); - p = buffer_ptr(&c->input); + p = sshbuf_ptr(c->input); if (p[0] != 0x05) return -1; - have = buffer_len(&c->input); + have = sshbuf_len(c->input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ if (have < 2) @@ -1178,9 +1285,16 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) c->self); return -1; } - buffer_consume(&c->input, nmethods + 2); - buffer_put_char(&c->output, 0x05); /* version */ - buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ + if ((r = sshbuf_consume(c->input, nmethods + 2)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } + /* version, method */ + if ((r = sshbuf_put_u8(c->output, 0x05)) != 0 || + (r = sshbuf_put_u8(c->output, SSH_SOCKS5_NOAUTH)) != 0) { + fatal("%s: channel %d: append reply: %s", __func__, + c->self, ssh_err(r)); + } FD_SET(c->sock, writeset); c->flags |= SSH_SOCKS5_AUTHDONE; debug2("channel %d: socks5 auth done", c->self); @@ -1218,11 +1332,22 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) need++; if (have < need) return 0; - buffer_consume(&c->input, sizeof(s5_req)); - if (s5_req.atyp == SSH_SOCKS5_DOMAIN) - buffer_consume(&c->input, 1); /* host string length */ - buffer_get(&c->input, &dest_addr, addrlen); - buffer_get(&c->input, (char *)&dest_port, 2); + if ((r = sshbuf_consume(c->input, sizeof(s5_req))) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { + /* host string length */ + if ((r = sshbuf_consume(c->input, 1)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } + } + if ((r = sshbuf_get(c->input, &dest_addr, addrlen)) != 0 || + (r = sshbuf_get(c->input, &dest_port, 2)) != 0) { + debug("channel %d: parse addr/port: %s", c->self, ssh_err(r)); + return -1; + } dest_addr[addrlen] = '\0'; free(c->path); c->path = NULL; @@ -1249,22 +1374,23 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) s5_rsp.atyp = SSH_SOCKS5_IPV4; dest_port = 0; /* ignored */ - buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); - buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ - buffer_append(&c->output, &dest_port, sizeof(dest_port)); + if ((r = sshbuf_put(c->output, &s5_rsp, sizeof(s5_rsp))) != 0 || + (r = sshbuf_put_u32(c->output, ntohl(INADDR_ANY))) != 0 || + (r = sshbuf_put(c->output, &dest_port, sizeof(dest_port))) != 0) + fatal("%s: channel %d: append reply: %s", __func__, + c->self, ssh_err(r)); return 1; } Channel * -channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, - int in, int out) +channel_connect_stdio_fwd(struct ssh *ssh, + const char *host_to_connect, u_short port_to_connect, int in, int out) { Channel *c; - debug("channel_connect_stdio_fwd %s:%d", host_to_connect, - port_to_connect); + debug("%s %s:%d", __func__, host_to_connect, port_to_connect); - c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, + c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "stdio-forward", /*nonblock*/0); @@ -1273,23 +1399,24 @@ channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, c->listening_port = 0; c->force_drain = 1; - channel_register_fds(c, in, out, -1, 0, 1, 0); - port_open_helper(c, "direct-tcpip"); + channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); + port_open_helper(ssh, c, "direct-tcpip"); return c; } /* dynamic port forwarding */ static void -channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) +channel_pre_dynamic(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { - u_char *p; + const u_char *p; u_int have; int ret; - have = buffer_len(&c->input); + have = sshbuf_len(c->input); debug2("channel %d: pre_dynamic: have %d", c->self, have); - /* buffer_dump(&c->input); */ + /* sshbuf_dump(c->input, stderr); */ /* check if the fixed size part of the packet is in buffer. */ if (have < 3) { /* need more */ @@ -1297,20 +1424,21 @@ channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) return; } /* try to guess the protocol */ - p = buffer_ptr(&c->input); + p = sshbuf_ptr(c->input); + /* XXX sshbuf_peek_u8? */ switch (p[0]) { case 0x04: - ret = channel_decode_socks4(c, readset, writeset); + ret = channel_decode_socks4(ssh, c, readset, writeset); break; case 0x05: - ret = channel_decode_socks5(c, readset, writeset); + ret = channel_decode_socks5(ssh, c, readset, writeset); break; default: ret = -1; break; } if (ret < 0) { - chan_mark_dead(c); + chan_mark_dead(ssh, c); } else if (ret == 0) { debug2("channel %d: pre_dynamic: need more", c->self); /* need more */ @@ -1318,75 +1446,75 @@ channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) } else { /* switch to the next state */ c->type = SSH_CHANNEL_OPENING; - port_open_helper(c, "direct-tcpip"); + port_open_helper(ssh, c, "direct-tcpip"); } } /* This is our fake X11 server socket. */ -/* ARGSUSED */ static void -channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_x11_listener(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; - int newsock, oerrno; + int r, newsock, oerrno, remote_port; socklen_t addrlen; char buf[16384], *remote_ipaddr; - int remote_port; - if (FD_ISSET(c->sock, readset)) { - debug("X11 connection requested."); - addrlen = sizeof(addr); - newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); - if (c->single_connection) { - oerrno = errno; - debug2("single_connection: closing X11 listener."); - channel_close_fd(&c->sock); - chan_mark_dead(c); - errno = oerrno; - } - if (newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK && - errno != ECONNABORTED) - error("accept: %.100s", strerror(errno)); - if (errno == EMFILE || errno == ENFILE) - c->notbefore = monotime() + 1; - return; - } - set_nodelay(newsock); - remote_ipaddr = get_peer_ipaddr(newsock); - remote_port = get_peer_port(newsock); - snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", - remote_ipaddr, remote_port); + if (!FD_ISSET(c->sock, readset)) + return; - nc = channel_new("accepted x11 socket", - SSH_CHANNEL_OPENING, newsock, newsock, -1, - c->local_window_max, c->local_maxpacket, 0, buf, 1); - packet_start(SSH2_MSG_CHANNEL_OPEN); - packet_put_cstring("x11"); - packet_put_int(nc->self); - packet_put_int(nc->local_window_max); - packet_put_int(nc->local_maxpacket); - /* originator ipaddr and port */ - packet_put_cstring(remote_ipaddr); - if (datafellows & SSH_BUG_X11FWD) { - debug2("ssh2 x11 bug compat mode"); - } else { - packet_put_int(remote_port); - } - packet_send(); - free(remote_ipaddr); + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (c->single_connection) { + oerrno = errno; + debug2("single_connection: closing X11 listener."); + channel_close_fd(ssh, &c->sock); + chan_mark_dead(ssh, c); + errno = oerrno; } + if (newsock < 0) { + if (errno != EINTR && errno != EWOULDBLOCK && + errno != ECONNABORTED) + error("accept: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + set_nodelay(newsock); + remote_ipaddr = get_peer_ipaddr(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_ipaddr, remote_port); + + nc = channel_new(ssh, "accepted x11 socket", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, 0, buf, 1); + open_preamble(ssh, __func__, nc, "x11"); + if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } + if ((datafellows & SSH_BUG_X11FWD) != 0) + debug2("channel %d: ssh2 x11 bug compat mode", nc->self); + else if ((r = sshpkt_put_u32(ssh, remote_port)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r)); + free(remote_ipaddr); } static void -port_open_helper(Channel *c, char *rtype) +port_open_helper(struct ssh *ssh, Channel *c, char *rtype) { - char buf[1024]; char *local_ipaddr = get_local_ipaddr(c->sock); int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); + int r; if (remote_port == -1) { /* Fake addr/port to appease peers that validate it (Tectia) */ @@ -1395,44 +1523,57 @@ port_open_helper(Channel *c, char *rtype) remote_port = 65535; } - snprintf(buf, sizeof buf, + free(c->remote_name); + xasprintf(&c->remote_name, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d to %.100s port %d", rtype, c->listening_port, c->path, c->host_port, remote_ipaddr, remote_port, local_ipaddr, local_port); - free(c->remote_name); - c->remote_name = xstrdup(buf); - - packet_start(SSH2_MSG_CHANNEL_OPEN); - packet_put_cstring(rtype); - packet_put_int(c->self); - packet_put_int(c->local_window_max); - packet_put_int(c->local_maxpacket); + open_preamble(ssh, __func__, c, rtype); if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ - packet_put_cstring(c->path); - packet_put_int(c->host_port); + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || + (r = sshpkt_put_u32(ssh, c->host_port)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { /* target path */ - packet_put_cstring(c->path); + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* listen path */ - packet_put_cstring(c->path); + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } else { /* listen address, port */ - packet_put_cstring(c->path); - packet_put_int(local_port); + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || + (r = sshpkt_put_u32(ssh, local_port)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* reserved for future owner/mode info */ - packet_put_cstring(""); + if ((r = sshpkt_put_cstring(ssh, "")) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } else { /* originator host and port */ - packet_put_cstring(remote_ipaddr); - packet_put_int((u_int)remote_port); + if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) { + fatal("%s: channel %i: reply %s", __func__, + c->self, ssh_err(r)); + } } - packet_send(); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r)); free(remote_ipaddr); free(local_ipaddr); } @@ -1451,17 +1592,17 @@ channel_set_reuseaddr(int fd) } void -channel_set_x11_refuse_time(u_int refuse_time) +channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) { - x11_refuse_time = refuse_time; + ssh->chanctxt->x11_refuse_time = refuse_time; } /* * This socket is listening for connections to a forwarded TCP/IP port. */ -/* ARGSUSED */ static void -channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_port_listener(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; @@ -1469,336 +1610,387 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) socklen_t addrlen; char *rtype; - 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); + if (!FD_ISSET(c->sock, readset)) + return; - if (c->type == SSH_CHANNEL_RPORT_LISTENER) { - nextstate = SSH_CHANNEL_OPENING; - rtype = "forwarded-tcpip"; - } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { - nextstate = SSH_CHANNEL_OPENING; - rtype = "forwarded-streamlocal@openssh.com"; - } else if (c->host_port == PORT_STREAMLOCAL) { - nextstate = SSH_CHANNEL_OPENING; - rtype = "direct-streamlocal@openssh.com"; - } else if (c->host_port == 0) { - nextstate = SSH_CHANNEL_DYNAMIC; - rtype = "dynamic-tcpip"; - } else { - nextstate = SSH_CHANNEL_OPENING; - rtype = "direct-tcpip"; - } + 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, (struct sockaddr *)&addr, &addrlen); - if (newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK && - errno != ECONNABORTED) - error("accept: %.100s", strerror(errno)); - if (errno == EMFILE || errno == ENFILE) - c->notbefore = monotime() + 1; - return; - } - if (c->host_port != PORT_STREAMLOCAL) - set_nodelay(newsock); - nc = channel_new(rtype, nextstate, newsock, newsock, -1, - c->local_window_max, c->local_maxpacket, 0, rtype, 1); - nc->listening_port = c->listening_port; - nc->host_port = c->host_port; - if (c->path != NULL) - nc->path = xstrdup(c->path); - - if (nextstate != SSH_CHANNEL_DYNAMIC) - port_open_helper(nc, rtype); + if (c->type == SSH_CHANNEL_RPORT_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-tcpip"; + } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-streamlocal@openssh.com"; + } else if (c->host_port == PORT_STREAMLOCAL) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; + } else if (c->host_port == 0) { + nextstate = SSH_CHANNEL_DYNAMIC; + rtype = "dynamic-tcpip"; + } else { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-tcpip"; } + + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (newsock < 0) { + if (errno != EINTR && errno != EWOULDBLOCK && + errno != ECONNABORTED) + error("accept: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + if (c->host_port != PORT_STREAMLOCAL) + set_nodelay(newsock); + nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, 0, rtype, 1); + nc->listening_port = c->listening_port; + nc->host_port = c->host_port; + if (c->path != NULL) + nc->path = xstrdup(c->path); + + if (nextstate != SSH_CHANNEL_DYNAMIC) + port_open_helper(ssh, nc, rtype); } /* * This is the authentication agent socket listening for connections from * clients. */ -/* ARGSUSED */ static void -channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_auth_listener(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { Channel *nc; - int newsock; + int r, newsock; struct sockaddr_storage addr; socklen_t addrlen; - if (FD_ISSET(c->sock, readset)) { - addrlen = sizeof(addr); - newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); - if (newsock < 0) { - error("accept from auth socket: %.100s", - strerror(errno)); - if (errno == EMFILE || errno == ENFILE) - c->notbefore = monotime() + 1; - return; - } - nc = channel_new("accepted auth socket", - SSH_CHANNEL_OPENING, newsock, newsock, -1, - c->local_window_max, c->local_maxpacket, - 0, "accepted auth socket", 1); - packet_start(SSH2_MSG_CHANNEL_OPEN); - packet_put_cstring("auth-agent@openssh.com"); - packet_put_int(nc->self); - packet_put_int(c->local_window_max); - packet_put_int(c->local_maxpacket); - packet_send(); + if (!FD_ISSET(c->sock, readset)) + return; + + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (newsock < 0) { + error("accept from auth socket: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; } + nc = channel_new(ssh, "accepted auth socket", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, "accepted auth socket", 1); + open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); } -/* ARGSUSED */ static void -channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_connecting(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { - int err = 0, sock; + int err = 0, sock, r; socklen_t sz = sizeof(err); - if (FD_ISSET(c->sock, writeset)) { - if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { - err = errno; - error("getsockopt SO_ERROR failed"); - } - if (err == 0) { - debug("channel %d: connected to %s port %d", - c->self, c->connect_ctx.host, c->connect_ctx.port); - channel_connect_ctx_free(&c->connect_ctx); - c->type = SSH_CHANNEL_OPEN; - packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(c->remote_id); - packet_put_int(c->self); - packet_put_int(c->local_window); - packet_put_int(c->local_maxpacket); - } else { - debug("channel %d: connection failed: %s", - c->self, strerror(err)); - /* Try next address, if any */ - if ((sock = connect_next(&c->connect_ctx)) > 0) { - close(c->sock); - c->sock = c->rfd = c->wfd = sock; - channel_max_fd = channel_find_maxfd(); - return; - } - /* Exhausted all addresses */ - error("connect_to %.100s port %d: failed.", - c->connect_ctx.host, c->connect_ctx.port); - channel_connect_ctx_free(&c->connect_ctx); - packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(c->remote_id); - packet_put_int(SSH2_OPEN_CONNECT_FAILED); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_cstring(strerror(err)); - packet_put_cstring(""); - } - chan_mark_dead(c); - } - packet_send(); + if (!FD_ISSET(c->sock, writeset)) + return; + + if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { + err = errno; + error("getsockopt SO_ERROR failed"); } + if (err == 0) { + debug("channel %d: connected to %s port %d", + c->self, c->connect_ctx.host, c->connect_ctx.port); + channel_connect_ctx_free(&c->connect_ctx); + c->type = SSH_CHANNEL_OPEN; + if ((r = sshpkt_start(ssh, + SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { + fatal("%s: channel %i: confirm: %s", __func__, + c->self, ssh_err(r)); + } + } else { + debug("channel %d: connection failed: %s", + c->self, strerror(err)); + /* Try next address, if any */ + if ((sock = connect_next(&c->connect_ctx)) > 0) { + close(c->sock); + c->sock = c->rfd = c->wfd = sock; + channel_find_maxfd(ssh->chanctxt); + return; + } + /* Exhausted all addresses */ + error("connect_to %.100s port %d: failed.", + c->connect_ctx.host, c->connect_ctx.port); + channel_connect_ctx_free(&c->connect_ctx); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED)) != 0) { + fatal("%s: channel %i: failure: %s", __func__, + c->self, ssh_err(r)); + } + if ((datafellows & SSH_BUG_OPENFAILURE) == 0 && + ((r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0)) { + fatal("%s: channel %i: failure: %s", __func__, + c->self, ssh_err(r)); + } + chan_mark_dead(ssh, c); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); } -/* ARGSUSED */ static int -channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) +channel_handle_rfd(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; - int len, force; + ssize_t len; + int r, force; force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; - if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { - errno = 0; - len = read(c->rfd, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || - ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) - return 1; + + if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset))) + return 1; + + errno = 0; + len = read(c->rfd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || + ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) + return 1; #ifndef PTY_ZEROREAD - if (len <= 0) { + if (len <= 0) { #else - if ((!c->isatty && len <= 0) || - (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { + if ((!c->isatty && len <= 0) || + (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { #endif - debug2("channel %d: read<=0 rfd %d len %d", - c->self, c->rfd, len); - if (c->type != SSH_CHANNEL_OPEN) { - debug2("channel %d: not open", c->self); - chan_mark_dead(c); - return -1; - } else { - chan_read_failed(c); - } + debug2("channel %d: read<=0 rfd %d len %zd", + c->self, c->rfd, len); + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(ssh, c); return -1; - } - if (c->input_filter != NULL) { - if (c->input_filter(c, buf, len) == -1) { - debug2("channel %d: filter stops", c->self); - chan_read_failed(c); - } - } else if (c->datagram) { - buffer_put_string(&c->input, buf, len); } else { - buffer_append(&c->input, buf, len); + chan_read_failed(ssh, c); } + return -1; + } + if (c->input_filter != NULL) { + if (c->input_filter(ssh, c, buf, len) == -1) { + debug2("channel %d: filter stops", c->self); + chan_read_failed(ssh, c); + } + } else if (c->datagram) { + if ((r = sshbuf_put_string(c->input, buf, len)) != 0) + fatal("%s: channel %d: put datagram: %s", __func__, + c->self, ssh_err(r)); + } else if ((r = sshbuf_put(c->input, buf, len)) != 0) { + fatal("%s: channel %d: put data: %s", __func__, + c->self, ssh_err(r)); } return 1; } -/* ARGSUSED */ static int -channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) +channel_handle_wfd(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { struct termios tio; - u_char *data = NULL, *buf; - u_int dlen, olen = 0; - int len; + u_char *data = NULL, *buf; /* XXX const; need filter API change */ + size_t dlen, olen = 0; + int r, len; + + if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || + sshbuf_len(c->output) == 0) + return 1; /* Send buffered output data to the socket. */ - if (c->wfd != -1 && - FD_ISSET(c->wfd, writeset) && - buffer_len(&c->output) > 0) { - olen = buffer_len(&c->output); - if (c->output_filter != NULL) { - if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { - debug2("channel %d: filter stops", c->self); - if (c->type != SSH_CHANNEL_OPEN) - chan_mark_dead(c); - else - chan_write_failed(c); - return -1; - } - } else if (c->datagram) { - buf = data = buffer_get_string(&c->output, &dlen); - } else { - buf = data = buffer_ptr(&c->output); - dlen = buffer_len(&c->output); - } - - if (c->datagram) { - /* ignore truncated writes, datagrams might get lost */ - len = write(c->wfd, buf, dlen); - free(data); - if (len < 0 && (errno == EINTR || errno == EAGAIN || - errno == EWOULDBLOCK)) - return 1; - if (len <= 0) { - if (c->type != SSH_CHANNEL_OPEN) - chan_mark_dead(c); - else - chan_write_failed(c); - return -1; - } - goto out; - } -#ifdef _AIX - /* XXX: Later AIX versions can't push as much data to tty */ - if (c->wfd_isatty) - dlen = MIN(dlen, 8*1024); -#endif - - len = write(c->wfd, buf, dlen); - if (len < 0 && - (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) - return 1; - if (len <= 0) { - if (c->type != SSH_CHANNEL_OPEN) { - debug2("channel %d: not open", c->self); - chan_mark_dead(c); - return -1; - } else { - chan_write_failed(c); - } + olen = sshbuf_len(c->output); + if (c->output_filter != NULL) { + if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { + debug2("channel %d: filter stops", c->self); + if (c->type != SSH_CHANNEL_OPEN) + chan_mark_dead(ssh, c); + else + chan_write_failed(ssh, c); return -1; } -#ifndef BROKEN_TCGETATTR_ICANON - if (c->isatty && dlen >= 1 && buf[0] != '\r') { - if (tcgetattr(c->wfd, &tio) == 0 && - !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { - /* - * Simulate echo to reduce the impact of - * traffic analysis. We need to match the - * size of a SSH2_MSG_CHANNEL_DATA message - * (4 byte channel id + buf) - */ - packet_send_ignore(4 + len); - packet_send(); - } - } + } else if (c->datagram) { + if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) + fatal("%s: channel %d: get datagram: %s", __func__, + c->self, ssh_err(r)); + } else { + buf = data = sshbuf_mutable_ptr(c->output); + dlen = sshbuf_len(c->output); + } + + if (c->datagram) { + /* ignore truncated writes, datagrams might get lost */ + len = write(c->wfd, data, dlen); + free(data); + if (len < 0 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) + return 1; + if (len <= 0) + goto write_fail; + goto out; + } + +#ifdef _AIX + /* XXX: Later AIX versions can't push as much data to tty */ + if (c->wfd_isatty) + dlen = MIN(dlen, 8*1024); #endif - buffer_consume(&c->output, len); + + len = write(c->wfd, buf, dlen); + if (len < 0 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) + return 1; + if (len <= 0) { + write_fail: + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(ssh, c); + return -1; + } else { + chan_write_failed(ssh, c); + } + return -1; + } +#ifndef BROKEN_TCGETATTR_ICANON + if (c->isatty && dlen >= 1 && buf[0] != '\r') { + if (tcgetattr(c->wfd, &tio) == 0 && + !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { + /* + * Simulate echo to reduce the impact of + * traffic analysis. We need to match the + * size of a SSH2_MSG_CHANNEL_DATA message + * (4 byte channel id + buf) + */ + if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %d: ignore: %s", + __func__, c->self, ssh_err(r)); + } + } +#endif /* BROKEN_TCGETATTR_ICANON */ + if ((r = sshbuf_consume(c->output, len)) != 0) { + fatal("%s: channel %d: consume: %s", + __func__, c->self, ssh_err(r)); } out: - if (olen > 0) - c->local_consumed += olen - buffer_len(&c->output); + c->local_consumed += olen - sshbuf_len(c->output); + return 1; } static int -channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) +channel_handle_efd_write(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) +{ + int r; + ssize_t len; + + if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0) + return 1; + + len = write(c->efd, sshbuf_ptr(c->extended), + sshbuf_len(c->extended)); + debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); + if (len < 0 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) + return 1; + if (len <= 0) { + debug2("channel %d: closing write-efd %d", c->self, c->efd); + channel_close_fd(ssh, &c->efd); + } else { + if ((r = sshbuf_consume(c->extended, len)) != 0) { + fatal("%s: channel %d: consume: %s", + __func__, c->self, ssh_err(r)); + } + c->local_consumed += len; + } + return 1; +} + +static int +channel_handle_efd_read(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; - int len; + int r; + ssize_t len; -/** XXX handle drain efd, too */ - if (c->efd != -1) { - if (c->extended_usage == CHAN_EXTENDED_WRITE && - FD_ISSET(c->efd, writeset) && - buffer_len(&c->extended) > 0) { - len = write(c->efd, buffer_ptr(&c->extended), - buffer_len(&c->extended)); - debug2("channel %d: written %d to efd %d", - c->self, len, c->efd); - if (len < 0 && (errno == EINTR || errno == EAGAIN || - errno == EWOULDBLOCK)) - return 1; - if (len <= 0) { - debug2("channel %d: closing write-efd %d", - c->self, c->efd); - channel_close_fd(&c->efd); - } else { - buffer_consume(&c->extended, len); - c->local_consumed += len; - } - } else if (c->efd != -1 && - (c->extended_usage == CHAN_EXTENDED_READ || - c->extended_usage == CHAN_EXTENDED_IGNORE) && - (c->detach_close || FD_ISSET(c->efd, readset))) { - len = read(c->efd, buf, sizeof(buf)); - debug2("channel %d: read %d from efd %d", - c->self, len, c->efd); - if (len < 0 && (errno == EINTR || ((errno == EAGAIN || - errno == EWOULDBLOCK) && !c->detach_close))) - return 1; - if (len <= 0) { - debug2("channel %d: closing read-efd %d", - c->self, c->efd); - channel_close_fd(&c->efd); - } else { - if (c->extended_usage == CHAN_EXTENDED_IGNORE) { - debug3("channel %d: discard efd", - c->self); - } else - buffer_append(&c->extended, buf, len); - } + if (!c->detach_close && !FD_ISSET(c->efd, readset)) + return 1; + + len = read(c->efd, buf, sizeof(buf)); + debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); + if (len < 0 && (errno == EINTR || ((errno == EAGAIN || + errno == EWOULDBLOCK) && !c->detach_close))) + return 1; + if (len <= 0) { + debug2("channel %d: closing read-efd %d", + c->self, c->efd); + channel_close_fd(ssh, &c->efd); + } else { + if (c->extended_usage == CHAN_EXTENDED_IGNORE) { + debug3("channel %d: discard efd", + c->self); + } else if ((r = sshbuf_put(c->extended, buf, len)) != 0) { + fatal("%s: channel %d: append: %s", + __func__, c->self, ssh_err(r)); } } return 1; } static int -channel_check_window(Channel *c) +channel_handle_efd(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { + if (c->efd == -1) + return 1; + + /** XXX handle drain efd, too */ + + if (c->extended_usage == CHAN_EXTENDED_WRITE) + return channel_handle_efd_write(ssh, c, readset, writeset); + else if (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) + return channel_handle_efd_read(ssh, c, readset, writeset); + + return 1; +} + +static int +channel_check_window(struct ssh *ssh, Channel *c) +{ + int r; + if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && ((c->local_window_max - c->local_window > c->local_maxpacket*3) || c->local_window < c->local_window_max/2) && c->local_consumed > 0) { - packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); - packet_put_int(c->remote_id); - packet_put_int(c->local_consumed); - packet_send(); + if ((r = sshpkt_start(ssh, + SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: %s", __func__, + c->self, ssh_err(r)); + } debug2("channel %d: window %d sent adjust %d", c->self, c->local_window, c->local_consumed); @@ -1809,85 +2001,112 @@ channel_check_window(Channel *c) } static void -channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_open(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { - channel_handle_rfd(c, readset, writeset); - channel_handle_wfd(c, readset, writeset); - channel_handle_efd(c, readset, writeset); - channel_check_window(c); + channel_handle_rfd(ssh, c, readset, writeset); + channel_handle_wfd(ssh, c, readset, writeset); + channel_handle_efd(ssh, c, readset, writeset); + channel_check_window(ssh, c); } static u_int -read_mux(Channel *c, u_int need) +read_mux(struct ssh *ssh, Channel *c, u_int need) { char buf[CHAN_RBUF]; - int len; + ssize_t len; u_int rlen; + int r; - if (buffer_len(&c->input) < need) { - rlen = need - buffer_len(&c->input); + if (sshbuf_len(c->input) < need) { + rlen = need - sshbuf_len(c->input); len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); if (len < 0 && (errno == EINTR || errno == EAGAIN)) - return buffer_len(&c->input); + return sshbuf_len(c->input); if (len <= 0) { - debug2("channel %d: ctl read<=0 rfd %d len %d", + debug2("channel %d: ctl read<=0 rfd %d len %zd", c->self, c->rfd, len); - chan_read_failed(c); + chan_read_failed(ssh, c); return 0; - } else - buffer_append(&c->input, buf, len); + } else if ((r = sshbuf_put(c->input, buf, len)) != 0) { + fatal("%s: channel %d: append: %s", + __func__, c->self, ssh_err(r)); + } } - return buffer_len(&c->input); + return sshbuf_len(c->input); } static void -channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_mux_client_read(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { u_int need; - ssize_t len; - if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && - (c->istate == CHAN_INPUT_OPEN || - c->istate == CHAN_INPUT_WAIT_DRAIN)) { - /* - * Don't not read past the precise end of packets to - * avoid disrupting fd passing. - */ - if (read_mux(c, 4) < 4) /* read header */ - return; - need = get_u32(buffer_ptr(&c->input)); + if (c->rfd == -1 || !FD_ISSET(c->rfd, readset)) + return; + if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) + return; + if (c->mux_pause) + return; + + /* + * Don't not read past the precise end of packets to + * avoid disrupting fd passing. + */ + if (read_mux(ssh, c, 4) < 4) /* read header */ + return; + /* XXX sshbuf_peek_u32 */ + need = PEEK_U32(sshbuf_ptr(c->input)); #define CHANNEL_MUX_MAX_PACKET (256 * 1024) - if (need > CHANNEL_MUX_MAX_PACKET) { - debug2("channel %d: packet too big %u > %u", - c->self, CHANNEL_MUX_MAX_PACKET, need); - chan_rcvd_oclose(c); - return; - } - if (read_mux(c, need + 4) < need + 4) /* read body */ - return; - if (c->mux_rcb(c) != 0) { - debug("channel %d: mux_rcb failed", c->self); - chan_mark_dead(c); - return; - } + if (need > CHANNEL_MUX_MAX_PACKET) { + debug2("channel %d: packet too big %u > %u", + c->self, CHANNEL_MUX_MAX_PACKET, need); + chan_rcvd_oclose(ssh, c); + return; } - - 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 && (errno == EINTR || errno == EAGAIN)) - return; - if (len <= 0) { - chan_mark_dead(c); - return; - } - buffer_consume(&c->output, len); + if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ + return; + if (c->mux_rcb(ssh, c) != 0) { + debug("channel %d: mux_rcb failed", c->self); + chan_mark_dead(ssh, c); + return; } } static void -channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) +channel_post_mux_client_write(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) +{ + ssize_t len; + int r; + + if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || + sshbuf_len(c->output) == 0) + return; + + len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return; + if (len <= 0) { + chan_mark_dead(ssh, c); + return; + } + if ((r = sshbuf_consume(c->output, len)) != 0) + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); +} + +static void +channel_post_mux_client(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) +{ + channel_post_mux_client_read(ssh, c, readset, writeset); + channel_post_mux_client_write(ssh, c, readset, writeset); +} + +static void +channel_post_mux_listener(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; @@ -1926,97 +2145,98 @@ channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) close(newsock); return; } - nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, + nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "mux-control", 1); nc->mux_rcb = c->mux_rcb; - debug3("%s: new mux channel %d fd %d", __func__, - nc->self, nc->sock); + debug3("%s: new mux channel %d fd %d", __func__, nc->self, nc->sock); /* establish state */ - nc->mux_rcb(nc); + nc->mux_rcb(ssh, nc); /* mux state transitions must not elicit protocol messages */ nc->flags |= CHAN_LOCAL; } static void -channel_handler_init(void) +channel_handler_init(struct ssh_channels *sc) { - int i; + chan_fn **pre, **post; - for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { - channel_pre[i] = NULL; - channel_post[i] = NULL; - } - channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; - channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; - channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; - channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; - channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; - channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; + if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || + (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) + fatal("%s: allocation failed", __func__); - channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; - channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; - channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; - channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; - channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; - channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; - channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; - channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; - channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; - channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; - channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; + pre[SSH_CHANNEL_OPEN] = &channel_pre_open; + pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; + pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; + pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; + + post[SSH_CHANNEL_OPEN] = &channel_post_open; + post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; + post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; + post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; + post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; + + sc->channel_pre = pre; + sc->channel_post = post; } /* gc dead channels */ static void -channel_garbage_collect(Channel *c) +channel_garbage_collect(struct ssh *ssh, Channel *c) { if (c == NULL) return; if (c->detach_user != NULL) { - if (!chan_is_dead(c, c->detach_close)) + if (!chan_is_dead(ssh, c, c->detach_close)) return; debug2("channel %d: gc: notify user", c->self); - c->detach_user(c->self, NULL); + c->detach_user(ssh, c->self, NULL); /* if we still have a callback */ if (c->detach_user != NULL) return; debug2("channel %d: gc: user detached", c->self); } - if (!chan_is_dead(c, 1)) + if (!chan_is_dead(ssh, c, 1)) return; debug2("channel %d: garbage collecting", c->self); - channel_free(c); + channel_free(ssh, c); } +enum channel_table { CHAN_PRE, CHAN_POST }; + static void -channel_handler(struct ssh *ssh, chan_fn *ftab[], +channel_handler(struct ssh *ssh, int table, fd_set *readset, fd_set *writeset, time_t *unpause_secs) { - static int did_init = 0; + struct ssh_channels *sc = ssh->chanctxt; + chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; u_int i, oalloc; Channel *c; time_t now; - if (!did_init) { - channel_handler_init(); - did_init = 1; - } now = monotime(); if (unpause_secs != NULL) *unpause_secs = 0; - for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { - c = channels[i]; + for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { + c = sc->channels[i]; if (c == NULL) continue; if (c->delayed) { - if (ftab == channel_pre) + if (table == CHAN_PRE) c->delayed = 0; else continue; @@ -2026,7 +2246,7 @@ channel_handler(struct ssh *ssh, chan_fn *ftab[], * Run handlers that are not paused. */ if (c->notbefore <= now) - (*ftab[c->type])(c, readset, writeset); + (*ftab[c->type])(ssh, c, readset, writeset); else if (unpause_secs != NULL) { /* * Collect the time that the earliest @@ -2040,7 +2260,7 @@ channel_handler(struct ssh *ssh, chan_fn *ftab[], *unpause_secs = c->notbefore - now; } } - channel_garbage_collect(c); + channel_garbage_collect(ssh, c); } if (unpause_secs != NULL && *unpause_secs != 0) debug3("%s: first channel unpauses in %d seconds", @@ -2057,7 +2277,7 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, { u_int n, sz, nfdset; - n = MAXIMUM(*maxfdp, channel_max_fd); + n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd); nfdset = howmany(n+1, NFDBITS); /* Explicitly test here, because xrealloc isn't always called */ @@ -2076,7 +2296,7 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, memset(*writesetp, 0, sz); if (!ssh_packet_is_rekeying(ssh)) - channel_handler(ssh, channel_pre, *readsetp, *writesetp, + channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp, minwait_secs); } @@ -2087,19 +2307,128 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, void channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset) { - channel_handler(ssh, channel_post, readset, writeset, NULL); + channel_handler(ssh, CHAN_POST, readset, writeset, NULL); } +/* + * Enqueue data for channels with open or draining c->input. + */ +static void +channel_output_poll_input_open(struct ssh *ssh, Channel *c) +{ + size_t len, dlen; + int r; + + if ((len = sshbuf_len(c->input)) == 0) { + if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + /* + * input-buffer is empty and read-socket shutdown: + * tell peer, that we will not send more data: + * send IEOF. + * hack for extended data: delay EOF if EFD still + * in use. + */ + if (CHANNEL_EFD_INPUT_ACTIVE(c)) + debug2("channel %d: " + "ibuf_empty delayed efd %d/(%zu)", + c->self, c->efd, sshbuf_len(c->extended)); + else + chan_ibuf_empty(ssh, c); + } + return; + } + + if (c->datagram) { + /* Check datagram will fit; drop if not */ + if ((r = sshbuf_peek_string_direct(c->input, NULL, &dlen)) != 0) + fatal("%s: channel %d: peek datagram: %s", __func__, + c->self, ssh_err(r)); + /* + * XXX this does tail-drop on the datagram queue which is + * usually suboptimal compared to head-drop. Better to have + * backpressure at read time? (i.e. read + discard) + */ + if (dlen > c->remote_window || dlen > c->remote_maxpacket) { + debug("channel %d: datagram too big", c->self); + return; + } + /* Enqueue it */ + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_stringb(ssh, c->input)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: datagram: %s", __func__, + c->self, ssh_err(r)); + } + c->remote_window -= dlen; + return; + } + + /* Enqueue packet for buffered data. */ + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + if (len == 0) + return; + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: data: %s", __func__, + c->self, ssh_err(r)); + } + if ((r = sshbuf_consume(c->input, len)) != 0) + fatal("%s: channel %i: consume: %s", __func__, + c->self, ssh_err(r)); + c->remote_window -= len; +} + +/* + * Enqueue data for channels with open c->extended in read mode. + */ +static void +channel_output_poll_extended_read(struct ssh *ssh, Channel *c) +{ + size_t len; + int r; + + if ((len = sshbuf_len(c->extended)) == 0) + return; + + debug2("channel %d: rwin %u elen %zu euse %d", c->self, + c->remote_window, sshbuf_len(c->extended), c->extended_usage); + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + if (len == 0) + return; + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || + (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: data: %s", __func__, + c->self, ssh_err(r)); + } + if ((r = sshbuf_consume(c->extended, len)) != 0) + fatal("%s: channel %i: consume: %s", __func__, + c->self, ssh_err(r)); + c->remote_window -= len; + debug2("channel %d: sent ext data %zu", c->self, len); +} /* If there is data to send to the connection, enqueue some of it now. */ void -channel_output_poll(void) +channel_output_poll(struct ssh *ssh) { + struct ssh_channels *sc = ssh->chanctxt; Channel *c; - u_int i, len; + u_int i; - 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) continue; @@ -2111,87 +2440,19 @@ channel_output_poll(void) continue; if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { /* XXX is this true? */ - debug3("channel %d: will not send data after close", c->self); + debug3("channel %d: will not send data after close", + c->self); continue; } /* Get the amount of buffered data for this channel. */ - if ((c->istate == CHAN_INPUT_OPEN || - c->istate == CHAN_INPUT_WAIT_DRAIN) && - (len = buffer_len(&c->input)) > 0) { - if (c->datagram) { - if (len > 0) { - u_char *data; - u_int dlen; - - data = buffer_get_string(&c->input, - &dlen); - if (dlen > c->remote_window || - dlen > c->remote_maxpacket) { - debug("channel %d: datagram " - "too big for channel", - c->self); - free(data); - continue; - } - packet_start(SSH2_MSG_CHANNEL_DATA); - packet_put_int(c->remote_id); - packet_put_string(data, dlen); - packet_send(); - c->remote_window -= dlen; - free(data); - } - continue; - } - /* - * Send some data for the other side over the secure - * connection. - */ - if (len > c->remote_window) - len = c->remote_window; - if (len > c->remote_maxpacket) - len = c->remote_maxpacket; - if (len > 0) { - packet_start(SSH2_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; - } - } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { - /* - * input-buffer is empty and read-socket shutdown: - * tell peer, that we will not send more data: send IEOF. - * hack for extended data: delay EOF if EFD still in use. - */ - if (CHANNEL_EFD_INPUT_ACTIVE(c)) - debug2("channel %d: ibuf_empty delayed efd %d/(%d)", - c->self, c->efd, buffer_len(&c->extended)); - else - chan_ibuf_empty(c); - } + if (c->istate == CHAN_INPUT_OPEN || + c->istate == CHAN_INPUT_WAIT_DRAIN) + channel_output_poll_input_open(ssh, c); /* Send extended data, i.e. stderr */ if (!(c->flags & CHAN_EOF_SENT) && - c->remote_window > 0 && - (len = buffer_len(&c->extended)) > 0 && - c->extended_usage == CHAN_EXTENDED_READ) { - debug2("channel %d: rwin %u elen %u euse %d", - c->self, c->remote_window, buffer_len(&c->extended), - c->extended_usage); - if (len > c->remote_window) - len = c->remote_window; - if (len > c->remote_maxpacket) - len = c->remote_maxpacket; - packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); - packet_put_int(c->remote_id); - packet_put_int(SSH2_EXTENDED_DATA_STDERR); - packet_put_string(buffer_ptr(&c->extended), len); - packet_send(); - buffer_consume(&c->extended, len); - c->remote_window -= len; - debug2("channel %d: sent ext data %d", c->self, len); - } + c->extended_usage == CHAN_EXTENDED_READ) + channel_output_poll_extended_read(ssh, c); } } @@ -2236,10 +2497,9 @@ channel_output_poll(void) * on channel creation. */ int -channel_proxy_downstream(Channel *downstream) +channel_proxy_downstream(struct ssh *ssh, Channel *downstream) { Channel *c = NULL; - struct ssh *ssh = active_state; struct sshbuf *original = NULL, *modified = NULL; const u_char *cp; char *ctype = NULL, *listen_host = NULL; @@ -2248,8 +2508,8 @@ channel_proxy_downstream(Channel *downstream) int ret = -1, r, idx; u_int id, remote_id, listen_port; - /* sshbuf_dump(&downstream->input, stderr); */ - if ((r = sshbuf_get_string_direct(&downstream->input, &cp, &have)) + /* sshbuf_dump(downstream->input, stderr); */ + if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) != 0) { error("%s: malformed message: %s", __func__, ssh_err(r)); return -1; @@ -2278,7 +2538,7 @@ channel_proxy_downstream(Channel *downstream) error("%s: parse error %s", __func__, ssh_err(r)); goto out; } - c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, + c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, ctype, 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; /* original downstream id */ @@ -2286,7 +2546,7 @@ channel_proxy_downstream(Channel *downstream) (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error("%s: compose error %s", __func__, ssh_err(r)); - channel_free(c); + channel_free(ssh, c); goto out; } break; @@ -2305,7 +2565,7 @@ channel_proxy_downstream(Channel *downstream) error("%s: parse error %s", __func__, ssh_err(r)); goto out; } - c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, + c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; @@ -2314,7 +2574,7 @@ channel_proxy_downstream(Channel *downstream) (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error("%s: compose error %s", __func__, ssh_err(r)); - channel_free(c); + channel_free(ssh, c); goto out; } break; @@ -2343,23 +2603,17 @@ channel_proxy_downstream(Channel *downstream) goto out; } /* Record that connection to this host/port is permitted. */ - permitted_opens = xreallocarray(permitted_opens, - num_permitted_opens + 1, sizeof(*permitted_opens)); - idx = num_permitted_opens++; - permitted_opens[idx].host_to_connect = xstrdup(""); - permitted_opens[idx].port_to_connect = -1; - permitted_opens[idx].listen_host = listen_host; - permitted_opens[idx].listen_port = (int)listen_port; - permitted_opens[idx].downstream = downstream; + idx = fwd_perm_list_add(ssh, FWDPERM_USER, "", -1, + listen_host, NULL, (int)listen_port, downstream); listen_host = NULL; break; case SSH2_MSG_CHANNEL_CLOSE: if (have < 4) break; remote_id = PEEK_U32(cp); - if ((c = channel_by_remote_id(remote_id)) != NULL) { + if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { if (c->flags & CHAN_CLOSE_RCVD) - channel_free(c); + channel_free(ssh, c); else c->flags |= CHAN_CLOSE_SENT; } @@ -2446,7 +2700,7 @@ channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) (r = sshbuf_put_u8(b, type)) != 0 || (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || (r = sshbuf_put(b, cp, len)) != 0 || - (r = sshbuf_put_stringb(&downstream->output, b)) != 0) { + (r = sshbuf_put_stringb(downstream->output, b)) != 0) { error("%s: compose for muxclient %s", __func__, ssh_err(r)); goto out; } @@ -2464,7 +2718,7 @@ channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) break; case SSH2_MSG_CHANNEL_CLOSE: if (c->flags & CHAN_CLOSE_SENT) - channel_free(c); + channel_free(ssh, c); else c->flags |= CHAN_CLOSE_RCVD; break; @@ -2475,20 +2729,46 @@ channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) /* -- protocol input */ -/* ARGSUSED */ +/* Parse a channel ID from the current packet */ +static int +channel_parse_id(struct ssh *ssh, const char *where, const char *what) +{ + u_int32_t id; + int r; + + if ((r = sshpkt_get_u32(ssh, &id)) != 0) { + error("%s: parse id: %s", where, ssh_err(r)); + ssh_packet_disconnect(ssh, "Invalid %s message", what); + } + if (id > INT_MAX) { + error("%s: bad channel id %u: %s", where, id, ssh_err(r)); + ssh_packet_disconnect(ssh, "Invalid %s channel id", what); + } + return (int)id; +} + +/* Lookup a channel from an ID in the current packet */ +static Channel * +channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) +{ + int id = channel_parse_id(ssh, where, what); + Channel *c; + + if ((c = channel_lookup(ssh, id)) == NULL) { + ssh_packet_disconnect(ssh, + "%s packet referred to nonexistent channel %d", what, id); + } + return c; +} + int channel_input_data(int type, u_int32_t seq, struct ssh *ssh) { - int id; const u_char *data; - u_int data_len, win_len; - Channel *c; + size_t data_len, win_len; + Channel *c = channel_from_packet_id(ssh, __func__, "data"); + int r; - /* Get the channel number and verify it. */ - id = packet_get_int(); - c = channel_lookup(id); - if (c == NULL) - packet_disconnect("Received data for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; @@ -2498,17 +2778,19 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh) return 0; /* Get the data. */ - data = packet_get_string_ptr(&data_len); + if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) + fatal("%s: channel %d: get data: %s", __func__, + c->self, ssh_err(r)); + ssh_packet_check_eom(ssh); + win_len = data_len; if (c->datagram) win_len += 4; /* string length header */ /* - * Ignore data for protocol > 1.3 if output end is no longer open. - * For protocol 2 the sending side is reducing its window as it sends - * data, so we must 'fake' consumption of the data in order to ensure - * that window updates are sent back. Otherwise the connection might - * deadlock. + * The sending side reduces its window as it sends data, so we + * must 'fake' consumption of the data in order to ensure that window + * updates are sent back. Otherwise the connection might deadlock. */ if (c->ostate != CHAN_OUTPUT_OPEN) { c->local_window -= win_len; @@ -2517,149 +2799,148 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh) } if (win_len > c->local_maxpacket) { - logit("channel %d: rcvd big packet %d, maxpack %d", + logit("channel %d: rcvd big packet %zu, maxpack %u", c->self, win_len, c->local_maxpacket); + return 0; } if (win_len > c->local_window) { - logit("channel %d: rcvd too much data %d, win %d", + logit("channel %d: rcvd too much data %zu, win %u", c->self, win_len, c->local_window); return 0; } c->local_window -= win_len; - if (c->datagram) - buffer_put_string(&c->output, data, data_len); - else - buffer_append(&c->output, data, data_len); - packet_check_eom(); + if (c->datagram) { + if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) + fatal("%s: channel %d: append datagram: %s", + __func__, c->self, ssh_err(r)); + } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) + fatal("%s: channel %d: append data: %s", + __func__, c->self, ssh_err(r)); + return 0; } -/* ARGSUSED */ int channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) { - int id; - char *data; - u_int data_len, tcode; - Channel *c; + const u_char *data; + size_t data_len; + u_int32_t tcode; + Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); + int r; - /* Get the channel number and verify it. */ - id = packet_get_int(); - c = channel_lookup(id); - - if (c == NULL) - packet_disconnect("Received extended_data for bad channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (c->type != SSH_CHANNEL_OPEN) { - logit("channel %d: ext data for non open", id); + logit("channel %d: ext data for non open", c->self); return 0; } if (c->flags & CHAN_EOF_RCVD) { if (datafellows & SSH_BUG_EXTEOF) - debug("channel %d: accepting ext data after eof", id); + debug("channel %d: accepting ext data after eof", + c->self); else - packet_disconnect("Received extended_data after EOF " - "on channel %d.", id); + ssh_packet_disconnect(ssh, "Received extended_data " + "after EOF on channel %d.", c->self); + } + + if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { + error("%s: parse tcode: %s", __func__, ssh_err(r)); + ssh_packet_disconnect(ssh, "Invalid extended_data message"); } - tcode = packet_get_int(); if (c->efd == -1 || c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); return 0; } - data = packet_get_string(&data_len); - packet_check_eom(); + if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) { + error("%s: parse data: %s", __func__, ssh_err(r)); + ssh_packet_disconnect(ssh, "Invalid extended_data message"); + } + ssh_packet_check_eom(ssh); + if (data_len > c->local_window) { - logit("channel %d: rcvd too much extended_data %d, win %d", + logit("channel %d: rcvd too much extended_data %zu, win %u", c->self, data_len, c->local_window); - free(data); return 0; } - debug2("channel %d: rcvd ext data %d", c->self, data_len); + debug2("channel %d: rcvd ext data %zu", c->self, data_len); + /* XXX sshpkt_getb? */ + if ((r = sshbuf_put(c->extended, data, data_len)) != 0) + error("%s: append: %s", __func__, ssh_err(r)); c->local_window -= data_len; - buffer_append(&c->extended, data, data_len); - free(data); return 0; } -/* ARGSUSED */ int channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) { - int id; - Channel *c; + Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); + + ssh_packet_check_eom(ssh); - id = packet_get_int(); - packet_check_eom(); - c = channel_lookup(id); - if (c == NULL) - packet_disconnect("Received ieof for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; - chan_rcvd_ieof(c); + chan_rcvd_ieof(ssh, c); /* XXX force input close */ if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { debug("channel %d: FORCE input drain", c->self); c->istate = CHAN_INPUT_WAIT_DRAIN; - if (buffer_len(&c->input) == 0) - chan_ibuf_empty(c); + if (sshbuf_len(c->input) == 0) + chan_ibuf_empty(ssh, c); } return 0; } -/* ARGSUSED */ int channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) { - int id = packet_get_int(); - Channel *c = channel_lookup(id); + Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); - if (c == NULL) - packet_disconnect("Received oclose for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; - packet_check_eom(); - chan_rcvd_oclose(c); + ssh_packet_check_eom(ssh); + chan_rcvd_oclose(ssh, c); return 0; } -/* ARGSUSED */ int channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) { - int id, remote_id; - Channel *c; + Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); + u_int32_t remote_window, remote_maxpacket; + int r; - id = packet_get_int(); - c = channel_lookup(id); - - if (c==NULL) - packet_disconnect("Received open confirmation for " - "unknown channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (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. */ - c->remote_id = remote_id; - c->type = SSH_CHANNEL_OPEN; + "non-opening channel %d.", c->self); + /* + * Record the remote channel number and mark that the channel + * is now open. + */ + c->remote_id = channel_parse_id(ssh, __func__, "open confirmation"); + if ((r = sshpkt_get_u32(ssh, &remote_window)) != 0 || + (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0) { + error("%s: window/maxpacket: %s", __func__, ssh_err(r)); + packet_disconnect("Invalid open confirmation message"); + } + ssh_packet_check_eom(ssh); - c->remote_window = packet_get_int(); - c->remote_maxpacket = packet_get_int(); + c->remote_window = remote_window; + c->remote_maxpacket = remote_maxpacket; + c->type = SSH_CHANNEL_OPEN; if (c->open_confirm) { - debug2("callback start"); - c->open_confirm(c->self, 1, c->open_confirm_ctx); - debug2("callback done"); + debug2("%s: channel %d: callback start", __func__, c->self); + c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); + debug2("%s: channel %d: callback done", __func__, c->self); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, c->remote_window, c->remote_maxpacket); - packet_check_eom(); return 0; } @@ -2679,97 +2960,97 @@ reason2txt(int reason) return "unknown reason"; } -/* ARGSUSED */ int channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) { - int id, reason; - char *msg = NULL, *lang = NULL; - Channel *c; + Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); + u_int32_t reason; + char *msg = NULL; + int r; - id = packet_get_int(); - c = channel_lookup(id); - - if (c==NULL) - packet_disconnect("Received open failure for " - "unknown channel %d.", id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (c->type != SSH_CHANNEL_OPENING) packet_disconnect("Received open failure for " - "non-opening channel %d.", id); - reason = packet_get_int(); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - msg = packet_get_string(NULL); - lang = packet_get_string(NULL); + "non-opening channel %d.", c->self); + if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { + error("%s: reason: %s", __func__, ssh_err(r)); + packet_disconnect("Invalid open failure message"); } - logit("channel %d: open failed: %s%s%s", id, + if ((datafellows & SSH_BUG_OPENFAILURE) == 0) { + /* skip language */ + if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_string_direct(ssh, NULL, NULL)) == 0) { + error("%s: message/lang: %s", __func__, ssh_err(r)); + packet_disconnect("Invalid open failure message"); + } + } + ssh_packet_check_eom(ssh); + logit("channel %d: open failed: %s%s%s", c->self, reason2txt(reason), msg ? ": ": "", msg ? msg : ""); free(msg); - free(lang); if (c->open_confirm) { - debug2("callback start"); - c->open_confirm(c->self, 0, c->open_confirm_ctx); - debug2("callback done"); + debug2("%s: channel %d: callback start", __func__, c->self); + c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); + debug2("%s: channel %d: callback done", __func__, c->self); } - packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ - chan_mark_dead(c); + chan_mark_dead(ssh, c); return 0; } -/* ARGSUSED */ int channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) { + int id = channel_parse_id(ssh, __func__, "window adjust"); Channel *c; - int id; - u_int adjust, tmp; + u_int32_t adjust; + u_int new_rwin; + int r; - /* Get the channel number and verify it. */ - id = packet_get_int(); - c = channel_lookup(id); - - if (c == NULL) { + if ((c = channel_lookup(ssh, id)) == NULL) { logit("Received window adjust for non-open channel %d.", id); return 0; - } + } + if (channel_proxy_upstream(c, type, seq, ssh)) return 0; - adjust = packet_get_int(); - packet_check_eom(); - debug2("channel %d: rcvd adjust %u", id, adjust); - if ((tmp = c->remote_window + adjust) < c->remote_window) + if ((r = sshpkt_get_u32(ssh, &adjust)) != 0) { + error("%s: adjust: %s", __func__, ssh_err(r)); + packet_disconnect("Invalid window adjust message"); + } + ssh_packet_check_eom(ssh); + debug2("channel %d: rcvd adjust %u", c->self, adjust); + if ((new_rwin = c->remote_window + adjust) < c->remote_window) { fatal("channel %d: adjust %u overflows remote window %u", - id, adjust, c->remote_window); - c->remote_window = tmp; + c->self, adjust, c->remote_window); + } + c->remote_window = new_rwin; return 0; } -/* ARGSUSED */ int channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) { + int id = channel_parse_id(ssh, __func__, "status confirm"); Channel *c; struct channel_confirm *cc; - int id; /* Reset keepalive timeout */ packet_set_alive_timeouts(0); - id = packet_get_int(); - debug2("channel_input_status_confirm: type %d id %d", type, id); + debug2("%s: type %d id %d", __func__, type, id); - if ((c = channel_lookup(id)) == NULL) { - logit("channel_input_status_confirm: %d: unknown", id); + if ((c = channel_lookup(ssh, id)) == NULL) { + logit("%s: %d: unknown", __func__, id); return 0; - } + } if (channel_proxy_upstream(c, type, seq, ssh)) return 0; - packet_check_eom(); + ssh_packet_check_eom(ssh); if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) return 0; - cc->cb(type, c, cc->ctx); + cc->cb(ssh, type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); explicit_bzero(cc, sizeof(*cc)); free(cc); @@ -2779,9 +3060,9 @@ channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) /* -- tcp forwarding */ void -channel_set_af(int af) +channel_set_af(struct ssh *ssh, int af) { - IPv4or6 = af; + ssh->chanctxt->IPv4or6 = af; } @@ -2849,8 +3130,9 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, } static int -channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, - int *allocated_listen_port, struct ForwardOptions *fwd_opts) +channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, + struct Forward *fwd, int *allocated_listen_port, + struct ForwardOptions *fwd_opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; @@ -2887,7 +3169,7 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; + hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", fwd->listen_port); @@ -2921,12 +3203,14 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, * If allocating a port for -R forwards, then use the * same port for all address families. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && - allocated_listen_port != NULL && *allocated_listen_port > 0) + if (type == SSH_CHANNEL_RPORT_LISTENER && + fwd->listen_port == 0 && allocated_listen_port != NULL && + *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), - strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("%s: getnameinfo failed", __func__); continue; } @@ -2947,7 +3231,10 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, /* Bind the socket to the address. */ if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - /* address can be in use ipv6 address is already bound */ + /* + * address can be in if use ipv6 address is + * already bound + */ if (!ai->ai_next) error("bind: %.100s", strerror(errno)); else @@ -2967,7 +3254,8 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, * fwd->listen_port == 0 requests a dynamically allocated port - * record what we got. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && + if (type == SSH_CHANNEL_RPORT_LISTENER && + fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_local_port(sock); @@ -2976,7 +3264,7 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, } /* Allocate a channel number for the socket. */ - c = channel_new("port listener", type, sock, sock, -1, + c = channel_new(ssh, "port listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); @@ -2997,8 +3285,8 @@ channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, } static int -channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, - struct ForwardOptions *fwd_opts) +channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, + struct Forward *fwd, struct ForwardOptions *fwd_opts) { struct sockaddr_un sunaddr; const char *path; @@ -3060,7 +3348,7 @@ channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, debug("Local forwarding listening on path %s.", fwd->listen_path); /* Allocate a channel number for the socket. */ - c = channel_new("unix listener", type, sock, sock, -1, + c = channel_new(ssh, "unix listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "unix listener", 1); c->path = xstrdup(path); @@ -3071,66 +3359,71 @@ channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, } static int -channel_cancel_rport_listener_tcpip(const char *host, u_short port) +channel_cancel_rport_listener_tcpip(struct ssh *ssh, + const char *host, u_short port) { u_int i; int found = 0; - for (i = 0; i < channels_alloc; i++) { - Channel *c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) continue; if (strcmp(c->path, host) == 0 && c->listening_port == port) { debug2("%s: close channel %d", __func__, i); - channel_free(c); + channel_free(ssh, c); found = 1; } } - return (found); + return found; } static int -channel_cancel_rport_listener_streamlocal(const char *path) +channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) { u_int i; int found = 0; - for (i = 0; i < channels_alloc; i++) { - Channel *c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) continue; if (c->path == NULL) continue; if (strcmp(c->path, path) == 0) { debug2("%s: close channel %d", __func__, i); - channel_free(c); + channel_free(ssh, c); found = 1; } } - return (found); + return found; } int -channel_cancel_rport_listener(struct Forward *fwd) +channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) { - if (fwd->listen_path != NULL) - return channel_cancel_rport_listener_streamlocal(fwd->listen_path); - else - return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); + if (fwd->listen_path != NULL) { + return channel_cancel_rport_listener_streamlocal(ssh, + fwd->listen_path); + } else { + return channel_cancel_rport_listener_tcpip(ssh, + fwd->listen_host, fwd->listen_port); + } } static int -channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, - int cport, struct ForwardOptions *fwd_opts) +channel_cancel_lport_listener_tcpip(struct ssh *ssh, + const char *lhost, u_short lport, int cport, + struct ForwardOptions *fwd_opts) { u_int i; int found = 0; const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); - for (i = 0; i < channels_alloc; i++) { - Channel *c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) continue; if (c->listening_port != lport) @@ -3148,16 +3441,16 @@ channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, continue; if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { debug2("%s: close channel %d", __func__, i); - channel_free(c); + channel_free(ssh, c); found = 1; } } - return (found); + return found; } static int -channel_cancel_lport_listener_streamlocal(const char *path) +channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) { u_int i; int found = 0; @@ -3167,54 +3460,59 @@ channel_cancel_lport_listener_streamlocal(const char *path) return 0; } - for (i = 0; i < channels_alloc; i++) { - Channel *c = channels[i]; + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) continue; if (c->listening_addr == NULL) continue; if (strcmp(c->listening_addr, path) == 0) { debug2("%s: close channel %d", __func__, i); - channel_free(c); + channel_free(ssh, c); found = 1; } } - return (found); + return found; } int -channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) +channel_cancel_lport_listener(struct ssh *ssh, + struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) { - if (fwd->listen_path != NULL) - return channel_cancel_lport_listener_streamlocal(fwd->listen_path); - else - return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); + if (fwd->listen_path != NULL) { + return channel_cancel_lport_listener_streamlocal(ssh, + fwd->listen_path); + } else { + return channel_cancel_lport_listener_tcpip(ssh, + fwd->listen_host, fwd->listen_port, cport, fwd_opts); + } } /* protocol local port fwd, used by ssh */ int -channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) +channel_setup_local_fwd_listener(struct ssh *ssh, + struct Forward *fwd, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { - return channel_setup_fwd_listener_streamlocal( + return channel_setup_fwd_listener_streamlocal(ssh, SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); } else { - return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, - fwd, NULL, fwd_opts); + return channel_setup_fwd_listener_tcpip(ssh, + SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); } } /* protocol v2 remote port fwd, used by sshd */ int -channel_setup_remote_fwd_listener(struct Forward *fwd, +channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, int *allocated_listen_port, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { - return channel_setup_fwd_listener_streamlocal( + return channel_setup_fwd_listener_streamlocal(ssh, SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); } else { - return channel_setup_fwd_listener_tcpip( + return channel_setup_fwd_listener_tcpip(ssh, SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, fwd_opts); } @@ -3248,56 +3546,61 @@ channel_rfwd_bind_host(const char *listen_host) * channel_update_permitted_opens(). */ int -channel_request_remote_forwarding(struct Forward *fwd) +channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) { - int success = 0, idx = -1; + int r, success = 0, idx = -1; + char *host_to_connect, *listen_host, *listen_path; + int port_to_connect, listen_port; /* Send the forward request to the remote side. */ - packet_start(SSH2_MSG_GLOBAL_REQUEST); if (fwd->listen_path != NULL) { - packet_put_cstring("streamlocal-forward@openssh.com"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(fwd->listen_path); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "streamlocal-forward@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal("%s: request streamlocal: %s", + __func__, ssh_err(r)); } else { - packet_put_cstring("tcpip-forward"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); - packet_put_int(fwd->listen_port); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, + channel_rfwd_bind_host(fwd->listen_host))) != 0 || + (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal("%s: request tcpip-forward: %s", + __func__, ssh_err(r)); } - packet_send(); - packet_write_wait(); /* Assume that server accepts the request */ success = 1; if (success) { /* Record that connection to this host/port is permitted. */ - permitted_opens = xreallocarray(permitted_opens, - num_permitted_opens + 1, sizeof(*permitted_opens)); - idx = num_permitted_opens++; + host_to_connect = listen_host = listen_path = NULL; + port_to_connect = listen_port = 0; if (fwd->connect_path != NULL) { - permitted_opens[idx].host_to_connect = - xstrdup(fwd->connect_path); - permitted_opens[idx].port_to_connect = - PORT_STREAMLOCAL; + host_to_connect = xstrdup(fwd->connect_path); + port_to_connect = PORT_STREAMLOCAL; } else { - permitted_opens[idx].host_to_connect = - xstrdup(fwd->connect_host); - permitted_opens[idx].port_to_connect = - fwd->connect_port; + host_to_connect = xstrdup(fwd->connect_host); + port_to_connect = fwd->connect_port; } if (fwd->listen_path != NULL) { - permitted_opens[idx].listen_host = NULL; - permitted_opens[idx].listen_path = - xstrdup(fwd->listen_path); - permitted_opens[idx].listen_port = PORT_STREAMLOCAL; + listen_path = xstrdup(fwd->listen_path); + listen_port = PORT_STREAMLOCAL; } else { - permitted_opens[idx].listen_host = - fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; - permitted_opens[idx].listen_path = NULL; - permitted_opens[idx].listen_port = fwd->listen_port; + if (fwd->listen_host != NULL) + listen_host = xstrdup(fwd->listen_host); + listen_port = fwd->listen_port; } - permitted_opens[idx].downstream = NULL; + idx = fwd_perm_list_add(ssh, FWDPERM_USER, + host_to_connect, port_to_connect, + listen_host, listen_path, listen_port, NULL); } - return (idx); + return idx; } static int @@ -3362,33 +3665,33 @@ open_listen_match_streamlocal(ForwardPermission *allowed_open, * local side. */ static int -channel_request_rforward_cancel_tcpip(const char *host, u_short port) +channel_request_rforward_cancel_tcpip(struct ssh *ssh, + const char *host, u_short port) { - int i; + struct ssh_channels *sc = ssh->chanctxt; + int r; + u_int i; + ForwardPermission *fp; - for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_listen_match_tcpip(fp, host, port, 0)) break; + fp = NULL; } - if (i >= num_permitted_opens) { + if (fp == NULL) { debug("%s: requested forward not found", __func__); return -1; } - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("cancel-tcpip-forward"); - packet_put_char(0); - packet_put_cstring(channel_rfwd_bind_host(host)); - packet_put_int(port); - packet_send(); + 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 || /* want reply */ + (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || + (r = sshpkt_put_u32(ssh, port)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send cancel: %s", __func__, ssh_err(r)); - 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; + fwd_perm_clear(fp); /* unregister */ return 0; } @@ -3398,32 +3701,32 @@ channel_request_rforward_cancel_tcpip(const char *host, u_short port) * path from local side. */ static int -channel_request_rforward_cancel_streamlocal(const char *path) +channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) { - int i; + struct ssh_channels *sc = ssh->chanctxt; + int r; + u_int i; + ForwardPermission *fp; - for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match_streamlocal(&permitted_opens[i], path)) + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_listen_match_streamlocal(fp, path)) break; + fp = NULL; } - if (i >= num_permitted_opens) { + if (fp == NULL) { debug("%s: requested forward not found", __func__); return -1; } - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("cancel-streamlocal-forward@openssh.com"); - packet_put_char(0); - packet_put_cstring(path); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "cancel-streamlocal-forward@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, path)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send cancel: %s", __func__, ssh_err(r)); - 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; - permitted_opens[i].listen_host = NULL; - free(permitted_opens[i].listen_path); - permitted_opens[i].listen_path = NULL; - permitted_opens[i].downstream = NULL; + fwd_perm_clear(fp); /* unregister */ return 0; } @@ -3432,14 +3735,15 @@ channel_request_rforward_cancel_streamlocal(const char *path) * Request cancellation of remote forwarding of a connection from local side. */ int -channel_request_rforward_cancel(struct Forward *fwd) +channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) { if (fwd->listen_path != NULL) { - return (channel_request_rforward_cancel_streamlocal( - fwd->listen_path)); + return channel_request_rforward_cancel_streamlocal(ssh, + fwd->listen_path); } else { - return (channel_request_rforward_cancel_tcpip(fwd->listen_host, - fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); + return channel_request_rforward_cancel_tcpip(ssh, + fwd->listen_host, + fwd->listen_port ? fwd->listen_port : fwd->allocated_port); } } @@ -3449,28 +3753,20 @@ channel_request_rforward_cancel(struct Forward *fwd) * anyway, and the server has no way to know but to trust the client anyway. */ void -channel_permit_all_opens(void) +channel_permit_all_opens(struct ssh *ssh) { - if (num_permitted_opens == 0) - all_opens_permitted = 1; + if (ssh->chanctxt->num_permitted_opens == 0) + ssh->chanctxt->all_opens_permitted = 1; } void -channel_add_permitted_opens(char *host, int port) +channel_add_permitted_opens(struct ssh *ssh, char *host, int port) { + struct ssh_channels *sc = ssh->chanctxt; + debug("allow port forwarding to host %s port %d", host, port); - - permitted_opens = xreallocarray(permitted_opens, - num_permitted_opens + 1, sizeof(*permitted_opens)); - permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); - permitted_opens[num_permitted_opens].port_to_connect = port; - permitted_opens[num_permitted_opens].listen_host = NULL; - permitted_opens[num_permitted_opens].listen_path = NULL; - permitted_opens[num_permitted_opens].listen_port = 0; - permitted_opens[num_permitted_opens].downstream = NULL; - num_permitted_opens++; - - all_opens_permitted = 0; + fwd_perm_list_add(ssh, FWDPERM_USER, host, port, NULL, NULL, 0, NULL); + sc->all_opens_permitted = 0; } /* @@ -3479,105 +3775,61 @@ channel_add_permitted_opens(char *host, int port) * passed then they entry will be invalidated. */ void -channel_update_permitted_opens(int idx, int newport) +channel_update_permitted_opens(struct ssh *ssh, int idx, int newport) { - if (idx < 0 || idx >= num_permitted_opens) { - debug("channel_update_permitted_opens: index out of range:" - " %d num_permitted_opens %d", idx, num_permitted_opens); + struct ssh_channels *sc = ssh->chanctxt; + + if (idx < 0 || (u_int)idx >= sc->num_permitted_opens) { + debug("%s: index out of range: %d num_permitted_opens %d", + __func__, idx, sc->num_permitted_opens); return; } debug("%s allowed port %d for forwarding to host %s port %d", newport > 0 ? "Updating" : "Removing", newport, - permitted_opens[idx].host_to_connect, - permitted_opens[idx].port_to_connect); - if (newport >= 0) { - permitted_opens[idx].listen_port = + sc->permitted_opens[idx].host_to_connect, + sc->permitted_opens[idx].port_to_connect); + if (newport <= 0) + fwd_perm_clear(&sc->permitted_opens[idx]); + else { + sc->permitted_opens[idx].listen_port = (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; - } else { - permitted_opens[idx].listen_port = 0; - permitted_opens[idx].port_to_connect = 0; - free(permitted_opens[idx].host_to_connect); - permitted_opens[idx].host_to_connect = NULL; - free(permitted_opens[idx].listen_host); - permitted_opens[idx].listen_host = NULL; - free(permitted_opens[idx].listen_path); - permitted_opens[idx].listen_path = NULL; } } int -channel_add_adm_permitted_opens(char *host, int port) +channel_add_adm_permitted_opens(struct ssh *ssh, char *host, int port) { debug("config allows port forwarding to host %s port %d", host, port); - - permitted_adm_opens = xreallocarray(permitted_adm_opens, - num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); - permitted_adm_opens[num_adm_permitted_opens].host_to_connect - = xstrdup(host); - permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; - permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; - permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; - permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; - return ++num_adm_permitted_opens; + return fwd_perm_list_add(ssh, FWDPERM_ADMIN, host, port, + NULL, NULL, 0, NULL); } void -channel_disable_adm_local_opens(void) +channel_disable_adm_local_opens(struct ssh *ssh) { - channel_clear_adm_permitted_opens(); - permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1); - permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; - num_adm_permitted_opens = 1; + channel_clear_adm_permitted_opens(ssh); + fwd_perm_list_add(ssh, FWDPERM_ADMIN, NULL, 0, NULL, NULL, 0, NULL); } void -channel_clear_permitted_opens(void) +channel_clear_permitted_opens(struct ssh *ssh) { - int i; + struct ssh_channels *sc = ssh->chanctxt; - for (i = 0; i < num_permitted_opens; i++) { - free(permitted_opens[i].host_to_connect); - free(permitted_opens[i].listen_host); - free(permitted_opens[i].listen_path); - } - free(permitted_opens); - permitted_opens = NULL; - num_permitted_opens = 0; + sc->permitted_opens = xrecallocarray(sc->permitted_opens, + sc->num_permitted_opens, 0, sizeof(*sc->permitted_opens)); + sc->num_permitted_opens = 0; } void -channel_clear_adm_permitted_opens(void) +channel_clear_adm_permitted_opens(struct ssh *ssh) { - int i; + struct ssh_channels *sc = ssh->chanctxt; - for (i = 0; i < num_adm_permitted_opens; i++) { - free(permitted_adm_opens[i].host_to_connect); - free(permitted_adm_opens[i].listen_host); - free(permitted_adm_opens[i].listen_path); - } - free(permitted_adm_opens); - permitted_adm_opens = NULL; - num_adm_permitted_opens = 0; -} - -void -channel_print_adm_permitted_opens(void) -{ - int i; - - printf("permitopen"); - if (num_adm_permitted_opens == 0) { - printf(" any\n"); - return; - } - for (i = 0; i < num_adm_permitted_opens; i++) - if (permitted_adm_opens[i].host_to_connect == NULL) - printf(" none"); - else - printf(" %s:%d", permitted_adm_opens[i].host_to_connect, - permitted_adm_opens[i].port_to_connect); - printf("\n"); + sc->permitted_adm_opens = xrecallocarray(sc->permitted_adm_opens, + sc->num_adm_permitted_opens, 0, sizeof(*sc->permitted_adm_opens)); + sc->num_adm_permitted_opens = 0; } /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ @@ -3599,7 +3851,8 @@ connect_next(struct channel_connect *cctx) { int sock, saved_errno; struct sockaddr_un *sunaddr; - char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))]; + char ntop[NI_MAXHOST]; + char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { switch (cctx->ai->ai_family) { @@ -3669,8 +3922,8 @@ channel_connect_ctx_free(struct channel_connect *cctx) * passing back the failure reason if appropriate. */ static Channel * -connect_to_reason(const char *name, int port, char *ctype, char *rname, - int *reason, const char **errmsg) +connect_to_reason(struct ssh *ssh, const char *name, int port, + char *ctype, char *rname, int *reason, const char **errmsg) { struct addrinfo hints; int gaierr; @@ -3708,7 +3961,7 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, cctx.aitop = ai; } else { memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; + hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) @@ -3733,7 +3986,7 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, channel_connect_ctx_free(&cctx); return NULL; } - c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, + c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->connect_ctx = cctx; return c; @@ -3741,9 +3994,10 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, /* Return CONNECTING channel to remote host:port or local socket path */ static Channel * -connect_to(const char *name, int port, char *ctype, char *rname) +connect_to(struct ssh *ssh, const char *name, int port, + char *ctype, char *rname) { - return connect_to_reason(name, port, ctype, rname, NULL, NULL); + return connect_to_reason(ssh, name, port, ctype, rname, NULL, NULL); } /* @@ -3751,19 +4005,21 @@ connect_to(const char *name, int port, char *ctype, char *rname) * that needs to deal with this connection. */ Channel * -channel_connect_by_listen_address(const char *listen_host, +channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, u_short listen_port, char *ctype, char *rname) { - int i; + struct ssh_channels *sc = ssh->chanctxt; + u_int i; + ForwardPermission *fp; - for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match_tcpip(&permitted_opens[i], listen_host, - listen_port, 1)) { - if (permitted_opens[i].downstream) - return permitted_opens[i].downstream; - return connect_to( - permitted_opens[i].host_to_connect, - permitted_opens[i].port_to_connect, ctype, rname); + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) { + if (fp->downstream) + return fp->downstream; + return connect_to(ssh, + fp->host_to_connect, fp->port_to_connect, + ctype, rname); } } error("WARNING: Server requests forwarding for unknown listen_port %d", @@ -3772,15 +4028,19 @@ channel_connect_by_listen_address(const char *listen_host, } Channel * -channel_connect_by_listen_path(const char *path, char *ctype, char *rname) +channel_connect_by_listen_path(struct ssh *ssh, const char *path, + char *ctype, char *rname) { - int i; + struct ssh_channels *sc = ssh->chanctxt; + u_int i; + ForwardPermission *fp; - for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match_streamlocal(&permitted_opens[i], path)) { - return connect_to( - permitted_opens[i].host_to_connect, - permitted_opens[i].port_to_connect, ctype, rname); + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_listen_match_streamlocal(fp, path)) { + return connect_to(ssh, + fp->host_to_connect, fp->port_to_connect, + ctype, rname); } } error("WARNING: Server requests forwarding for unknown path %.100s", @@ -3790,27 +4050,33 @@ channel_connect_by_listen_path(const char *path, char *ctype, char *rname) /* Check if connecting to that port is permitted and connect. */ Channel * -channel_connect_to_port(const char *host, u_short port, char *ctype, - char *rname, int *reason, const char **errmsg) +channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, + char *ctype, char *rname, int *reason, const char **errmsg) { - int i, permit, permit_adm = 1; + struct ssh_channels *sc = ssh->chanctxt; + u_int i, permit, permit_adm = 1; + ForwardPermission *fp; - permit = all_opens_permitted; + permit = sc->all_opens_permitted; if (!permit) { - for (i = 0; i < num_permitted_opens; i++) - if (open_match(&permitted_opens[i], host, port)) { + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_match(fp, host, port)) { permit = 1; break; } + } } - if (num_adm_permitted_opens > 0) { + if (sc->num_adm_permitted_opens > 0) { permit_adm = 0; - for (i = 0; i < num_adm_permitted_opens; i++) - if (open_match(&permitted_adm_opens[i], host, port)) { + for (i = 0; i < sc->num_adm_permitted_opens; i++) { + fp = &sc->permitted_adm_opens[i]; + if (open_match(fp, host, port)) { permit_adm = 1; break; } + } } if (!permit || !permit_adm) { @@ -3820,31 +4086,38 @@ channel_connect_to_port(const char *host, u_short port, char *ctype, *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; return NULL; } - return connect_to_reason(host, port, ctype, rname, reason, errmsg); + return connect_to_reason(ssh, host, port, ctype, rname, reason, errmsg); } /* Check if connecting to that path is permitted and connect. */ Channel * -channel_connect_to_path(const char *path, char *ctype, char *rname) +channel_connect_to_path(struct ssh *ssh, const char *path, + char *ctype, char *rname) { - int i, permit, permit_adm = 1; + struct ssh_channels *sc = ssh->chanctxt; + u_int i, permit, permit_adm = 1; + ForwardPermission *fp; - permit = all_opens_permitted; + permit = sc->all_opens_permitted; if (!permit) { - for (i = 0; i < num_permitted_opens; i++) - if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { + for (i = 0; i < sc->num_permitted_opens; i++) { + fp = &sc->permitted_opens[i]; + if (open_match(fp, path, PORT_STREAMLOCAL)) { permit = 1; break; } + } } - if (num_adm_permitted_opens > 0) { + if (sc->num_adm_permitted_opens > 0) { permit_adm = 0; - for (i = 0; i < num_adm_permitted_opens; i++) - if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { + for (i = 0; i < sc->num_adm_permitted_opens; i++) { + fp = &sc->permitted_adm_opens[i]; + if (open_match(fp, path, PORT_STREAMLOCAL)) { permit_adm = 1; break; } + } } if (!permit || !permit_adm) { @@ -3852,27 +4125,31 @@ channel_connect_to_path(const char *path, char *ctype, char *rname) "but the request was denied.", path); return NULL; } - return connect_to(path, PORT_STREAMLOCAL, ctype, rname); + return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); } void -channel_send_window_changes(void) +channel_send_window_changes(struct ssh *ssh) { - u_int i; + struct ssh_channels *sc = ssh->chanctxt; struct winsize ws; + int r; + u_int i; - for (i = 0; i < channels_alloc; i++) { - if (channels[i] == NULL || !channels[i]->client_tty || - channels[i]->type != SSH_CHANNEL_OPEN) + for (i = 0; i < sc->channels_alloc; i++) { + if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || + sc->channels[i]->type != SSH_CHANNEL_OPEN) continue; - if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) + if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) < 0) continue; - channel_request_start(i, "window-change", 0); - packet_put_int((u_int)ws.ws_col); - packet_put_int((u_int)ws.ws_row); - packet_put_int((u_int)ws.ws_xpixel); - packet_put_int((u_int)ws.ws_ypixel); - packet_send(); + channel_request_start(ssh, i, "window-change", 0); + if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %u: send window-change: %s", + __func__, i, ssh_err(r)); } } @@ -3884,8 +4161,9 @@ channel_send_window_changes(void) * stored in display_numberp , or -1 if an error occurs. */ int -x11_create_display_inet(int x11_display_offset, int x11_use_localhost, - int single_connection, u_int *display_numberp, int **chanids) +x11_create_display_inet(struct ssh *ssh, int x11_display_offset, + int x11_use_localhost, int single_connection, + u_int *display_numberp, int **chanids) { Channel *nc = NULL; int display_number, sock; @@ -3902,16 +4180,18 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, display_number++) { port = 6000 + display_number; memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; + hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { + if ((gaierr = getaddrinfo(NULL, strport, + &hints, &aitop)) != 0) { error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + if (ai->ai_family != AF_INET && + ai->ai_family != AF_INET6) continue; sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); @@ -3935,12 +4215,11 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, if (x11_use_localhost) channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - debug2("bind port %d: %.100s", port, strerror(errno)); + debug2("%s: bind port %d: %.100s", __func__, + port, strerror(errno)); close(sock); - - for (n = 0; n < num_socks; n++) { + for (n = 0; n < num_socks; n++) close(socks[n]); - } num_socks = 0; break; } @@ -3970,7 +4249,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); for (n = 0; n < num_socks; n++) { sock = socks[n]; - nc = channel_new("x11 listener", + nc = channel_new(ssh, "x11 listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); @@ -3981,7 +4260,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; - return (0); + return 0; } static int @@ -4039,7 +4318,7 @@ is_path_to_xsocket(const char *display, char *path, size_t pathlen) #endif int -x11_connect_display(void) +x11_connect_display(struct ssh *ssh) { u_int display_number; const char *display; @@ -4084,9 +4363,10 @@ x11_connect_display(void) if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* Connect to the unix domain socket. */ - if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { - error("Could not parse display number from DISPLAY: %.100s", - display); + if (sscanf(strrchr(display, ':') + 1, "%u", + &display_number) != 1) { + error("Could not parse display number from DISPLAY: " + "%.100s", display); return -1; } /* Create a socket. */ @@ -4108,7 +4388,10 @@ x11_connect_display(void) return -1; } *cp = 0; - /* buf now contains the host name. But first we parse the display number. */ + /* + * buf now contains the host name. But first we parse the + * display number. + */ if (sscanf(cp + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); @@ -4117,7 +4400,7 @@ x11_connect_display(void) /* Look up the host address */ memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; + hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { @@ -4144,8 +4427,8 @@ x11_connect_display(void) } freeaddrinfo(aitop); if (!ai) { - error("connect %.100s port %u: %.100s", buf, 6000 + display_number, - strerror(errno)); + error("connect %.100s port %u: %.100s", buf, + 6000 + display_number, strerror(errno)); return -1; } set_nodelay(sock); @@ -4158,18 +4441,19 @@ x11_connect_display(void) * This should be called in the client only. */ void -x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, - const char *proto, const char *data, int want_reply) +x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, + const char *disp, const char *proto, const char *data, int want_reply) { + struct ssh_channels *sc = ssh->chanctxt; u_int data_len = (u_int) strlen(data) / 2; u_int i, value; - char *new_data; - int screen_number; const char *cp; + char *new_data; + int r, screen_number; - if (x11_saved_display == NULL) - x11_saved_display = xstrdup(disp); - else if (strcmp(disp, x11_saved_display) != 0) { + if (sc->x11_saved_display == NULL) + sc->x11_saved_display = xstrdup(disp); + else if (strcmp(disp, sc->x11_saved_display) != 0) { error("x11_request_forwarding_with_spoofing: different " "$DISPLAY already forwarded"); return; @@ -4183,36 +4467,37 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, else screen_number = 0; - if (x11_saved_proto == NULL) { + if (sc->x11_saved_proto == NULL) { /* Save protocol name. */ - x11_saved_proto = xstrdup(proto); + sc->x11_saved_proto = xstrdup(proto); /* Extract real authentication data. */ - x11_saved_data = xmalloc(data_len); + sc->x11_saved_data = xmalloc(data_len); for (i = 0; i < data_len; i++) { if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad " "authentication data: %.100s", data); - x11_saved_data[i] = value; + sc->x11_saved_data[i] = value; } - x11_saved_data_len = data_len; + sc->x11_saved_data_len = data_len; /* Generate fake data of the same length. */ - x11_fake_data = xmalloc(data_len); - arc4random_buf(x11_fake_data, data_len); - x11_fake_data_len = data_len; + sc->x11_fake_data = xmalloc(data_len); + arc4random_buf(sc->x11_fake_data, data_len); + sc->x11_fake_data_len = data_len; } /* Convert the fake data into hex. */ - new_data = tohex(x11_fake_data, data_len); + new_data = tohex(sc->x11_fake_data, data_len); /* Send the request packet. */ - channel_request_start(client_session_id, "x11-req", want_reply); - packet_put_char(0); /* XXX bool single connection */ - packet_put_cstring(proto); - packet_put_cstring(new_data); - packet_put_int(screen_number); - packet_send(); - packet_write_wait(); + channel_request_start(ssh, client_session_id, "x11-req", want_reply); + if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ + (r = sshpkt_put_cstring(ssh, proto)) != 0 || + (r = sshpkt_put_cstring(ssh, new_data)) != 0 || + (r = sshpkt_put_u32(ssh, screen_number)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal("%s: send x11-req: %s", __func__, ssh_err(r)); free(new_data); } diff --git a/channels.h b/channels.h index 5ecb4d7c0..f04c43afa 100644 --- a/channels.h +++ b/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.127 2017/08/30 03:59:08 djm Exp $ */ +/* $OpenBSD: channels.h,v 1.128 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -64,16 +64,18 @@ struct ssh; struct Channel; typedef struct Channel Channel; +struct fwd_perm_list; -typedef void channel_open_fn(int, int, void *); -typedef void channel_callback_fn(int, void *); -typedef int channel_infilter_fn(struct Channel *, char *, int); -typedef void channel_filter_cleanup_fn(int, void *); -typedef u_char *channel_outfilter_fn(struct Channel *, u_char **, u_int *); +typedef void channel_open_fn(struct ssh *, int, int, void *); +typedef void channel_callback_fn(struct ssh *, int, void *); +typedef int channel_infilter_fn(struct ssh *, struct Channel *, char *, int); +typedef void channel_filter_cleanup_fn(struct ssh *, int, void *); +typedef u_char *channel_outfilter_fn(struct ssh *, struct Channel *, + u_char **, size_t *); /* Channel success/failure callbacks */ -typedef void channel_confirm_cb(int, struct Channel *, void *); -typedef void channel_confirm_abandon_cb(struct Channel *, void *); +typedef void channel_confirm_cb(struct ssh *, int, struct Channel *, void *); +typedef void channel_confirm_abandon_cb(struct ssh *, struct Channel *, void *); struct channel_confirm { TAILQ_ENTRY(channel_confirm) entry; channel_confirm_cb *cb; @@ -90,12 +92,13 @@ struct channel_connect { }; /* Callbacks for mux channels back into client-specific code */ -typedef int mux_callback_fn(struct Channel *); +typedef int mux_callback_fn(struct ssh *, struct Channel *); struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ int remote_id; /* channel identifier for remote peer */ + /* XXX should be uint32_t */ u_int istate; /* input from channel (state of receive half) */ u_int ostate; /* output to channel (state of transmit half) */ int flags; /* close sent/rcvd */ @@ -116,11 +119,12 @@ struct Channel { * to a matching pre-select handler. * this way post-select handlers are not * accidentally called if a FD gets reused */ - Buffer input; /* data read from socket, to be sent over + struct sshbuf *input; /* data read from socket, to be sent over * encrypted connection */ - Buffer output; /* data received over encrypted connection for + struct sshbuf *output; /* data received over encrypted connection for * send on socket */ - Buffer extended; + struct sshbuf *extended; + char *path; /* path for unix domain sockets, or host name for forwards */ int listening_port; /* port being listened for forwards */ @@ -156,6 +160,7 @@ struct Channel { int datagram; /* non-blocking connect */ + /* XXX make this a pointer so the structure can be opaque */ struct channel_connect connect_ctx; /* multiplexing protocol hook, called for each packet received */ @@ -195,44 +200,55 @@ struct Channel { #define CHAN_EOF_RCVD 0x08 #define CHAN_LOCAL 0x10 -#define CHAN_RBUF 16*1024 +/* Read buffer size */ +#define CHAN_RBUF (16*1024) + +/* Hard limit on number of channels */ +#define CHANNELS_MAX_CHANNELS (16*1024) /* check whether 'efd' is still in use */ #define CHANNEL_EFD_INPUT_ACTIVE(c) \ (c->extended_usage == CHAN_EXTENDED_READ && \ (c->efd != -1 || \ - buffer_len(&c->extended) > 0)) + sshbuf_len(c->extended) > 0)) #define CHANNEL_EFD_OUTPUT_ACTIVE(c) \ (c->extended_usage == CHAN_EXTENDED_WRITE && \ c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \ - buffer_len(&c->extended) > 0)) + sshbuf_len(c->extended) > 0)) + +/* Add channel management structures to SSH transport instance */ +void channel_init_channels(struct ssh *ssh); /* channel management */ -Channel *channel_by_id(int); -Channel *channel_by_remote_id(int); -Channel *channel_lookup(int); -Channel *channel_new(char *, int, int, int, int, u_int, u_int, int, char *, int); -void channel_set_fds(int, int, int, int, int, int, int, u_int); -void channel_free(Channel *); -void channel_free_all(void); -void channel_stop_listening(void); +Channel *channel_by_id(struct ssh *, int); +Channel *channel_by_remote_id(struct ssh *, int); +Channel *channel_lookup(struct ssh *, int); +Channel *channel_new(struct ssh *, char *, int, int, int, int, + u_int, u_int, int, char *, int); +void channel_set_fds(struct ssh *, int, int, int, int, int, + int, int, u_int); +void channel_free(struct ssh *, Channel *); +void channel_free_all(struct ssh *); +void channel_stop_listening(struct ssh *); -void channel_send_open(int); -void channel_request_start(int, char *, int); -void channel_register_cleanup(int, channel_callback_fn *, int); -void channel_register_open_confirm(int, channel_open_fn *, void *); -void channel_register_filter(int, channel_infilter_fn *, - channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); -void channel_register_status_confirm(int, channel_confirm_cb *, - channel_confirm_abandon_cb *, void *); -void channel_cancel_cleanup(int); -int channel_close_fd(int *); -void channel_send_window_changes(void); +void channel_send_open(struct ssh *, int); +void channel_request_start(struct ssh *, int, char *, int); +void channel_register_cleanup(struct ssh *, int, + channel_callback_fn *, int); +void channel_register_open_confirm(struct ssh *, int, + channel_open_fn *, void *); +void channel_register_filter(struct ssh *, int, channel_infilter_fn *, + channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); +void channel_register_status_confirm(struct ssh *, int, + channel_confirm_cb *, channel_confirm_abandon_cb *, void *); +void channel_cancel_cleanup(struct ssh *, int); +int channel_close_fd(struct ssh *, int *); +void channel_send_window_changes(struct ssh *); /* mux proxy support */ -int channel_proxy_downstream(Channel *mc); +int channel_proxy_downstream(struct ssh *, Channel *mc); int channel_proxy_upstream(Channel *, int, u_int32_t, struct ssh *); /* protocol handler */ @@ -252,63 +268,69 @@ int channel_input_status_confirm(int, u_int32_t, struct ssh *); void channel_prepare_select(struct ssh *, fd_set **, fd_set **, int *, u_int*, time_t*); void channel_after_select(struct ssh *, fd_set *, fd_set *); -void channel_output_poll(void); +void channel_output_poll(struct ssh *); -int channel_not_very_much_buffered_data(void); -void channel_close_all(void); -int channel_still_open(void); -char *channel_open_message(void); -int channel_find_open(void); +int channel_not_very_much_buffered_data(struct ssh *); +void channel_close_all(struct ssh *); +int channel_still_open(struct ssh *); +char *channel_open_message(struct ssh *); +int channel_find_open(struct ssh *); /* tcp forwarding */ struct Forward; struct ForwardOptions; -void channel_set_af(int af); -void channel_permit_all_opens(void); -void channel_add_permitted_opens(char *, int); -int channel_add_adm_permitted_opens(char *, int); -void channel_disable_adm_local_opens(void); -void channel_update_permitted_opens(int, int); -void channel_clear_permitted_opens(void); -void channel_clear_adm_permitted_opens(void); -void channel_print_adm_permitted_opens(void); -Channel *channel_connect_to_port(const char *, u_short, char *, char *, int *, - const char **); -Channel *channel_connect_to_path(const char *, char *, char *); -Channel *channel_connect_stdio_fwd(const char*, u_short, int, int); -Channel *channel_connect_by_listen_address(const char *, u_short, - char *, char *); -Channel *channel_connect_by_listen_path(const char *, char *, char *); -int channel_request_remote_forwarding(struct Forward *); -int channel_setup_local_fwd_listener(struct Forward *, struct ForwardOptions *); -int channel_request_rforward_cancel(struct Forward *); -int channel_setup_remote_fwd_listener(struct Forward *, int *, struct ForwardOptions *); -int channel_cancel_rport_listener(struct Forward *); -int channel_cancel_lport_listener(struct Forward *, int, struct ForwardOptions *); +void channel_set_af(struct ssh *, int af); +void channel_permit_all_opens(struct ssh *); +void channel_add_permitted_opens(struct ssh *, char *, int); +int channel_add_adm_permitted_opens(struct ssh *, char *, int); +void channel_copy_adm_permitted_opens(struct ssh *, + const struct fwd_perm_list *); +void channel_disable_adm_local_opens(struct ssh *); +void channel_update_permitted_opens(struct ssh *, int, int); +void channel_clear_permitted_opens(struct ssh *); +void channel_clear_adm_permitted_opens(struct ssh *); +void channel_print_adm_permitted_opens(struct ssh *); +Channel *channel_connect_to_port(struct ssh *, const char *, u_short, + char *, char *, int *, const char **); +Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *); +Channel *channel_connect_stdio_fwd(struct ssh *, const char*, + u_short, int, int); +Channel *channel_connect_by_listen_address(struct ssh *, const char *, + u_short, char *, char *); +Channel *channel_connect_by_listen_path(struct ssh *, const char *, + char *, char *); +int channel_request_remote_forwarding(struct ssh *, struct Forward *); +int channel_setup_local_fwd_listener(struct ssh *, struct Forward *, + struct ForwardOptions *); +int channel_request_rforward_cancel(struct ssh *, struct Forward *); +int channel_setup_remote_fwd_listener(struct ssh *, struct Forward *, + int *, struct ForwardOptions *); +int channel_cancel_rport_listener(struct ssh *, struct Forward *); +int channel_cancel_lport_listener(struct ssh *, struct Forward *, + int, struct ForwardOptions *); int permitopen_port(const char *); /* x11 forwarding */ -void channel_set_x11_refuse_time(u_int); -int x11_connect_display(void); -int x11_create_display_inet(int, int, int, u_int *, int **); -void x11_request_forwarding_with_spoofing(int, const char *, const char *, - const char *, int); +void channel_set_x11_refuse_time(struct ssh *, u_int); +int x11_connect_display(struct ssh *); +int x11_create_display_inet(struct ssh *, int, int, int, u_int *, int **); +void x11_request_forwarding_with_spoofing(struct ssh *, int, + const char *, const char *, const char *, int); /* channel close */ -int chan_is_dead(Channel *, int); -void chan_mark_dead(Channel *); +int chan_is_dead(struct ssh *, Channel *, int); +void chan_mark_dead(struct ssh *, Channel *); /* channel events */ -void chan_rcvd_oclose(Channel *); -void chan_rcvd_eow(Channel *); /* SSH2-only */ -void chan_read_failed(Channel *); -void chan_ibuf_empty(Channel *); - -void chan_rcvd_ieof(Channel *); -void chan_write_failed(Channel *); -void chan_obuf_empty(Channel *); +void chan_rcvd_oclose(struct ssh *, Channel *); +void chan_rcvd_eow(struct ssh *, Channel *); +void chan_read_failed(struct ssh *, Channel *); +void chan_ibuf_empty(struct ssh *, Channel *); +void chan_rcvd_ieof(struct ssh *, Channel *); +void chan_write_failed(struct ssh *, Channel *); +void chan_obuf_empty(struct ssh *, Channel *); #endif diff --git a/clientloop.c b/clientloop.c index 2934c4763..1218b3b71 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.302 2017/08/30 03:59:08 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.303 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -177,6 +177,7 @@ struct channel_reply_ctx { }; /* Global request success/failure callbacks */ +/* XXX move to struct ssh? */ struct global_confirm { TAILQ_ENTRY(global_confirm) entry; global_confirm_cb *cb; @@ -244,13 +245,13 @@ get_current_time(void) * control master process, or if there is no ControlPersist timeout. */ static void -set_control_persist_exit_time(void) +set_control_persist_exit_time(struct ssh *ssh) { if (muxserver_sock == -1 || !options.control_persist || options.control_persist_timeout == 0) { /* not using a ControlPersist timeout */ control_persist_exit_time = 0; - } else if (channel_still_open()) { + } else if (channel_still_open(ssh)) { /* some client connections are still open */ if (control_persist_exit_time > 0) debug2("%s: cancel scheduled exit", __func__); @@ -288,8 +289,9 @@ client_x11_display_valid(const char *display) #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" #define X11_TIMEOUT_SLACK 60 int -client_x11_get_proto(const char *display, const char *xauth_path, - u_int trusted, u_int timeout, char **_proto, char **_data) +client_x11_get_proto(struct ssh *ssh, const char *display, + const char *xauth_path, u_int trusted, u_int timeout, + char **_proto, char **_data) { char cmd[1024], line[512], xdisplay[512]; char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; @@ -373,7 +375,8 @@ client_x11_get_proto(const char *display, const char *xauth_path, x11_refuse_time = UINT_MAX; else x11_refuse_time = now + timeout; - channel_set_x11_refuse_time(x11_refuse_time); + channel_set_x11_refuse_time(ssh, + x11_refuse_time); } if (system(cmd) == 0) generated = 1; @@ -446,7 +449,7 @@ client_x11_get_proto(const char *display, const char *xauth_path, */ static void -client_check_window_change(void) +client_check_window_change(struct ssh *ssh) { if (!received_window_change_signal) return; @@ -455,7 +458,7 @@ client_check_window_change(void) debug2("%s: changed", __func__); - channel_send_window_changes(); + channel_send_window_changes(ssh); } static int @@ -466,7 +469,7 @@ client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) return 0; if (gc->cb != NULL) - gc->cb(type, seq, gc->ctx); + gc->cb(ssh, type, seq, gc->ctx); if (--gc->ref_count <= 0) { TAILQ_REMOVE(&global_confirms, gc, entry); explicit_bzero(gc, sizeof(*gc)); @@ -497,7 +500,8 @@ server_alive_check(void) * one of the file descriptors). */ static void -client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, +client_wait_until_can_do_something(struct ssh *ssh, + fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; @@ -510,7 +514,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, nallocp, &minwait_secs); /* channel_prepare_select could have closed the last channel */ - if (session_closed && !channel_still_open() && + if (session_closed && !channel_still_open(ssh) && !packet_have_data_to_write()) { /* clear mask since we did not call select() */ memset(*readsetp, 0, *nallocp); @@ -537,7 +541,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, } if (options.rekey_interval > 0 && !rekeying) timeout_secs = MINIMUM(timeout_secs, packet_get_rekey_timeout()); - set_control_persist_exit_time(); + set_control_persist_exit_time(ssh); if (control_persist_exit_time > 0) { timeout_secs = MINIMUM(timeout_secs, control_persist_exit_time - now); @@ -668,7 +672,7 @@ client_process_net_input(fd_set *readset) } static void -client_status_confirm(int type, Channel *c, void *ctx) +client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) { struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; char errmsg[256]; @@ -707,8 +711,7 @@ client_status_confirm(int type, Channel *c, void *ctx) * their stderr. */ if (tochan) { - buffer_append(&c->extended, errmsg, - strlen(errmsg)); + buffer_append(c->extended, errmsg, strlen(errmsg)); } else error("%s", errmsg); if (cr->action == CONFIRM_TTY) { @@ -719,23 +722,23 @@ client_status_confirm(int type, Channel *c, void *ctx) if (c->self == session_ident) leave_raw_mode(0); else - mux_tty_alloc_failed(c); + mux_tty_alloc_failed(ssh, c); } else if (cr->action == CONFIRM_CLOSE) { - chan_read_failed(c); - chan_write_failed(c); + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); } } free(cr); } static void -client_abandon_status_confirm(Channel *c, void *ctx) +client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) { free(ctx); } void -client_expect_confirm(int id, const char *request, +client_expect_confirm(struct ssh *ssh, int id, const char *request, enum confirm_action action) { struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); @@ -743,7 +746,7 @@ client_expect_confirm(int id, const char *request, cr->request_type = request; cr->action = action; - channel_register_status_confirm(id, client_status_confirm, + channel_register_status_confirm(ssh, id, client_status_confirm, client_abandon_status_confirm, cr); } @@ -769,7 +772,7 @@ client_register_global_confirm(global_confirm_cb *cb, void *ctx) } static void -process_cmdline(void) +process_cmdline(struct ssh *ssh) { void (*handler)(int); char *s, *cmd; @@ -843,12 +846,12 @@ process_cmdline(void) goto out; } if (remote) - ok = channel_request_rforward_cancel(&fwd) == 0; + ok = channel_request_rforward_cancel(ssh, &fwd) == 0; else if (dynamic) - ok = channel_cancel_lport_listener(&fwd, + ok = channel_cancel_lport_listener(ssh, &fwd, 0, &options.fwd_opts) > 0; else - ok = channel_cancel_lport_listener(&fwd, + ok = channel_cancel_lport_listener(ssh, &fwd, CHANNEL_CANCEL_PORT_STATIC, &options.fwd_opts) > 0; if (!ok) { @@ -862,13 +865,13 @@ process_cmdline(void) goto out; } if (local || dynamic) { - if (!channel_setup_local_fwd_listener(&fwd, + if (!channel_setup_local_fwd_listener(ssh, &fwd, &options.fwd_opts)) { logit("Port forwarding failed."); goto out; } } else { - if (channel_request_remote_forwarding(&fwd) < 0) { + if (channel_request_remote_forwarding(ssh, &fwd) < 0) { logit("Port forwarding failed."); goto out; } @@ -945,7 +948,8 @@ print_escape_help(Buffer *b, int escape_char, int mux_client, int using_stderr) * Process the characters one by one. */ static int -process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, +process_escapes(struct ssh *ssh, Channel *c, + Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) { char string[1024]; @@ -981,13 +985,15 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, buffer_append(berr, string, strlen(string)); if (c && c->ctl_chan != -1) { - chan_read_failed(c); - chan_write_failed(c); - if (c->detach_user) - c->detach_user(c->self, NULL); + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); + if (c->detach_user) { + c->detach_user(ssh, + c->self, NULL); + } c->type = SSH_CHANNEL_ABANDONED; - buffer_clear(&c->input); - chan_ibuf_empty(c); + buffer_clear(c->input); + chan_ibuf_empty(ssh, c); return 0; } else quit_pending = 1; @@ -1025,7 +1031,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, snprintf(string, sizeof string, "%cB\r\n", efc->escape_char); buffer_append(berr, string, strlen(string)); - channel_request_start(c->self, "break", 0); + channel_request_start(ssh, c->self, "break", 0); packet_put_int(1000); packet_send(); continue; @@ -1077,7 +1083,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, options.request_tty == REQUEST_TTY_FORCE); /* Stop listening for new connections. */ - channel_stop_listening(); + channel_stop_listening(ssh); snprintf(string, sizeof string, "%c& [backgrounded]\n", efc->escape_char); @@ -1107,7 +1113,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, snprintf(string, sizeof string, "%c#\r\n", efc->escape_char); buffer_append(berr, string, strlen(string)); - s = channel_open_message(); + s = channel_open_message(ssh); buffer_append(berr, s, strlen(s)); free(s); continue; @@ -1115,7 +1121,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, case 'C': if (c && c->ctl_chan != -1) goto noescape; - process_cmdline(); + process_cmdline(ssh); continue; default: @@ -1186,25 +1192,25 @@ client_new_escape_filter_ctx(int escape_char) /* Free the escape filter context on channel free */ void -client_filter_cleanup(int cid, void *ctx) +client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) { free(ctx); } int -client_simple_escape_filter(Channel *c, char *buf, int len) +client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) { if (c->extended_usage != CHAN_EXTENDED_WRITE) return 0; - return process_escapes(c, &c->input, &c->output, &c->extended, + return process_escapes(ssh, c, c->input, c->output, c->extended, buf, len); } static void -client_channel_closed(int id, void *arg) +client_channel_closed(struct ssh *ssh, int id, void *arg) { - channel_cancel_cleanup(id); + channel_cancel_cleanup(ssh, id); session_closed = 1; leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } @@ -1215,9 +1221,9 @@ client_channel_closed(int id, void *arg) * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character * used as an escape character for terminating or suspending the session. */ - int -client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) +client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + int ssh2_chan_id) { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; @@ -1295,13 +1301,13 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) session_ident = ssh2_chan_id; if (session_ident != -1) { if (escape_char_arg != SSH_ESCAPECHAR_NONE) { - channel_register_filter(session_ident, + channel_register_filter(ssh, session_ident, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx( escape_char_arg)); } - channel_register_cleanup(session_ident, + channel_register_cleanup(ssh, session_ident, client_channel_closed, 0); } @@ -1311,15 +1317,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(); - if (session_closed && !channel_still_open()) + if (session_closed && !channel_still_open(ssh)) break; - if (ssh_packet_is_rekeying(active_state)) { + if (ssh_packet_is_rekeying(ssh)) { debug("rekeying in progress"); } else if (need_rekeying) { /* manual rekey request */ debug("need rekeying"); - if ((r = kex_start_rekex(active_state)) != 0) + if ((r = kex_start_rekex(ssh)) != 0) fatal("%s: kex_start_rekex: %s", __func__, ssh_err(r)); need_rekeying = 0; @@ -1329,13 +1335,13 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) * enqueue them for sending to the server. */ if (packet_not_very_much_data_to_write()) - channel_output_poll(); + channel_output_poll(ssh); /* * Check if the window size has changed, and buffer a * message about it to the server if so. */ - client_check_window_change(); + client_check_window_change(ssh); if (quit_pending) break; @@ -1345,15 +1351,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) * available on one of the descriptors). */ max_fd2 = max_fd; - client_wait_until_can_do_something(&readset, &writeset, - &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state)); + client_wait_until_can_do_something(ssh, &readset, &writeset, + &max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); if (quit_pending) break; /* Do channel operations unless rekeying in progress. */ - if (!ssh_packet_is_rekeying(active_state)) - channel_after_select(active_state, readset, writeset); + if (!ssh_packet_is_rekeying(ssh)) + channel_after_select(ssh, readset, writeset); /* Buffer input from the connection. */ client_process_net_input(readset); @@ -1395,7 +1401,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) packet_send(); packet_write_wait(); - channel_free_all(); + channel_free_all(ssh); if (have_pty) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); @@ -1463,8 +1469,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /*********/ static Channel * -client_request_forwarded_tcpip(const char *request_type, int rchan, - u_int rwindow, u_int rmaxpack) +client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, + int rchan, u_int rwindow, u_int rmaxpack) { Channel *c = NULL; struct sshbuf *b = NULL; @@ -1482,7 +1488,7 @@ client_request_forwarded_tcpip(const char *request_type, int rchan, debug("%s: listen %s port %d, originator %s port %d", __func__, listen_address, listen_port, originator_address, originator_port); - c = channel_connect_by_listen_address(listen_address, listen_port, + c = channel_connect_by_listen_address(ssh, listen_address, listen_port, "forwarded-tcpip", originator_address); if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { @@ -1501,7 +1507,7 @@ client_request_forwarded_tcpip(const char *request_type, int rchan, (r = sshbuf_put_u32(b, listen_port)) != 0 || (r = sshbuf_put_cstring(b, originator_address)) != 0 || (r = sshbuf_put_u32(b, originator_port)) != 0 || - (r = sshbuf_put_stringb(&c->output, b)) != 0) { + (r = sshbuf_put_stringb(c->output, b)) != 0) { error("%s: compose for muxclient %s", __func__, ssh_err(r)); goto out; @@ -1516,7 +1522,8 @@ client_request_forwarded_tcpip(const char *request_type, int rchan, } static Channel * -client_request_forwarded_streamlocal(const char *request_type, int rchan) +client_request_forwarded_streamlocal(struct ssh *ssh, + const char *request_type, int rchan) { Channel *c = NULL; char *listen_path; @@ -1530,14 +1537,14 @@ client_request_forwarded_streamlocal(const char *request_type, int rchan) debug("%s: %s", __func__, listen_path); - c = channel_connect_by_listen_path(listen_path, + c = channel_connect_by_listen_path(ssh, listen_path, "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); free(listen_path); return c; } static Channel * -client_request_x11(const char *request_type, int rchan) +client_request_x11(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; char *originator; @@ -1567,10 +1574,10 @@ client_request_x11(const char *request_type, int rchan) debug("client_request_x11: request from %s %d", originator, originator_port); free(originator); - sock = x11_connect_display(); + sock = x11_connect_display(ssh); if (sock < 0) return NULL; - c = channel_new("x11", + c = channel_new(ssh, "x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; @@ -1578,7 +1585,7 @@ client_request_x11(const char *request_type, int rchan) } static Channel * -client_request_agent(const char *request_type, int rchan) +client_request_agent(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; int r, sock; @@ -1595,7 +1602,7 @@ client_request_agent(const char *request_type, int rchan) __func__, ssh_err(r)); return NULL; } - c = channel_new("authentication agent connection", + c = channel_new(ssh, "authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "authentication agent connection", 1); @@ -1604,7 +1611,8 @@ client_request_agent(const char *request_type, int rchan) } int -client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) +client_request_tun_fwd(struct ssh *ssh, int tun_mode, + int local_tun, int remote_tun) { Channel *c; int fd; @@ -1620,7 +1628,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) return -1; } - c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, + c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; @@ -1660,14 +1668,14 @@ client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "forwarded-tcpip") == 0) { - c = client_request_forwarded_tcpip(ctype, rchan, rwindow, + c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, rmaxpack); } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { - c = client_request_forwarded_streamlocal(ctype, rchan); + c = client_request_forwarded_streamlocal(ssh, ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { - c = client_request_x11(ctype, rchan); + c = client_request_x11(ssh, ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { - c = client_request_agent(ctype, rchan); + c = client_request_agent(ssh, ctype, rchan); } if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { debug3("proxied to downstream: %s", ctype); @@ -1707,7 +1715,7 @@ client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) char *rtype; id = packet_get_int(); - c = channel_lookup(id); + c = channel_lookup(ssh, id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; rtype = packet_get_string(NULL); @@ -1723,11 +1731,11 @@ client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) "unknown channel", id); } else if (strcmp(rtype, "eow@openssh.com") == 0) { packet_check_eom(); - chan_rcvd_eow(c); + chan_rcvd_eow(ssh, c); } else if (strcmp(rtype, "exit-status") == 0) { exitval = packet_get_int(); if (c->ctl_chan != -1) { - mux_exit_message(c, exitval); + mux_exit_message(ssh, c, exitval); success = 1; } else if (id == session_ident) { /* Record exit value of local session */ @@ -1895,9 +1903,9 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) } static void -client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) +client_global_hostkeys_private_confirm(struct ssh *ssh, int type, + u_int32_t seq, void *_ctx) { - struct ssh *ssh = active_state; /* XXX */ struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i, ndone; struct sshbuf *signdata; @@ -2161,7 +2169,7 @@ client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) } void -client_session2_setup(int id, int want_tty, int want_subsystem, +client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) { int len; @@ -2169,8 +2177,8 @@ client_session2_setup(int id, int want_tty, int want_subsystem, debug2("%s: id %d", __func__, id); - if ((c = channel_lookup(id)) == NULL) - fatal("client_session2_setup: channel %d: unknown channel", id); + if ((c = channel_lookup(ssh, id)) == NULL) + fatal("%s: channel %d: unknown channel", __func__, id); packet_set_interactive(want_tty, options.ip_qos_interactive, options.ip_qos_bulk); @@ -2182,8 +2190,8 @@ client_session2_setup(int id, int want_tty, int want_subsystem, if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); - channel_request_start(id, "pty-req", 1); - client_expect_confirm(id, "PTY allocation", CONFIRM_TTY); + channel_request_start(ssh, id, "pty-req", 1); + client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); packet_put_cstring(term != NULL ? term : ""); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); @@ -2226,7 +2234,7 @@ client_session2_setup(int id, int want_tty, int want_subsystem, } debug("Sending env %s = %s", name, val); - channel_request_start(id, "env", 0); + channel_request_start(ssh, id, "env", 0); packet_put_cstring(name); packet_put_cstring(val); packet_send(); @@ -2241,19 +2249,20 @@ client_session2_setup(int id, int want_tty, int want_subsystem, if (want_subsystem) { debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "subsystem", 1); - client_expect_confirm(id, "subsystem", CONFIRM_CLOSE); + channel_request_start(ssh, id, "subsystem", 1); + client_expect_confirm(ssh, id, "subsystem", + CONFIRM_CLOSE); } else { debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "exec", 1); - client_expect_confirm(id, "exec", CONFIRM_CLOSE); + channel_request_start(ssh, id, "exec", 1); + client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); } packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); packet_send(); } else { - channel_request_start(id, "shell", 1); - client_expect_confirm(id, "shell", CONFIRM_CLOSE); + channel_request_start(ssh, id, "shell", 1); + client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); packet_send(); } } diff --git a/clientloop.h b/clientloop.h index ae83aa8cf..a1975ccc8 100644 --- a/clientloop.h +++ b/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.33 2016/09/30 09:19:13 markus Exp $ */ +/* $OpenBSD: clientloop.h,v 1.34 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -37,28 +37,31 @@ #include +struct ssh; + /* Client side main loop for the interactive session. */ -int client_loop(int, int, int); -int client_x11_get_proto(const char *, const char *, u_int, u_int, - char **, char **); +int client_loop(struct ssh *, int, int, int); +int client_x11_get_proto(struct ssh *, const char *, const char *, + u_int, u_int, char **, char **); void client_global_request_reply_fwd(int, u_int32_t, void *); -void client_session2_setup(int, int, int, const char *, struct termios *, - int, Buffer *, char **); -int client_request_tun_fwd(int, int, int); +void client_session2_setup(struct ssh *, int, int, int, + const char *, struct termios *, int, Buffer *, char **); +int client_request_tun_fwd(struct ssh *, int, int, int); void client_stop_mux(void); /* Escape filter for protocol 2 sessions */ void *client_new_escape_filter_ctx(int); -void client_filter_cleanup(int, void *); -int client_simple_escape_filter(Channel *, char *, int); +void client_filter_cleanup(struct ssh *, int, void *); +int client_simple_escape_filter(struct ssh *, Channel *, char *, int); /* Global request confirmation callbacks */ -typedef void global_confirm_cb(int, u_int32_t seq, void *); +typedef void global_confirm_cb(struct ssh *, int, u_int32_t, void *); void client_register_global_confirm(global_confirm_cb *, void *); /* Channel request confirmation callbacks */ enum confirm_action { CONFIRM_WARN = 0, CONFIRM_CLOSE, CONFIRM_TTY }; -void client_expect_confirm(int, const char *, enum confirm_action); +void client_expect_confirm(struct ssh *, int, const char *, + enum confirm_action); /* Multiplexing protocol version */ #define SSHMUX_VER 4 @@ -73,8 +76,8 @@ void client_expect_confirm(int, const char *, enum confirm_action); #define SSHMUX_COMMAND_CANCEL_FWD 7 /* Cancel forwarding(s) */ #define SSHMUX_COMMAND_PROXY 8 /* Open new connection */ -void muxserver_listen(void); +void muxserver_listen(struct ssh *); int muxclient(const char *); -void mux_exit_message(Channel *, int); -void mux_tty_alloc_failed(Channel *); +void mux_exit_message(struct ssh *, Channel *, int); +void mux_tty_alloc_failed(struct ssh *ssh, Channel *); diff --git a/monitor.c b/monitor.c index 8a7897bde..bdb4e8552 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.172 2017/06/24 06:34:38 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.173 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -1519,13 +1519,14 @@ mm_answer_pty_cleanup(int sock, Buffer *m) int mm_answer_term(int sock, Buffer *req) { + struct ssh *ssh = active_state; /* XXX */ extern struct monitor *pmonitor; int res, status; debug3("%s: tearing down sessions", __func__); /* The child is terminating */ - session_destroy_all(&mm_session_close); + session_destroy_all(ssh, &mm_session_close); #ifdef USE_PAM if (options.use_pam) diff --git a/monitor_wrap.c b/monitor_wrap.c index 25f3e9678..287af0667 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.92 2017/05/30 14:10:53 markus Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.93 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -242,6 +242,7 @@ mm_key_sign(struct sshkey *key, u_char **sigp, u_int *lenp, struct passwd * mm_getpwnamallow(const char *username) { + struct ssh *ssh = active_state; /* XXX */ Buffer m; struct passwd *pw; u_int len, i; @@ -296,6 +297,7 @@ out: copy_set_server_options(&options, newopts, 1); log_change_level(options.log_level); + process_permitopen(ssh, &options); free(newopts); buffer_free(&m); diff --git a/mux.c b/mux.c index 3dde4da40..9eee287b9 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.65 2017/06/09 06:47:13 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.66 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -161,22 +161,32 @@ struct mux_master_state { #define MUX_FWD_REMOTE 2 #define MUX_FWD_DYNAMIC 3 -static void mux_session_confirm(int, int, void *); -static void mux_stdio_confirm(int, int, void *); +static void mux_session_confirm(struct ssh *, int, int, void *); +static void mux_stdio_confirm(struct ssh *, int, int, void *); -static int process_mux_master_hello(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_new_session(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_alive_check(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_terminate(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_open_fwd(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_stop_listening(u_int, Channel *, Buffer *, Buffer *); -static int process_mux_proxy(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_master_hello(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_new_session(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_alive_check(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_terminate(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_open_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_close_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_stdio_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_stop_listening(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int process_mux_proxy(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); static const struct { u_int type; - int (*handler)(u_int, Channel *, Buffer *, Buffer *); + int (*handler)(struct ssh *, u_int, Channel *, + struct sshbuf *, struct sshbuf *); } mux_master_handlers[] = { { MUX_MSG_HELLO, process_mux_master_hello }, { MUX_C_NEW_SESSION, process_mux_new_session }, @@ -193,36 +203,36 @@ static const struct { /* Cleanup callback fired on closure of mux slave _session_ channel */ /* ARGSUSED */ static void -mux_master_session_cleanup_cb(int cid, void *unused) +mux_master_session_cleanup_cb(struct ssh *ssh, int cid, void *unused) { - Channel *cc, *c = channel_by_id(cid); + Channel *cc, *c = channel_by_id(ssh, cid); debug3("%s: entering for channel %d", __func__, cid); if (c == NULL) fatal("%s: channel_by_id(%i) == NULL", __func__, cid); if (c->ctl_chan != -1) { - if ((cc = channel_by_id(c->ctl_chan)) == NULL) + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal("%s: channel %d missing control channel %d", __func__, c->self, c->ctl_chan); c->ctl_chan = -1; cc->remote_id = -1; - chan_rcvd_oclose(cc); + chan_rcvd_oclose(ssh, cc); } - channel_cancel_cleanup(c->self); + channel_cancel_cleanup(ssh, c->self); } /* Cleanup callback fired on closure of mux slave _control_ channel */ /* ARGSUSED */ static void -mux_master_control_cleanup_cb(int cid, void *unused) +mux_master_control_cleanup_cb(struct ssh *ssh, int cid, void *unused) { - Channel *sc, *c = channel_by_id(cid); + Channel *sc, *c = channel_by_id(ssh, cid); debug3("%s: entering for channel %d", __func__, cid); if (c == NULL) fatal("%s: channel_by_id(%i) == NULL", __func__, cid); if (c->remote_id != -1) { - if ((sc = channel_by_id(c->remote_id)) == NULL) + if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) fatal("%s: channel %d missing session channel %d", __func__, c->self, c->remote_id); c->remote_id = -1; @@ -230,15 +240,15 @@ mux_master_control_cleanup_cb(int cid, void *unused) if (sc->type != SSH_CHANNEL_OPEN && sc->type != SSH_CHANNEL_OPENING) { debug2("%s: channel %d: not open", __func__, sc->self); - chan_mark_dead(sc); + chan_mark_dead(ssh, sc); } else { if (sc->istate == CHAN_INPUT_OPEN) - chan_read_failed(sc); + chan_read_failed(ssh, sc); if (sc->ostate == CHAN_OUTPUT_OPEN) - chan_write_failed(sc); + chan_write_failed(ssh, sc); } } - channel_cancel_cleanup(c->self); + channel_cancel_cleanup(ssh, c->self); } /* Check mux client environment variables before passing them to mux master. */ @@ -266,7 +276,8 @@ env_permitted(char *env) /* Mux master protocol message handlers */ static int -process_mux_master_hello(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_master_hello(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { u_int ver; struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; @@ -308,7 +319,8 @@ process_mux_master_hello(u_int rid, Channel *c, Buffer *m, Buffer *r) } static int -process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_new_session(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { Channel *nc; struct mux_session_confirm_ctx *cctx; @@ -453,7 +465,7 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) packetmax >>= 1; } - nc = channel_new("session", SSH_CHANNEL_OPENING, + nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, new_fd[0], new_fd[1], new_fd[2], window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); @@ -461,7 +473,7 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) c->remote_id = nc->self; /* link control -> session channel */ if (cctx->want_tty && escape_char != 0xffffffff) { - channel_register_filter(nc->self, + channel_register_filter(ssh, nc->self, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx((int)escape_char)); @@ -470,17 +482,19 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) debug2("%s: channel_new: %d linked to control channel %d", __func__, nc->self, nc->ctl_chan); - channel_send_open(nc->self); - channel_register_open_confirm(nc->self, mux_session_confirm, cctx); + channel_send_open(ssh, nc->self); + channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ - channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); + channel_register_cleanup(ssh, nc->self, + mux_master_session_cleanup_cb, 1); /* reply is deferred, sent by mux_session_confirm */ return 0; } static int -process_mux_alive_check(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_alive_check(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { debug2("%s: channel %d: alive check", __func__, c->self); @@ -493,7 +507,8 @@ process_mux_alive_check(u_int rid, Channel *c, Buffer *m, Buffer *r) } static int -process_mux_terminate(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_terminate(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { debug2("%s: channel %d: terminate request", __func__, c->self); @@ -582,7 +597,7 @@ compare_forward(struct Forward *a, struct Forward *b) } static void -mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) +mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) { struct mux_channel_confirm_ctx *fctx = ctxt; char *failmsg = NULL; @@ -590,7 +605,7 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) Channel *c; Buffer out; - if ((c = channel_by_id(fctx->cid)) == NULL) { + if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { /* no channel for reply */ error("%s: unknown channel", __func__); return; @@ -616,7 +631,7 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) buffer_put_int(&out, MUX_S_REMOTE_PORT); buffer_put_int(&out, fctx->rid); buffer_put_int(&out, rfwd->allocated_port); - channel_update_permitted_opens(rfwd->handle, + channel_update_permitted_opens(ssh, rfwd->handle, rfwd->allocated_port); } else { buffer_put_int(&out, MUX_S_OK); @@ -625,7 +640,7 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) goto out; } else { if (rfwd->listen_port == 0) - channel_update_permitted_opens(rfwd->handle, -1); + channel_update_permitted_opens(ssh, rfwd->handle, -1); if (rfwd->listen_path != NULL) xasprintf(&failmsg, "remote port forwarding failed for " "listen path %s", rfwd->listen_path); @@ -651,7 +666,7 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) buffer_put_cstring(&out, failmsg); free(failmsg); out: - buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); + buffer_put_string(c->output, buffer_ptr(&out), buffer_len(&out)); buffer_free(&out); if (c->mux_pause <= 0) fatal("%s: mux_pause %d", __func__, c->mux_pause); @@ -659,7 +674,8 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } static int -process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_open_fwd(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { struct Forward fwd; char *fwd_desc = NULL; @@ -727,13 +743,16 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) fwd.listen_port); goto invalid; } - if ((fwd.connect_port != PORT_STREAMLOCAL && fwd.connect_port >= 65536) - || (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { + if ((fwd.connect_port != PORT_STREAMLOCAL && + fwd.connect_port >= 65536) || + (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && + fwd.connect_port == 0)) { logit("%s: invalid connect port %u", __func__, fwd.connect_port); goto invalid; } - if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && fwd.connect_path == NULL) { + if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && + fwd.connect_path == NULL) { logit("%s: missing connect host", __func__); goto invalid; } @@ -784,7 +803,7 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { - if (!channel_setup_local_fwd_listener(&fwd, + if (!channel_setup_local_fwd_listener(ssh, &fwd, &options.fwd_opts)) { fail: logit("slave-requested %s failed", fwd_desc); @@ -798,7 +817,7 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } else { struct mux_channel_confirm_ctx *fctx; - fwd.handle = channel_request_remote_forwarding(&fwd); + fwd.handle = channel_request_remote_forwarding(ssh, &fwd); if (fwd.handle < 0) goto fail; add_remote_forward(&options, &fwd); @@ -827,7 +846,8 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } static int -process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_close_fwd(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { struct Forward fwd, *found_fwd; char *fwd_desc = NULL; @@ -908,11 +928,11 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) * However, for dynamic allocated listen ports we need * to use the actual listen port. */ - if (channel_request_rforward_cancel(found_fwd) == -1) + if (channel_request_rforward_cancel(ssh, found_fwd) == -1) error_reason = "port not in permitted opens"; } else { /* local and dynamic forwards */ /* Ditto */ - if (channel_cancel_lport_listener(&fwd, fwd.connect_port, + if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, &options.fwd_opts) == -1) error_reason = "port not found"; } @@ -942,7 +962,8 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } static int -process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_stdio_fwd(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { Channel *nc; char *reserved, *chost; @@ -1018,7 +1039,7 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) if (!isatty(new_fd[1])) set_nonblock(new_fd[1]); - nc = channel_connect_stdio_fwd(chost, cport, new_fd[0], new_fd[1]); + nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1]); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ @@ -1026,11 +1047,12 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) debug2("%s: channel_new: %d linked to control channel %d", __func__, nc->self, nc->ctl_chan); - channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); + channel_register_cleanup(ssh, nc->self, + mux_master_session_cleanup_cb, 1); cctx = xcalloc(1, sizeof(*cctx)); cctx->rid = rid; - channel_register_open_confirm(nc->self, mux_stdio_confirm, cctx); + channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ /* reply is deferred, sent by mux_session_confirm */ @@ -1039,7 +1061,7 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) /* Callback on open confirmation in mux master for a mux stdio fwd session. */ static void -mux_stdio_confirm(int id, int success, void *arg) +mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) { struct mux_stdio_confirm_ctx *cctx = arg; Channel *c, *cc; @@ -1047,9 +1069,9 @@ mux_stdio_confirm(int id, int success, void *arg) if (cctx == NULL) fatal("%s: cctx == NULL", __func__); - if ((c = channel_by_id(id)) == NULL) + if ((c = channel_by_id(ssh, id)) == NULL) fatal("%s: no channel for id %d", __func__, id); - if ((cc = channel_by_id(c->ctl_chan)) == NULL) + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal("%s: channel %d lacks control channel %d", __func__, id, c->ctl_chan); @@ -1072,7 +1094,7 @@ mux_stdio_confirm(int id, int success, void *arg) done: /* Send reply */ - buffer_put_string(&cc->output, buffer_ptr(&reply), buffer_len(&reply)); + buffer_put_string(cc->output, buffer_ptr(&reply), buffer_len(&reply)); buffer_free(&reply); if (cc->mux_pause <= 0) @@ -1083,7 +1105,8 @@ mux_stdio_confirm(int id, int success, void *arg) } static int -process_mux_stop_listening(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_stop_listening(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { debug("%s: channel %d: stop listening", __func__, c->self); @@ -1100,7 +1123,7 @@ process_mux_stop_listening(u_int rid, Channel *c, Buffer *m, Buffer *r) } if (mux_listener_channel != NULL) { - channel_free(mux_listener_channel); + channel_free(ssh, mux_listener_channel); client_stop_mux(); free(options.control_path); options.control_path = NULL; @@ -1116,7 +1139,8 @@ process_mux_stop_listening(u_int rid, Channel *c, Buffer *m, Buffer *r) } static int -process_mux_proxy(u_int rid, Channel *c, Buffer *m, Buffer *r) +process_mux_proxy(struct ssh *ssh, u_int rid, + Channel *c, Buffer *m, Buffer *r) { debug("%s: channel %d: proxy request", __func__, c->self); @@ -1129,7 +1153,7 @@ process_mux_proxy(u_int rid, Channel *c, Buffer *m, Buffer *r) /* Channel callbacks fired on read/write from mux slave fd */ static int -mux_master_read_cb(Channel *c) +mux_master_read_cb(struct ssh *ssh, Channel *c) { struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; Buffer in, out; @@ -1141,7 +1165,7 @@ mux_master_read_cb(Channel *c) if (c->mux_ctx == NULL) { state = xcalloc(1, sizeof(*state)); c->mux_ctx = state; - channel_register_cleanup(c->self, + channel_register_cleanup(ssh, c->self, mux_master_control_cleanup_cb, 0); /* Send hello */ @@ -1149,7 +1173,7 @@ mux_master_read_cb(Channel *c) buffer_put_int(&out, MUX_MSG_HELLO); buffer_put_int(&out, SSHMUX_VER); /* no extensions */ - buffer_put_string(&c->output, buffer_ptr(&out), + buffer_put_string(c->output, buffer_ptr(&out), buffer_len(&out)); buffer_free(&out); debug3("%s: channel %d: hello sent", __func__, c->self); @@ -1160,7 +1184,7 @@ mux_master_read_cb(Channel *c) buffer_init(&out); /* Channel code ensures that we receive whole packets */ - if ((ptr = buffer_get_string_ptr_ret(&c->input, &have)) == NULL) { + if ((ptr = buffer_get_string_ptr_ret(c->input, &have)) == NULL) { malf: error("%s: malformed message", __func__); goto out; @@ -1186,7 +1210,8 @@ mux_master_read_cb(Channel *c) for (i = 0; mux_master_handlers[i].handler != NULL; i++) { if (type == mux_master_handlers[i].type) { - ret = mux_master_handlers[i].handler(rid, c, &in, &out); + ret = mux_master_handlers[i].handler(ssh, rid, + c, &in, &out); break; } } @@ -1199,7 +1224,7 @@ mux_master_read_cb(Channel *c) } /* Enqueue reply packet */ if (buffer_len(&out) != 0) { - buffer_put_string(&c->output, buffer_ptr(&out), + buffer_put_string(c->output, buffer_ptr(&out), buffer_len(&out)); } out: @@ -1209,7 +1234,7 @@ mux_master_read_cb(Channel *c) } void -mux_exit_message(Channel *c, int exitval) +mux_exit_message(struct ssh *ssh, Channel *c, int exitval) { Buffer m; Channel *mux_chan; @@ -1217,7 +1242,7 @@ mux_exit_message(Channel *c, int exitval) debug3("%s: channel %d: exit message, exitval %d", __func__, c->self, exitval); - if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) + if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal("%s: channel %d missing mux channel %d", __func__, c->self, c->ctl_chan); @@ -1227,19 +1252,19 @@ mux_exit_message(Channel *c, int exitval) buffer_put_int(&m, c->self); buffer_put_int(&m, exitval); - buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); + buffer_put_string(mux_chan->output, buffer_ptr(&m), buffer_len(&m)); buffer_free(&m); } void -mux_tty_alloc_failed(Channel *c) +mux_tty_alloc_failed(struct ssh *ssh, Channel *c) { Buffer m; Channel *mux_chan; debug3("%s: channel %d: TTY alloc failed", __func__, c->self); - if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) + if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal("%s: channel %d missing mux channel %d", __func__, c->self, c->ctl_chan); @@ -1248,13 +1273,13 @@ mux_tty_alloc_failed(Channel *c) buffer_put_int(&m, MUX_S_TTY_ALLOC_FAIL); buffer_put_int(&m, c->self); - buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); + buffer_put_string(mux_chan->output, buffer_ptr(&m), buffer_len(&m)); buffer_free(&m); } /* Prepare a mux master to listen on a Unix domain socket. */ void -muxserver_listen(void) +muxserver_listen(struct ssh *ssh) { mode_t old_umask; char *orig_control_path = options.control_path; @@ -1327,7 +1352,7 @@ muxserver_listen(void) set_nonblock(muxserver_sock); - mux_listener_channel = channel_new("mux listener", + mux_listener_channel = channel_new(ssh, "mux listener", SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, options.control_path, 1); @@ -1338,7 +1363,7 @@ muxserver_listen(void) /* Callback on open confirmation in mux master for a mux client session. */ static void -mux_session_confirm(int id, int success, void *arg) +mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) { struct mux_session_confirm_ctx *cctx = arg; const char *display; @@ -1348,9 +1373,9 @@ mux_session_confirm(int id, int success, void *arg) if (cctx == NULL) fatal("%s: cctx == NULL", __func__); - if ((c = channel_by_id(id)) == NULL) + if ((c = channel_by_id(ssh, id)) == NULL) fatal("%s: no channel for id %d", __func__, id); - if ((cc = channel_by_id(c->ctl_chan)) == NULL) + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal("%s: channel %d lacks control channel %d", __func__, id, c->ctl_chan); @@ -1369,27 +1394,27 @@ mux_session_confirm(int id, int success, void *arg) char *proto, *data; /* Get reasonable local authentication information. */ - if (client_x11_get_proto(display, options.xauth_location, + if (client_x11_get_proto(ssh, display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); - x11_request_forwarding_with_spoofing(id, display, proto, - data, 1); + x11_request_forwarding_with_spoofing(ssh, id, + display, proto, data, 1); /* XXX exit_on_forward_failure */ - client_expect_confirm(id, "X11 forwarding", + client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); } } if (cctx->want_agent_fwd && options.forward_agent) { debug("Requesting authentication agent forwarding."); - channel_request_start(id, "auth-agent-req@openssh.com", 0); + channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); packet_send(); } - client_session2_setup(id, cctx->want_tty, cctx->want_subsys, + client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); debug3("%s: sending success reply", __func__); @@ -1401,7 +1426,7 @@ mux_session_confirm(int id, int success, void *arg) done: /* Send reply */ - buffer_put_string(&cc->output, buffer_ptr(&reply), buffer_len(&reply)); + buffer_put_string(cc->output, buffer_ptr(&reply), buffer_len(&reply)); buffer_free(&reply); if (cc->mux_pause <= 0) diff --git a/nchan.c b/nchan.c index 36da8904a..74c855c90 100644 --- a/nchan.c +++ b/nchan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nchan.c,v 1.65 2017/04/30 23:28:42 djm Exp $ */ +/* $OpenBSD: nchan.c,v 1.66 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * @@ -34,7 +34,8 @@ #include "openbsd-compat/sys-queue.h" #include "ssh2.h" -#include "buffer.h" +#include "sshbuf.h" +#include "ssherr.h" #include "packet.h" #include "channels.h" #include "compat.h" @@ -73,15 +74,15 @@ /* * ACTIONS: should never update the channel states */ -static void chan_send_eof2(Channel *); -static void chan_send_eow2(Channel *); +static void chan_send_eof2(struct ssh *, Channel *); +static void chan_send_eow2(struct ssh *, Channel *); /* helper */ -static void chan_shutdown_write(Channel *); -static void chan_shutdown_read(Channel *); +static void chan_shutdown_write(struct ssh *, Channel *); +static void chan_shutdown_read(struct ssh *, Channel *); -static char *ostates[] = { "open", "drain", "wait_ieof", "closed" }; -static char *istates[] = { "open", "drain", "wait_oclose", "closed" }; +static const char *ostates[] = { "open", "drain", "wait_ieof", "closed" }; +static const char *istates[] = { "open", "drain", "wait_oclose", "closed" }; static void chan_set_istate(Channel *c, u_int next) @@ -104,12 +105,12 @@ chan_set_ostate(Channel *c, u_int next) } void -chan_read_failed(Channel *c) +chan_read_failed(struct ssh *ssh, Channel *c) { debug2("channel %d: read failed", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: - chan_shutdown_read(c); + chan_shutdown_read(ssh, c); chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN); break; default: @@ -120,10 +121,10 @@ chan_read_failed(Channel *c) } void -chan_ibuf_empty(Channel *c) +chan_ibuf_empty(struct ssh *ssh, Channel *c) { debug2("channel %d: ibuf empty", c->self); - if (buffer_len(&c->input)) { + if (sshbuf_len(c->input)) { error("channel %d: chan_ibuf_empty for non empty buffer", c->self); return; @@ -131,7 +132,7 @@ chan_ibuf_empty(Channel *c) switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL))) - chan_send_eof2(c); + chan_send_eof2(ssh, c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; default: @@ -142,17 +143,17 @@ chan_ibuf_empty(Channel *c) } void -chan_obuf_empty(Channel *c) +chan_obuf_empty(struct ssh *ssh, Channel *c) { debug2("channel %d: obuf empty", c->self); - if (buffer_len(&c->output)) { + if (sshbuf_len(c->output)) { error("channel %d: chan_obuf_empty for non empty buffer", c->self); return; } switch (c->ostate) { case CHAN_OUTPUT_WAIT_DRAIN: - chan_shutdown_write(c); + chan_shutdown_write(ssh, c); chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: @@ -163,26 +164,29 @@ chan_obuf_empty(Channel *c) } void -chan_rcvd_eow(Channel *c) +chan_rcvd_eow(struct ssh *ssh, Channel *c) { debug2("channel %d: rcvd eow", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: - chan_shutdown_read(c); + chan_shutdown_read(ssh, c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; } } static void -chan_send_eof2(Channel *c) +chan_send_eof2(struct ssh *ssh, Channel *c) { + int r; + debug2("channel %d: send eof", c->self); switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: - packet_start(SSH2_MSG_CHANNEL_EOF); - packet_put_int(c->remote_id); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EOF)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send CHANNEL_EOF: %s", __func__, ssh_err(r)); c->flags |= CHAN_EOF_SENT; break; default: @@ -193,8 +197,10 @@ chan_send_eof2(Channel *c) } static void -chan_send_close2(Channel *c) +chan_send_close2(struct ssh *ssh, Channel *c) { + int r; + debug2("channel %d: send close", c->self); if (c->ostate != CHAN_OUTPUT_CLOSED || c->istate != CHAN_INPUT_CLOSED) { @@ -203,16 +209,19 @@ chan_send_close2(Channel *c) } else if (c->flags & CHAN_CLOSE_SENT) { error("channel %d: already sent close", c->self); } else { - packet_start(SSH2_MSG_CHANNEL_CLOSE); - packet_put_int(c->remote_id); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_CLOSE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send CHANNEL_EOF: %s", __func__, ssh_err(r)); c->flags |= CHAN_CLOSE_SENT; } } static void -chan_send_eow2(Channel *c) +chan_send_eow2(struct ssh *ssh, Channel *c) { + int r; + debug2("channel %d: send eow", c->self); if (c->ostate == CHAN_OUTPUT_CLOSED) { error("channel %d: must not sent eow on closed output", @@ -221,30 +230,31 @@ chan_send_eow2(Channel *c) } if (!(datafellows & SSH_NEW_OPENSSH)) return; - packet_start(SSH2_MSG_CHANNEL_REQUEST); - packet_put_int(c->remote_id); - packet_put_cstring("eow@openssh.com"); - packet_put_char(0); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_cstring(ssh, "eow@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send CHANNEL_EOF: %s", __func__, ssh_err(r)); } /* shared */ void -chan_rcvd_ieof(Channel *c) +chan_rcvd_ieof(struct ssh *ssh, Channel *c) { debug2("channel %d: rcvd eof", c->self); c->flags |= CHAN_EOF_RCVD; if (c->ostate == CHAN_OUTPUT_OPEN) chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN && - buffer_len(&c->output) == 0 && + sshbuf_len(c->output) == 0 && !CHANNEL_EFD_OUTPUT_ACTIVE(c)) - chan_obuf_empty(c); + chan_obuf_empty(ssh, c); } void -chan_rcvd_oclose(Channel *c) +chan_rcvd_oclose(struct ssh *ssh, Channel *c) { debug2("channel %d: rcvd close", c->self); if (!(c->flags & CHAN_LOCAL)) { @@ -270,27 +280,27 @@ chan_rcvd_oclose(Channel *c) } switch (c->istate) { case CHAN_INPUT_OPEN: - chan_shutdown_read(c); + chan_shutdown_read(ssh, c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; case CHAN_INPUT_WAIT_DRAIN: if (!(c->flags & CHAN_LOCAL)) - chan_send_eof2(c); + chan_send_eof2(ssh, c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; } } void -chan_write_failed(Channel *c) +chan_write_failed(struct ssh *ssh, Channel *c) { debug2("channel %d: write failed", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: case CHAN_OUTPUT_WAIT_DRAIN: - chan_shutdown_write(c); + chan_shutdown_write(ssh, c); if (strcmp(c->ctype, "session") == 0) - chan_send_eow2(c); + chan_send_eow2(ssh, c); chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: @@ -301,13 +311,13 @@ chan_write_failed(Channel *c) } void -chan_mark_dead(Channel *c) +chan_mark_dead(struct ssh *ssh, Channel *c) { c->type = SSH_CHANNEL_ZOMBIE; } int -chan_is_dead(Channel *c, int do_send) +chan_is_dead(struct ssh *ssh, Channel *c, int do_send) { if (c->type == SSH_CHANNEL_ZOMBIE) { debug2("channel %d: zombie", c->self); @@ -318,9 +328,9 @@ chan_is_dead(Channel *c, int do_send) if ((datafellows & SSH_BUG_EXTEOF) && c->extended_usage == CHAN_EXTENDED_WRITE && c->efd != -1 && - buffer_len(&c->extended) > 0) { - debug2("channel %d: active efd: %d len %d", - c->self, c->efd, buffer_len(&c->extended)); + sshbuf_len(c->extended) > 0) { + debug2("channel %d: active efd: %d len %zu", + c->self, c->efd, sshbuf_len(c->extended)); return 0; } if (c->flags & CHAN_LOCAL) { @@ -329,7 +339,7 @@ chan_is_dead(Channel *c, int do_send) } if (!(c->flags & CHAN_CLOSE_SENT)) { if (do_send) { - chan_send_close2(c); + chan_send_close2(ssh, c); } else { /* channel would be dead if we sent a close */ if (c->flags & CHAN_CLOSE_RCVD) { @@ -349,9 +359,9 @@ chan_is_dead(Channel *c, int do_send) /* helper */ static void -chan_shutdown_write(Channel *c) +chan_shutdown_write(struct ssh *ssh, Channel *c) { - buffer_clear(&c->output); + sshbuf_reset(c->output); if (c->type == SSH_CHANNEL_LARVAL) return; /* shutdown failure is allowed if write failed already */ @@ -362,7 +372,7 @@ chan_shutdown_write(Channel *c) "shutdown() failed for fd %d: %.100s", c->self, c->sock, strerror(errno)); } else { - if (channel_close_fd(&c->wfd) < 0) + if (channel_close_fd(ssh, &c->wfd) < 0) logit("channel %d: chan_shutdown_write: " "close() failed for fd %d: %.100s", c->self, c->wfd, strerror(errno)); @@ -370,7 +380,7 @@ chan_shutdown_write(Channel *c) } static void -chan_shutdown_read(Channel *c) +chan_shutdown_read(struct ssh *ssh, Channel *c) { if (c->type == SSH_CHANNEL_LARVAL) return; @@ -388,7 +398,7 @@ chan_shutdown_read(Channel *c) c->self, c->sock, c->istate, c->ostate, strerror(errno)); } else { - if (channel_close_fd(&c->rfd) < 0) + if (channel_close_fd(ssh, &c->rfd) < 0) logit("channel %d: chan_shutdown_read: " "close() failed for fd %d: %.100s", c->self, c->rfd, strerror(errno)); diff --git a/packet.c b/packet.c index ff69b6601..f114ea52c 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.263 2017/07/23 23:37:02 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.264 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2090,35 +2090,6 @@ ssh_packet_get_maxsize(struct ssh *ssh) return ssh->state->max_packet_size; } -/* - * 9.2. Ignored Data Message - * - * byte SSH_MSG_IGNORE - * string data - * - * All implementations MUST understand (and ignore) this message at any - * time (after receiving the protocol version). No implementation is - * required to send them. This message can be used as an additional - * protection measure against advanced traffic analysis techniques. - */ -void -ssh_packet_send_ignore(struct ssh *ssh, int nbytes) -{ - u_int32_t rnd = 0; - int r, i; - - if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || - (r = sshpkt_put_u32(ssh, nbytes)) != 0) - fatal("%s: %s", __func__, ssh_err(r)); - for (i = 0; i < nbytes; i++) { - if (i % 4 == 0) - rnd = arc4random(); - if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) - fatal("%s: %s", __func__, ssh_err(r)); - rnd >>= 8; - } -} - void ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds) { @@ -2538,6 +2509,12 @@ sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp); } +int +sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) +{ + return sshbuf_peek_string_direct(ssh->state->incoming_packet, valp, lenp); +} + int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) { @@ -2621,6 +2598,37 @@ ssh_packet_send_mux(struct ssh *ssh) return 0; } +/* + * 9.2. Ignored Data Message + * + * byte SSH_MSG_IGNORE + * string data + * + * All implementations MUST understand (and ignore) this message at any + * time (after receiving the protocol version). No implementation is + * required to send them. This message can be used as an additional + * protection measure against advanced traffic analysis techniques. + */ +int +sshpkt_msg_ignore(struct ssh *ssh, u_int nbytes) +{ + u_int32_t rnd = 0; + int r; + u_int i; + + if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || + (r = sshpkt_put_u32(ssh, nbytes)) != 0) + return r; + for (i = 0; i < nbytes; i++) { + if (i % 4 == 0) + rnd = arc4random(); + if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) + return r; + rnd >>= 8; + } + return 0; +} + /* send it */ int diff --git a/packet.h b/packet.h index 6ce6dd560..40837e9db 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.81 2017/05/31 08:09:45 markus Exp $ */ +/* $OpenBSD: packet.h,v 1.82 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -80,6 +80,9 @@ struct ssh { /* Client/Server authentication context */ void *authctxt; + /* Channels context */ + struct ssh_channels *chanctxt; + /* APP data */ void *app_data; }; @@ -143,7 +146,6 @@ int ssh_packet_not_very_much_data_to_write(struct ssh *); int ssh_packet_connection_is_on_socket(struct ssh *); int ssh_packet_remaining(struct ssh *); -void ssh_packet_send_ignore(struct ssh *, int); void tty_make_modes(int, struct termios *); void tty_parse_modes(int, int *); @@ -174,6 +176,7 @@ int sshpkt_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int sshpkt_add_padding(struct ssh *, u_char); void sshpkt_fatal(struct ssh *ssh, const char *tag, int r); +int sshpkt_msg_ignore(struct ssh *, u_int); int sshpkt_put(struct ssh *ssh, const void *v, size_t len); int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b); @@ -192,6 +195,7 @@ int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp); int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp); int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp); int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); +int sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp); int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g); int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v); diff --git a/servconf.c b/servconf.c index ed1fc71cf..e0e43c3dd 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.309 2017/06/24 06:34:38 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.310 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -149,7 +149,7 @@ initialize_server_options(ServerOptions *options) options->num_authkeys_files = 0; options->num_accept_env = 0; options->permit_tun = -1; - options->num_permitted_opens = -1; + options->permitted_opens = NULL; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->authorized_keys_command = NULL; @@ -697,6 +697,44 @@ process_queued_listen_addrs(ServerOptions *options) options->num_queued_listens = 0; } +/* + * Inform channels layer of permitopen options from configuration. + */ +void +process_permitopen(struct ssh *ssh, ServerOptions *options) +{ + u_int i; + int port; + char *host, *arg, *oarg; + + channel_clear_adm_permitted_opens(ssh); + if (options->num_permitted_opens == 0) + return; /* permit any */ + + /* handle keywords: "any" / "none" */ + if (options->num_permitted_opens == 1 && + strcmp(options->permitted_opens[0], "any") == 0) + return; + if (options->num_permitted_opens == 1 && + strcmp(options->permitted_opens[0], "none") == 0) { + channel_disable_adm_local_opens(ssh); + return; + } + /* Otherwise treat it as a list of permitted host:port */ + for (i = 0; i < options->num_permitted_opens; i++) { + oarg = arg = xstrdup(options->permitted_opens[i]); + host = hpdelim(&arg); + if (host == NULL) + fatal("%s: missing host in PermitOpen", __func__); + host = cleanhostname(host); + if (arg == NULL || ((port = permitopen_port(arg)) < 0)) + fatal("%s: bad port number in PermitOpen", __func__); + /* Send it to channels layer */ + channel_add_adm_permitted_opens(ssh, host, port); + free(oarg); + } +} + struct connection_info * get_connection_info(int populate, int use_dns) { @@ -954,7 +992,7 @@ process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, struct connection_info *connectinfo) { - char *cp, **charptr, *arg, *p; + char *cp, **charptr, *arg, *arg2, *p; int cmdline = 0, *intptr, value, value2, n, port; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; @@ -1625,24 +1663,17 @@ process_server_config_line(ServerOptions *options, char *line, if (!arg || *arg == '\0') fatal("%s line %d: missing PermitOpen specification", filename, linenum); - n = options->num_permitted_opens; /* modified later */ - if (strcmp(arg, "any") == 0) { - if (*activep && n == -1) { - channel_clear_adm_permitted_opens(); - options->num_permitted_opens = 0; - } - break; - } - if (strcmp(arg, "none") == 0) { - if (*activep && n == -1) { + i = options->num_permitted_opens; /* modified later */ + if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { + if (*activep && i == 0) options->num_permitted_opens = 1; - channel_disable_adm_local_opens(); - } + options->permitted_opens = xcalloc(1, + sizeof(*options->permitted_opens)); + options->permitted_opens[0] = xstrdup(arg); break; } - if (*activep && n == -1) - channel_clear_adm_permitted_opens(); for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { + arg2 = xstrdup(arg); p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: missing host in PermitOpen", @@ -1651,9 +1682,16 @@ process_server_config_line(ServerOptions *options, char *line, if (arg == NULL || ((port = permitopen_port(arg)) < 0)) fatal("%s line %d: bad port number in " "PermitOpen", filename, linenum); - if (*activep && n == -1) - options->num_permitted_opens = - channel_add_adm_permitted_opens(p, port); + if (*activep && i == 0) { + options->permitted_opens = xrecallocarray( + options->permitted_opens, + options->num_permitted_opens, + options->num_permitted_opens + 1, + sizeof(*options->permitted_opens)); + i = options->num_permitted_opens++; + options->permitted_opens[i] = arg2; + } else + free(arg2); } break; @@ -2352,5 +2390,12 @@ dump_config(ServerOptions *o) printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, o->rekey_interval); - channel_print_adm_permitted_opens(); + printf("permitopen"); + if (o->num_permitted_opens == 0) + printf(" any"); + else { + for (i = 0; i < o->num_permitted_opens; i++) + printf(" %s", o->permitted_opens[i]); + } + printf("\n"); } diff --git a/servconf.h b/servconf.h index c2848a765..ffcbc3319 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.124 2017/06/24 06:34:38 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.125 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -48,12 +48,19 @@ #define FORWARD_LOCAL (1<<1) #define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) +/* PermitOpen */ +#define PERMITOPEN_ANY 0 +#define PERMITOPEN_NONE -2 + #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ /* Magic name for internal sftp-server */ #define INTERNAL_SFTP_NAME "internal-sftp" +struct ssh; +struct fwd_perm_list; + typedef struct { u_int num_ports; u_int ports_from_cmdline; @@ -169,7 +176,8 @@ typedef struct { int permit_tun; - int num_permitted_opens; + char **permitted_opens; + u_int num_permitted_opens; /* May also be one of PERMITOPEN_* */ char *chroot_directory; char *revoked_keys_file; @@ -229,6 +237,7 @@ struct connection_info { M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ M_CP_STRARRAYOPT(accept_env, num_accept_env); \ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ + M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ } while (0) struct connection_info *get_connection_info(int, int); @@ -236,6 +245,7 @@ void initialize_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *); int process_server_config_line(ServerOptions *, char *, const char *, int, int *, struct connection_info *); +void process_permitopen(struct ssh *ssh, ServerOptions *options); void load_server_config(const char *, Buffer *); void parse_server_config(ServerOptions *, const char *, Buffer *, struct connection_info *); diff --git a/serverloop.c b/serverloop.c index bc56709b1..567159410 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.196 2017/08/30 03:59:08 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.197 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -165,7 +165,7 @@ sigterm_handler(int sig) } static void -client_alive_check(void) +client_alive_check(struct ssh *ssh) { int channel_id; @@ -179,12 +179,13 @@ client_alive_check(void) * send a bogus global/channel request with "wantreply", * we should get back a failure */ - if ((channel_id = channel_find_open()) == -1) { + if ((channel_id = channel_find_open(ssh)) == -1) { packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("keepalive@openssh.com"); packet_put_char(1); /* boolean: want reply */ } else { - channel_request_start(channel_id, "keepalive@openssh.com", 1); + channel_request_start(ssh, channel_id, + "keepalive@openssh.com", 1); } packet_send(); } @@ -196,7 +197,8 @@ client_alive_check(void) * for the duration of the wait (0 = infinite). */ static void -wait_until_can_do_something(int connection_in, int connection_out, +wait_until_can_do_something(struct ssh *ssh, + int connection_in, int connection_out, fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, u_int64_t max_time_ms) { @@ -207,7 +209,7 @@ wait_until_can_do_something(int connection_in, int connection_out, static time_t last_client_time; /* Allocate and update select() masks for channel descriptors. */ - channel_prepare_select(active_state, readsetp, writesetp, maxfdp, + channel_prepare_select(ssh, readsetp, writesetp, maxfdp, nallocp, &minwait_secs); /* XXX need proper deadline system for rekey/client alive */ @@ -273,12 +275,12 @@ wait_until_can_do_something(int connection_in, int connection_out, time_t now = monotime(); if (ret == 0) { /* timeout */ - client_alive_check(); + client_alive_check(ssh); } else if (FD_ISSET(connection_in, *readsetp)) { last_client_time = now; } else if (last_client_time != 0 && last_client_time + options.client_alive_interval <= now) { - client_alive_check(); + client_alive_check(ssh); last_client_time = now; } } @@ -291,9 +293,8 @@ wait_until_can_do_something(int connection_in, int connection_out, * in buffers and processed later. */ static int -process_input(fd_set *readset, int connection_in) +process_input(struct ssh *ssh, fd_set *readset, int connection_in) { - struct ssh *ssh = active_state; /* XXX */ int len; char buf[16384]; @@ -333,13 +334,13 @@ process_output(fd_set *writeset, int connection_out) } static void -process_buffered_input_packets(void) +process_buffered_input_packets(struct ssh *ssh) { - ssh_dispatch_run_fatal(active_state, DISPATCH_NONBLOCK, NULL); + ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, NULL); } static void -collect_children(void) +collect_children(struct ssh *ssh) { pid_t pid; sigset_t oset, nset; @@ -354,14 +355,14 @@ collect_children(void) while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) if (pid > 0) - session_close_by_pid(pid, status); + session_close_by_pid(ssh, pid, status); child_terminated = 0; } sigprocmask(SIG_SETMASK, &oset, NULL); } void -server_loop2(Authctxt *authctxt) +server_loop2(struct ssh *ssh, Authctxt *authctxt) { fd_set *readset = NULL, *writeset = NULL; int max_fd; @@ -389,18 +390,17 @@ server_loop2(Authctxt *authctxt) server_init_dispatch(); for (;;) { - process_buffered_input_packets(); + process_buffered_input_packets(ssh); - if (!ssh_packet_is_rekeying(active_state) && + if (!ssh_packet_is_rekeying(ssh) && packet_not_very_much_data_to_write()) - channel_output_poll(); - if (options.rekey_interval > 0 && - !ssh_packet_is_rekeying(active_state)) + channel_output_poll(ssh); + if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) rekey_timeout_ms = packet_get_rekey_timeout() * 1000; else rekey_timeout_ms = 0; - wait_until_can_do_something(connection_in, connection_out, + wait_until_can_do_something(ssh, connection_in, connection_out, &readset, &writeset, &max_fd, &nalloc, rekey_timeout_ms); if (received_sigterm) { @@ -409,23 +409,23 @@ server_loop2(Authctxt *authctxt) cleanup_exit(255); } - collect_children(); - if (!ssh_packet_is_rekeying(active_state)) - channel_after_select(active_state, readset, writeset); - if (process_input(readset, connection_in) < 0) + collect_children(ssh); + if (!ssh_packet_is_rekeying(ssh)) + channel_after_select(ssh, readset, writeset); + if (process_input(ssh, readset, connection_in) < 0) break; process_output(writeset, connection_out); } - collect_children(); + collect_children(ssh); free(readset); free(writeset); /* free all channels, no more reads and writes */ - channel_free_all(); + channel_free_all(ssh); /* free remaining sessions, e.g. remove wtmp entries */ - session_destroy_all(NULL); + session_destroy_all(ssh, NULL); } static int @@ -442,7 +442,7 @@ server_input_keep_alive(int type, u_int32_t seq, struct ssh *ssh) } static Channel * -server_request_direct_tcpip(int *reason, const char **errmsg) +server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg) { Channel *c = NULL; char *target, *originator; @@ -460,7 +460,7 @@ server_request_direct_tcpip(int *reason, const char **errmsg) /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag && !options.disable_forwarding) { - c = channel_connect_to_port(target, target_port, + c = channel_connect_to_port(ssh, target, target_port, "direct-tcpip", "direct-tcpip", reason, errmsg); } else { logit("refused local port forward: " @@ -477,7 +477,7 @@ server_request_direct_tcpip(int *reason, const char **errmsg) } static Channel * -server_request_direct_streamlocal(void) +server_request_direct_streamlocal(struct ssh *ssh) { Channel *c = NULL; char *target, *originator; @@ -499,7 +499,7 @@ server_request_direct_streamlocal(void) if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag && !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) { - c = channel_connect_to_path(target, + c = channel_connect_to_path(ssh, target, "direct-streamlocal@openssh.com", "direct-streamlocal"); } else { logit("refused streamlocal port forward: " @@ -514,7 +514,7 @@ server_request_direct_streamlocal(void) } static Channel * -server_request_tun(void) +server_request_tun(struct ssh *ssh) { Channel *c = NULL; int mode, tun; @@ -544,7 +544,7 @@ server_request_tun(void) sock = tun_open(tun, mode); if (sock < 0) goto done; - c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1, + c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) @@ -560,7 +560,7 @@ server_request_tun(void) } static Channel * -server_request_session(void) +server_request_session(struct ssh *ssh) { Channel *c; @@ -578,15 +578,15 @@ server_request_session(void) * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all * CHANNEL_REQUEST messages is registered. */ - c = channel_new("session", SSH_CHANNEL_LARVAL, + c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 0, "server-session", 1); if (session_open(the_authctxt, c->self) != 1) { debug("session open failed, free channel %d", c->self); - channel_free(c); + channel_free(ssh, c); return NULL; } - channel_register_cleanup(c->self, session_close_by_channel, 0); + channel_register_cleanup(ssh, c->self, session_close_by_channel, 0); return c; } @@ -608,13 +608,13 @@ server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "session") == 0) { - c = server_request_session(); + c = server_request_session(ssh); } else if (strcmp(ctype, "direct-tcpip") == 0) { - c = server_request_direct_tcpip(&reason, &errmsg); + c = server_request_direct_tcpip(ssh, &reason, &errmsg); } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { - c = server_request_direct_streamlocal(); + c = server_request_direct_streamlocal(ssh); } else if (strcmp(ctype, "tun@openssh.com") == 0) { - c = server_request_tun(); + c = server_request_tun(ssh); } if (c != NULL) { debug("server_input_channel_open: confirm %s", ctype); @@ -645,9 +645,8 @@ server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) } static int -server_input_hostkeys_prove(struct sshbuf **respp) +server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) { - struct ssh *ssh = active_state; /* XXX */ struct sshbuf *resp = NULL; struct sshbuf *sigbuf = NULL; struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; @@ -750,7 +749,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ - success = channel_setup_remote_fwd_listener(&fwd, + success = channel_setup_remote_fwd_listener(ssh, &fwd, &allocated_listen_port, &options.fwd_opts); } free(fwd.listen_host); @@ -768,7 +767,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) debug("%s: cancel-tcpip-forward addr %s port %d", __func__, fwd.listen_host, fwd.listen_port); - success = channel_cancel_rport_listener(&fwd); + success = channel_cancel_rport_listener(ssh, &fwd); free(fwd.listen_host); } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { struct Forward fwd; @@ -787,7 +786,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) "streamlocal forwarding."); } else { /* Start listening on the socket */ - success = channel_setup_remote_fwd_listener( + success = channel_setup_remote_fwd_listener(ssh, &fwd, NULL, &options.fwd_opts); } free(fwd.listen_path); @@ -799,19 +798,19 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) debug("%s: cancel-streamlocal-forward path %s", __func__, fwd.listen_path); - success = channel_cancel_rport_listener(&fwd); + success = channel_cancel_rport_listener(ssh, &fwd); free(fwd.listen_path); } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { no_more_sessions = 1; success = 1; } else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) { - success = server_input_hostkeys_prove(&resp); + success = server_input_hostkeys_prove(ssh, &resp); } if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); if (success && resp != NULL) - ssh_packet_put_raw(active_state, sshbuf_ptr(resp), + ssh_packet_put_raw(ssh, sshbuf_ptr(resp), sshbuf_len(resp)); packet_send(); packet_write_wait(); @@ -835,15 +834,15 @@ server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) debug("server_input_channel_req: channel %d request %s reply %d", id, rtype, reply); - if ((c = channel_lookup(id)) == NULL) + if ((c = channel_lookup(ssh, id)) == NULL) packet_disconnect("server_input_channel_req: " "unknown channel %d", id); if (!strcmp(rtype, "eow@openssh.com")) { packet_check_eom(); - chan_rcvd_eow(c); + chan_rcvd_eow(ssh, c); } else if ((c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) - success = session_input_channel_req(c, rtype); + success = session_input_channel_req(ssh, c, rtype); if (reply && !(c->flags & CHAN_CLOSE_SENT)) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); diff --git a/serverloop.h b/serverloop.h index d5fbda16f..fd2cf63f7 100644 --- a/serverloop.h +++ b/serverloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.h,v 1.7 2016/08/13 17:47:41 markus Exp $ */ +/* $OpenBSD: serverloop.h,v 1.8 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -21,6 +21,8 @@ #ifndef SERVERLOOP_H #define SERVERLOOP_H -void server_loop2(Authctxt *); +struct ssh; + +void server_loop2(struct ssh *, Authctxt *); #endif diff --git a/session.c b/session.c index cd03fd423..4bccb62d1 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.291 2017/08/18 05:36:45 djm Exp $ */ +/* $OpenBSD: session.c,v 1.292 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -113,24 +113,24 @@ /* func */ Session *session_new(void); -void session_set_fds(Session *, int, int, int, int, int); +void session_set_fds(struct ssh *, Session *, int, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); -int session_setup_x11fwd(Session *); -int do_exec_pty(Session *, const char *); -int do_exec_no_pty(Session *, const char *); -int do_exec(Session *, const char *); -void do_login(Session *, const char *); +int session_setup_x11fwd(struct ssh *, Session *); +int do_exec_pty(struct ssh *, Session *, const char *); +int do_exec_no_pty(struct ssh *, Session *, const char *); +int do_exec(struct ssh *, Session *, const char *); +void do_login(struct ssh *, Session *, const char *); +void do_child(struct ssh *, Session *, const char *); #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s); #endif -void do_child(Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); -static void do_authenticated2(Authctxt *); +static void do_authenticated2(struct ssh *, Authctxt *); -static int session_pty_req(Session *); +static int session_pty_req(struct ssh *, Session *); /* import */ extern ServerOptions options; @@ -183,7 +183,7 @@ auth_sock_cleanup_proc(struct passwd *pw) } static int -auth_input_request_forwarding(struct passwd * pw) +auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw) { Channel *nc; int sock = -1; @@ -223,7 +223,7 @@ auth_input_request_forwarding(struct passwd * pw) goto authsock_err; /* Allocate a channel for the authentication agent socket. */ - nc = channel_new("auth socket", + nc = channel_new(ssh, "auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); @@ -288,7 +288,7 @@ prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) } void -do_authenticated(Authctxt *authctxt) +do_authenticated(struct ssh *ssh, Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); @@ -296,17 +296,17 @@ do_authenticated(Authctxt *authctxt) /* XXX - streamlocal? */ if (no_port_forwarding_flag || options.disable_forwarding || (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) - channel_disable_adm_local_opens(); + channel_disable_adm_local_opens(ssh); else - channel_permit_all_opens(); + channel_permit_all_opens(ssh); auth_debug_send(); prepare_auth_info_file(authctxt->pw, authctxt->session_info); - do_authenticated2(authctxt); + do_authenticated2(ssh, authctxt); - do_cleanup(authctxt); + do_cleanup(ssh, authctxt); } /* Check untrusted xauth strings for metacharacters */ @@ -331,7 +331,7 @@ xauth_valid_string(const char *s) * setting up file descriptors and such. */ int -do_exec_no_pty(Session *s, const char *command) +do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) { pid_t pid; @@ -456,7 +456,7 @@ do_exec_no_pty(Session *s, const char *command) #endif /* Do processing for the child (exec command etc). */ - do_child(s, command); + do_child(ssh, s, command); /* NOTREACHED */ default: break; @@ -487,7 +487,7 @@ do_exec_no_pty(Session *s, const char *command) close(pout[1]); close(perr[1]); - session_set_fds(s, pin[1], pout[0], perr[0], + session_set_fds(ssh, s, pin[1], pout[0], perr[0], s->is_subsystem, 0); #else /* We are the parent. Close the child sides of the socket pairs. */ @@ -511,7 +511,7 @@ do_exec_no_pty(Session *s, const char *command) * lastlog, and other such operations. */ int -do_exec_pty(Session *s, const char *command) +do_exec_pty(struct ssh *ssh, Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; @@ -580,13 +580,13 @@ do_exec_pty(Session *s, const char *command) cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* _UNICOS */ #ifndef HAVE_OSF_SIA - do_login(s, command); + do_login(ssh, s, command); #endif /* * Do common processing for the child, such as execing * the command. */ - do_child(s, command); + do_child(ssh, s, command); /* NOTREACHED */ default: break; @@ -608,7 +608,7 @@ do_exec_pty(Session *s, const char *command) s->ptymaster = ptymaster; packet_set_interactive(1, options.ip_qos_interactive, options.ip_qos_bulk); - session_set_fds(s, ptyfd, fdout, -1, 1, 1); + session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1); return 0; } @@ -646,9 +646,8 @@ do_pre_login(Session *s) * to be forced, execute that instead. */ int -do_exec(Session *s, const char *command) +do_exec(struct ssh *ssh, Session *s, const char *command) { - struct ssh *ssh = active_state; /* XXX */ int ret; const char *forced = NULL, *tty = NULL; char session_type[1024]; @@ -707,9 +706,9 @@ do_exec(Session *s, const char *command) } #endif if (s->ttyfd != -1) - ret = do_exec_pty(s, command); + ret = do_exec_pty(ssh, s, command); else - ret = do_exec_no_pty(s, command); + ret = do_exec_no_pty(ssh, s, command); original_command = NULL; @@ -725,9 +724,8 @@ do_exec(Session *s, const char *command) /* administrative, login(1)-like work */ void -do_login(Session *s, const char *command) +do_login(struct ssh *ssh, Session *s, const char *command) { - struct ssh *ssh = active_state; /* XXX */ socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; @@ -960,9 +958,8 @@ copy_environment(char **source, char ***env, u_int *envsize) } static char ** -do_setup_env(Session *s, const char *shell) +do_setup_env(struct ssh *ssh, Session *s, const char *shell) { - struct ssh *ssh = active_state; /* XXX */ char buf[256]; u_int i, envsize; char **env, *laddr; @@ -1421,7 +1418,7 @@ do_pwchange(Session *s) } static void -child_close_fds(void) +child_close_fds(struct ssh *ssh) { extern int auth_sock; @@ -1441,7 +1438,7 @@ child_close_fds(void) * open in the parent. */ /* XXX better use close-on-exec? -markus */ - channel_close_all(); + channel_close_all(ssh); /* * Close any extra file descriptors. Note that there may still be @@ -1465,7 +1462,7 @@ child_close_fds(void) */ #define ARGV_MAX 10 void -do_child(Session *s, const char *command) +do_child(struct ssh *ssh, Session *s, const char *command) { extern char **environ; char **env; @@ -1481,7 +1478,7 @@ do_child(Session *s, const char *command) /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); - child_close_fds(); + child_close_fds(ssh); do_pwchange(s); exit(1); } @@ -1530,7 +1527,7 @@ do_child(Session *s, const char *command) * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ - env = do_setup_env(s, shell); + env = do_setup_env(ssh, s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); @@ -1543,7 +1540,7 @@ do_child(Session *s, const char *command) * closed before building the environment, as we call * ssh_remote_ipaddr there. */ - child_close_fds(); + child_close_fds(ssh); /* * Must take new environment into use so that .ssh/rc, @@ -1840,7 +1837,7 @@ session_by_pid(pid_t pid) } static int -session_window_change_req(Session *s) +session_window_change_req(struct ssh *ssh, Session *s) { s->col = packet_get_int(); s->row = packet_get_int(); @@ -1852,7 +1849,7 @@ session_window_change_req(Session *s) } static int -session_pty_req(Session *s) +session_pty_req(struct ssh *ssh, Session *s) { u_int len; int n_bytes; @@ -1905,7 +1902,7 @@ session_pty_req(Session *s) } static int -session_subsystem_req(Session *s) +session_subsystem_req(struct ssh *ssh, Session *s) { struct stat st; u_int len; @@ -1932,7 +1929,7 @@ session_subsystem_req(Session *s) s->is_subsystem = SUBSYSTEM_EXT; debug("subsystem: exec() %s", cmd); } - success = do_exec(s, cmd) == 0; + success = do_exec(ssh, s, cmd) == 0; break; } } @@ -1945,7 +1942,7 @@ session_subsystem_req(Session *s) } static int -session_x11_req(Session *s) +session_x11_req(struct ssh *ssh, Session *s) { int success; @@ -1962,7 +1959,7 @@ session_x11_req(Session *s) if (xauth_valid_string(s->auth_proto) && xauth_valid_string(s->auth_data)) - success = session_setup_x11fwd(s); + success = session_setup_x11fwd(ssh, s); else { success = 0; error("Invalid X11 forwarding data"); @@ -1977,26 +1974,26 @@ session_x11_req(Session *s) } static int -session_shell_req(Session *s) +session_shell_req(struct ssh *ssh, Session *s) { packet_check_eom(); - return do_exec(s, NULL) == 0; + return do_exec(ssh, s, NULL) == 0; } static int -session_exec_req(Session *s) +session_exec_req(struct ssh *ssh, Session *s) { u_int len, success; char *command = packet_get_string(&len); packet_check_eom(); - success = do_exec(s, command) == 0; + success = do_exec(ssh, s, command) == 0; free(command); return success; } static int -session_break_req(Session *s) +session_break_req(struct ssh *ssh, Session *s) { packet_get_int(); /* ignored */ @@ -2008,7 +2005,7 @@ session_break_req(Session *s) } static int -session_env_req(Session *s) +session_env_req(struct ssh *ssh, Session *s) { char *name, *val; u_int name_len, val_len, i; @@ -2043,7 +2040,7 @@ session_env_req(Session *s) } static int -session_auth_agent_req(Session *s) +session_auth_agent_req(struct ssh *ssh, Session *s) { static int called = 0; packet_check_eom(); @@ -2055,22 +2052,21 @@ session_auth_agent_req(Session *s) return 0; } else { called = 1; - return auth_input_request_forwarding(s->pw); + return auth_input_request_forwarding(ssh, s->pw); } } int -session_input_channel_req(Channel *c, const char *rtype) +session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { - logit("session_input_channel_req: no session %d req %.100s", - c->self, rtype); + logit("%s: no session %d req %.100s", __func__, c->self, rtype); return 0; } - debug("session_input_channel_req: session %d req %s", s->self, rtype); + debug("%s: session %d req %s", __func__, s->self, rtype); /* * a session is in LARVAL state until a shell, a command @@ -2078,33 +2074,33 @@ session_input_channel_req(Channel *c, const char *rtype) */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { - success = session_shell_req(s); + success = session_shell_req(ssh, s); } else if (strcmp(rtype, "exec") == 0) { - success = session_exec_req(s); + success = session_exec_req(ssh, s); } else if (strcmp(rtype, "pty-req") == 0) { - success = session_pty_req(s); + success = session_pty_req(ssh, s); } else if (strcmp(rtype, "x11-req") == 0) { - success = session_x11_req(s); + success = session_x11_req(ssh, s); } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { - success = session_auth_agent_req(s); + success = session_auth_agent_req(ssh, s); } else if (strcmp(rtype, "subsystem") == 0) { - success = session_subsystem_req(s); + success = session_subsystem_req(ssh, s); } else if (strcmp(rtype, "env") == 0) { - success = session_env_req(s); + success = session_env_req(ssh, s); } } if (strcmp(rtype, "window-change") == 0) { - success = session_window_change_req(s); + success = session_window_change_req(ssh, s); } else if (strcmp(rtype, "break") == 0) { - success = session_break_req(s); + success = session_break_req(ssh, s); } return success; } void -session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, - int is_tty) +session_set_fds(struct ssh *ssh, Session *s, + int fdin, int fdout, int fderr, int ignore_fderr, int is_tty) { /* * now that have a child and a pipe to the child, @@ -2112,7 +2108,7 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, */ if (s->chanid == -1) fatal("no channel for session %d", s->self); - channel_set_fds(s->chanid, + channel_set_fds(ssh, s->chanid, fdout, fdin, fderr, ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); @@ -2183,40 +2179,40 @@ sig2name(int sig) } static void -session_close_x11(int id) +session_close_x11(struct ssh *ssh, int id) { Channel *c; - if ((c = channel_by_id(id)) == NULL) { - debug("session_close_x11: x11 channel %d missing", id); + if ((c = channel_by_id(ssh, id)) == NULL) { + debug("%s: x11 channel %d missing", __func__, id); } else { /* Detach X11 listener */ - debug("session_close_x11: detach x11 channel %d", id); - channel_cancel_cleanup(id); + debug("%s: detach x11 channel %d", __func__, id); + channel_cancel_cleanup(ssh, id); if (c->ostate != CHAN_OUTPUT_CLOSED) - chan_mark_dead(c); + chan_mark_dead(ssh, c); } } static void -session_close_single_x11(int id, void *arg) +session_close_single_x11(struct ssh *ssh, int id, void *arg) { Session *s; u_int i; - debug3("session_close_single_x11: channel %d", id); - channel_cancel_cleanup(id); + debug3("%s: channel %d", __func__, id); + channel_cancel_cleanup(ssh, id); if ((s = session_by_x11_channel(id)) == NULL) - fatal("session_close_single_x11: no x11 channel %d", id); + fatal("%s: no x11 channel %d", __func__, id); for (i = 0; s->x11_chanids[i] != -1; i++) { - debug("session_close_single_x11: session %d: " - "closing channel %d", s->self, s->x11_chanids[i]); + debug("%s: session %d: closing channel %d", + __func__, s->self, s->x11_chanids[i]); /* * The channel "id" is already closing, but make sure we * close all of its siblings. */ if (s->x11_chanids[i] != id) - session_close_x11(s->x11_chanids[i]); + session_close_x11(ssh, s->x11_chanids[i]); } free(s->x11_chanids); s->x11_chanids = NULL; @@ -2231,22 +2227,22 @@ session_close_single_x11(int id, void *arg) } static void -session_exit_message(Session *s, int status) +session_exit_message(struct ssh *ssh, Session *s, int status) { Channel *c; - if ((c = channel_lookup(s->chanid)) == NULL) - fatal("session_exit_message: session %d: no channel %d", - s->self, s->chanid); - debug("session_exit_message: session %d channel %d pid %ld", - s->self, s->chanid, (long)s->pid); + if ((c = channel_lookup(ssh, s->chanid)) == NULL) + fatal("%s: session %d: no channel %d", + __func__, s->self, s->chanid); + debug("%s: session %d channel %d pid %ld", + __func__, s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { - channel_request_start(s->chanid, "exit-status", 0); + channel_request_start(ssh, s->chanid, "exit-status", 0); packet_put_int(WEXITSTATUS(status)); packet_send(); } else if (WIFSIGNALED(status)) { - channel_request_start(s->chanid, "exit-signal", 0); + channel_request_start(ssh, s->chanid, "exit-signal", 0); packet_put_cstring(sig2name(WTERMSIG(status))); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)? 1 : 0); @@ -2262,14 +2258,14 @@ session_exit_message(Session *s, int status) } /* disconnect channel */ - debug("session_exit_message: release channel %d", s->chanid); + debug("%s: release channel %d", __func__, s->chanid); /* * Adjust cleanup callback attachment to send close messages when * the channel gets EOF. The session will be then be closed * by session_close_by_channel when the childs close their fds. */ - channel_register_cleanup(c->self, session_close_by_channel, 1); + channel_register_cleanup(ssh, c->self, session_close_by_channel, 1); /* * emulate a write failure with 'chan_write_failed', nobody will be @@ -2278,13 +2274,12 @@ session_exit_message(Session *s, int status) * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) - chan_write_failed(c); + chan_write_failed(ssh, c); } void -session_close(Session *s) +session_close(struct ssh *ssh, Session *s) { - struct ssh *ssh = active_state; /* XXX */ u_int i; verbose("Close session: user %s from %.200s port %d id %d", @@ -2314,16 +2309,15 @@ session_close(Session *s) } void -session_close_by_pid(pid_t pid, int status) +session_close_by_pid(struct ssh *ssh, pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { - debug("session_close_by_pid: no session for pid %ld", - (long)pid); + debug("%s: no session for pid %ld", __func__, (long)pid); return; } if (s->chanid != -1) - session_exit_message(s, status); + session_exit_message(ssh, s, status); if (s->ttyfd != -1) session_pty_cleanup(s); s->pid = 0; @@ -2334,19 +2328,18 @@ session_close_by_pid(pid_t pid, int status) * the session 'child' itself dies */ void -session_close_by_channel(int id, void *arg) +session_close_by_channel(struct ssh *ssh, int id, void *arg) { Session *s = session_by_channel(id); u_int i; if (s == NULL) { - debug("session_close_by_channel: no session for id %d", id); + debug("%s: no session for id %d", __func__, id); return; } - debug("session_close_by_channel: channel %d child %ld", - id, (long)s->pid); + debug("%s: channel %d child %ld", __func__, id, (long)s->pid); if (s->pid != 0) { - debug("session_close_by_channel: channel %d: has child", id); + debug("%s: channel %d: has child", __func__, id); /* * delay detach of session, but release pty, since * the fd's to the child are already closed @@ -2356,22 +2349,22 @@ session_close_by_channel(int id, void *arg) return; } /* detach by removing callback */ - channel_cancel_cleanup(s->chanid); + channel_cancel_cleanup(ssh, s->chanid); /* Close any X11 listeners associated with this session */ if (s->x11_chanids != NULL) { for (i = 0; s->x11_chanids[i] != -1; i++) { - session_close_x11(s->x11_chanids[i]); + session_close_x11(ssh, s->x11_chanids[i]); s->x11_chanids[i] = -1; } } s->chanid = -1; - session_close(s); + session_close(ssh, s); } void -session_destroy_all(void (*closefunc)(Session *)) +session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *)) { int i; for (i = 0; i < sessions_nalloc; i++) { @@ -2380,7 +2373,7 @@ session_destroy_all(void (*closefunc)(Session *)) if (closefunc != NULL) closefunc(s); else - session_close(s); + session_close(ssh, s); } } } @@ -2423,7 +2416,7 @@ session_proctitle(Session *s) } int -session_setup_x11fwd(Session *s) +session_setup_x11fwd(struct ssh *ssh, Session *s) { struct stat st; char display[512], auth_display[512]; @@ -2447,14 +2440,14 @@ session_setup_x11fwd(Session *s) debug("X11 display already set."); return 0; } - if (x11_create_display_inet(options.x11_display_offset, + if (x11_create_display_inet(ssh, options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number, &s->x11_chanids) == -1) { debug("x11_create_display_inet failed."); return 0; } for (i = 0; s->x11_chanids[i] != -1; i++) { - channel_register_cleanup(s->x11_chanids[i], + channel_register_cleanup(ssh, s->x11_chanids[i], session_close_single_x11, 0); } @@ -2499,13 +2492,13 @@ session_setup_x11fwd(Session *s) } static void -do_authenticated2(Authctxt *authctxt) +do_authenticated2(struct ssh *ssh, Authctxt *authctxt) { - server_loop2(authctxt); + server_loop2(ssh, authctxt); } void -do_cleanup(Authctxt *authctxt) +do_cleanup(struct ssh *ssh, Authctxt *authctxt) { static int called = 0; @@ -2561,7 +2554,7 @@ do_cleanup(Authctxt *authctxt) * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) - session_destroy_all(session_pty_cleanup2); + session_destroy_all(ssh, session_pty_cleanup2); } /* Return a name for the remote host that fits inside utmp_size */ diff --git a/session.h b/session.h index 74c557db6..54dd1f0ca 100644 --- a/session.h +++ b/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.34 2017/08/18 05:36:45 djm Exp $ */ +/* $OpenBSD: session.h,v 1.35 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -62,20 +62,20 @@ struct Session { } *env; }; -void do_authenticated(Authctxt *); -void do_cleanup(Authctxt *); +void do_authenticated(struct ssh *, Authctxt *); +void do_cleanup(struct ssh *, Authctxt *); int session_open(Authctxt *, int); void session_unused(int); -int session_input_channel_req(Channel *, const char *); -void session_close_by_pid(pid_t, int); -void session_close_by_channel(int, void *); -void session_destroy_all(void (*)(Session *)); +int session_input_channel_req(struct ssh *, Channel *, const char *); +void session_close_by_pid(struct ssh *ssh, pid_t, int); +void session_close_by_channel(struct ssh *, int, void *); +void session_destroy_all(struct ssh *, void (*)(Session *)); void session_pty_cleanup2(Session *); Session *session_new(void); Session *session_by_tty(char *); -void session_close(Session *); +void session_close(struct ssh *, Session *); void do_setusercontext(struct passwd *); const char *session_get_remote_name_or_ip(struct ssh *, u_int, int); diff --git a/ssh.c b/ssh.c index 019d1d31c..ecc50f37e 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.462 2017/08/12 06:46:01 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.463 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -208,7 +208,7 @@ usage(void) exit(255); } -static int ssh_session2(void); +static int ssh_session2(struct ssh *); static void load_public_identity_files(void); static void main_sigchld_handler(int); @@ -596,6 +596,14 @@ main(int ac, char **av) */ initialize_options(&options); + /* + * Prepare main ssh transport/connection structures + */ + if ((ssh = ssh_alloc_session_state()) == NULL) + fatal("Couldn't allocate session state"); + channel_init_channels(ssh); + active_state = ssh; /* XXX legacy API compat */ + /* Parse command-line arguments. */ host = NULL; use_syslog = 0; @@ -1108,7 +1116,7 @@ main(int ac, char **av) if (options.port == 0) options.port = default_ssh_port(); - channel_set_af(options.address_family); + channel_set_af(ssh, options.address_family); /* Tidy and check options */ if (options.host_key_alias != NULL) @@ -1253,8 +1261,7 @@ main(int ac, char **av) if (options.control_path != NULL) { int sock; if ((sock = muxclient(options.control_path)) >= 0) { - packet_set_connection(sock, sock); - ssh = active_state; /* XXX */ + ssh_packet_set_connection(ssh, sock, sock); packet_set_mux(); goto skip_connect; } @@ -1274,7 +1281,7 @@ main(int ac, char **av) timeout_ms = options.connection_timeout * 1000; /* Open a connection to the remote host. */ - if (ssh_connect(host, addrs, &hostaddr, options.port, + if (ssh_connect(ssh, host, addrs, &hostaddr, options.port, options.address_family, options.connection_attempts, &timeout_ms, options.tcp_keep_alive, options.use_privileged_port) != 0) @@ -1457,7 +1464,7 @@ main(int ac, char **av) } skip_connect: - exit_status = ssh_session2(); + exit_status = ssh_session2(ssh); packet_close(); if (options.control_path != NULL && muxserver_sock != -1) @@ -1530,7 +1537,7 @@ fork_postauth(void) /* Callback for remote forward global requests */ static void -ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) +ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) { struct Forward *rfwd = (struct Forward *)ctxt; @@ -1548,10 +1555,10 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) logit("Allocated port %u for remote forward to %s:%d", rfwd->allocated_port, rfwd->connect_host, rfwd->connect_port); - channel_update_permitted_opens(rfwd->handle, - rfwd->allocated_port); + channel_update_permitted_opens(ssh, + rfwd->handle, rfwd->allocated_port); } else { - channel_update_permitted_opens(rfwd->handle, -1); + channel_update_permitted_opens(ssh, rfwd->handle, -1); } } @@ -1580,21 +1587,21 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } static void -client_cleanup_stdio_fwd(int id, void *arg) +client_cleanup_stdio_fwd(struct ssh *ssh, int id, void *arg) { debug("stdio forwarding: done"); cleanup_exit(0); } static void -ssh_stdio_confirm(int id, int success, void *arg) +ssh_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) { if (!success) fatal("stdio forwarding failed"); } static void -ssh_init_stdio_forwarding(void) +ssh_init_stdio_forwarding(struct ssh *ssh) { Channel *c; int in, out; @@ -1608,15 +1615,15 @@ ssh_init_stdio_forwarding(void) if ((in = dup(STDIN_FILENO)) < 0 || (out = dup(STDOUT_FILENO)) < 0) fatal("channel_connect_stdio_fwd: dup() in/out failed"); - if ((c = channel_connect_stdio_fwd(options.stdio_forward_host, + if ((c = channel_connect_stdio_fwd(ssh, options.stdio_forward_host, options.stdio_forward_port, in, out)) == NULL) fatal("%s: channel_connect_stdio_fwd failed", __func__); - channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0); - channel_register_open_confirm(c->self, ssh_stdio_confirm, NULL); + channel_register_cleanup(ssh, c->self, client_cleanup_stdio_fwd, 0); + channel_register_open_confirm(ssh, c->self, ssh_stdio_confirm, NULL); } static void -ssh_init_forwarding(void) +ssh_init_forwarding(struct ssh *ssh) { int success = 0; int i; @@ -1635,7 +1642,7 @@ ssh_init_forwarding(void) options.local_forwards[i].connect_path : options.local_forwards[i].connect_host, options.local_forwards[i].connect_port); - success += channel_setup_local_fwd_listener( + success += channel_setup_local_fwd_listener(ssh, &options.local_forwards[i], &options.fwd_opts); } if (i > 0 && success != i && options.exit_on_forward_failure) @@ -1657,7 +1664,7 @@ ssh_init_forwarding(void) options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port); options.remote_forwards[i].handle = - channel_request_remote_forwarding( + channel_request_remote_forwarding(ssh, &options.remote_forwards[i]); if (options.remote_forwards[i].handle < 0) { if (options.exit_on_forward_failure) @@ -1666,14 +1673,15 @@ ssh_init_forwarding(void) logit("Warning: Could not request remote " "forwarding."); } else { - client_register_global_confirm(ssh_confirm_remote_forward, + client_register_global_confirm( + ssh_confirm_remote_forward, &options.remote_forwards[i]); } } /* Initiate tunnel forwarding. */ if (options.tun_open != SSH_TUNMODE_NO) { - if (client_request_tun_fwd(options.tun_open, + if (client_request_tun_fwd(ssh, options.tun_open, options.tun_local, options.tun_remote) == -1) { if (options.exit_on_forward_failure) fatal("Could not request tunnel forwarding."); @@ -1700,7 +1708,7 @@ check_agent_present(void) } static void -ssh_session2_setup(int id, int success, void *arg) +ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) { extern char **environ; const char *display; @@ -1713,15 +1721,15 @@ ssh_session2_setup(int id, int success, void *arg) display = getenv("DISPLAY"); if (display == NULL && options.forward_x11) debug("X11 forwarding requested but DISPLAY not set"); - if (options.forward_x11 && client_x11_get_proto(display, + if (options.forward_x11 && client_x11_get_proto(ssh, display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); - x11_request_forwarding_with_spoofing(id, display, proto, + x11_request_forwarding_with_spoofing(ssh, id, display, proto, data, 1); - client_expect_confirm(id, "X11 forwarding", CONFIRM_WARN); + client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); /* XXX exit_on_forward_failure */ interactive = 1; } @@ -1729,7 +1737,7 @@ ssh_session2_setup(int id, int success, void *arg) check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); - channel_request_start(id, "auth-agent-req@openssh.com", 0); + channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); packet_send(); } @@ -1737,13 +1745,13 @@ ssh_session2_setup(int id, int success, void *arg) packet_set_interactive(interactive, options.ip_qos_interactive, options.ip_qos_bulk); - client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), + client_session2_setup(ssh, id, tty_flag, subsystem_flag, getenv("TERM"), NULL, fileno(stdin), &command, environ); } /* open new channel for a session */ static int -ssh_session2_open(void) +ssh_session2_open(struct ssh *ssh) { Channel *c; int window, packetmax, in, out, err; @@ -1773,34 +1781,34 @@ ssh_session2_open(void) window >>= 1; packetmax >>= 1; } - c = channel_new( + c = channel_new(ssh, "session", SSH_CHANNEL_OPENING, in, out, err, window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); - debug3("ssh_session2_open: channel_new: %d", c->self); + debug3("%s: channel_new: %d", __func__, c->self); - channel_send_open(c->self); + channel_send_open(ssh, c->self); if (!no_shell_flag) - channel_register_open_confirm(c->self, + channel_register_open_confirm(ssh, c->self, ssh_session2_setup, NULL); return c->self; } static int -ssh_session2(void) +ssh_session2(struct ssh *ssh) { int id = -1; /* XXX should be pre-session */ if (!options.control_persist) - ssh_init_stdio_forwarding(); - ssh_init_forwarding(); + ssh_init_stdio_forwarding(ssh); + ssh_init_forwarding(ssh); /* Start listening for multiplex clients */ if (!packet_get_mux()) - muxserver_listen(); + muxserver_listen(ssh); /* * If we are in control persist mode and have a working mux listen @@ -1828,10 +1836,10 @@ ssh_session2(void) * stdio forward setup that we skipped earlier. */ if (options.control_persist && muxserver_sock == -1) - ssh_init_stdio_forwarding(); + ssh_init_stdio_forwarding(ssh); if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) - id = ssh_session2_open(); + id = ssh_session2_open(ssh); else { packet_set_interactive( options.control_master == SSHCTL_MASTER_NO, @@ -1866,7 +1874,7 @@ ssh_session2(void) fork_postauth(); } - return client_loop(tty_flag, tty_flag ? + return client_loop(ssh, tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); } diff --git a/sshbuf.h b/sshbuf.h index 1ac4b8c0a..77f1e9e6d 100644 --- a/sshbuf.h +++ b/sshbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshbuf.h,v 1.8 2016/11/25 23:22:04 djm Exp $ */ +/* $OpenBSD: sshbuf.h,v 1.9 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -211,6 +211,7 @@ int sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, /* Another variant: "peeks" into the buffer without modifying it */ int sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, size_t *lenp); +/* XXX peek_u8 / peek_u32 */ /* * Functions to extract or store SSH wire encoded bignums and elliptic diff --git a/sshconnect.c b/sshconnect.c index 2842d9e59..608566207 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.285 2017/09/03 23:33:13 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.286 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -105,7 +105,7 @@ expand_proxy_command(const char *proxy_command, const char *user, * a connected fd back to us. */ static int -ssh_proxy_fdpass_connect(const char *host, u_short port, +ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port, const char *proxy_command) { char *command_string; @@ -176,7 +176,8 @@ ssh_proxy_fdpass_connect(const char *host, u_short port, fatal("Couldn't wait for child: %s", strerror(errno)); /* Set the connection file descriptors. */ - packet_set_connection(sock, sock); + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ return 0; } @@ -185,7 +186,8 @@ ssh_proxy_fdpass_connect(const char *host, u_short port, * Connect to the given ssh server using a proxy command. */ static int -ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) +ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, + const char *proxy_command) { char *command_string; int pin[2], pout[2]; @@ -252,9 +254,9 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) free(command_string); /* Set the connection file descriptors. */ - packet_set_connection(pout[0], pin[1]); + if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) + return -1; /* ssh_packet_set_connection logs error */ - /* Indicate OK return */ return 0; } @@ -410,7 +412,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, * the daemon. */ static int -ssh_connect_direct(const char *host, struct addrinfo *aitop, +ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, struct sockaddr_storage *hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) { @@ -484,27 +486,31 @@ ssh_connect_direct(const char *host, struct addrinfo *aitop, error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ - packet_set_connection(sock, sock); + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ - return 0; + return 0; } int -ssh_connect(const char *host, struct addrinfo *addrs, +ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) { if (options.proxy_command == NULL) { - return ssh_connect_direct(host, addrs, hostaddr, port, family, - connection_attempts, timeout_ms, want_keepalive, needpriv); + return ssh_connect_direct(ssh, host, addrs, hostaddr, port, + family, connection_attempts, timeout_ms, want_keepalive, + needpriv); } else if (strcmp(options.proxy_command, "-") == 0) { - packet_set_connection(STDIN_FILENO, STDOUT_FILENO); - return 0; /* Always succeeds */ + if ((ssh_packet_set_connection(ssh, + STDIN_FILENO, STDOUT_FILENO)) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + return 0; } else if (options.proxy_use_fdpass) { - return ssh_proxy_fdpass_connect(host, port, + return ssh_proxy_fdpass_connect(ssh, host, port, options.proxy_command); } - return ssh_proxy_connect(host, port, options.proxy_command); + return ssh_proxy_connect(ssh, host, port, options.proxy_command); } static void diff --git a/sshconnect.h b/sshconnect.h index f4e73f7b1..b5029e234 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.h,v 1.30 2017/05/30 08:52:19 markus Exp $ */ +/* $OpenBSD: sshconnect.h,v 1.31 2017/09/12 06:32:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -32,8 +32,10 @@ struct Sensitive { }; struct addrinfo; -int ssh_connect(const char *, struct addrinfo *, struct sockaddr_storage *, - u_short, int, int, int *, int, int); +struct ssh; + +int ssh_connect(struct ssh *, const char *, struct addrinfo *, + struct sockaddr_storage *, u_short, int, int, int *, int, int); void ssh_kill_proxy_command(void); void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short, diff --git a/sshd.c b/sshd.c index 1d19ce679..51a1aaf6e 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.491 2017/07/01 13:50:45 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.492 2017/09/12 06:32:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1621,9 +1621,6 @@ main(int ac, char **av) "enabled authentication methods"); } - /* set default channel AF */ - channel_set_af(options.address_family); - /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); @@ -1955,8 +1952,14 @@ main(int ac, char **av) packet_set_connection(sock_in, sock_out); packet_set_server(); ssh = active_state; /* XXX */ + check_ip_options(ssh); + /* Prepare the channels layer */ + channel_init_channels(ssh); + channel_set_af(ssh, options.address_family); + process_permitopen(ssh, &options); + /* Set SO_KEEPALIVE if requested. */ if (options.tcp_keep_alive && packet_connection_is_on_socket() && setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) @@ -2080,10 +2083,10 @@ main(int ac, char **av) options.client_alive_count_max); /* Try to send all our hostkeys to the client */ - notify_hostkeys(active_state); + notify_hostkeys(ssh); /* Start session. */ - do_authenticated(authctxt); + do_authenticated(ssh, authctxt); /* The connection has been terminated. */ packet_get_bytes(&ibytes, &obytes); @@ -2211,8 +2214,10 @@ do_ssh2_kex(void) void cleanup_exit(int i) { + struct ssh *ssh = active_state; /* XXX */ + if (the_authctxt) { - do_cleanup(the_authctxt); + do_cleanup(ssh, the_authctxt); if (use_privsep && privsep_is_preauth && pmonitor != NULL && pmonitor->m_pid > 1) { debug("Killing privsep child %d", pmonitor->m_pid); diff --git a/ssherr.c b/ssherr.c index 4bd5f59cc..3c0009d69 100644 --- a/ssherr.c +++ b/ssherr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssherr.c,v 1.6 2017/05/07 23:15:59 djm Exp $ */ +/* $OpenBSD: ssherr.c,v 1.7 2017/09/12 06:32:08 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -137,6 +137,8 @@ ssh_err(int n) return "Protocol error"; case SSH_ERR_KEY_LENGTH: return "Invalid key length"; + case SSH_ERR_NUMBER_TOO_LARGE: + return "number is too large"; default: return "unknown error"; } diff --git a/ssherr.h b/ssherr.h index a30781620..c0b59211e 100644 --- a/ssherr.h +++ b/ssherr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssherr.h,v 1.4 2017/05/07 23:15:59 djm Exp $ */ +/* $OpenBSD: ssherr.h,v 1.5 2017/09/12 06:32:08 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -78,6 +78,7 @@ #define SSH_ERR_CONN_CORRUPT -54 #define SSH_ERR_PROTOCOL_ERROR -55 #define SSH_ERR_KEY_LENGTH -56 +#define SSH_ERR_NUMBER_TOO_LARGE -57 /* Translate a numeric error code to a human-readable error string */ const char *ssh_err(int n);