diff --git a/.gitignore b/.gitignore index 84dfe1636..d6c19cf3d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ ssh-keyscan ssh-keysign ssh-pkcs11-helper sshd +!regress/misc/fuzz-harness/Makefile + # Ignores in Windows fork /bin/x64/Debug /contrib/win32/openssh/.vs/Win32-OpenSSH/v14 diff --git a/PROTOCOL.agent b/PROTOCOL.agent index 54cf2be4d..da3381942 100644 --- a/PROTOCOL.agent +++ b/PROTOCOL.agent @@ -1,3 +1,5 @@ This file used to contain a description of the SSH agent protocol -implemented by OpenSSH. It has since been superseded by -https://tools.ietf.org/html/draft-miller-ssh-agent-00 +implemented by OpenSSH. It has since been superseded by an Internet- +draft that is available from: + +https://tools.ietf.org/html/draft-miller-ssh-agent-02 diff --git a/README b/README index fb59f1bdc..103d43e9b 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -See https://www.openssh.com/releasenotes.html#7.5p1 for the release notes. +See https://www.openssh.com/releasenotes.html#7.6p1 for the release notes. Please read https://www.openssh.com/report.html for bug reporting instructions and note that we do not use Github for bug reporting or 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 81b1eb681..0ab6ed3bb 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. * @@ -592,6 +592,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/auth2-pubkey.c b/auth2-pubkey.c index 42d8e71a7..f6b017231 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.70 2017/08/18 05:48:04 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.71 2017/09/07 23:48:09 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -482,7 +482,7 @@ check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw) char *cp, *key_options = NULL, *fp = NULL; const char *reason = NULL; - /* Always consume entrire file */ + /* Always consume entire file */ if (found_key) continue; if (found != NULL) diff --git a/channels.c b/channels.c index 4b21c2182..2e920e9eb 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.365 2017/05/31 08:58:52 deraadt Exp $ */ +/* $OpenBSD: channels.c,v 1.375 2017/09/24 13:45:34 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -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,101 +115,153 @@ 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 *); +static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); +static int rdynamic_connect_finish(struct ssh *, Channel *); + +/* 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, u_int remote_id) { Channel *c; u_int i; - for (i = 0; i < channels_alloc; i++) { - c = channels[i]; - if (c != NULL && c->remote_id == remote_id) + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c != NULL && c->have_remote_id && c->remote_id == remote_id) return c; } return NULL; @@ -228,26 +272,28 @@ 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: case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_OPENING: 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 +301,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 +347,220 @@ 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 +569,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; + explicit_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 +596,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 +624,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 +637,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 +658,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) { @@ -596,6 +675,7 @@ channel_still_open(void) case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: @@ -606,6 +686,7 @@ channel_still_open(void) continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_PROXY: @@ -620,18 +701,20 @@ 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]; - if (c == NULL || c->remote_id < 0) + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL || !c->have_remote_id) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: @@ -664,18 +747,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) { @@ -694,73 +780,95 @@ channel_open_message(void) case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_OPEN: 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 %s%u 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->type, + c->have_remote_id ? "r" : "nr", c->remote_id, + 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; } + if (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", __func__, c->self); + 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 +878,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 +892,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 +906,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 +919,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 +935,75 @@ 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); + if (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", __func__, c->self); + + 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 +1019,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 +1052,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 +1081,63 @@ 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(Channel *c, struct sshbuf *input, struct sshbuf *output) { - char *p, *host; + const u_char *p; + char *host; u_int len, have, i, found, need; char username[256]; struct { @@ -1035,14 +1146,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(input); len = sizeof(s4_req); if (have < len) return 0; - p = (char *)buffer_ptr(&c->input); + p = sshbuf_ptr(input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ @@ -1067,12 +1179,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(input, &s4_req.version, 1)) != 0 || + (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || + (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || + (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { + debug("channels %d: decode socks4: %s", c->self, ssh_err(r)); + return -1; + } + have = sshbuf_len(input); + p = sshbuf_ptr(input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4: user not nul terminated", c->self); @@ -1080,21 +1195,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(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(input); + p = sshbuf_ptr(input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4a: host not nul " "terminated", c->self); @@ -1110,7 +1224,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(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 +1243,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(output, &s4_rsp, sizeof(s4_rsp))) != 0) { + fatal("%s: channel %d: append reply: %s", __func__, + c->self, ssh_err(r)); + } return 1; } @@ -1139,10 +1259,10 @@ 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(Channel *c, struct sshbuf *input, struct sshbuf *output) { + /* XXX use get/put_u8 instead of trusting struct padding */ struct { u_int8_t version; u_int8_t command; @@ -1151,14 +1271,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(input); if (p[0] != 0x05) return -1; - have = buffer_len(&c->input); + have = sshbuf_len(input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ if (have < 2) @@ -1178,10 +1299,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 */ - FD_SET(c->sock, writeset); + if ((r = sshbuf_consume(input, nmethods + 2)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } + /* version, method */ + if ((r = sshbuf_put_u8(output, 0x05)) != 0 || + (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) { + fatal("%s: channel %d: append reply: %s", __func__, + c->self, ssh_err(r)); + } c->flags |= SSH_SOCKS5_AUTHDONE; debug2("channel %d: socks5 auth done", c->self); return 0; /* need more */ @@ -1218,11 +1345,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(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(input, 1)) != 0) { + fatal("%s: channel %d: consume: %s", __func__, + c->self, ssh_err(r)); + } + } + if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || + (r = sshbuf_get(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 +1387,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(output, &s5_rsp, sizeof(s5_rsp))) != 0 || + (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || + (r = sshbuf_put(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 +1412,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,96 +1437,174 @@ 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(c, c->input, c->output); break; case 0x05: - ret = channel_decode_socks5(c, readset, writeset); + ret = channel_decode_socks5(c, c->input, c->output); 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 */ FD_SET(c->sock, readset); + if (sshbuf_len(c->output)) + FD_SET(c->sock, 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"); + } +} + +/* simulate read-error */ +static void +rdynamic_close(struct ssh *ssh, Channel *c) +{ + c->type = SSH_CHANNEL_OPEN; + chan_read_failed(ssh, c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + sshbuf_reset(c->output); + chan_write_failed(ssh, c); +} + +/* reverse dynamic port forwarding */ +static void +channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c) +{ + const u_char *p; + u_int have, len; + int r, ret; + + have = sshbuf_len(c->output); + debug2("channel %d: pre_rdynamic: have %d", c->self, have); + /* sshbuf_dump(c->output, stderr); */ + /* EOF received */ + if (c->flags & CHAN_EOF_RCVD) { + if ((r = sshbuf_consume(c->output, have)) != 0) { + fatal("%s: channel %d: consume: %s", + __func__, c->self, ssh_err(r)); + } + rdynamic_close(ssh, c); + return; + } + /* check if the fixed size part of the packet is in buffer. */ + if (have < 3) + return; + /* try to guess the protocol */ + p = sshbuf_ptr(c->output); + switch (p[0]) { + case 0x04: + /* switch input/output for reverse forwarding */ + ret = channel_decode_socks4(c, c->output, c->input); + break; + case 0x05: + ret = channel_decode_socks5(c, c->output, c->input); + break; + default: + ret = -1; + break; + } + if (ret < 0) { + rdynamic_close(ssh, c); + } else if (ret == 0) { + debug2("channel %d: pre_rdynamic: need more", c->self); + /* send socks request to peer */ + len = sshbuf_len(c->input); + if (len > 0 && len < c->remote_window) { + 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: rdynamic: %s", __func__, + c->self, ssh_err(r)); + } + if ((r = sshbuf_consume(c->input, len)) != 0) { + fatal("%s: channel %d: consume: %s", + __func__, c->self, ssh_err(r)); + } + c->remote_window -= len; + } + } else if (rdynamic_connect_finish(ssh, c) < 0) { + /* the connect failed */ + rdynamic_close(ssh, c); } } /* 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 +1613,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 +1682,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 +1700,406 @@ 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, isopen, 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); + if (!FD_ISSET(c->sock, writeset)) + return; + if (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", __func__, c->self); + /* for rdynamic the OPEN_CONFIRMATION has been sent already */ + isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); + 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 (isopen) { + /* no message necessary */ } 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); + 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)); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %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 (isopen) { + rdynamic_close(ssh, c); + } else { + 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)); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: channel %i: %s", __func__, c->self, + ssh_err(r)); + chan_mark_dead(ssh, c); } - packet_send(); } } -/* 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)); + buf = data; + } 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, buf, 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 (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", + __func__, c->self); + 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 +2110,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; @@ -1928,97 +2256,100 @@ channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) return; } #endif /* !WINDOWS */ - 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_RDYNAMIC_FINISH] = &channel_pre_connecting; + 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_RDYNAMIC_FINISH] = &channel_post_connecting; + 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(chan_fn *ftab[], fd_set *readset, fd_set *writeset, - time_t *unpause_secs) +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; @@ -2028,7 +2359,7 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, * 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 @@ -2042,24 +2373,47 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, *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", __func__, (int)*unpause_secs); } +/* + * Create sockets before allocating the select bitmasks. + * This is necessary for things that need to happen after reading + * the network-input but before channel_prepare_select(). + */ +static void +channel_before_prepare_select(struct ssh *ssh) +{ + struct ssh_channels *sc = ssh->chanctxt; + Channel *c; + u_int i, oalloc; + + for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; + if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) + channel_before_prepare_select_rdynamic(ssh, c); + } +} + /* * Allocate/update select bitmasks and add any bits relevant to channels in * select bitmasks. */ void -channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, - u_int *nallocp, time_t *minwait_secs, int rekeying) +channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, + int *maxfdp, u_int *nallocp, time_t *minwait_secs) { u_int n, sz, nfdset; - n = MAXIMUM(*maxfdp, channel_max_fd); + channel_before_prepare_select(ssh); /* might update 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 */ @@ -2077,8 +2431,8 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, memset(*readsetp, 0, sz); memset(*writesetp, 0, sz); - if (!rekeying) - channel_handler(channel_pre, *readsetp, *writesetp, + if (!ssh_packet_is_rekeying(ssh)) + channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp, minwait_secs); } @@ -2087,21 +2441,136 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, * events pending. */ void -channel_after_select(fd_set *readset, fd_set *writeset) +channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset) { - channel_handler(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, plen; + const u_char *pkt; + 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->have_remote_id) + fatal(":%s: channel %d: no remote id", __func__, c->self); + + if (c->datagram) { + /* Check datagram will fit; drop if not */ + if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) + fatal("%s: channel %d: get 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 (plen > c->remote_window || plen > 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_string(ssh, pkt, plen)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: datagram: %s", __func__, + c->self, ssh_err(r)); + } + c->remote_window -= plen; + 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 (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", __func__, c->self); + 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; @@ -2113,87 +2582,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); } } @@ -2238,20 +2639,19 @@ 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; u_char type; size_t have; - int ret = -1, r, idx; + int ret = -1, r; 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; @@ -2280,7 +2680,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 */ @@ -2288,7 +2688,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; @@ -2307,16 +2707,17 @@ 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; c->remote_id = remote_id; + c->have_remote_id = 1; if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || (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; @@ -2345,23 +2746,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; + 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; } @@ -2448,7 +2843,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; } @@ -2461,12 +2856,14 @@ channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) switch (type) { case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ - if (cp && len > 4) + if (cp && len > 4) { c->remote_id = PEEK_U32(cp); + c->have_remote_id = 1; + } 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; @@ -2477,40 +2874,70 @@ 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; /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_RDYNAMIC_OPEN && + c->type != SSH_CHANNEL_RDYNAMIC_FINISH && c->type != SSH_CHANNEL_X11_OPEN) 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; @@ -2519,149 +2946,149 @@ 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. + */ + if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || + (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->have_remote_id = 1; + 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; } @@ -2681,97 +3108,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); @@ -2781,9 +3208,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; } @@ -2851,8 +3278,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; @@ -2889,7 +3317,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); @@ -2923,12 +3351,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; } @@ -2949,7 +3379,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 @@ -2969,7 +3402,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); @@ -2978,7 +3412,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); @@ -2999,8 +3433,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; @@ -3062,7 +3496,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); @@ -3073,66 +3507,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) @@ -3150,16 +3589,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; @@ -3169,54 +3608,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); } @@ -3250,56 +3694,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 @@ -3364,33 +3813,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; } @@ -3400,32 +3849,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; } @@ -3434,14 +3883,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); } } @@ -3451,28 +3901,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; } /* @@ -3481,105 +3923,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 */ @@ -3601,7 +3999,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) { @@ -3667,21 +4066,18 @@ channel_connect_ctx_free(struct channel_connect *cctx) } /* - * Return CONNECTING channel to remote host:port or local socket path, + * Return connecting socket to remote host:port or local socket path, * 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) +static int +connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, + char *ctype, char *rname, struct channel_connect *cctx, + int *reason, const char **errmsg) { struct addrinfo hints; int gaierr; int sock = -1; char strport[NI_MAXSERV]; - struct channel_connect cctx; - Channel *c; - - memset(&cctx, 0, sizeof(cctx)); if (port == PORT_STREAMLOCAL) { struct sockaddr_un *sunaddr; @@ -3689,7 +4085,7 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, if (strlen(name) > sizeof(sunaddr->sun_path)) { error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); - return (NULL); + return -1; } /* @@ -3702,18 +4098,18 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, ai->ai_addr = (struct sockaddr *)(ai + 1); ai->ai_addrlen = sizeof(*sunaddr); ai->ai_family = AF_UNIX; - ai->ai_socktype = SOCK_STREAM; + ai->ai_socktype = socktype; ai->ai_protocol = PF_UNSPEC; sunaddr = (struct sockaddr_un *)ai->ai_addr; sunaddr->sun_family = AF_UNIX; strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); - cctx.aitop = ai; + cctx->aitop = ai; } else { memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_socktype = SOCK_STREAM; + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_socktype = socktype; snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) + if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) != 0) { if (errmsg != NULL) *errmsg = ssh_gai_strerror(gaierr); @@ -3721,31 +4117,46 @@ connect_to_reason(const char *name, int port, char *ctype, char *rname, *reason = SSH2_OPEN_CONNECT_FAILED; error("connect_to %.100s: unknown host (%s)", name, ssh_gai_strerror(gaierr)); - return NULL; + return -1; } } - cctx.host = xstrdup(name); - cctx.port = port; - cctx.ai = cctx.aitop; + cctx->host = xstrdup(name); + cctx->port = port; + cctx->ai = cctx->aitop; - if ((sock = connect_next(&cctx)) == -1) { + if ((sock = connect_next(cctx)) == -1) { error("connect to %.100s port %d failed: %s", name, port, strerror(errno)); - channel_connect_ctx_free(&cctx); - return NULL; + return -1; } - c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, - CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); - c->connect_ctx = cctx; - return c; + + return sock; } /* 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 *host, int port, + char *ctype, char *rname) { - return connect_to_reason(name, port, ctype, rname, NULL, NULL); + struct channel_connect cctx; + Channel *c; + int sock; + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, + &cctx, NULL, NULL); + if (sock == -1) { + channel_connect_ctx_free(&cctx); + return NULL; + } + c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = port; + c->path = xstrdup(host); + c->connect_ctx = cctx; + + return c; } /* @@ -3753,19 +4164,24 @@ 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; + if (fp->port_to_connect == 0) + return rdynamic_connect_prepare(ssh, + ctype, rname); + return connect_to(ssh, + fp->host_to_connect, fp->port_to_connect, + ctype, rname); } } error("WARNING: Server requests forwarding for unknown listen_port %d", @@ -3774,15 +4190,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", @@ -3792,27 +4212,36 @@ 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; + struct channel_connect cctx; + Channel *c; + u_int i, permit, permit_adm = 1; + int sock; + 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) { @@ -3822,31 +4251,53 @@ 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); + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, + &cctx, reason, errmsg); + if (sock == -1) { + channel_connect_ctx_free(&cctx); + return NULL; + } + + c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = port; + c->path = xstrdup(host); + c->connect_ctx = cctx; + + return c; } /* 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) { @@ -3854,30 +4305,82 @@ 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)); } } +/* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ +static Channel * +rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) +{ + Channel *c; + int r; + + c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = 0; + c->path = NULL; + + /* + * We need to open the channel before we have a FD, + * so that we can get SOCKS header from peer. + */ + 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)); + } + return c; +} + +/* Return CONNECTING socket to remote host:port or local socket path */ +static int +rdynamic_connect_finish(struct ssh *ssh, Channel *c) +{ + struct channel_connect cctx; + int sock; + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, + NULL, &cctx, NULL, NULL); + if (sock == -1) + channel_connect_ctx_free(&cctx); + else { + /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ + c->type = SSH_CHANNEL_RDYNAMIC_FINISH; + c->connect_ctx = cctx; + channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); + } + return sock; +} + /* -- X11 forwarding */ /* @@ -3886,8 +4389,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; @@ -3904,16 +4408,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); @@ -3937,12 +4443,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; } @@ -3972,7 +4477,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); @@ -3983,7 +4488,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 @@ -4041,7 +4546,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; @@ -4086,9 +4591,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. */ @@ -4110,7 +4616,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); @@ -4119,7 +4628,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) { @@ -4146,8 +4655,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); @@ -4160,18 +4669,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; @@ -4185,36 +4695,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 36e5363aa..126b04345 100644 --- a/channels.h +++ b/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.126 2017/05/30 14:23:52 markus Exp $ */ +/* $OpenBSD: channels.h,v 1.130 2017/09/21 19:16:53 markus Exp $ */ /* * Author: Tatu Ylonen @@ -57,23 +57,27 @@ #define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */ #define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */ #define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-slave */ -#define SSH_CHANNEL_MAX_TYPE 21 +#define SSH_CHANNEL_RDYNAMIC_OPEN 21 /* reverse SOCKS, parsing request */ +#define SSH_CHANNEL_RDYNAMIC_FINISH 22 /* reverse SOCKS, finishing connect */ +#define SSH_CHANNEL_MAX_TYPE 23 #define CHANNEL_CANCEL_PORT_STATIC -1 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 +94,14 @@ 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 */ + uint32_t remote_id; /* channel identifier for remote peer */ + int have_remote_id; /* non-zero if remote_id is valid */ + 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 +122,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 +163,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 +203,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 *, u_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 */ @@ -249,66 +268,72 @@ int channel_input_status_confirm(int, u_int32_t, struct ssh *); /* file descriptor handling (read/write) */ -void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, - time_t*, int); -void channel_after_select(fd_set *, fd_set *); -void channel_output_poll(void); +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(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 b4a58b00d..bd77224cd 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.301 2017/07/14 03:18:21 dtucker Exp $ */ +/* $OpenBSD: clientloop.c,v 1.305 2017/09/19 04:24:22 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; @@ -506,11 +510,11 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int ret; /* Add any selections by the channel mechanism. */ - channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, - &minwait_secs, rekeying); + channel_prepare_select(active_state, readsetp, writesetp, maxfdp, + 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); @@ -596,13 +600,9 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); - /* - * Free (and clear) the buffer to reduce the amount of data that gets - * written to swap. - */ - buffer_free(bin); - buffer_free(bout); - buffer_free(berr); + sshbuf_reset(bin); + sshbuf_reset(bout); + sshbuf_reset(berr); /* Send the suspend signal to the program itself. */ kill(getpid(), SIGTSTP); @@ -610,11 +610,6 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) /* Reset window sizes in case they have changed */ received_window_change_signal = 1; - /* OK, we have been continued by the user. Reinitialize buffers. */ - buffer_init(bin); - buffer_init(bout); - buffer_init(berr); - enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } @@ -668,7 +663,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 +702,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 +713,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 +737,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 +763,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 +837,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 +856,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 +939,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 +976,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 +1022,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; @@ -1080,7 +1077,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); @@ -1111,7 +1108,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; @@ -1119,7 +1116,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: @@ -1190,25 +1187,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); } @@ -1219,9 +1216,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; @@ -1299,13 +1296,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); } @@ -1315,15 +1312,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; @@ -1333,13 +1330,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; @@ -1349,15 +1346,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(readset, writeset); + if (!ssh_packet_is_rekeying(ssh)) + channel_after_select(ssh, readset, writeset); /* Buffer input from the connection. */ client_process_net_input(readset); @@ -1399,7 +1396,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); @@ -1467,8 +1464,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; @@ -1486,7 +1483,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) { @@ -1505,7 +1502,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; @@ -1520,7 +1517,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; @@ -1534,14 +1532,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; @@ -1571,10 +1569,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; @@ -1582,7 +1580,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; @@ -1599,7 +1597,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); @@ -1608,7 +1606,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; @@ -1624,13 +1623,13 @@ 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; #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) - channel_register_filter(c->self, sys_tun_infilter, + channel_register_filter(ssh, c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif @@ -1664,20 +1663,21 @@ 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); } else if (c != NULL) { debug("confirm %s", ctype); c->remote_id = rchan; + c->have_remote_id = 1; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { @@ -1711,7 +1711,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); @@ -1727,11 +1727,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 */ @@ -1745,6 +1745,9 @@ client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) packet_check_eom(); } if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { + if (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", + __func__, c->self); packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); packet_put_int(c->remote_id); @@ -1899,9 +1902,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; @@ -2165,7 +2168,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; @@ -2173,8 +2176,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); @@ -2186,8 +2189,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); @@ -2230,7 +2233,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(); @@ -2245,19 +2248,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/configure.ac b/configure.ac index 49c5caa26..889f50637 100644 --- a/configure.ac +++ b/configure.ac @@ -163,8 +163,8 @@ if test "$GCC" = "yes" || test "$GCC" = "egcs"; then OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) - OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) if test "x$use_toolchain_hardening" = "x1"; then + OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) @@ -289,6 +289,16 @@ AC_ARG_WITH([cflags], fi ] ) + +AC_ARG_WITH([cflags-after], + [ --with-cflags-after Specify additional flags to pass to compiler after configure], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + CFLAGS_AFTER="$withval" + fi + ] +) AC_ARG_WITH([cppflags], [ --with-cppflags Specify additional flags to pass to preprocessor] , [ @@ -307,6 +317,15 @@ AC_ARG_WITH([ldflags], fi ] ) +AC_ARG_WITH([ldflags-after], + [ --with-ldflags-after Specify additional flags to pass to linker after configure], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + LDFLAGS_AFTER="$withval" + fi + ] +) AC_ARG_WITH([libs], [ --with-libs Specify additional libraries to link with], [ @@ -370,7 +389,6 @@ AC_CHECK_HEADERS([ \ sys/audit.h \ sys/bitypes.h \ sys/bsdtty.h \ - sys/capability.h \ sys/cdefs.h \ sys/dir.h \ sys/mman.h \ @@ -402,6 +420,13 @@ AC_CHECK_HEADERS([ \ wchar.h \ ]) +# sys/capsicum.h requires sys/types.h +AC_CHECK_HEADERS([sys/capsicum.h], [], [], [ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +]) + # lastlog.h requires sys/time.h to be included first on Solaris AC_CHECK_HEADERS([lastlog.h], [], [], [ #ifdef HAVE_SYS_TIME_H @@ -1306,7 +1331,17 @@ AC_CHECK_FUNCS([fmt_scaled scan_scaled login logout openpty updwtmp logwtmp]) AC_SEARCH_LIBS([inet_ntop], [resolv nsl]) AC_SEARCH_LIBS([gethostbyname], [resolv nsl]) +# "Particular Function Checks" +# see https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Particular-Functions.html AC_FUNC_STRFTIME +AC_FUNC_MALLOC +AC_FUNC_REALLOC +# autoconf doesn't have AC_FUNC_CALLOC so fake it if malloc returns NULL; +if test "x$ac_cv_func_malloc_0_nonnull" != "xyes"; then + AC_DEFINE(HAVE_CALLOC, 0, [calloc(x, 0) returns NULL]) + AC_DEFINE(calloc, rpl_calloc, + [Define to rpl_calloc if the replacement function should be used.]) +fi # Check for ALTDIRFUNC glob() extension AC_MSG_CHECKING([for GLOB_ALTDIRFUNC support]) @@ -1671,6 +1706,7 @@ AC_CHECK_FUNCS([ \ fchmod \ fchown \ freeaddrinfo \ + freezero \ fstatfs \ fstatvfs \ futimes \ @@ -1744,6 +1780,7 @@ AC_CHECK_FUNCS([ \ strnlen \ strnvis \ strptime \ + strsignal \ strtonum \ strtoll \ strtoul \ @@ -2512,7 +2549,11 @@ if test "x$openssl" = "xyes" ; then 10000*|0*) AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) ;; - *) ;; + 100*) ;; # 1.0.x + 200*) ;; # LibreSSL + *) + AC_MSG_ERROR([OpenSSL >= 1.1.0 is not yet supported (have "$ssl_library_ver")]) + ;; esac AC_MSG_RESULT([$ssl_library_ver]) ], @@ -3256,10 +3297,10 @@ elif test "x$sandbox_arg" = "xseccomp_filter" || \ AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter]) elif test "x$sandbox_arg" = "xcapsicum" || \ ( test -z "$sandbox_arg" && \ - test "x$ac_cv_header_sys_capability_h" = "xyes" && \ + test "x$ac_cv_header_sys_capsicum_h" = "xyes" && \ test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then - test "x$ac_cv_header_sys_capability_h" != "xyes" && \ - AC_MSG_ERROR([capsicum sandbox requires sys/capability.h header]) + test "x$ac_cv_header_sys_capsicum_h" != "xyes" && \ + AC_MSG_ERROR([capsicum sandbox requires sys/capsicum.h header]) test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ AC_MSG_ERROR([capsicum sandbox requires cap_rights_limit function]) SANDBOX_STYLE="capsicum" @@ -5016,6 +5057,9 @@ AC_SUBST([TEST_SSH_UTF8], [$TEST_SSH_UTF8]) AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) AC_SUBST([UNSUPPORTED_ALGORITHMS], [$unsupported_algorithms]) +CFLAGS="${CFLAGS} ${CFLAGS_AFTER}" +LDFLAGS="${LDFLAGS} ${LDFLAGS_AFTER}" + AC_EXEEXT AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \ openbsd-compat/Makefile openbsd-compat/regress/Makefile \ diff --git a/contrib/redhat/openssh.spec b/contrib/redhat/openssh.spec index 7de45457a..a96a36e49 100644 --- a/contrib/redhat/openssh.spec +++ b/contrib/redhat/openssh.spec @@ -1,4 +1,4 @@ -%define ver 7.5p1 +%define ver 7.6p1 %define rel 1 # OpenSSH privilege separation requires a user & group ID diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id index bef5c95d9..b83b83619 100644 --- a/contrib/ssh-copy-id +++ b/contrib/ssh-copy-id @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (c) 1999-2013 Philip Hands +# Copyright (c) 1999-2016 Philip Hands # 2013 Martin Kletzander # 2010 Adeodato =?iso-8859-1?Q?Sim=F3?= # 2010 Eric Moret @@ -56,7 +56,8 @@ then fi fi -DEFAULT_PUB_ID_FILE="$HOME/$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)" +most_recent_id="$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)" +DEFAULT_PUB_ID_FILE="${most_recent_id:+$HOME/}$most_recent_id" usage () { printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 @@ -74,6 +75,11 @@ quote() { use_id_file() { local L_ID_FILE="$1" + if [ -z "$L_ID_FILE" ] ; then + printf "%s: ERROR: no ID file found\n" "$0" + exit 1 + fi + if expr "$L_ID_FILE" : ".*\.pub$" >/dev/null ; then PUB_ID_FILE="$L_ID_FILE" else @@ -287,9 +293,10 @@ case "$REMOTE_VERSION" in *) # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect populate_new_ids 0 - # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; 'cd' to be at $HOME; and all on one line, because tcsh. + # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; + # 'cd' to be at $HOME; add a newline if it's missing; and all on one line, because tcsh. [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ - ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ + ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && { [ -z "'`tail -1c .ssh/authorized_keys 2>/dev/null`'" ] || echo >> .ssh/authorized_keys ; } && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ || exit 1 ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) ;; diff --git a/contrib/suse/openssh.spec b/contrib/suse/openssh.spec index e62be39d0..fdb3578cb 100644 --- a/contrib/suse/openssh.spec +++ b/contrib/suse/openssh.spec @@ -13,7 +13,7 @@ Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation Name: openssh -Version: 7.5p1 +Version: 7.6p1 URL: https://www.openssh.com/ Release: 1 Source0: openssh-%{version}.tar.gz diff --git a/dns.c b/dns.c index 3af5cd007..a5337b8ed 100644 --- a/dns.c +++ b/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.35 2015/08/20 22:32:42 deraadt Exp $ */ +/* $OpenBSD: dns.c,v 1.37 2017/09/14 04:32:21 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. diff --git a/dns.h b/dns.h index 30e2b19b3..68443f7cb 100644 --- a/dns.h +++ b/dns.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.h,v 1.15 2015/05/08 06:45:13 djm Exp $ */ +/* $OpenBSD: dns.h,v 1.17 2017/09/14 04:32:21 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. diff --git a/monitor.c b/monitor.c index 8a7897bde..f517da482 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.174 2017/10/02 19:33:20 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -760,10 +760,12 @@ mm_answer_pwnamallow(int sock, Buffer *m) for (i = 0; i < options.nx; i++) \ buffer_put_cstring(m, options.x[i]); \ } while (0) +#define M_CP_STRARRAYOPT_ALLOC(x, nx) M_CP_STRARRAYOPT(x, nx) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT +#undef M_CP_STRARRAYOPT_ALLOC /* Create valid auth method lists */ if (auth2_setup_methods_lists(authctxt) != 0) { @@ -1519,13 +1521,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..69212aaf3 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.94 2017/10/02 19:33:20 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; @@ -289,13 +290,20 @@ out: for (i = 0; i < newopts->nx; i++) \ newopts->x[i] = buffer_get_string(&m, NULL); \ } while (0) +#define M_CP_STRARRAYOPT_ALLOC(x, nx) do { \ + newopts->x = newopts->nx == 0 ? \ + NULL : xcalloc(newopts->nx, sizeof(*newopts->x)); \ + M_CP_STRARRAYOPT(x, nx); \ + } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT +#undef M_CP_STRARRAYOPT_ALLOC 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..5ae454410 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.69 2017/09/20 05:19:00 dtucker 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,52 +203,54 @@ 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); + cc->remote_id = 0; + cc->have_remote_id = 0; + 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) - fatal("%s: channel %d missing session channel %d", + if (c->have_remote_id) { + if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) + fatal("%s: channel %d missing session channel %u", __func__, c->self, c->remote_id); - c->remote_id = -1; + c->remote_id = 0; + c->have_remote_id = 0; sc->ctl_chan = -1; 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 +278,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 +321,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; @@ -401,7 +415,7 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) new_fd[0], new_fd[1], new_fd[2]); /* XXX support multiple child sessions in future */ - if (c->remote_id != -1) { + if (c->have_remote_id) { debug2("%s: session already open", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); @@ -453,15 +467,16 @@ 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); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ + c->have_remote_id = 1; 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 +485,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 +510,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 +600,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 +608,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 +634,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 +643,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 +669,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 +677,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 +746,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 +806,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 +820,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 +849,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 +931,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 +965,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; @@ -986,7 +1010,7 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) new_fd[0], new_fd[1]); /* XXX support multiple child sessions in future */ - if (c->remote_id != -1) { + if (c->have_remote_id) { debug2("%s: session already open", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); @@ -1018,19 +1042,21 @@ 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 */ + c->have_remote_id = 1; 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 +1065,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 +1073,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 +1098,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 +1109,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 +1127,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 +1143,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 +1157,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 +1169,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 +1177,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 +1188,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 +1214,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 +1228,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 +1238,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 +1246,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 +1256,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 +1277,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 +1356,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 +1367,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 +1377,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 +1398,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 +1430,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) @@ -1972,7 +2001,7 @@ mux_client_request_session(int fd) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (muxclient_terminate) { - debug2("Exiting on signal %d", muxclient_terminate); + debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); exitval = 255; } else if (!exitval_seen) { debug2("Control master terminated unexpectedly"); diff --git a/nchan.c b/nchan.c index 36da8904a..24929556d 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.67 2017/09/12 06:35:32 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,32 @@ 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 (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", + __func__, c->self); + 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 +200,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 +212,22 @@ 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 (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", + __func__, c->self); + 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 +236,33 @@ 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 (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", __func__, c->self); + 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 +288,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 +319,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 +336,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 +347,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 +367,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 +380,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 +388,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 +406,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/openbsd-compat/Makefile.in b/openbsd-compat/Makefile.in index b5088c479..ac8ae4305 100644 --- a/openbsd-compat/Makefile.in +++ b/openbsd-compat/Makefile.in @@ -16,9 +16,9 @@ RANLIB=@RANLIB@ INSTALL=@INSTALL@ LDFLAGS=-L. @LDFLAGS@ -OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o reallocarray.o realpath.o recallocarray.o rresvport.o setenv.o setproctitle.o sha1.o sha2.o rmd160.o md5.o sigact.o strcasestr.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o +OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o reallocarray.o realpath.o recallocarray.o rresvport.o setenv.o setproctitle.o sha1.o sha2.o rmd160.o md5.o sigact.o strcasestr.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o freezero.o -COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-err.o bsd-getpagesize.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-setres_id.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xcrypt.o kludge-fd_set.o +COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-err.o bsd-getpagesize.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-malloc.o bsd-setres_id.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xcrypt.o kludge-fd_set.o PORTS=port-aix.o port-irix.o port-linux.o port-solaris.o port-tun.o port-uw.o diff --git a/openbsd-compat/bsd-malloc.c b/openbsd-compat/bsd-malloc.c new file mode 100644 index 000000000..6402ab588 --- /dev/null +++ b/openbsd-compat/bsd-malloc.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#undef malloc +#undef calloc +#undef realloc + +#include +#include + +#if defined(HAVE_MALLOC) && HAVE_MALLOC == 0 +void * +rpl_malloc(size_t size) +{ + if (size == 0) + size = 1; + return malloc(size); +} +#endif + +#if defined(HAVE_CALLOC) && HAVE_CALLOC == 0 +void * +rpl_calloc(size_t nmemb, size_t size) +{ + if (nmemb == 0) + nmemb = 1; + if (size == 0) + size = 1; + return calloc(nmemb, size); +} +#endif + +#if defined (HAVE_REALLOC) && HAVE_REALLOC == 0 +void * +rpl_realloc(void *ptr, size_t size) +{ + if (size == 0) + size = 1; + return realloc(ptr, size); +} +#endif diff --git a/openbsd-compat/bsd-misc.c b/openbsd-compat/bsd-misc.c index e51694662..4ee97959e 100644 --- a/openbsd-compat/bsd-misc.c +++ b/openbsd-compat/bsd-misc.c @@ -104,6 +104,16 @@ const char *strerror(int e) } #endif +#if !defined(HAVE_STRSIGNAL) +char *strsignal(int sig) +{ + static char buf[16]; + + (void)snprintf(buf, sizeof(buf), "%d", sig); + return buf; +} +#endif + #ifndef HAVE_UTIMES int utimes(char *filename, struct timeval *tvp) { diff --git a/openbsd-compat/bsd-misc.h b/openbsd-compat/bsd-misc.h index 70a538f04..0b1a3504f 100644 --- a/openbsd-compat/bsd-misc.h +++ b/openbsd-compat/bsd-misc.h @@ -49,6 +49,10 @@ int setegid(uid_t); const char *strerror(int); #endif +#if !defined(HAVE_STRSIGNAL) +char *strsignal(int); +#endif + #if !defined(HAVE_SETLINEBUF) #define setlinebuf(a) (setvbuf((a), NULL, _IOLBF, 0)) #endif diff --git a/openbsd-compat/freezero.c b/openbsd-compat/freezero.c new file mode 100644 index 000000000..3af8f4a73 --- /dev/null +++ b/openbsd-compat/freezero.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifndef HAVE_FREEZERO + +void +freezero(void *ptr, size_t sz) +{ + explicit_bzero(ptr, sz); + free(ptr); +} + +#endif /* HAVE_FREEZERO */ + diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index 58ab08592..0a41317e0 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -307,6 +307,10 @@ int bcrypt_pbkdf(const char *, size_t, const u_int8_t *, size_t, void explicit_bzero(void *p, size_t n); #endif +#ifndef HAVE_FREEZERO +void freezero(void *, size_t); +#endif + char *xcrypt(const char *password, const char *salt); char *shadow_pw(struct passwd *pw); diff --git a/openbsd-compat/port-tun.c b/openbsd-compat/port-tun.c index a7a5d949a..7579c6084 100644 --- a/openbsd-compat/port-tun.c +++ b/openbsd-compat/port-tun.c @@ -207,7 +207,7 @@ sys_tun_open(int tun, int mode) #define OPENBSD_AF_INET6 24 int -sys_tun_infilter(struct Channel *c, char *buf, int _len) +sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len) { int r; size_t len; @@ -245,24 +245,22 @@ sys_tun_infilter(struct Channel *c, char *buf, int _len) POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); #endif - if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0) + if ((r = sshbuf_put_string(c->input, ptr, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); return (0); } u_char * -sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen) +sys_tun_outfilter(struct ssh *ssh, struct Channel *c, + u_char **data, size_t *dlen) { u_char *buf; u_int32_t af; int r; - size_t xxx_dlen; /* XXX new API is incompatible with this signature. */ - if ((r = sshbuf_get_string(&c->output, data, &xxx_dlen)) != 0) + if ((r = sshbuf_get_string(c->output, data, dlen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); - if (dlen != NULL) - *dlen = xxx_dlen; if (*dlen < sizeof(af)) return (NULL); buf = *data; diff --git a/openbsd-compat/port-tun.h b/openbsd-compat/port-tun.h index c53df01fc..103514370 100644 --- a/openbsd-compat/port-tun.h +++ b/openbsd-compat/port-tun.h @@ -18,6 +18,7 @@ #define _PORT_TUN_H struct Channel; +struct ssh; #if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD) # define CUSTOM_SYS_TUN_OPEN @@ -26,8 +27,8 @@ int sys_tun_open(int, int); #if defined(SSH_TUN_COMPAT_AF) || defined(SSH_TUN_PREPEND_AF) # define SSH_TUN_FILTER -int sys_tun_infilter(struct Channel *, char *, int); -u_char *sys_tun_outfilter(struct Channel *, u_char **, u_int *); +int sys_tun_infilter(struct ssh *, struct Channel *, char *, int); +u_char *sys_tun_outfilter(struct ssh *, struct Channel *, u_char **, size_t *); #endif #endif 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/readconf.c b/readconf.c index 6375602c8..eeb25741d 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.277 2017/05/30 18:58:37 bluhm Exp $ */ +/* $OpenBSD: readconf.c,v 1.279 2017/09/21 19:16:53 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -757,6 +757,16 @@ static const struct multistate multistate_yesnoask[] = { { "ask", 2 }, { NULL, -1 } }; +static const struct multistate multistate_strict_hostkey[] = { + { "true", SSH_STRICT_HOSTKEY_YES }, + { "false", SSH_STRICT_HOSTKEY_OFF }, + { "yes", SSH_STRICT_HOSTKEY_YES }, + { "no", SSH_STRICT_HOSTKEY_OFF }, + { "ask", SSH_STRICT_HOSTKEY_ASK }, + { "off", SSH_STRICT_HOSTKEY_OFF }, + { "accept-new", SSH_STRICT_HOSTKEY_NEW }, + { NULL, -1 } +}; static const struct multistate multistate_yesnoaskconfirm[] = { { "true", 1 }, { "false", 0 }, @@ -832,6 +842,7 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host, char **cpptr, fwdarg[256]; u_int i, *uintptr, max_entries = 0; int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0; + int remotefwd, dynamicfwd; LogLevel *log_level_ptr; SyslogFacility *log_facility_ptr; long long val64; @@ -990,7 +1001,7 @@ parse_time: case oStrictHostKeyChecking: intptr = &options->strict_host_key_checking; - multistate_ptr = multistate_yesnoask; + multistate_ptr = multistate_strict_hostkey; goto parse_multistate; case oCompression: @@ -1251,31 +1262,36 @@ parse_keytypes: fatal("%.200s line %d: Missing port argument.", filename, linenum); - if (opcode == oLocalForward || - opcode == oRemoteForward) { + remotefwd = (opcode == oRemoteForward); + dynamicfwd = (opcode == oDynamicForward); + + if (!dynamicfwd) { arg2 = strdelim(&s); - if (arg2 == NULL || *arg2 == '\0') - fatal("%.200s line %d: Missing target argument.", - filename, linenum); - - /* construct a string for parse_forward */ - snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); - } else if (opcode == oDynamicForward) { - strlcpy(fwdarg, arg, sizeof(fwdarg)); + if (arg2 == NULL || *arg2 == '\0') { + if (remotefwd) + dynamicfwd = 1; + else + fatal("%.200s line %d: Missing target " + "argument.", filename, linenum); + } else { + /* construct a string for parse_forward */ + snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, + arg2); + } } + if (dynamicfwd) + strlcpy(fwdarg, arg, sizeof(fwdarg)); - if (parse_forward(&fwd, fwdarg, - opcode == oDynamicForward ? 1 : 0, - opcode == oRemoteForward ? 1 : 0) == 0) + if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) fatal("%.200s line %d: Bad forwarding specification.", filename, linenum); if (*activep) { - if (opcode == oLocalForward || - opcode == oDynamicForward) - add_local_forward(options, &fwd); - else if (opcode == oRemoteForward) + if (remotefwd) { add_remote_forward(options, &fwd); + } else { + add_local_forward(options, &fwd); + } } break; @@ -1944,7 +1960,7 @@ fill_default_options(Options * options) if (options->check_host_ip == -1) options->check_host_ip = 1; if (options->strict_host_key_checking == -1) - options->strict_host_key_checking = 2; /* 2 is default */ + options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK; if (options->compression == -1) options->compression = 0; if (options->tcp_keep_alive == -1) @@ -2346,9 +2362,10 @@ fmt_intarg(OpCodes code, int val) case oAddressFamily: return fmt_multistate_int(val, multistate_addressfamily); case oVerifyHostKeyDNS: - case oStrictHostKeyChecking: case oUpdateHostkeys: return fmt_multistate_int(val, multistate_yesnoask); + case oStrictHostKeyChecking: + return fmt_multistate_int(val, multistate_strict_hostkey); case oControlMaster: return fmt_multistate_int(val, multistate_controlmaster); case oTunnel: diff --git a/readconf.h b/readconf.h index 94dd427f5..22fe5c187 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.122 2017/05/30 18:58:37 bluhm Exp $ */ +/* $OpenBSD: readconf.h,v 1.123 2017/09/03 23:33:13 djm Exp $ */ /* * Author: Tatu Ylonen @@ -190,6 +190,11 @@ typedef struct { #define SSH_UPDATE_HOSTKEYS_YES 1 #define SSH_UPDATE_HOSTKEYS_ASK 2 +#define SSH_STRICT_HOSTKEY_OFF 0 +#define SSH_STRICT_HOSTKEY_NEW 1 +#define SSH_STRICT_HOSTKEY_YES 2 +#define SSH_STRICT_HOSTKEY_ASK 3 + void initialize_options(Options *); void fill_default_options(Options *); void fill_default_options_for_canonicalization(Options *); diff --git a/regress/agent-getpeereid.sh b/regress/agent-getpeereid.sh index 34bced154..037a50914 100644 --- a/regress/agent-getpeereid.sh +++ b/regress/agent-getpeereid.sh @@ -1,4 +1,4 @@ -# $OpenBSD: agent-getpeereid.sh,v 1.8 2017/01/06 02:51:16 djm Exp $ +# $OpenBSD: agent-getpeereid.sh,v 1.9 2017/09/13 14:58:26 bluhm Exp $ # Placed in the Public Domain. tid="disallow agent attach from other uid" diff --git a/regress/dynamic-forward.sh b/regress/dynamic-forward.sh index 2e2115f84..84f8ee192 100644 --- a/regress/dynamic-forward.sh +++ b/regress/dynamic-forward.sh @@ -1,4 +1,4 @@ -# $OpenBSD: dynamic-forward.sh,v 1.12 2017/04/30 23:34:55 djm Exp $ +# $OpenBSD: dynamic-forward.sh,v 1.13 2017/09/21 19:18:12 markus Exp $ # Placed in the Public Domain. tid="dynamic forwarding" @@ -17,33 +17,34 @@ trace "will use ProxyCommand $proxycmd" start_sshd -for p in ${SSH_PROTOCOLS}; do +for d in D R; do n=0 error="1" trace "start dynamic forwarding, fork to background" + while [ "$error" -ne 0 -a "$n" -lt 3 ]; do n=`expr $n + 1` - ${SSH} -$p -F $OBJ/ssh_config -f -D $FWDPORT -q \ + ${SSH} -F $OBJ/ssh_config -f -$d $FWDPORT -q \ -oExitOnForwardFailure=yes somehost exec sh -c \ \'"echo \$\$ > $OBJ/remote_pid; exec sleep 444"\' error=$? if [ "$error" -ne 0 ]; then - trace "forward failed proto $p attempt $n err $error" + trace "forward failed attempt $n err $error" sleep $n fi done if [ "$error" -ne 0 ]; then - fatal "failed to start dynamic forwarding proto $p" + fatal "failed to start dynamic forwarding" fi for s in 4 5; do for h in 127.0.0.1 localhost; do - trace "testing ssh protocol $p socks version $s host $h" + trace "testing ssh socks version $s host $h (-$d)" ${SSH} -F $OBJ/ssh_config \ -o "ProxyCommand ${proxycmd}${s} $h $PORT" \ - somehost cat $DATA > $OBJ/ls.copy - test -f $OBJ/ls.copy || fail "failed copy $DATA" - cmp $DATA $OBJ/ls.copy || fail "corrupted copy of $DATA" + somehost cat ${DATA} > ${COPY} + test -f ${COPY} || fail "failed copy ${DATA}" + cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" done done @@ -56,4 +57,5 @@ for p in ${SSH_PROTOCOLS}; do else fail "no pid file: $OBJ/remote_pid" fi + done diff --git a/regress/login-timeout.sh b/regress/login-timeout.sh index 12207fd99..4c2d07dc2 100644 --- a/regress/login-timeout.sh +++ b/regress/login-timeout.sh @@ -1,4 +1,4 @@ -# $OpenBSD: login-timeout.sh,v 1.8 2016/12/16 01:06:27 dtucker Exp $ +# $OpenBSD: login-timeout.sh,v 1.9 2017/08/07 00:53:51 dtucker Exp $ # Placed in the Public Domain. tid="connect after login grace timeout" @@ -10,23 +10,9 @@ echo "LoginGraceTime 10s" >> $OBJ/sshd_config echo "MaxStartups 1" >> $OBJ/sshd_config start_sshd -(echo SSH-2.0-fake; sleep 60) | telnet 127.0.0.1 ${PORT} >/dev/null 2>&1 & +(echo SSH-2.0-fake; sleep 60) | telnet 127.0.0.1 ${PORT} >/dev/null 2>&1 & sleep 15 ${SSH} -F $OBJ/ssh_config somehost true if [ $? -ne 0 ]; then - fail "ssh connect after login grace timeout failed with privsep" -fi - -stop_sshd - -trace "test login grace without privsep" -echo "UsePrivilegeSeparation no" >> $OBJ/sshd_config -start_sshd -sleep 1 - -(echo SSH-2.0-fake; sleep 60) | telnet 127.0.0.1 ${PORT} >/dev/null 2>&1 & -sleep 15 -${SSH} -F $OBJ/ssh_config somehost true -if [ $? -ne 0 ]; then - fail "ssh connect after login grace timeout failed without privsep" + fail "ssh connect after login grace timeout failed" fi diff --git a/regress/misc/fuzz-harness/Makefile b/regress/misc/fuzz-harness/Makefile new file mode 100644 index 000000000..8fbfc20c6 --- /dev/null +++ b/regress/misc/fuzz-harness/Makefile @@ -0,0 +1,22 @@ +# NB. libssh and libopenbsd-compat should be built with the same sanitizer opts. +CXX=clang++-3.9 +FUZZ_FLAGS=-fsanitize=address,undefined -fsanitize-coverage=edge +FUZZ_LIBS=-lFuzzer + +CXXFLAGS=-O2 -g -Wall -Wextra -I ../../.. $(FUZZ_FLAGS) +LDFLAGS=-L ../../.. -L ../../../openbsd-compat -g $(FUZZ_FLAGS) +LIBS=-lssh -lopenbsd-compat -lcrypto $(FUZZ_LIBS) + +all: pubkey_fuzz sig_fuzz + +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +pubkey_fuzz: pubkey_fuzz.o + $(CXX) -o $@ pubkey_fuzz.o $(LDFLAGS) $(LIBS) + +sig_fuzz: sig_fuzz.o + $(CXX) -o $@ sig_fuzz.o $(LDFLAGS) $(LIBS) + +clean: + -rm -f *.o pubkey_fuzz sig_fuzz diff --git a/regress/misc/fuzz-harness/README b/regress/misc/fuzz-harness/README new file mode 100644 index 000000000..ae6fbe75d --- /dev/null +++ b/regress/misc/fuzz-harness/README @@ -0,0 +1 @@ +This directory contains fuzzing harnesses for use with clang's libfuzzer. diff --git a/regress/misc/fuzz-harness/pubkey_fuzz.cc b/regress/misc/fuzz-harness/pubkey_fuzz.cc new file mode 100644 index 000000000..8bbc11093 --- /dev/null +++ b/regress/misc/fuzz-harness/pubkey_fuzz.cc @@ -0,0 +1,18 @@ +#include +#include +#include + +extern "C" { + +#include "sshkey.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + struct sshkey *k = NULL; + int r = sshkey_from_blob(data, size, &k); + if (r == 0) sshkey_free(k); + return 0; +} + +} // extern + diff --git a/regress/misc/fuzz-harness/sig_fuzz.cc b/regress/misc/fuzz-harness/sig_fuzz.cc new file mode 100644 index 000000000..0e535b49a --- /dev/null +++ b/regress/misc/fuzz-harness/sig_fuzz.cc @@ -0,0 +1,50 @@ +// cc_fuzz_target test for public key parsing. + +#include +#include +#include +#include +#include + +extern "C" { + +#include "includes.h" +#include "sshkey.h" +#include "ssherr.h" + +static struct sshkey *generate_or_die(int type, unsigned bits) { + int r; + struct sshkey *ret; + if ((r = sshkey_generate(type, bits, &ret)) != 0) { + fprintf(stderr, "generate(%d, %u): %s", type, bits, ssh_err(r)); + abort(); + } + return ret; +} + +int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen) +{ +#ifdef WITH_OPENSSL + static struct sshkey *rsa = generate_or_die(KEY_RSA, 2048); + static struct sshkey *dsa = generate_or_die(KEY_DSA, 1024); + static struct sshkey *ecdsa256 = generate_or_die(KEY_ECDSA, 256); + static struct sshkey *ecdsa384 = generate_or_die(KEY_ECDSA, 384); + static struct sshkey *ecdsa521 = generate_or_die(KEY_ECDSA, 521); +#endif + static struct sshkey *ed25519 = generate_or_die(KEY_ED25519, 0); + static const char *data = "If everyone started announcing his nose had " + "run away, I don’t know how it would all end"; + static const size_t dlen = strlen(data); + +#ifdef WITH_OPENSSL + sshkey_verify(rsa, sig, slen, (const u_char *)data, dlen, 0); + sshkey_verify(dsa, sig, slen, (const u_char *)data, dlen, 0); + sshkey_verify(ecdsa256, sig, slen, (const u_char *)data, dlen, 0); + sshkey_verify(ecdsa384, sig, slen, (const u_char *)data, dlen, 0); + sshkey_verify(ecdsa521, sig, slen, (const u_char *)data, dlen, 0); +#endif + sshkey_verify(ed25519, sig, slen, (const u_char *)data, dlen, 0); + return 0; +} + +} // extern diff --git a/regress/reexec.sh b/regress/reexec.sh index ce23a1af3..2192456cd 100644 --- a/regress/reexec.sh +++ b/regress/reexec.sh @@ -1,4 +1,4 @@ -# $OpenBSD: reexec.sh,v 1.11 2017/04/30 23:34:55 djm Exp $ +# $OpenBSD: reexec.sh,v 1.12 2017/08/07 03:52:55 dtucker Exp $ # Placed in the Public Domain. tid="reexec tests" @@ -51,17 +51,4 @@ rm -f $SSHD_COPY copy_tests stop_sshd - -verbose "test reexec fallback without privsep" - -cp $OBJ/sshd_config.orig $OBJ/sshd_config -echo "UsePrivilegeSeparation=no" >> $OBJ/sshd_config - -start_sshd_copy -rm -f $SSHD_COPY - -copy_tests - -stop_sshd - fi diff --git a/regress/test-exec.sh b/regress/test-exec.sh index 1480f13fc..68f010b70 100644 --- a/regress/test-exec.sh +++ b/regress/test-exec.sh @@ -1,4 +1,4 @@ -# $OpenBSD: test-exec.sh,v 1.60 2017/04/30 23:34:55 djm Exp $ +# $OpenBSD: test-exec.sh,v 1.61 2017/07/28 10:32:08 dtucker Exp $ # Placed in the Public Domain. #SUDO=sudo @@ -304,8 +304,15 @@ stop_sshd () i=`expr $i + 1` sleep $i done - test -f $PIDFILE && \ - fatal "sshd didn't exit port $PORT pid $pid" + if test -f $PIDFILE; then + if $SUDO kill -0 $pid; then + echo "sshd didn't exit " \ + "port $PORT pid $pid" + else + echo "sshd died without cleanup" + fi + exit 1 + fi fi fi fi diff --git a/sandbox-capsicum.c b/sandbox-capsicum.c index 655f0d217..e10bad7e8 100644 --- a/sandbox-capsicum.c +++ b/sandbox-capsicum.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/servconf.c b/servconf.c index e6229eb67..e763bb6e1 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.312 2017/10/02 19:33:20 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; @@ -703,6 +703,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) { @@ -960,7 +998,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; @@ -1631,24 +1669,18 @@ 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", @@ -1657,9 +1689,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; @@ -2030,6 +2069,13 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) dst->n[dst->num_n] = xstrdup(src->n[dst->num_n]); \ } \ } while(0) +#define M_CP_STRARRAYOPT_ALLOC(n, num_n) do { \ + if (src->num_n != 0) { \ + dst->n = xcalloc(src->num_n, sizeof(*dst->n)); \ + M_CP_STRARRAYOPT(n, num_n); \ + dst->num_n = src->num_n; \ + } \ +} while(0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); @@ -2060,6 +2106,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) #undef M_CP_INTOPT #undef M_CP_STROPT #undef M_CP_STRARRAYOPT +#undef M_CP_STRARRAYOPT_ALLOC void parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, @@ -2384,5 +2431,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..1dca702e6 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.126 2017/10/02 19:33:20 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_ALLOC(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 5cc3fc099..24bbae322 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.195 2017/08/11 04:16:35 dtucker Exp $ */ +/* $OpenBSD: serverloop.c,v 1.198 2017/09/12 06:35:32 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,8 +209,8 @@ 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(readsetp, writesetp, maxfdp, nallocp, - &minwait_secs, 0); + channel_prepare_select(ssh, readsetp, writesetp, maxfdp, + nallocp, &minwait_secs); /* XXX need proper deadline system for rekey/client alive */ if (minwait_secs != 0) @@ -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(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,12 +544,12 @@ 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) if (mode == SSH_TUNMODE_POINTOPOINT) - channel_register_filter(c->self, sys_tun_infilter, + channel_register_filter(ssh, c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif @@ -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,17 +608,18 @@ 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); c->remote_id = rchan; + c->have_remote_id = 1; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { @@ -645,9 +646,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 +750,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 +768,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 +787,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 +799,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,16 +835,19 @@ 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)) { + if (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", + __func__, c->self); packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); packet_put_int(c->remote_id); 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 1d354ec2d..5924551b9 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) { #ifdef WINDOWS packet_send_debug("Agent forwarding not supported in Windows yet"); @@ -227,7 +227,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); @@ -293,7 +293,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); @@ -301,17 +301,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 */ @@ -608,7 +608,7 @@ do_exec_pty(Session *s, const char *command) { * 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; @@ -733,7 +733,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; @@ -764,7 +764,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. */ @@ -788,7 +788,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; @@ -857,13 +857,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; @@ -885,7 +885,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; } #endif /* !WINDOWS */ @@ -924,9 +924,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]; @@ -985,9 +984,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; @@ -1003,9 +1002,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; @@ -1238,9 +1236,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; @@ -1699,7 +1696,7 @@ do_pwchange(Session *s) } static void -child_close_fds(void) +child_close_fds(struct ssh *ssh) { extern int auth_sock; @@ -1719,7 +1716,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 @@ -1743,7 +1740,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) { #ifdef WINDOWS /*not called for Windows */ @@ -1763,7 +1760,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); } @@ -1812,7 +1809,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); @@ -1825,7 +1822,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, @@ -2123,7 +2120,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(); @@ -2135,7 +2132,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; @@ -2188,7 +2185,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; @@ -2215,7 +2212,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; } } @@ -2228,7 +2225,7 @@ session_subsystem_req(Session *s) } static int -session_x11_req(Session *s) +session_x11_req(struct ssh *ssh, Session *s) { int success; @@ -2245,7 +2242,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"); @@ -2260,26 +2257,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 */ @@ -2291,7 +2288,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; @@ -2326,7 +2323,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(); @@ -2338,22 +2335,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 @@ -2361,33 +2357,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, @@ -2395,7 +2391,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); @@ -2466,40 +2462,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; @@ -2514,22 +2510,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); @@ -2545,14 +2541,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 @@ -2561,13 +2557,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", @@ -2597,16 +2592,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; @@ -2617,19 +2611,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 @@ -2639,22 +2632,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++) { @@ -2663,7 +2656,7 @@ session_destroy_all(void (*closefunc)(Session *)) if (closefunc != NULL) closefunc(s); else - session_close(s); + session_close(ssh, s); } } } @@ -2706,7 +2699,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]; @@ -2730,14 +2723,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); } @@ -2782,13 +2775,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; @@ -2844,7 +2837,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-add.1 b/ssh-add.1 index 509d8fb47..d5da9279c 100644 --- a/ssh-add.1 +++ b/ssh-add.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-add.1,v 1.64 2017/05/05 10:41:58 naddy Exp $ +.\" $OpenBSD: ssh-add.1,v 1.66 2017/08/29 13:05:58 jmc Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: May 5 2017 $ +.Dd $Mdocdate: August 29 2017 $ .Dt SSH-ADD 1 .Os .Sh NAME @@ -43,7 +43,7 @@ .Nd adds private key identities to the authentication agent .Sh SYNOPSIS .Nm ssh-add -.Op Fl cDdkLlXx +.Op Fl cDdkLlqXx .Op Fl E Ar fingerprint_hash .Op Fl t Ar life .Op Ar @@ -126,6 +126,8 @@ Lists public key parameters of all identities currently represented by the agent. .It Fl l Lists fingerprints of all identities currently represented by the agent. +.It Fl q +Be quiet after a successful operation. .It Fl s Ar pkcs11 Add keys provided by the PKCS#11 shared library .Ar pkcs11 . diff --git a/ssh-add.c b/ssh-add.c index 72d89db4a..2afd48330 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.133 2017/07/01 13:50:45 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -102,7 +102,7 @@ clear_pass(void) } static int -delete_file(int agent_fd, const char *filename, int key_only) +delete_file(int agent_fd, const char *filename, int key_only, int qflag) { struct sshkey *public, *cert = NULL; char *certpath = NULL, *comment = NULL; @@ -113,7 +113,10 @@ delete_file(int agent_fd, const char *filename, int key_only) return -1; } if ((r = ssh_remove_identity(agent_fd, public)) == 0) { - fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); + if (!qflag) { + fprintf(stderr, "Identity removed: %s (%s)\n", + filename, comment); + } ret = 0; } else fprintf(stderr, "Could not remove identity \"%s\": %s\n", @@ -138,8 +141,10 @@ delete_file(int agent_fd, const char *filename, int key_only) certpath, filename); if ((r = ssh_remove_identity(agent_fd, cert)) == 0) { - fprintf(stderr, "Identity removed: %s (%s)\n", certpath, - comment); + if (!qflag) { + fprintf(stderr, "Identity removed: %s (%s)\n", + certpath, comment); + } ret = 0; } else fprintf(stderr, "Could not remove identity \"%s\": %s\n", @@ -179,7 +184,7 @@ delete_all(int agent_fd) } static int -add_file(int agent_fd, const char *filename, int key_only) +add_file(int agent_fd, const char *filename, int key_only, int qflag) { struct sshkey *private, *cert; char *comment = NULL; @@ -427,13 +432,13 @@ lock_agent(int agent_fd, int lock) } static int -do_file(int agent_fd, int deleting, int key_only, char *file) +do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) { if (deleting) { - if (delete_file(agent_fd, file, key_only) == -1) + if (delete_file(agent_fd, file, key_only, qflag) == -1) return -1; } else { - if (add_file(agent_fd, file, key_only) == -1) + if (add_file(agent_fd, file, key_only, qflag) == -1) return -1; } return 0; @@ -456,6 +461,7 @@ usage(void) fprintf(stderr, " -X Unlock agent.\n"); fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); + fprintf(stderr, " -q Be quiet after a successful operation.\n"); } int @@ -466,7 +472,7 @@ main(int argc, char **argv) int agent_fd; char *pkcs11provider = NULL; int r, i, ch, deleting = 0, ret = 0, key_only = 0; - int xflag = 0, lflag = 0, Dflag = 0; + int xflag = 0, lflag = 0, Dflag = 0, qflag = 0; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ @@ -494,7 +500,7 @@ main(int argc, char **argv) exit(2); } - while ((ch = getopt(argc, argv, "klLcdDxXE:e:s:t:")) != -1) { + while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) { switch (ch) { case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); @@ -539,6 +545,9 @@ main(int argc, char **argv) goto done; } break; + case 'q': + qflag = 1; + break; default: usage(); ret = 1; @@ -587,7 +596,8 @@ main(int argc, char **argv) default_files[i]); if (stat(buf, &st) < 0) continue; - if (do_file(agent_fd, deleting, key_only, buf) == -1) + if (do_file(agent_fd, deleting, key_only, buf, + qflag) == -1) ret = 1; else count++; @@ -597,7 +607,7 @@ main(int argc, char **argv) } else { for (i = 0; i < argc; i++) { if (do_file(agent_fd, deleting, key_only, - argv[i]) == -1) + argv[i], qflag) == -1) ret = 1; } } diff --git a/ssh.1 b/ssh.1 index 3aacec415..2ab1697f9 100644 --- a/ssh.1 +++ b/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.383 2017/06/09 06:43:01 djm Exp $ -.Dd $Mdocdate: June 9 2017 $ +.\" $OpenBSD: ssh.1,v 1.384 2017/09/21 19:16:53 markus Exp $ +.Dd $Mdocdate: September 21 2017 $ .Dt SSH 1 .Os .Sh NAME @@ -592,21 +592,30 @@ Causes most warning and diagnostic messages to be suppressed. .Ar remote_socket : local_socket .Sm on .Xc +.It Fl R Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port +.Sm on +.Xc Specifies that connections to the given TCP port or Unix socket on the remote -(server) host are to be forwarded to the given host and port, or Unix socket, -on the local side. +(server) host are to be forwarded to the local side. +.Pp This works by allocating a socket to listen to either a TCP .Ar port or to a Unix socket on the remote side. Whenever a connection is made to this port or Unix socket, the connection is forwarded over the secure channel, and a connection -is made to either +is made from the local machine to either an explicit destination specified by .Ar host port .Ar hostport , or .Ar local_socket , -from the local machine. +or, if no explicit destination was specified, +.Nm +will act as a SOCKS 4/5 proxy and forward connections to the destinations +requested by the remote SOCKS client. .Pp Port forwardings can also be specified in the configuration file. Privileged ports can be forwarded only when diff --git a/ssh.c b/ssh.c index 62e1278bc..7372d407c 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.464 2017/09/21 19:16:53 markus 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; @@ -860,7 +868,8 @@ main(int ac, char **av) break; case 'R': - if (parse_forward(&fwd, optarg, 0, 1)) { + if (parse_forward(&fwd, optarg, 0, 1) || + parse_forward(&fwd, optarg, 1, 1)) { add_remote_forward(&options, &fwd); } else { fprintf(stderr, @@ -1108,7 +1117,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 +1262,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 +1282,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 +1465,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) @@ -1539,7 +1547,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; @@ -1557,10 +1565,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); } } @@ -1589,21 +1597,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; @@ -1617,15 +1625,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; @@ -1644,7 +1652,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) @@ -1666,7 +1674,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) @@ -1675,14 +1683,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."); @@ -1709,7 +1718,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; @@ -1722,15 +1731,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; } @@ -1738,7 +1747,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(); } @@ -1746,13 +1755,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; @@ -1782,34 +1791,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 @@ -1837,10 +1846,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, @@ -1875,7 +1884,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/ssh_config.5 b/ssh_config.5 index 15ca0b4f9..eab8dd01c 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.253 2017/07/23 23:37:02 djm Exp $ -.Dd $Mdocdate: July 23 2017 $ +.\" $OpenBSD: ssh_config.5,v 1.256 2017/09/21 19:16:53 markus Exp $ +.Dd $Mdocdate: September 21 2017 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -1298,13 +1298,19 @@ accept the tokens described in the section. .It Cm RemoteForward Specifies that a TCP port on the remote machine be forwarded over -the secure channel to the specified host and port from the local machine. +the secure channel. +The remote port may either be fowarded to a specified host and port +from the local machine, or may act as a SOCKS 4/5 proxy that allows a remote +client to connect to arbitrary destinations from the local machine. The first argument must be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on -and the second argument must be -.Ar host : Ns Ar hostport . +If forwarding to a specific destination then the second argument must be +.Ar host : Ns Ar hostport , +otherwise if no destination argument is specified then the remote forwarding +will be established as a SOCKS proxy. +.Pp IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. @@ -1458,10 +1464,19 @@ file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. +.Pp If this flag is set to -.Cm no , -ssh will automatically add new host keys to the -user known hosts files. +.Dq accept-new +then ssh will automatically add new host keys to the user +known hosts files, but will not permit connections to hosts with +changed host keys. +If this flag is set to +.Dq no +or +.Dq off , +ssh will automatically add new host keys to the user known hosts files +and allow connections to hosts with changed hostkeys to proceed, +subject to some restrictions. If this flag is set to .Cm ask (the default), 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 32e39830b..8333bb52f 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.283 2017/07/01 13:50:45 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -104,7 +104,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) { #ifdef WINDOWS @@ -185,7 +185,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; #endif /* !WINDOWS */ @@ -195,7 +196,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) { #ifdef WINDOWS fatal("Proxy connect is not supported in Windows yet"); @@ -266,9 +268,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; #endif /* !WINDOWS */ } @@ -425,7 +427,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) { @@ -499,27 +501,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 @@ -896,7 +902,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, if (readonly || want_cert) goto fail; /* The host is new. */ - if (options.strict_host_key_checking == 1) { + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_YES) { /* * User has requested strict host key checking. We * will not add the host key automatically. The only @@ -905,7 +912,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, error("No %s host key is known for %.200s and you " "have requested strict checking.", type, host); goto fail; - } else if (options.strict_host_key_checking == 2) { + } else if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { char msg1[1024], msg2[1024]; if (show_other_keys(host_hostkeys, host_key)) @@ -949,8 +957,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, hostkey_trusted = 1; /* user explicitly confirmed */ } /* - * If not in strict mode, add the key automatically to the - * local known_hosts file. + * If in "new" or "off" strict mode, add the key automatically + * to the local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); @@ -992,7 +1000,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ - if (options.strict_host_key_checking) { + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { error("%s host key for %.200s was revoked and you have " "requested strict checking.", type, host); goto fail; @@ -1045,7 +1054,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ - if (options.strict_host_key_checking) { + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { error("%s host key for %.200s has changed and you have " "requested strict checking.", type, host); goto fail; @@ -1132,15 +1142,17 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, "\nMatching host key in %s:%lu", host_found->file, host_found->line); } - if (options.strict_host_key_checking == 1) { - logit("%s", msg); - error("Exiting, you have requested strict checking."); - goto fail; - } else if (options.strict_host_key_checking == 2) { + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { strlcat(msg, "\nAre you sure you want " "to continue connecting (yes/no)? ", sizeof(msg)); if (!confirm(msg)) goto fail; + } else if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { + logit("%s", msg); + error("Exiting, you have requested strict checking."); + goto fail; } else { logit("%s", msg); } 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/sshconnect2.c b/sshconnect2.c index 560697f7f..c822d054e 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.265 2017/08/11 04:47:12 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.266 2017/08/27 00:38:41 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -900,7 +900,7 @@ int userauth_passwd(Authctxt *authctxt) { static int attempt = 0; - char prompt[150]; + char prompt[256]; char *password; const char *host = options.host_key_alias ? options.host_key_alias : authctxt->host; @@ -940,7 +940,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; char *info, *lang, *password = NULL, *retype = NULL; - char prompt[150]; + char prompt[256]; const char *host; debug2("input_userauth_passwd_changereq"); diff --git a/sshd.c b/sshd.c index 1142de128..dedf1f96f 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 @@ -1714,9 +1714,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]); @@ -2075,8 +2072,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) @@ -2200,10 +2203,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); @@ -2331,8 +2334,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/sshd_config.5 b/sshd_config.5 index 76e157f2e..251b7467f 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.250 2017/07/23 23:37:02 djm Exp $ -.Dd $Mdocdate: July 23 2017 $ +.\" $OpenBSD: sshd_config.5,v 1.253 2017/09/27 06:45:53 jmc Exp $ +.Dd $Mdocdate: September 27 2017 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -223,6 +223,18 @@ requires successful authentication using two different public keys. .Pp Note that each authentication method listed should also be explicitly enabled in the configuration. +.Pp +The available authentication methods are: +.Qq gssapi-with-mic , +.Qq hostbased , +.Qq keyboard-interactive , +.Qq none +(used for access to password-less accounts when +.Cm PermitEmptyPassword +is enabled), +.Qq password +and +.Qq publickey . .It Cm AuthorizedKeysCommand Specifies a program to be used to look up the user's public keys. The program must be owned by root, not writable by group or others and @@ -563,11 +575,13 @@ TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations. .It Cm ExposeAuthInfo -Enables writing a file containing a list of authentication methods and +Writes a temporary file containing a list of authentication methods and public credentials (e.g. keys) used to authenticate the user. The location of the file is exposed to the user session through the .Ev SSH_USER_AUTH environment variable. +The default is +.Cm no . .It Cm FingerprintHash Specifies the hash algorithm used when logging key fingerprints. Valid options are: 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); diff --git a/version.h b/version.h index c86e2097c..e093f623b 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ -/* $OpenBSD: version.h,v 1.79 2017/03/20 01:18:59 djm Exp $ */ +/* $OpenBSD: version.h,v 1.80 2017/09/30 22:26:33 djm Exp $ */ -#define SSH_VERSION "OpenSSH_7.5" +#define SSH_VERSION "OpenSSH_7.6" #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE