diff options
Diffstat (limited to 'channels.c')
| -rw-r--r-- | channels.c | 472 | 
1 files changed, 401 insertions, 71 deletions
| diff --git a/channels.c b/channels.c index 9f9e972f42919..bef8ad6aa2b37 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.351 2016/07/19 11:38:53 dtucker Exp $ */ +/* $OpenBSD: channels.c,v 1.356 2016/10/18 17:32:54 dtucker Exp $ */  /*   * Author: Tatu Ylonen <ylo@cs.hut.fi>   * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -42,7 +42,6 @@  #include "includes.h"  #include <sys/types.h> -#include <sys/param.h>	/* MIN MAX */  #include <sys/stat.h>  #include <sys/ioctl.h>  #include <sys/un.h> @@ -72,6 +71,7 @@  #include "ssh.h"  #include "ssh1.h"  #include "ssh2.h" +#include "ssherr.h"  #include "packet.h"  #include "log.h"  #include "misc.h" @@ -121,6 +121,7 @@ typedef struct {  	char *listen_host;		/* Remote side should listen address. */  	char *listen_path;		/* Remote side should listen path. */  	int listen_port;		/* Remote side should listen port. */ +	Channel *downstream;		/* Downstream mux*/  } ForwardPermission;  /* List of all permitted host/port pairs to connect by the user. */ @@ -184,6 +185,7 @@ static int IPv4or6 = AF_UNSPEC;  /* helper */  static void port_open_helper(Channel *c, char *rtype); +static const char *channel_rfwd_bind_host(const char *listen_host);  /* non-blocking connect helpers */  static int connect_next(struct channel_connect *); @@ -208,6 +210,20 @@ channel_by_id(int id)  	return c;  } +Channel * +channel_by_remote_id(int remote_id) +{ +	Channel *c; +	u_int i; + +	for (i = 0; i < channels_alloc; i++) { +		c = channels[i]; +		if (c != NULL && c->remote_id == remote_id) +			return c; +	} +	return NULL; +} +  /*   * Returns the channel if it is allowed to receive protocol messages.   * Private channels, like listening sockets, may not receive messages. @@ -230,6 +246,7 @@ channel_lookup(int id)  	case SSH_CHANNEL_INPUT_DRAINING:  	case SSH_CHANNEL_OUTPUT_DRAINING:  	case SSH_CHANNEL_ABANDONED: +	case SSH_CHANNEL_MUX_PROXY:  		return (c);  	}  	logit("Non-public channel %d, type %d.", id, c->type); @@ -245,9 +262,9 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd,      int extusage, int nonblock, int is_tty)  {  	/* Update the maximum file descriptor value. */ -	channel_max_fd = MAX(channel_max_fd, rfd); -	channel_max_fd = MAX(channel_max_fd, wfd); -	channel_max_fd = MAX(channel_max_fd, efd); +	channel_max_fd = MAXIMUM(channel_max_fd, rfd); +	channel_max_fd = MAXIMUM(channel_max_fd, wfd); +	channel_max_fd = MAXIMUM(channel_max_fd, efd);  	if (rfd != -1)  		fcntl(rfd, F_SETFD, FD_CLOEXEC); @@ -373,9 +390,9 @@ channel_find_maxfd(void)  	for (i = 0; i < channels_alloc; i++) {  		c = channels[i];  		if (c != NULL) { -			max = MAX(max, c->rfd); -			max = MAX(max, c->wfd); -			max = MAX(max, c->efd); +			max = MAXIMUM(max, c->rfd); +			max = MAXIMUM(max, c->wfd); +			max = MAXIMUM(max, c->efd);  		}  	}  	return max; @@ -411,14 +428,56 @@ channel_free(Channel *c)  {  	char *s;  	u_int i, n; +	Channel *other;  	struct channel_confirm *cc; -	for (n = 0, i = 0; i < channels_alloc; i++) -		if (channels[i]) +	for (n = 0, i = 0; i < channels_alloc; i++) { +		if ((other = channels[i]) != NULL) {  			n++; + +			/* detach from mux client and prepare for closing */ +			if (c->type == SSH_CHANNEL_MUX_CLIENT && +			    other->type == SSH_CHANNEL_MUX_PROXY && +			    other->mux_ctx == c) { +				other->mux_ctx = NULL; +				other->type = SSH_CHANNEL_OPEN; +				other->istate = CHAN_INPUT_CLOSED; +				other->ostate = CHAN_OUTPUT_CLOSED; +			} +		} +	}  	debug("channel %d: free: %s, nchannels %u", c->self,  	    c->remote_name ? c->remote_name : "???", n); +	/* XXX more MUX cleanup: remove remote forwardings */ +	if (c->type == SSH_CHANNEL_MUX_CLIENT) { +		for (i = 0; i < (u_int)num_permitted_opens; i++) { +			if (permitted_opens[i].downstream != c) +				continue; +			/* cancel on the server, since mux client is gone */ +			debug("channel %d: cleanup remote forward for %s:%u", +			    c->self, +			    permitted_opens[i].listen_host, +			    permitted_opens[i].listen_port); +			packet_start(SSH2_MSG_GLOBAL_REQUEST); +			packet_put_cstring("cancel-tcpip-forward"); +			packet_put_char(0); +			packet_put_cstring(channel_rfwd_bind_host( +			    permitted_opens[i].listen_host)); +			packet_put_int(permitted_opens[i].listen_port); +			packet_send(); +			/* unregister */ +			permitted_opens[i].listen_port = 0; +			permitted_opens[i].port_to_connect = 0; +			free(permitted_opens[i].host_to_connect); +			permitted_opens[i].host_to_connect = NULL; +			free(permitted_opens[i].listen_host); +			permitted_opens[i].listen_host = NULL; +			permitted_opens[i].listen_path = NULL; +			permitted_opens[i].downstream = NULL; +		} +	} +  	s = channel_open_message();  	debug3("channel %d: status: %s", c->self, s);  	free(s); @@ -564,6 +623,7 @@ channel_still_open(void)  		case SSH_CHANNEL_OPEN:  		case SSH_CHANNEL_X11_OPEN:  		case SSH_CHANNEL_MUX_CLIENT: +		case SSH_CHANNEL_MUX_PROXY:  			return 1;  		case SSH_CHANNEL_INPUT_DRAINING:  		case SSH_CHANNEL_OUTPUT_DRAINING: @@ -597,6 +657,7 @@ channel_find_open(void)  		case SSH_CHANNEL_RPORT_LISTENER:  		case SSH_CHANNEL_MUX_LISTENER:  		case SSH_CHANNEL_MUX_CLIENT: +		case SSH_CHANNEL_MUX_PROXY:  		case SSH_CHANNEL_OPENING:  		case SSH_CHANNEL_CONNECTING:  		case SSH_CHANNEL_ZOMBIE: @@ -622,7 +683,6 @@ channel_find_open(void)  	return -1;  } -  /*   * Returns a message describing the currently open forwarded connections,   * suitable for sending to the client.  The message contains crlf pairs for @@ -651,7 +711,6 @@ channel_open_message(void)  		case SSH_CHANNEL_AUTH_SOCKET:  		case SSH_CHANNEL_ZOMBIE:  		case SSH_CHANNEL_ABANDONED: -		case SSH_CHANNEL_MUX_CLIENT:  		case SSH_CHANNEL_MUX_LISTENER:  		case SSH_CHANNEL_UNIX_LISTENER:  		case SSH_CHANNEL_RUNIX_LISTENER: @@ -664,6 +723,8 @@ channel_open_message(void)  		case SSH_CHANNEL_X11_OPEN:  		case SSH_CHANNEL_INPUT_DRAINING:  		case SSH_CHANNEL_OUTPUT_DRAINING: +		case SSH_CHANNEL_MUX_PROXY: +		case SSH_CHANNEL_MUX_CLIENT:  			snprintf(buf, sizeof buf,  			    "  #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n",  			    c->self, c->remote_name, @@ -1898,7 +1959,7 @@ read_mux(Channel *c, u_int need)  	if (buffer_len(&c->input) < need) {  		rlen = need - buffer_len(&c->input); -		len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); +		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));  		if (len < 0 && (errno == EINTR || errno == EAGAIN))  			return buffer_len(&c->input);  		if (len <= 0) { @@ -2201,7 +2262,7 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,  {  	u_int n, sz, nfdset; -	n = MAX(*maxfdp, channel_max_fd); +	n = MAXIMUM(*maxfdp, channel_max_fd);  	nfdset = howmany(n+1, NFDBITS);  	/* Explicitly test here, because xrealloc isn't always called */ @@ -2361,6 +2422,284 @@ channel_output_poll(void)  	}  } +/* -- mux proxy support  */ + +/* + * When multiplexing channel messages for mux clients we have to deal + * with downstream messages from the mux client and upstream messages + * from the ssh server: + * 1) Handling downstream messages is straightforward and happens + *    in channel_proxy_downstream(): + *    - We forward all messages (mostly) unmodified to the server. + *    - However, in order to route messages from upstream to the correct + *      downstream client, we have to replace the channel IDs used by the + *      mux clients with a unique channel ID because the mux clients might + *      use conflicting channel IDs. + *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and + *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local + *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID + *      with the newly allocated channel ID. + * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY + *    channels and procesed by channel_proxy_upstream(). The local channel ID + *    is then translated back to the original mux client ID. + * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE + *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. + * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the + *    downstream mux client are removed. + * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server + *    requires more work, because they are not addressed to a specific + *    channel. E.g. client_request_forwarded_tcpip() needs to figure + *    out whether the request is addressed to the local client or a + *    specific downstream client based on the listen-address/port. + * 6) Agent and X11-Forwarding have a similar problem and are currenly + *    not supported as the matching session/channel cannot be identified + *    easily. + */ + +/* + * receive packets from downstream mux clients: + * channel callback fired on read from mux client, creates + * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs + * on channel creation. + */ +int +channel_proxy_downstream(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; +	u_int id, remote_id, listen_port; + +	/* 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; +	} +	if (have < 2) { +		error("%s: short message", __func__); +		return -1; +	} +	type = cp[1]; +	/* skip padlen + type */ +	cp += 2; +	have -= 2; +	if (ssh_packet_log_type(type)) +		debug3("%s: channel %u: down->up: type %u", __func__, +		    downstream->self, type); + +	switch (type) { +	case SSH2_MSG_CHANNEL_OPEN: +		if ((original = sshbuf_from(cp, have)) == NULL || +		    (modified = sshbuf_new()) == NULL) { +			error("%s: alloc", __func__); +			goto out; +		} +		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || +		    (r = sshbuf_get_u32(original, &id)) != 0) { +			error("%s: parse error %s", __func__, ssh_err(r)); +			goto out; +		} +		c = channel_new("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 */ +		if ((r = sshbuf_put_cstring(modified, ctype)) != 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); +			goto out; +		} +		break; +	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: +		/* +		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we +		 * need to parse 'remote_id' instead of 'ctype'. +		 */ +		if ((original = sshbuf_from(cp, have)) == NULL || +		    (modified = sshbuf_new()) == NULL) { +			error("%s: alloc", __func__); +			goto out; +		} +		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || +		    (r = sshbuf_get_u32(original, &id)) != 0) { +			error("%s: parse error %s", __func__, ssh_err(r)); +			goto out; +		} +		c = channel_new("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; +		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); +			goto out; +		} +		break; +	case SSH2_MSG_GLOBAL_REQUEST: +		if ((original = sshbuf_from(cp, have)) == NULL) { +			error("%s: alloc", __func__); +			goto out; +		} +		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { +			error("%s: parse error %s", __func__, ssh_err(r)); +			goto out; +		} +		if (strcmp(ctype, "tcpip-forward") != 0) { +			error("%s: unsupported request %s", __func__, ctype); +			goto out; +		} +		if ((r = sshbuf_get_u8(original, NULL)) != 0 || +		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || +		    (r = sshbuf_get_u32(original, &listen_port)) != 0) { +			error("%s: parse error %s", __func__, ssh_err(r)); +			goto out; +		} +		if (listen_port > 65535) { +			error("%s: tcpip-forward for %s: bad port %u", +			    __func__, listen_host, listen_port); +			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("<mux>"); +		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; +		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->flags & CHAN_CLOSE_RCVD) +				channel_free(c); +			else +				c->flags |= CHAN_CLOSE_SENT; +		} +		break; +	} +	if (modified) { +		if ((r = sshpkt_start(ssh, type)) != 0 || +		    (r = sshpkt_putb(ssh, modified)) != 0 || +		    (r = sshpkt_send(ssh)) != 0) { +			error("%s: send %s", __func__, ssh_err(r)); +			goto out; +		} +	} else { +		if ((r = sshpkt_start(ssh, type)) != 0 || +		    (r = sshpkt_put(ssh, cp, have)) != 0 || +		    (r = sshpkt_send(ssh)) != 0) { +			error("%s: send %s", __func__, ssh_err(r)); +			goto out; +		} +	} +	ret = 0; + out: +	free(ctype); +	free(listen_host); +	sshbuf_free(original); +	sshbuf_free(modified); +	return ret; +} + +/* + * receive packets from upstream server and de-multiplex packets + * to correct downstream: + * implemented as a helper for channel input handlers, + * replaces local (proxy) channel ID with downstream channel ID. + */ +int +channel_proxy_upstream(Channel *c, int type, u_int32_t seq, void *ctxt) +{ +	struct ssh *ssh = active_state; +	struct sshbuf *b = NULL; +	Channel *downstream; +	const u_char *cp = NULL; +	size_t len; +	int r; + +	/* +	 * When receiving packets from the peer we need to check whether we +	 * need to forward the packets to the mux client. In this case we +	 * restore the orignal channel id and keep track of CLOSE messages, +	 * so we can cleanup the channel. +	 */ +	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) +		return 0; +	if ((downstream = c->mux_ctx) == NULL) +		return 0; +	switch (type) { +	case SSH2_MSG_CHANNEL_CLOSE: +	case SSH2_MSG_CHANNEL_DATA: +	case SSH2_MSG_CHANNEL_EOF: +	case SSH2_MSG_CHANNEL_EXTENDED_DATA: +	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: +	case SSH2_MSG_CHANNEL_OPEN_FAILURE: +	case SSH2_MSG_CHANNEL_WINDOW_ADJUST: +	case SSH2_MSG_CHANNEL_SUCCESS: +	case SSH2_MSG_CHANNEL_FAILURE: +	case SSH2_MSG_CHANNEL_REQUEST: +		break; +	default: +		debug2("%s: channel %u: unsupported type %u", __func__, +		    c->self, type); +		return 0; +	} +	if ((b = sshbuf_new()) == NULL) { +		error("%s: alloc reply", __func__); +		goto out; +	} +	/* get remaining payload (after id) */ +	cp = sshpkt_ptr(ssh, &len); +	if (cp == NULL) { +		error("%s: no packet", __func__); +		goto out; +	} +	/* translate id and send to muxclient */ +	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */ +	    (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) { +		error("%s: compose for muxclient %s", __func__, ssh_err(r)); +		goto out; +	} +	/* sshbuf_dump(b, stderr); */ +	if (ssh_packet_log_type(type)) +		debug3("%s: channel %u: up->down: type %u", __func__, c->self, +		    type); + out: +	/* update state */ +	switch (type) { +	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: +		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ +		if (cp && len > 4) +			c->remote_id = PEEK_U32(cp); +		break; +	case SSH2_MSG_CHANNEL_CLOSE: +		if (c->flags & CHAN_CLOSE_SENT) +			channel_free(c); +		else +			c->flags |= CHAN_CLOSE_RCVD; +		break; +	} +	sshbuf_free(b); +	return 1; +}  /* -- protocol input */ @@ -2378,6 +2717,8 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)  	c = channel_lookup(id);  	if (c == NULL)  		packet_disconnect("Received data for nonexistent channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0;  	/* Ignore any data for non-open channels (might happen on close) */  	if (c->type != SSH_CHANNEL_OPEN && @@ -2440,6 +2781,8 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt)  	if (c == NULL)  		packet_disconnect("Received extended_data for bad channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0;  	if (c->type != SSH_CHANNEL_OPEN) {  		logit("channel %d: ext data for non open", id);  		return 0; @@ -2485,6 +2828,8 @@ channel_input_ieof(int type, u_int32_t seq, void *ctxt)  	c = channel_lookup(id);  	if (c == NULL)  		packet_disconnect("Received ieof for nonexistent channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0;  	chan_rcvd_ieof(c);  	/* XXX force input close */ @@ -2509,7 +2854,8 @@ channel_input_close(int type, u_int32_t seq, void *ctxt)  	c = channel_lookup(id);  	if (c == NULL)  		packet_disconnect("Received close for nonexistent channel %d.", id); - +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0;  	/*  	 * Send a confirmation that we have closed the channel and no more  	 * data is coming for it. @@ -2544,9 +2890,11 @@ channel_input_oclose(int type, u_int32_t seq, void *ctxt)  	int id = packet_get_int();  	Channel *c = channel_lookup(id); -	packet_check_eom();  	if (c == NULL)  		packet_disconnect("Received oclose for nonexistent channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0; +	packet_check_eom();  	chan_rcvd_oclose(c);  	return 0;  } @@ -2558,10 +2906,12 @@ channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)  	int id = packet_get_int();  	Channel *c = channel_lookup(id); -	packet_check_eom();  	if (c == NULL)  		packet_disconnect("Received close confirmation for "  		    "out-of-range channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0; +	packet_check_eom();  	if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED)  		packet_disconnect("Received close confirmation for "  		    "non-closed channel %d (type %d).", id, c->type); @@ -2579,7 +2929,12 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)  	id = packet_get_int();  	c = channel_lookup(id); -	if (c==NULL || c->type != SSH_CHANNEL_OPENING) +	if (c==NULL) +		packet_disconnect("Received open confirmation for " +		    "unknown channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0; +	if (c->type != SSH_CHANNEL_OPENING)  		packet_disconnect("Received open confirmation for "  		    "non-opening channel %d.", id);  	remote_id = packet_get_int(); @@ -2629,7 +2984,12 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt)  	id = packet_get_int();  	c = channel_lookup(id); -	if (c==NULL || c->type != SSH_CHANNEL_OPENING) +	if (c==NULL) +		packet_disconnect("Received open failure for " +		    "unknown channel %d.", id); +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0; +	if (c->type != SSH_CHANNEL_OPENING)  		packet_disconnect("Received open failure for "  		    "non-opening channel %d.", id);  	if (compat20) { @@ -2673,6 +3033,8 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)  		logit("Received window adjust for non-open channel %d.", id);  		return 0;  	} +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0;  	adjust = packet_get_int();  	packet_check_eom();  	debug2("channel %d: rcvd adjust %u", id, adjust); @@ -2727,14 +3089,15 @@ channel_input_status_confirm(int type, u_int32_t seq, void *ctxt)  	packet_set_alive_timeouts(0);  	id = packet_get_int(); -	packet_check_eom(); -  	debug2("channel_input_status_confirm: type %d id %d", type, id);  	if ((c = channel_lookup(id)) == NULL) {  		logit("channel_input_status_confirm: %d: unknown", id);  		return 0;  	}	 +	if (channel_proxy_upstream(c, type, seq, ctxt)) +		return 0; +	packet_check_eom();  	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)  		return 0;  	cc->cb(type, c, cc->ctx); @@ -3288,6 +3651,7 @@ channel_request_remote_forwarding(struct Forward *fwd)  			permitted_opens[idx].listen_path = NULL;  			permitted_opens[idx].listen_port = fwd->listen_port;  		} +		permitted_opens[idx].downstream = NULL;  	}  	return (idx);  } @@ -3383,6 +3747,7 @@ channel_request_rforward_cancel_tcpip(const char *host, u_short port)  	free(permitted_opens[i].listen_host);  	permitted_opens[i].listen_host = NULL;  	permitted_opens[i].listen_path = NULL; +	permitted_opens[i].downstream = NULL;  	return 0;  } @@ -3420,6 +3785,7 @@ channel_request_rforward_cancel_streamlocal(const char *path)  	permitted_opens[i].listen_host = NULL;  	free(permitted_opens[i].listen_path);  	permitted_opens[i].listen_path = NULL; +	permitted_opens[i].downstream = NULL;  	return 0;  } @@ -3440,45 +3806,6 @@ channel_request_rforward_cancel(struct Forward *fwd)  }  /* - * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates - * listening for the port, and sends back a success reply (or disconnect - * message if there was an error). - */ -int -channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) -{ -	int success = 0; -	struct Forward fwd; - -	/* Get arguments from the packet. */ -	memset(&fwd, 0, sizeof(fwd)); -	fwd.listen_port = packet_get_int(); -	fwd.connect_host = packet_get_string(NULL); -	fwd.connect_port = packet_get_int(); - -#ifndef HAVE_CYGWIN -	/* -	 * Check that an unprivileged user is not trying to forward a -	 * privileged port. -	 */ -	if (fwd.listen_port < IPPORT_RESERVED && !is_root) -		packet_disconnect( -		    "Requested forwarding of port %d but user is not root.", -		    fwd.listen_port); -	if (fwd.connect_port == 0) -		packet_disconnect("Dynamic forwarding denied."); -#endif - -	/* Initiate forwarding */ -	success = channel_setup_local_fwd_listener(&fwd, fwd_opts); - -	/* Free the argument string. */ -	free(fwd.connect_host); - -	return (success ? 0 : -1); -} - -/*   * Permits opening to any host/port if permitted_opens[] is empty.  This is   * usually called by the server, because the user could connect to any port   * anyway, and the server has no way to know but to trust the client anyway. @@ -3502,6 +3829,7 @@ channel_add_permitted_opens(char *host, int 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; @@ -3633,7 +3961,7 @@ connect_next(struct channel_connect *cctx)  {  	int sock, saved_errno;  	struct sockaddr_un *sunaddr; -	char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; +	char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))];  	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {  		switch (cctx->ai->ai_family) { @@ -3764,6 +4092,10 @@ connect_to(const char *name, int port, char *ctype, char *rname)  	return c;  } +/* + * returns either the newly connected channel or the downstream channel + * that needs to deal with this connection. + */  Channel *  channel_connect_by_listen_address(const char *listen_host,      u_short listen_port, char *ctype, char *rname) @@ -3773,6 +4105,8 @@ channel_connect_by_listen_address(const char *listen_host,  	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); @@ -4216,7 +4550,6 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,  	char *new_data;  	int screen_number;  	const char *cp; -	u_int32_t rnd = 0;  	if (x11_saved_display == NULL)  		x11_saved_display = xstrdup(disp); @@ -4237,23 +4570,20 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,  	if (x11_saved_proto == NULL) {  		/* Save protocol name. */  		x11_saved_proto = xstrdup(proto); -		/* -		 * Extract real authentication data and generate fake data -		 * of the same length. -		 */ + +		/* Extract real authentication data. */  		x11_saved_data = xmalloc(data_len); -		x11_fake_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); -			if (i % 4 == 0) -				rnd = arc4random();  			x11_saved_data[i] = value; -			x11_fake_data[i] = rnd & 0xff; -			rnd >>= 8;  		}  		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;  	} | 
