xref: /netbsd-src/crypto/external/bsd/openssh/dist/channels.c (revision 9469f4f13c84743995b7d51c506f9c9849ba30de)
1*9469f4f1Schristos /*	$NetBSD: channels.c,v 1.45 2024/09/24 21:32:18 christos Exp $	*/
2*9469f4f1Schristos /* $OpenBSD: channels.c,v 1.439 2024/07/25 22:40:08 djm Exp $ */
3a629fefcSchristos 
4ca32bd8dSchristos /*
5ca32bd8dSchristos  * Author: Tatu Ylonen <ylo@cs.hut.fi>
6ca32bd8dSchristos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7ca32bd8dSchristos  *                    All rights reserved
8ca32bd8dSchristos  * This file contains functions for generic socket connection forwarding.
9ca32bd8dSchristos  * There is also code for initiating connection forwarding for X11 connections,
10ca32bd8dSchristos  * arbitrary tcp/ip connections, and the authentication agent connection.
11ca32bd8dSchristos  *
12ca32bd8dSchristos  * As far as I am concerned, the code I have written for this software
13ca32bd8dSchristos  * can be used freely for any purpose.  Any derived versions of this
14ca32bd8dSchristos  * software must be clearly marked as such, and if the derived work is
15ca32bd8dSchristos  * incompatible with the protocol description in the RFC file, it must be
16ca32bd8dSchristos  * called by a name other than "ssh" or "Secure Shell".
17ca32bd8dSchristos  *
18ca32bd8dSchristos  * SSH2 support added by Markus Friedl.
19ca32bd8dSchristos  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
20ca32bd8dSchristos  * Copyright (c) 1999 Dug Song.  All rights reserved.
21ca32bd8dSchristos  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
22ca32bd8dSchristos  *
23ca32bd8dSchristos  * Redistribution and use in source and binary forms, with or without
24ca32bd8dSchristos  * modification, are permitted provided that the following conditions
25ca32bd8dSchristos  * are met:
26ca32bd8dSchristos  * 1. Redistributions of source code must retain the above copyright
27ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer.
28ca32bd8dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
29ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer in the
30ca32bd8dSchristos  *    documentation and/or other materials provided with the distribution.
31ca32bd8dSchristos  *
32ca32bd8dSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
33ca32bd8dSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34ca32bd8dSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
35ca32bd8dSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
36ca32bd8dSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37ca32bd8dSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38ca32bd8dSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39ca32bd8dSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40ca32bd8dSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41ca32bd8dSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42ca32bd8dSchristos  */
43ca32bd8dSchristos 
44313c6c94Schristos #include "includes.h"
45*9469f4f1Schristos __RCSID("$NetBSD: channels.c,v 1.45 2024/09/24 21:32:18 christos Exp $");
46313c6c94Schristos #include <sys/param.h>
47ca32bd8dSchristos #include <sys/types.h>
488a4530f9Schristos #include <sys/stat.h>
49ca32bd8dSchristos #include <sys/ioctl.h>
50ca32bd8dSchristos #include <sys/un.h>
51ca32bd8dSchristos #include <sys/socket.h>
52ca32bd8dSchristos #include <sys/time.h>
53ca32bd8dSchristos #include <sys/queue.h>
54ca32bd8dSchristos 
55ca32bd8dSchristos #include <netinet/in.h>
56ca32bd8dSchristos #include <arpa/inet.h>
57ca32bd8dSchristos 
58ca32bd8dSchristos #include <errno.h>
5934b27b53Sadam #include <fcntl.h>
607a183406Schristos #include <limits.h>
61ca32bd8dSchristos #include <netdb.h>
62a03ec00cSchristos #include <poll.h>
637a183406Schristos #include <stdarg.h>
64e4d43b82Schristos #include <stdint.h>
65ca32bd8dSchristos #include <stdio.h>
66ca32bd8dSchristos #include <stdlib.h>
67ca32bd8dSchristos #include <string.h>
68ca32bd8dSchristos #include <termios.h>
69ca32bd8dSchristos #include <unistd.h>
70ca32bd8dSchristos 
71ca32bd8dSchristos #include "xmalloc.h"
72ca32bd8dSchristos #include "ssh.h"
73ca32bd8dSchristos #include "ssh2.h"
74ee85abc4Schristos #include "ssherr.h"
757a183406Schristos #include "sshbuf.h"
76ca32bd8dSchristos #include "packet.h"
77ca32bd8dSchristos #include "log.h"
78ca32bd8dSchristos #include "misc.h"
79ca32bd8dSchristos #include "channels.h"
80ca32bd8dSchristos #include "compat.h"
81ca32bd8dSchristos #include "canohost.h"
8255a4608bSchristos #include "sshkey.h"
83ca32bd8dSchristos #include "authfd.h"
84ca32bd8dSchristos #include "pathnames.h"
8555a4608bSchristos #include "match.h"
86ca32bd8dSchristos 
87313c6c94Schristos 
88313c6c94Schristos static int hpn_disabled = 0;
89313c6c94Schristos static int hpn_buffer_size = 2 * 1024 * 1024;
90313c6c94Schristos 
91a03ec00cSchristos /* XXX remove once we're satisfied there's no lurking bugs */
92a03ec00cSchristos /* #define DEBUG_CHANNEL_POLL 1 */
93a03ec00cSchristos 
947a183406Schristos /* -- agent forwarding */
957a183406Schristos #define	NUM_SOCKS	10
96ca32bd8dSchristos 
977a183406Schristos /* -- X11 forwarding */
987a183406Schristos /* Maximum number of fake X11 displays to try. */
997a183406Schristos #define MAX_DISPLAYS  1000
100ca32bd8dSchristos 
101a03ec00cSchristos /* Per-channel callback for pre/post IO actions */
102a03ec00cSchristos typedef void chan_fn(struct ssh *, Channel *c);
10355a4608bSchristos 
104ca32bd8dSchristos /*
105ca32bd8dSchristos  * Data structure for storing which hosts are permitted for forward requests.
106ca32bd8dSchristos  * The local sides of any remote forwards are stored in this array to prevent
107ca32bd8dSchristos  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
108ca32bd8dSchristos  * network (which might be behind a firewall).
109ca32bd8dSchristos  */
1108a4530f9Schristos /* XXX: streamlocal wants a path instead of host:port */
1118a4530f9Schristos /*      Overload host_to_connect; we could just make this match Forward */
1128a4530f9Schristos /*	XXX - can we use listen_host instead of listen_path? */
11355a4608bSchristos struct permission {
114ca32bd8dSchristos 	char *host_to_connect;		/* Connect to 'host'. */
1158a4530f9Schristos 	int port_to_connect;		/* Connect to 'port'. */
1168a4530f9Schristos 	char *listen_host;		/* Remote side should listen address. */
1178a4530f9Schristos 	char *listen_path;		/* Remote side should listen path. */
1188a4530f9Schristos 	int listen_port;		/* Remote side should listen port. */
119ee85abc4Schristos 	Channel *downstream;		/* Downstream mux*/
12055a4608bSchristos };
121ca32bd8dSchristos 
12255a4608bSchristos /*
12355a4608bSchristos  * Stores the forwarding permission state for a single direction (local or
12455a4608bSchristos  * remote).
12555a4608bSchristos  */
12655a4608bSchristos struct permission_set {
12755a4608bSchristos 	/*
12855a4608bSchristos 	 * List of all local permitted host/port pairs to allow for the
12955a4608bSchristos 	 * user.
13055a4608bSchristos 	 */
13155a4608bSchristos 	u_int num_permitted_user;
13255a4608bSchristos 	struct permission *permitted_user;
13355a4608bSchristos 
13455a4608bSchristos 	/*
13555a4608bSchristos 	 * List of all permitted host/port pairs to allow for the admin.
13655a4608bSchristos 	 */
13755a4608bSchristos 	u_int num_permitted_admin;
13855a4608bSchristos 	struct permission *permitted_admin;
13955a4608bSchristos 
14055a4608bSchristos 	/*
14155a4608bSchristos 	 * If this is true, all opens/listens are permitted.  This is the
14255a4608bSchristos 	 * case on the server on which we have to trust the client anyway,
14355a4608bSchristos 	 * and the user could do anything after logging in.
14455a4608bSchristos 	 */
14555a4608bSchristos 	int all_permitted;
14655a4608bSchristos };
147ca32bd8dSchristos 
148b1066cf3Schristos /* Used to record timeouts per channel type */
149b1066cf3Schristos struct ssh_channel_timeout {
150b1066cf3Schristos 	char *type_pattern;
151a629fefcSchristos 	int timeout_secs;
152b1066cf3Schristos };
153b1066cf3Schristos 
1547a183406Schristos /* Master structure for channels state */
1557a183406Schristos struct ssh_channels {
1567a183406Schristos 	/*
1577a183406Schristos 	 * Pointer to an array containing all allocated channels.  The array
1587a183406Schristos 	 * is dynamically extended as needed.
1597a183406Schristos 	 */
1607a183406Schristos 	Channel **channels;
1615101d403Schristos 
162ca32bd8dSchristos 	/*
1637a183406Schristos 	 * Size of the channel array.  All slots of the array must always be
1647a183406Schristos 	 * initialized (at least the type field); unused slots set to NULL
165ca32bd8dSchristos 	 */
1667a183406Schristos 	u_int channels_alloc;
167ca32bd8dSchristos 
1687a183406Schristos 	/*
169a03ec00cSchristos 	 * 'channel_pre*' are called just before IO to add any bits
170a03ec00cSchristos 	 * relevant to channels in the c->io_want bitmasks.
1717a183406Schristos 	 *
1727a183406Schristos 	 * 'channel_post*': perform any appropriate operations for
173a03ec00cSchristos 	 * channels which have c->io_ready events pending.
1747a183406Schristos 	 */
1757a183406Schristos 	chan_fn **channel_pre;
1767a183406Schristos 	chan_fn **channel_post;
1777a183406Schristos 
1787a183406Schristos 	/* -- tcp forwarding */
17955a4608bSchristos 	struct permission_set local_perms;
18055a4608bSchristos 	struct permission_set remote_perms;
181ca32bd8dSchristos 
182ca32bd8dSchristos 	/* -- X11 forwarding */
183ca32bd8dSchristos 
184ca32bd8dSchristos 	/* Saved X11 local (client) display. */
1857a183406Schristos 	char *x11_saved_display;
186ca32bd8dSchristos 
187ca32bd8dSchristos 	/* Saved X11 authentication protocol name. */
1887a183406Schristos 	char *x11_saved_proto;
189ca32bd8dSchristos 
190ca32bd8dSchristos 	/* Saved X11 authentication data.  This is the real data. */
1917a183406Schristos 	char *x11_saved_data;
1927a183406Schristos 	u_int x11_saved_data_len;
193ca32bd8dSchristos 
1944054ffb0Schristos 	/* Deadline after which all X11 connections are refused */
195b1066cf3Schristos 	time_t x11_refuse_time;
1964054ffb0Schristos 
197ca32bd8dSchristos 	/*
1987a183406Schristos 	 * Fake X11 authentication data.  This is what the server will be
1997a183406Schristos 	 * sending us; we should replace any occurrences of this by the
2007a183406Schristos 	 * real data.
201ca32bd8dSchristos 	 */
2027a183406Schristos 	u_char *x11_fake_data;
2037a183406Schristos 	u_int x11_fake_data_len;
204ca32bd8dSchristos 
205ca32bd8dSchristos 	/* AF_UNSPEC or AF_INET or AF_INET6 */
2067a183406Schristos 	int IPv4or6;
207b1066cf3Schristos 
208b1066cf3Schristos 	/* Channel timeouts by type */
209b1066cf3Schristos 	struct ssh_channel_timeout *timeouts;
210b1066cf3Schristos 	size_t ntimeouts;
211c5555919Schristos 	/* Global timeout for all OPEN channels */
212c5555919Schristos 	int global_deadline;
213c5555919Schristos 	time_t lastused;
2147a183406Schristos };
215ca32bd8dSchristos 
216ca32bd8dSchristos /* helper */
2177a183406Schristos static void port_open_helper(struct ssh *ssh, Channel *c, const char *rtype);
218ee85abc4Schristos static const char *channel_rfwd_bind_host(const char *listen_host);
219ca32bd8dSchristos 
220ca32bd8dSchristos /* non-blocking connect helpers */
221ca32bd8dSchristos static int connect_next(struct channel_connect *);
222ca32bd8dSchristos static void channel_connect_ctx_free(struct channel_connect *);
2237a183406Schristos static Channel *rdynamic_connect_prepare(struct ssh *, const char *,
2247a183406Schristos     const char *);
2257a183406Schristos static int rdynamic_connect_finish(struct ssh *, Channel *);
2267a183406Schristos 
2277a183406Schristos /* Setup helper */
2287a183406Schristos static void channel_handler_init(struct ssh_channels *sc);
229ca32bd8dSchristos 
230ca32bd8dSchristos /* -- channel core */
231ca32bd8dSchristos 
2327a183406Schristos void
2337a183406Schristos channel_init_channels(struct ssh *ssh)
2347a183406Schristos {
2357a183406Schristos 	struct ssh_channels *sc;
2367a183406Schristos 
237aa36fcacSchristos 	if ((sc = calloc(1, sizeof(*sc))) == NULL)
23817418e98Schristos 		fatal_f("allocation failed");
2397a183406Schristos 	sc->channels_alloc = 10;
2407a183406Schristos 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2417a183406Schristos 	sc->IPv4or6 = AF_UNSPEC;
2427a183406Schristos 	channel_handler_init(sc);
2437a183406Schristos 
2447a183406Schristos 	ssh->chanctxt = sc;
2457a183406Schristos }
2467a183406Schristos 
247ca32bd8dSchristos Channel *
2487a183406Schristos channel_by_id(struct ssh *ssh, int id)
249ca32bd8dSchristos {
250ca32bd8dSchristos 	Channel *c;
251ca32bd8dSchristos 
2527a183406Schristos 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
25317418e98Schristos 		logit_f("%d: bad id", id);
254ca32bd8dSchristos 		return NULL;
255ca32bd8dSchristos 	}
2567a183406Schristos 	c = ssh->chanctxt->channels[id];
257ca32bd8dSchristos 	if (c == NULL) {
25817418e98Schristos 		logit_f("%d: bad id: channel free", id);
259ca32bd8dSchristos 		return NULL;
260ca32bd8dSchristos 	}
261ca32bd8dSchristos 	return c;
262ca32bd8dSchristos }
263ca32bd8dSchristos 
264ee85abc4Schristos Channel *
2657a183406Schristos channel_by_remote_id(struct ssh *ssh, u_int remote_id)
266ee85abc4Schristos {
267ee85abc4Schristos 	Channel *c;
268ee85abc4Schristos 	u_int i;
269ee85abc4Schristos 
2707a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2717a183406Schristos 		c = ssh->chanctxt->channels[i];
2727a183406Schristos 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
273ee85abc4Schristos 			return c;
274ee85abc4Schristos 	}
275ee85abc4Schristos 	return NULL;
276ee85abc4Schristos }
277ee85abc4Schristos 
278ca32bd8dSchristos /*
279ca32bd8dSchristos  * Returns the channel if it is allowed to receive protocol messages.
280ca32bd8dSchristos  * Private channels, like listening sockets, may not receive messages.
281ca32bd8dSchristos  */
282ca32bd8dSchristos Channel *
2837a183406Schristos channel_lookup(struct ssh *ssh, int id)
284ca32bd8dSchristos {
285ca32bd8dSchristos 	Channel *c;
286ca32bd8dSchristos 
2877a183406Schristos 	if ((c = channel_by_id(ssh, id)) == NULL)
2887a183406Schristos 		return NULL;
289ca32bd8dSchristos 
290ca32bd8dSchristos 	switch (c->type) {
291ca32bd8dSchristos 	case SSH_CHANNEL_X11_OPEN:
292ca32bd8dSchristos 	case SSH_CHANNEL_LARVAL:
293ca32bd8dSchristos 	case SSH_CHANNEL_CONNECTING:
294ca32bd8dSchristos 	case SSH_CHANNEL_DYNAMIC:
2957a183406Schristos 	case SSH_CHANNEL_RDYNAMIC_OPEN:
2967a183406Schristos 	case SSH_CHANNEL_RDYNAMIC_FINISH:
297ca32bd8dSchristos 	case SSH_CHANNEL_OPENING:
298ca32bd8dSchristos 	case SSH_CHANNEL_OPEN:
29900a838c4Schristos 	case SSH_CHANNEL_ABANDONED:
300ee85abc4Schristos 	case SSH_CHANNEL_MUX_PROXY:
3017a183406Schristos 		return c;
302ca32bd8dSchristos 	}
303ca32bd8dSchristos 	logit("Non-public channel %d, type %d.", id, c->type);
3047a183406Schristos 	return NULL;
305ca32bd8dSchristos }
306ca32bd8dSchristos 
307ca32bd8dSchristos /*
308b1066cf3Schristos  * Add a timeout for open channels whose c->ctype (or c->xctype if it is set)
309b1066cf3Schristos  * match type_pattern.
310b1066cf3Schristos  */
311b1066cf3Schristos void
312b1066cf3Schristos channel_add_timeout(struct ssh *ssh, const char *type_pattern,
313a629fefcSchristos     int timeout_secs)
314b1066cf3Schristos {
315b1066cf3Schristos 	struct ssh_channels *sc = ssh->chanctxt;
316b1066cf3Schristos 
317c5555919Schristos 	if (strcmp(type_pattern, "global") == 0) {
318c5555919Schristos 		debug2_f("global channel timeout %d seconds", timeout_secs);
319c5555919Schristos 		sc->global_deadline = timeout_secs;
320c5555919Schristos 		return;
321c5555919Schristos 	}
322a629fefcSchristos 	debug2_f("channel type \"%s\" timeout %d seconds",
323b1066cf3Schristos 	    type_pattern, timeout_secs);
324b1066cf3Schristos 	sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts,
325b1066cf3Schristos 	    sc->ntimeouts + 1, sizeof(*sc->timeouts));
326b1066cf3Schristos 	sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern);
327b1066cf3Schristos 	sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs;
328b1066cf3Schristos 	sc->ntimeouts++;
329b1066cf3Schristos }
330b1066cf3Schristos 
331b1066cf3Schristos /* Clears all previously-added channel timeouts */
332b1066cf3Schristos void
333b1066cf3Schristos channel_clear_timeouts(struct ssh *ssh)
334b1066cf3Schristos {
335b1066cf3Schristos 	struct ssh_channels *sc = ssh->chanctxt;
336b1066cf3Schristos 	size_t i;
337b1066cf3Schristos 
338b1066cf3Schristos 	debug3_f("clearing");
339b1066cf3Schristos 	for (i = 0; i < sc->ntimeouts; i++)
340b1066cf3Schristos 		free(sc->timeouts[i].type_pattern);
341b1066cf3Schristos 	free(sc->timeouts);
342b1066cf3Schristos 	sc->timeouts = NULL;
343b1066cf3Schristos 	sc->ntimeouts = 0;
344b1066cf3Schristos }
345b1066cf3Schristos 
346a629fefcSchristos static int
347b1066cf3Schristos lookup_timeout(struct ssh *ssh, const char *type)
348b1066cf3Schristos {
349b1066cf3Schristos 	struct ssh_channels *sc = ssh->chanctxt;
350b1066cf3Schristos 	size_t i;
351b1066cf3Schristos 
352b1066cf3Schristos 	for (i = 0; i < sc->ntimeouts; i++) {
353b1066cf3Schristos 		if (match_pattern(type, sc->timeouts[i].type_pattern))
354b1066cf3Schristos 			return sc->timeouts[i].timeout_secs;
355b1066cf3Schristos 	}
356b1066cf3Schristos 
357b1066cf3Schristos 	return 0;
358b1066cf3Schristos }
359b1066cf3Schristos 
360b1066cf3Schristos /*
361b1066cf3Schristos  * Sets "extended type" of a channel; used by session layer to add additional
362b1066cf3Schristos  * information about channel types (e.g. shell, login, subsystem) that can then
363b1066cf3Schristos  * be used to select timeouts.
364b1066cf3Schristos  * Will reset c->inactive_deadline as a side-effect.
365b1066cf3Schristos  */
366b1066cf3Schristos void
367b1066cf3Schristos channel_set_xtype(struct ssh *ssh, int id, const char *xctype)
368b1066cf3Schristos {
369b1066cf3Schristos 	Channel *c;
370b1066cf3Schristos 
371b1066cf3Schristos 	if ((c = channel_by_id(ssh, id)) == NULL)
372b1066cf3Schristos 		fatal_f("missing channel %d", id);
373b1066cf3Schristos 	if (c->xctype != NULL)
374b1066cf3Schristos 		free(c->xctype);
375b1066cf3Schristos 	c->xctype = xstrdup(xctype);
376b1066cf3Schristos 	/* Type has changed, so look up inactivity deadline again */
377b1066cf3Schristos 	c->inactive_deadline = lookup_timeout(ssh, c->xctype);
378b1066cf3Schristos 	debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype,
379b1066cf3Schristos 	    c->inactive_deadline);
380b1066cf3Schristos }
381b1066cf3Schristos 
382b1066cf3Schristos /*
383c5555919Schristos  * update "last used" time on a channel.
384c5555919Schristos  * NB. nothing else should update lastused except to clear it.
385c5555919Schristos  */
386c5555919Schristos static void
387c5555919Schristos channel_set_used_time(struct ssh *ssh, Channel *c)
388c5555919Schristos {
389c5555919Schristos 	ssh->chanctxt->lastused = monotime();
390c5555919Schristos 	if (c != NULL)
391c5555919Schristos 		c->lastused = ssh->chanctxt->lastused;
392c5555919Schristos }
393c5555919Schristos 
394c5555919Schristos /*
395c5555919Schristos  * Get the time at which a channel is due to time out for inactivity.
396c5555919Schristos  * Returns 0 if the channel is not due to time out ever.
397c5555919Schristos  */
398c5555919Schristos static time_t
399c5555919Schristos channel_get_expiry(struct ssh *ssh, Channel *c)
400c5555919Schristos {
401c5555919Schristos 	struct ssh_channels *sc = ssh->chanctxt;
402c5555919Schristos 	time_t expiry = 0, channel_expiry;
403c5555919Schristos 
404c5555919Schristos 	if (sc->lastused != 0 && sc->global_deadline != 0)
405c5555919Schristos 		expiry = sc->lastused + sc->global_deadline;
406c5555919Schristos 	if (c->lastused != 0 && c->inactive_deadline != 0) {
407c5555919Schristos 		channel_expiry = c->lastused + c->inactive_deadline;
408c5555919Schristos 		if (expiry == 0 || channel_expiry < expiry)
409c5555919Schristos 			expiry = channel_expiry;
410c5555919Schristos 	}
411c5555919Schristos 	return expiry;
412c5555919Schristos }
413c5555919Schristos 
414c5555919Schristos /*
415ca32bd8dSchristos  * Register filedescriptors for a channel, used when allocating a channel or
416ca32bd8dSchristos  * when the channel consumer/producer is ready, e.g. shell exec'd
417ca32bd8dSchristos  */
418ca32bd8dSchristos static void
4197a183406Schristos channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
420ca32bd8dSchristos     int extusage, int nonblock, int is_tty)
421ca32bd8dSchristos {
422e160b4e8Schristos 	int val;
423e160b4e8Schristos 
42434b27b53Sadam 	if (rfd != -1)
425b1066cf3Schristos 		(void)fcntl(rfd, F_SETFD, FD_CLOEXEC);
42634b27b53Sadam 	if (wfd != -1 && wfd != rfd)
427b1066cf3Schristos 		(void)fcntl(wfd, F_SETFD, FD_CLOEXEC);
42834b27b53Sadam 	if (efd != -1 && efd != rfd && efd != wfd)
429b1066cf3Schristos 		(void)fcntl(efd, F_SETFD, FD_CLOEXEC);
430ca32bd8dSchristos 
431ca32bd8dSchristos 	c->rfd = rfd;
432ca32bd8dSchristos 	c->wfd = wfd;
433ca32bd8dSchristos 	c->sock = (rfd == wfd) ? rfd : -1;
434ca32bd8dSchristos 	c->efd = efd;
435ca32bd8dSchristos 	c->extended_usage = extusage;
436ca32bd8dSchristos 
437ca32bd8dSchristos 	if ((c->isatty = is_tty) != 0)
438ca32bd8dSchristos 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
439ca32bd8dSchristos 
440ca32bd8dSchristos 	/* enable nonblocking mode */
441b592f463Schristos 	c->restore_block = 0;
442b592f463Schristos 	if (nonblock == CHANNEL_NONBLOCK_STDIO) {
443b592f463Schristos 		/*
444b592f463Schristos 		 * Special handling for stdio file descriptors: do not set
445b592f463Schristos 		 * non-blocking mode if they are TTYs. Otherwise prepare to
446b592f463Schristos 		 * restore their blocking state on exit to avoid interfering
447b592f463Schristos 		 * with other programs that follow.
448b592f463Schristos 		 */
449e160b4e8Schristos 		if (rfd != -1 && !isatty(rfd) &&
450e160b4e8Schristos 		    (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
451b1066cf3Schristos 			c->restore_flags[0] = val;
452b592f463Schristos 			c->restore_block |= CHANNEL_RESTORE_RFD;
453b592f463Schristos 			set_nonblock(rfd);
454b592f463Schristos 		}
455e160b4e8Schristos 		if (wfd != -1 && !isatty(wfd) &&
456e160b4e8Schristos 		    (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
457b1066cf3Schristos 			c->restore_flags[1] = val;
458b592f463Schristos 			c->restore_block |= CHANNEL_RESTORE_WFD;
459b592f463Schristos 			set_nonblock(wfd);
460b592f463Schristos 		}
461e160b4e8Schristos 		if (efd != -1 && !isatty(efd) &&
462e160b4e8Schristos 		    (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
463b1066cf3Schristos 			c->restore_flags[2] = val;
464b592f463Schristos 			c->restore_block |= CHANNEL_RESTORE_EFD;
465b592f463Schristos 			set_nonblock(efd);
466b592f463Schristos 		}
467b592f463Schristos 	} else if (nonblock) {
468ca32bd8dSchristos 		if (rfd != -1)
469ca32bd8dSchristos 			set_nonblock(rfd);
470ca32bd8dSchristos 		if (wfd != -1)
471ca32bd8dSchristos 			set_nonblock(wfd);
472ca32bd8dSchristos 		if (efd != -1)
473ca32bd8dSchristos 			set_nonblock(efd);
474ca32bd8dSchristos 	}
475c5555919Schristos 	/* channel might be entering a larval state, so reset global timeout */
476c5555919Schristos 	channel_set_used_time(ssh, NULL);
477ca32bd8dSchristos }
478ca32bd8dSchristos 
479ca32bd8dSchristos /*
480e160b4e8Schristos  * Allocate a new channel object and set its type and socket.
481ca32bd8dSchristos  */
482ca32bd8dSchristos Channel *
4837a183406Schristos channel_new(struct ssh *ssh, const char *ctype, int type, int rfd, int wfd,
4847a183406Schristos     int efd, u_int window, u_int maxpack, int extusage, const char *remote_name,
485185c8f97Schristos     int nonblock)
486ca32bd8dSchristos {
4877a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
488411ab7c4Srin 	u_int i, found = 0 /* XXXGCC12 */;
489ca32bd8dSchristos 	Channel *c;
4902d3b0f52Schristos 	int r;
491ca32bd8dSchristos 
492ca32bd8dSchristos 	/* Try to find a free slot where to put the new channel. */
4937a183406Schristos 	for (i = 0; i < sc->channels_alloc; i++) {
4947a183406Schristos 		if (sc->channels[i] == NULL) {
495ca32bd8dSchristos 			/* Found a free slot. */
4967a183406Schristos 			found = i;
497ca32bd8dSchristos 			break;
498ca32bd8dSchristos 		}
4997a183406Schristos 	}
5007a183406Schristos 	if (i >= sc->channels_alloc) {
5017a183406Schristos 		/*
5027a183406Schristos 		 * There are no free slots. Take last+1 slot and expand
5037a183406Schristos 		 * the array.
5047a183406Schristos 		 */
5057a183406Schristos 		found = sc->channels_alloc;
5067a183406Schristos 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
50717418e98Schristos 			fatal_f("internal error: channels_alloc %d too big",
50817418e98Schristos 			    sc->channels_alloc);
5097a183406Schristos 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
5107a183406Schristos 		    sc->channels_alloc + 10, sizeof(*sc->channels));
5117a183406Schristos 		sc->channels_alloc += 10;
5127a183406Schristos 		debug2("channel: expanding %d", sc->channels_alloc);
513ca32bd8dSchristos 	}
514ca32bd8dSchristos 	/* Initialize and return new channel. */
5157a183406Schristos 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
5167a183406Schristos 	if ((c->input = sshbuf_new()) == NULL ||
5177a183406Schristos 	    (c->output = sshbuf_new()) == NULL ||
5187a183406Schristos 	    (c->extended = sshbuf_new()) == NULL)
51917418e98Schristos 		fatal_f("sshbuf_new failed");
5202d3b0f52Schristos 	if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
52117418e98Schristos 		fatal_fr(r, "sshbuf_set_max_size");
522ca32bd8dSchristos 	c->ostate = CHAN_OUTPUT_OPEN;
523ca32bd8dSchristos 	c->istate = CHAN_INPUT_OPEN;
5247a183406Schristos 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
525ca32bd8dSchristos 	c->self = found;
526ca32bd8dSchristos 	c->type = type;
527185c8f97Schristos 	c->ctype = __UNCONST(ctype);
528ca32bd8dSchristos 	c->local_window = window;
529ca32bd8dSchristos 	c->local_window_max = window;
530ca32bd8dSchristos 	c->local_maxpacket = maxpack;
531313c6c94Schristos 	c->dynamic_window = 0;
532ca32bd8dSchristos 	c->remote_id = -1;
533ca32bd8dSchristos 	c->remote_name = xstrdup(remote_name);
53434b27b53Sadam 	c->ctl_chan = -1;
53534b27b53Sadam 	c->delayed = 1;		/* prevent call to channel_post handler */
536b1066cf3Schristos 	c->inactive_deadline = lookup_timeout(ssh, c->ctype);
537ca32bd8dSchristos 	TAILQ_INIT(&c->status_confirms);
538b1066cf3Schristos 	debug("channel %d: new %s [%s] (inactive timeout: %u)",
539b1066cf3Schristos 	    found, c->ctype, remote_name, c->inactive_deadline);
540ca32bd8dSchristos 	return c;
541ca32bd8dSchristos }
542ca32bd8dSchristos 
543ca32bd8dSchristos int
544b592f463Schristos channel_close_fd(struct ssh *ssh, Channel *c, int *fdp)
545ca32bd8dSchristos {
546b592f463Schristos 	int ret, fd = *fdp;
547ca32bd8dSchristos 
548b592f463Schristos 	if (fd == -1)
549b592f463Schristos 		return 0;
550b592f463Schristos 
551b1066cf3Schristos 	/* restore blocking */
552b1066cf3Schristos 	if (*fdp == c->rfd &&
553b1066cf3Schristos 	    (c->restore_block & CHANNEL_RESTORE_RFD) != 0)
554b1066cf3Schristos 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[0]);
555b1066cf3Schristos 	else if (*fdp == c->wfd &&
556b1066cf3Schristos 	    (c->restore_block & CHANNEL_RESTORE_WFD) != 0)
557b1066cf3Schristos 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[1]);
558b1066cf3Schristos 	else if (*fdp == c->efd &&
559b1066cf3Schristos 	    (c->restore_block & CHANNEL_RESTORE_EFD) != 0)
560b1066cf3Schristos 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[2]);
561b592f463Schristos 
562a03ec00cSchristos 	if (*fdp == c->rfd) {
563a03ec00cSchristos 		c->io_want &= ~SSH_CHAN_IO_RFD;
564a03ec00cSchristos 		c->io_ready &= ~SSH_CHAN_IO_RFD;
565a03ec00cSchristos 		c->rfd = -1;
566c4271af5Schristos 		c->pfds[0] = -1;
567a03ec00cSchristos 	}
568a03ec00cSchristos 	if (*fdp == c->wfd) {
569a03ec00cSchristos 		c->io_want &= ~SSH_CHAN_IO_WFD;
570a03ec00cSchristos 		c->io_ready &= ~SSH_CHAN_IO_WFD;
571a03ec00cSchristos 		c->wfd = -1;
572c4271af5Schristos 		c->pfds[1] = -1;
573a03ec00cSchristos 	}
574a03ec00cSchristos 	if (*fdp == c->efd) {
575a03ec00cSchristos 		c->io_want &= ~SSH_CHAN_IO_EFD;
576a03ec00cSchristos 		c->io_ready &= ~SSH_CHAN_IO_EFD;
577a03ec00cSchristos 		c->efd = -1;
578c4271af5Schristos 		c->pfds[2] = -1;
579a03ec00cSchristos 	}
580a03ec00cSchristos 	if (*fdp == c->sock) {
581a03ec00cSchristos 		c->io_want &= ~SSH_CHAN_IO_SOCK;
582a03ec00cSchristos 		c->io_ready &= ~SSH_CHAN_IO_SOCK;
583a03ec00cSchristos 		c->sock = -1;
584c4271af5Schristos 		c->pfds[3] = -1;
585a03ec00cSchristos 	}
586a03ec00cSchristos 
587ca32bd8dSchristos 	ret = close(fd);
588a03ec00cSchristos 	*fdp = -1; /* probably redundant */
589ca32bd8dSchristos 	return ret;
590ca32bd8dSchristos }
591ca32bd8dSchristos 
592ca32bd8dSchristos /* Close all channel fd/socket. */
593ca32bd8dSchristos static void
5947a183406Schristos channel_close_fds(struct ssh *ssh, Channel *c)
595ca32bd8dSchristos {
596ffae97bbSchristos 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
597ffae97bbSchristos 
598b592f463Schristos 	channel_close_fd(ssh, c, &c->sock);
599ffae97bbSchristos 	if (rfd != sock)
600b592f463Schristos 		channel_close_fd(ssh, c, &c->rfd);
601ffae97bbSchristos 	if (wfd != sock && wfd != rfd)
602b592f463Schristos 		channel_close_fd(ssh, c, &c->wfd);
603ffae97bbSchristos 	if (efd != sock && efd != rfd && efd != wfd)
604b592f463Schristos 		channel_close_fd(ssh, c, &c->efd);
6057a183406Schristos }
6067a183406Schristos 
6077a183406Schristos static void
60855a4608bSchristos fwd_perm_clear(struct permission *perm)
6097a183406Schristos {
61055a4608bSchristos 	free(perm->host_to_connect);
61155a4608bSchristos 	free(perm->listen_host);
61255a4608bSchristos 	free(perm->listen_path);
6138db691beSchristos 	memset(perm, 0, sizeof(*perm));
6147a183406Schristos }
6157a183406Schristos 
61655a4608bSchristos /* Returns an printable name for the specified forwarding permission list */
61755a4608bSchristos static const char *
61855a4608bSchristos fwd_ident(int who, int where)
61955a4608bSchristos {
62055a4608bSchristos 	if (who == FORWARD_ADM) {
62155a4608bSchristos 		if (where == FORWARD_LOCAL)
62255a4608bSchristos 			return "admin local";
62355a4608bSchristos 		else if (where == FORWARD_REMOTE)
62455a4608bSchristos 			return "admin remote";
62555a4608bSchristos 	} else if (who == FORWARD_USER) {
62655a4608bSchristos 		if (where == FORWARD_LOCAL)
62755a4608bSchristos 			return "user local";
62855a4608bSchristos 		else if (where == FORWARD_REMOTE)
62955a4608bSchristos 			return "user remote";
63055a4608bSchristos 	}
63155a4608bSchristos 	fatal("Unknown forward permission list %d/%d", who, where);
63255a4608bSchristos }
6337a183406Schristos 
63455a4608bSchristos /* Returns the forwarding permission list for the specified direction */
63555a4608bSchristos static struct permission_set *
63655a4608bSchristos permission_set_get(struct ssh *ssh, int where)
63755a4608bSchristos {
63855a4608bSchristos 	struct ssh_channels *sc = ssh->chanctxt;
63955a4608bSchristos 
64055a4608bSchristos 	switch (where) {
64155a4608bSchristos 	case FORWARD_LOCAL:
64255a4608bSchristos 		return &sc->local_perms;
64355a4608bSchristos 		break;
64455a4608bSchristos 	case FORWARD_REMOTE:
64555a4608bSchristos 		return &sc->remote_perms;
64655a4608bSchristos 		break;
64755a4608bSchristos 	default:
64817418e98Schristos 		fatal_f("invalid forwarding direction %d", where);
64955a4608bSchristos 	}
65055a4608bSchristos }
65155a4608bSchristos 
652cd4ada6aSchristos /* Returns pointers to the specified forwarding list and its element count */
65355a4608bSchristos static void
65455a4608bSchristos permission_set_get_array(struct ssh *ssh, int who, int where,
65555a4608bSchristos     struct permission ***permpp, u_int **npermpp)
65655a4608bSchristos {
65755a4608bSchristos 	struct permission_set *pset = permission_set_get(ssh, where);
65855a4608bSchristos 
65955a4608bSchristos 	switch (who) {
66055a4608bSchristos 	case FORWARD_USER:
66155a4608bSchristos 		*permpp = &pset->permitted_user;
66255a4608bSchristos 		*npermpp = &pset->num_permitted_user;
66355a4608bSchristos 		break;
66455a4608bSchristos 	case FORWARD_ADM:
66555a4608bSchristos 		*permpp = &pset->permitted_admin;
66655a4608bSchristos 		*npermpp = &pset->num_permitted_admin;
66755a4608bSchristos 		break;
66855a4608bSchristos 	default:
66917418e98Schristos 		fatal_f("invalid forwarding client %d", who);
67055a4608bSchristos 	}
67155a4608bSchristos }
67255a4608bSchristos 
673a03ec00cSchristos /* Adds an entry to the specified forwarding list */
6747a183406Schristos static int
67555a4608bSchristos permission_set_add(struct ssh *ssh, int who, int where,
6767a183406Schristos     const char *host_to_connect, int port_to_connect,
6777a183406Schristos     const char *listen_host, const char *listen_path, int listen_port,
6787a183406Schristos     Channel *downstream)
6797a183406Schristos {
68055a4608bSchristos 	struct permission **permp;
68155a4608bSchristos 	u_int n, *npermp;
6827a183406Schristos 
68355a4608bSchristos 	permission_set_get_array(ssh, who, where, &permp, &npermp);
6847a183406Schristos 
68555a4608bSchristos 	if (*npermp >= INT_MAX)
68617418e98Schristos 		fatal_f("%s overflow", fwd_ident(who, where));
6877a183406Schristos 
68855a4608bSchristos 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
68955a4608bSchristos 	n = (*npermp)++;
6907a183406Schristos #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
69155a4608bSchristos 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
69255a4608bSchristos 	(*permp)[n].port_to_connect = port_to_connect;
69355a4608bSchristos 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
69455a4608bSchristos 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
69555a4608bSchristos 	(*permp)[n].listen_port = listen_port;
69655a4608bSchristos 	(*permp)[n].downstream = downstream;
6977a183406Schristos #undef MAYBE_DUP
6987a183406Schristos 	return (int)n;
6997a183406Schristos }
7007a183406Schristos 
7017a183406Schristos static void
7027a183406Schristos mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
7037a183406Schristos {
7047a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
70555a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
70655a4608bSchristos 	struct permission *perm;
7077a183406Schristos 	int r;
7087a183406Schristos 	u_int i;
7097a183406Schristos 
71055a4608bSchristos 	for (i = 0; i < pset->num_permitted_user; i++) {
71155a4608bSchristos 		perm = &pset->permitted_user[i];
71255a4608bSchristos 		if (perm->downstream != c)
7137a183406Schristos 			continue;
7147a183406Schristos 
7157a183406Schristos 		/* cancel on the server, since mux client is gone */
7167a183406Schristos 		debug("channel %d: cleanup remote forward for %s:%u",
71755a4608bSchristos 		    c->self, perm->listen_host, perm->listen_port);
7187a183406Schristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
7197a183406Schristos 		    (r = sshpkt_put_cstring(ssh,
7207a183406Schristos 		    "cancel-tcpip-forward")) != 0 ||
7217a183406Schristos 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
7227a183406Schristos 		    (r = sshpkt_put_cstring(ssh,
72355a4608bSchristos 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
72455a4608bSchristos 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
7257a183406Schristos 		    (r = sshpkt_send(ssh)) != 0) {
72617418e98Schristos 			fatal_fr(r, "channel %i", c->self);
7277a183406Schristos 		}
72855a4608bSchristos 		fwd_perm_clear(perm); /* unregister */
7297a183406Schristos 	}
730ca32bd8dSchristos }
731ca32bd8dSchristos 
732ca32bd8dSchristos /* Free the channel and close its fd/socket. */
733ca32bd8dSchristos void
7347a183406Schristos channel_free(struct ssh *ssh, Channel *c)
735ca32bd8dSchristos {
7367a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
737ca32bd8dSchristos 	char *s;
738ca32bd8dSchristos 	u_int i, n;
739ee85abc4Schristos 	Channel *other;
740ca32bd8dSchristos 	struct channel_confirm *cc;
741ca32bd8dSchristos 
7427a183406Schristos 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
7437a183406Schristos 		if ((other = sc->channels[i]) == NULL)
7447a183406Schristos 			continue;
745ca32bd8dSchristos 		n++;
746ee85abc4Schristos 		/* detach from mux client and prepare for closing */
747ee85abc4Schristos 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
748ee85abc4Schristos 		    other->type == SSH_CHANNEL_MUX_PROXY &&
749ee85abc4Schristos 		    other->mux_ctx == c) {
750ee85abc4Schristos 			other->mux_ctx = NULL;
751ee85abc4Schristos 			other->type = SSH_CHANNEL_OPEN;
752ee85abc4Schristos 			other->istate = CHAN_INPUT_CLOSED;
753ee85abc4Schristos 			other->ostate = CHAN_OUTPUT_CLOSED;
754ee85abc4Schristos 		}
755ee85abc4Schristos 	}
756ca32bd8dSchristos 	debug("channel %d: free: %s, nchannels %u", c->self,
757ca32bd8dSchristos 	    c->remote_name ? c->remote_name : "???", n);
758ca32bd8dSchristos 
759f1ad81b8Schristos 	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
7607a183406Schristos 		mux_remove_remote_forwardings(ssh, c);
761f1ad81b8Schristos 		free(c->mux_ctx);
762f1ad81b8Schristos 		c->mux_ctx = NULL;
763f1ad81b8Schristos 	} else if (c->type == SSH_CHANNEL_MUX_LISTENER) {
7642d3b0f52Schristos 		free(c->mux_ctx);
7652d3b0f52Schristos 		c->mux_ctx = NULL;
7662d3b0f52Schristos 	}
767ee85abc4Schristos 
76855a4608bSchristos 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
7697a183406Schristos 		s = channel_open_message(ssh);
770ca32bd8dSchristos 		debug3("channel %d: status: %s", c->self, s);
77100a838c4Schristos 		free(s);
77255a4608bSchristos 	}
773ca32bd8dSchristos 
7747a183406Schristos 	channel_close_fds(ssh, c);
7757a183406Schristos 	sshbuf_free(c->input);
7767a183406Schristos 	sshbuf_free(c->output);
7777a183406Schristos 	sshbuf_free(c->extended);
7787a183406Schristos 	c->input = c->output = c->extended = NULL;
77900a838c4Schristos 	free(c->remote_name);
780ca32bd8dSchristos 	c->remote_name = NULL;
78100a838c4Schristos 	free(c->path);
782ca32bd8dSchristos 	c->path = NULL;
78300a838c4Schristos 	free(c->listening_addr);
784091c4109Schristos 	c->listening_addr = NULL;
785b1066cf3Schristos 	free(c->xctype);
786b1066cf3Schristos 	c->xctype = NULL;
787ca32bd8dSchristos 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
788ca32bd8dSchristos 		if (cc->abandon_cb != NULL)
7897a183406Schristos 			cc->abandon_cb(ssh, c, cc->ctx);
790ca32bd8dSchristos 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
7918db691beSchristos 		freezero(cc, sizeof(*cc));
792ca32bd8dSchristos 	}
793ca32bd8dSchristos 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
7947a183406Schristos 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
7957a183406Schristos 	sc->channels[c->self] = NULL;
7968db691beSchristos 	freezero(c, sizeof(*c));
797ca32bd8dSchristos }
798ca32bd8dSchristos 
799ca32bd8dSchristos void
8007a183406Schristos channel_free_all(struct ssh *ssh)
801ca32bd8dSchristos {
802ca32bd8dSchristos 	u_int i;
803cd4ada6aSchristos 	struct ssh_channels *sc = ssh->chanctxt;
804ca32bd8dSchristos 
805cd4ada6aSchristos 	for (i = 0; i < sc->channels_alloc; i++)
806cd4ada6aSchristos 		if (sc->channels[i] != NULL)
807cd4ada6aSchristos 			channel_free(ssh, sc->channels[i]);
808cd4ada6aSchristos 
809cd4ada6aSchristos 	free(sc->channels);
810cd4ada6aSchristos 	sc->channels = NULL;
811cd4ada6aSchristos 	sc->channels_alloc = 0;
812cd4ada6aSchristos 
813cd4ada6aSchristos 	free(sc->x11_saved_display);
814cd4ada6aSchristos 	sc->x11_saved_display = NULL;
815cd4ada6aSchristos 
816cd4ada6aSchristos 	free(sc->x11_saved_proto);
817cd4ada6aSchristos 	sc->x11_saved_proto = NULL;
818cd4ada6aSchristos 
819cd4ada6aSchristos 	free(sc->x11_saved_data);
820cd4ada6aSchristos 	sc->x11_saved_data = NULL;
821cd4ada6aSchristos 	sc->x11_saved_data_len = 0;
822cd4ada6aSchristos 
823cd4ada6aSchristos 	free(sc->x11_fake_data);
824cd4ada6aSchristos 	sc->x11_fake_data = NULL;
825cd4ada6aSchristos 	sc->x11_fake_data_len = 0;
826ca32bd8dSchristos }
827ca32bd8dSchristos 
828ca32bd8dSchristos /*
829ca32bd8dSchristos  * Closes the sockets/fds of all channels.  This is used to close extra file
830ca32bd8dSchristos  * descriptors after a fork.
831ca32bd8dSchristos  */
832ca32bd8dSchristos void
8337a183406Schristos channel_close_all(struct ssh *ssh)
834ca32bd8dSchristos {
835ca32bd8dSchristos 	u_int i;
836ca32bd8dSchristos 
8377a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
8387a183406Schristos 		if (ssh->chanctxt->channels[i] != NULL)
8397a183406Schristos 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
840ca32bd8dSchristos }
841ca32bd8dSchristos 
842ca32bd8dSchristos /*
843ca32bd8dSchristos  * Stop listening to channels.
844ca32bd8dSchristos  */
845ca32bd8dSchristos void
8467a183406Schristos channel_stop_listening(struct ssh *ssh)
847ca32bd8dSchristos {
848ca32bd8dSchristos 	u_int i;
849ca32bd8dSchristos 	Channel *c;
850ca32bd8dSchristos 
8517a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8527a183406Schristos 		c = ssh->chanctxt->channels[i];
853ca32bd8dSchristos 		if (c != NULL) {
854ca32bd8dSchristos 			switch (c->type) {
855ca32bd8dSchristos 			case SSH_CHANNEL_AUTH_SOCKET:
856ca32bd8dSchristos 			case SSH_CHANNEL_PORT_LISTENER:
857ca32bd8dSchristos 			case SSH_CHANNEL_RPORT_LISTENER:
858ca32bd8dSchristos 			case SSH_CHANNEL_X11_LISTENER:
8598a4530f9Schristos 			case SSH_CHANNEL_UNIX_LISTENER:
8608a4530f9Schristos 			case SSH_CHANNEL_RUNIX_LISTENER:
861b592f463Schristos 				channel_close_fd(ssh, c, &c->sock);
8627a183406Schristos 				channel_free(ssh, c);
863ca32bd8dSchristos 				break;
864ca32bd8dSchristos 			}
865ca32bd8dSchristos 		}
866ca32bd8dSchristos 	}
867ca32bd8dSchristos }
868ca32bd8dSchristos 
869ca32bd8dSchristos /*
870ca32bd8dSchristos  * Returns true if no channel has too much buffered data, and false if one or
871ca32bd8dSchristos  * more channel is overfull.
872ca32bd8dSchristos  */
873ca32bd8dSchristos int
8747a183406Schristos channel_not_very_much_buffered_data(struct ssh *ssh)
875ca32bd8dSchristos {
876ca32bd8dSchristos 	u_int i;
8777a183406Schristos 	u_int maxsize = ssh_packet_get_maxsize(ssh);
878ca32bd8dSchristos 	Channel *c;
879ca32bd8dSchristos 
8807a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8817a183406Schristos 		c = ssh->chanctxt->channels[i];
8827a183406Schristos 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
8837a183406Schristos 			continue;
8847a183406Schristos 		if (sshbuf_len(c->output) > maxsize) {
8857a183406Schristos 			debug2("channel %d: big output buffer %zu > %u",
8867a183406Schristos 			    c->self, sshbuf_len(c->output), maxsize);
887ca32bd8dSchristos 			return 0;
888ca32bd8dSchristos 		}
889ca32bd8dSchristos 	}
890ca32bd8dSchristos 	return 1;
891ca32bd8dSchristos }
892ca32bd8dSchristos 
893ca32bd8dSchristos /* Returns true if any channel is still open. */
894ca32bd8dSchristos int
8957a183406Schristos channel_still_open(struct ssh *ssh)
896ca32bd8dSchristos {
897ca32bd8dSchristos 	u_int i;
898ca32bd8dSchristos 	Channel *c;
899ca32bd8dSchristos 
9007a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9017a183406Schristos 		c = ssh->chanctxt->channels[i];
902ca32bd8dSchristos 		if (c == NULL)
903ca32bd8dSchristos 			continue;
904ca32bd8dSchristos 		switch (c->type) {
905ca32bd8dSchristos 		case SSH_CHANNEL_X11_LISTENER:
906ca32bd8dSchristos 		case SSH_CHANNEL_PORT_LISTENER:
907ca32bd8dSchristos 		case SSH_CHANNEL_RPORT_LISTENER:
90834b27b53Sadam 		case SSH_CHANNEL_MUX_LISTENER:
909ca32bd8dSchristos 		case SSH_CHANNEL_CLOSED:
910ca32bd8dSchristos 		case SSH_CHANNEL_AUTH_SOCKET:
911ca32bd8dSchristos 		case SSH_CHANNEL_DYNAMIC:
9127a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_OPEN:
913ca32bd8dSchristos 		case SSH_CHANNEL_CONNECTING:
914ca32bd8dSchristos 		case SSH_CHANNEL_ZOMBIE:
91500a838c4Schristos 		case SSH_CHANNEL_ABANDONED:
9168a4530f9Schristos 		case SSH_CHANNEL_UNIX_LISTENER:
9178a4530f9Schristos 		case SSH_CHANNEL_RUNIX_LISTENER:
918ca32bd8dSchristos 			continue;
919ca32bd8dSchristos 		case SSH_CHANNEL_LARVAL:
920ca32bd8dSchristos 			continue;
921ca32bd8dSchristos 		case SSH_CHANNEL_OPENING:
922ca32bd8dSchristos 		case SSH_CHANNEL_OPEN:
9237a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_FINISH:
924ca32bd8dSchristos 		case SSH_CHANNEL_X11_OPEN:
92534b27b53Sadam 		case SSH_CHANNEL_MUX_CLIENT:
926ee85abc4Schristos 		case SSH_CHANNEL_MUX_PROXY:
927ca32bd8dSchristos 			return 1;
928ca32bd8dSchristos 		default:
92917418e98Schristos 			fatal_f("bad channel type %d", c->type);
930ca32bd8dSchristos 			/* NOTREACHED */
931ca32bd8dSchristos 		}
932ca32bd8dSchristos 	}
933ca32bd8dSchristos 	return 0;
934ca32bd8dSchristos }
935ca32bd8dSchristos 
936514b5d45Schristos /* Returns true if a channel with a TTY is open. */
937514b5d45Schristos int
938514b5d45Schristos channel_tty_open(struct ssh *ssh)
939514b5d45Schristos {
940514b5d45Schristos 	u_int i;
941514b5d45Schristos 	Channel *c;
942514b5d45Schristos 
943514b5d45Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
944514b5d45Schristos 		c = ssh->chanctxt->channels[i];
945514b5d45Schristos 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
946514b5d45Schristos 			continue;
947514b5d45Schristos 		if (c->client_tty)
948514b5d45Schristos 			return 1;
949514b5d45Schristos 	}
950514b5d45Schristos 	return 0;
951514b5d45Schristos }
952514b5d45Schristos 
953ca32bd8dSchristos /* Returns the id of an open channel suitable for keepaliving */
954ca32bd8dSchristos int
9557a183406Schristos channel_find_open(struct ssh *ssh)
956ca32bd8dSchristos {
957ca32bd8dSchristos 	u_int i;
958ca32bd8dSchristos 	Channel *c;
959ca32bd8dSchristos 
9607a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9617a183406Schristos 		c = ssh->chanctxt->channels[i];
9627a183406Schristos 		if (c == NULL || !c->have_remote_id)
963ca32bd8dSchristos 			continue;
964ca32bd8dSchristos 		switch (c->type) {
965ca32bd8dSchristos 		case SSH_CHANNEL_CLOSED:
966ca32bd8dSchristos 		case SSH_CHANNEL_DYNAMIC:
9677a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_OPEN:
9687a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_FINISH:
969ca32bd8dSchristos 		case SSH_CHANNEL_X11_LISTENER:
970ca32bd8dSchristos 		case SSH_CHANNEL_PORT_LISTENER:
971ca32bd8dSchristos 		case SSH_CHANNEL_RPORT_LISTENER:
97234b27b53Sadam 		case SSH_CHANNEL_MUX_LISTENER:
97334b27b53Sadam 		case SSH_CHANNEL_MUX_CLIENT:
974ee85abc4Schristos 		case SSH_CHANNEL_MUX_PROXY:
975ca32bd8dSchristos 		case SSH_CHANNEL_OPENING:
976ca32bd8dSchristos 		case SSH_CHANNEL_CONNECTING:
977ca32bd8dSchristos 		case SSH_CHANNEL_ZOMBIE:
97800a838c4Schristos 		case SSH_CHANNEL_ABANDONED:
9798a4530f9Schristos 		case SSH_CHANNEL_UNIX_LISTENER:
9808a4530f9Schristos 		case SSH_CHANNEL_RUNIX_LISTENER:
981ca32bd8dSchristos 			continue;
982ca32bd8dSchristos 		case SSH_CHANNEL_LARVAL:
983ca32bd8dSchristos 		case SSH_CHANNEL_AUTH_SOCKET:
984ca32bd8dSchristos 		case SSH_CHANNEL_OPEN:
985ca32bd8dSchristos 		case SSH_CHANNEL_X11_OPEN:
986ca32bd8dSchristos 			return i;
987ca32bd8dSchristos 		default:
98817418e98Schristos 			fatal_f("bad channel type %d", c->type);
989ca32bd8dSchristos 			/* NOTREACHED */
990ca32bd8dSchristos 		}
991ca32bd8dSchristos 	}
992ca32bd8dSchristos 	return -1;
993ca32bd8dSchristos }
994ca32bd8dSchristos 
995aa36fcacSchristos /* Returns the state of the channel's extended usage flag */
996aa36fcacSchristos const char *
997aa36fcacSchristos channel_format_extended_usage(const Channel *c)
998aa36fcacSchristos {
999aa36fcacSchristos 	if (c->efd == -1)
1000aa36fcacSchristos 		return "closed";
1001aa36fcacSchristos 
1002aa36fcacSchristos 	switch (c->extended_usage) {
1003aa36fcacSchristos 	case CHAN_EXTENDED_WRITE:
1004aa36fcacSchristos 		return "write";
1005aa36fcacSchristos 	case CHAN_EXTENDED_READ:
1006aa36fcacSchristos 		return "read";
1007aa36fcacSchristos 	case CHAN_EXTENDED_IGNORE:
1008aa36fcacSchristos 		return "ignore";
1009aa36fcacSchristos 	default:
1010aa36fcacSchristos 		return "UNKNOWN";
1011aa36fcacSchristos 	}
1012aa36fcacSchristos }
1013aa36fcacSchristos 
1014aa36fcacSchristos static char *
1015aa36fcacSchristos channel_format_status(const Channel *c)
1016aa36fcacSchristos {
1017aa36fcacSchristos 	char *ret = NULL;
1018aa36fcacSchristos 
1019*9469f4f1Schristos 	xasprintf(&ret, "t%d [%s] %s%u %s%u i%u/%zu o%u/%zu e[%s]/%zu "
1020*9469f4f1Schristos 	    "fd %d/%d/%d sock %d cc %d %s%u io 0x%02x/0x%02x",
1021b1066cf3Schristos 	    c->type, c->xctype != NULL ? c->xctype : c->ctype,
1022aa36fcacSchristos 	    c->have_remote_id ? "r" : "nr", c->remote_id,
1023*9469f4f1Schristos 	    c->mux_ctx != NULL ? "m" : "nm", c->mux_downstream_id,
1024aa36fcacSchristos 	    c->istate, sshbuf_len(c->input),
1025aa36fcacSchristos 	    c->ostate, sshbuf_len(c->output),
1026aa36fcacSchristos 	    channel_format_extended_usage(c), sshbuf_len(c->extended),
1027a03ec00cSchristos 	    c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan,
1028*9469f4f1Schristos 	    c->have_ctl_child_id ? "c" : "nc", c->ctl_child_id,
1029a03ec00cSchristos 	    c->io_want, c->io_ready);
1030aa36fcacSchristos 	return ret;
1031aa36fcacSchristos }
1032aa36fcacSchristos 
1033ca32bd8dSchristos /*
1034ca32bd8dSchristos  * Returns a message describing the currently open forwarded connections,
1035ca32bd8dSchristos  * suitable for sending to the client.  The message contains crlf pairs for
1036ca32bd8dSchristos  * newlines.
1037ca32bd8dSchristos  */
1038ca32bd8dSchristos char *
10397a183406Schristos channel_open_message(struct ssh *ssh)
1040ca32bd8dSchristos {
10417a183406Schristos 	struct sshbuf *buf;
1042ca32bd8dSchristos 	Channel *c;
1043ca32bd8dSchristos 	u_int i;
10447a183406Schristos 	int r;
1045aa36fcacSchristos 	char *cp, *ret;
1046ca32bd8dSchristos 
10477a183406Schristos 	if ((buf = sshbuf_new()) == NULL)
104817418e98Schristos 		fatal_f("sshbuf_new");
10497a183406Schristos 	if ((r = sshbuf_putf(buf,
10507a183406Schristos 	    "The following connections are open:\r\n")) != 0)
105117418e98Schristos 		fatal_fr(r, "sshbuf_putf");
10527a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
10537a183406Schristos 		c = ssh->chanctxt->channels[i];
1054ca32bd8dSchristos 		if (c == NULL)
1055ca32bd8dSchristos 			continue;
1056ca32bd8dSchristos 		switch (c->type) {
1057ca32bd8dSchristos 		case SSH_CHANNEL_X11_LISTENER:
1058ca32bd8dSchristos 		case SSH_CHANNEL_PORT_LISTENER:
1059ca32bd8dSchristos 		case SSH_CHANNEL_RPORT_LISTENER:
1060ca32bd8dSchristos 		case SSH_CHANNEL_CLOSED:
1061ca32bd8dSchristos 		case SSH_CHANNEL_AUTH_SOCKET:
1062ca32bd8dSchristos 		case SSH_CHANNEL_ZOMBIE:
106300a838c4Schristos 		case SSH_CHANNEL_ABANDONED:
106434b27b53Sadam 		case SSH_CHANNEL_MUX_LISTENER:
10658a4530f9Schristos 		case SSH_CHANNEL_UNIX_LISTENER:
10668a4530f9Schristos 		case SSH_CHANNEL_RUNIX_LISTENER:
1067ca32bd8dSchristos 			continue;
1068ca32bd8dSchristos 		case SSH_CHANNEL_LARVAL:
1069ca32bd8dSchristos 		case SSH_CHANNEL_OPENING:
1070ca32bd8dSchristos 		case SSH_CHANNEL_CONNECTING:
1071ca32bd8dSchristos 		case SSH_CHANNEL_DYNAMIC:
10727a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_OPEN:
10737a183406Schristos 		case SSH_CHANNEL_RDYNAMIC_FINISH:
1074ca32bd8dSchristos 		case SSH_CHANNEL_OPEN:
1075ca32bd8dSchristos 		case SSH_CHANNEL_X11_OPEN:
1076ee85abc4Schristos 		case SSH_CHANNEL_MUX_PROXY:
1077ee85abc4Schristos 		case SSH_CHANNEL_MUX_CLIENT:
1078aa36fcacSchristos 			cp = channel_format_status(c);
1079aa36fcacSchristos 			if ((r = sshbuf_putf(buf, "  #%d %.300s (%s)\r\n",
1080aa36fcacSchristos 			    c->self, c->remote_name, cp)) != 0) {
1081aa36fcacSchristos 				free(cp);
108217418e98Schristos 				fatal_fr(r, "sshbuf_putf");
1083aa36fcacSchristos 			}
1084aa36fcacSchristos 			free(cp);
1085ca32bd8dSchristos 			continue;
1086ca32bd8dSchristos 		default:
108717418e98Schristos 			fatal_f("bad channel type %d", c->type);
1088ca32bd8dSchristos 			/* NOTREACHED */
1089ca32bd8dSchristos 		}
1090ca32bd8dSchristos 	}
10917a183406Schristos 	if ((ret = sshbuf_dup_string(buf)) == NULL)
109217418e98Schristos 		fatal_f("sshbuf_dup_string");
10937a183406Schristos 	sshbuf_free(buf);
10947a183406Schristos 	return ret;
10957a183406Schristos }
10967a183406Schristos 
10977a183406Schristos static void
10987a183406Schristos open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
10997a183406Schristos {
11007a183406Schristos 	int r;
11017a183406Schristos 
11027a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
11037a183406Schristos 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
11047a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
11057a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
11067a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
110717418e98Schristos 		fatal_r(r, "%s: channel %i: open", where, c->self);
11087a183406Schristos 	}
1109ca32bd8dSchristos }
1110ca32bd8dSchristos 
1111ca32bd8dSchristos void
11127a183406Schristos channel_send_open(struct ssh *ssh, int id)
1113ca32bd8dSchristos {
11147a183406Schristos 	Channel *c = channel_lookup(ssh, id);
11157a183406Schristos 	int r;
1116ca32bd8dSchristos 
1117ca32bd8dSchristos 	if (c == NULL) {
1118ca32bd8dSchristos 		logit("channel_send_open: %d: bad id", id);
1119ca32bd8dSchristos 		return;
1120ca32bd8dSchristos 	}
1121ca32bd8dSchristos 	debug2("channel %d: send open", id);
11227a183406Schristos 	open_preamble(ssh, __func__, c, c->ctype);
11237a183406Schristos 	if ((r = sshpkt_send(ssh)) != 0)
112417418e98Schristos 		fatal_fr(r, "channel %i", c->self);
1125ca32bd8dSchristos }
1126ca32bd8dSchristos 
1127ca32bd8dSchristos void
11287a183406Schristos channel_request_start(struct ssh *ssh, int id, const char *service,
11297a183406Schristos     int wantconfirm)
1130ca32bd8dSchristos {
11317a183406Schristos 	Channel *c = channel_lookup(ssh, id);
11327a183406Schristos 	int r;
1133ca32bd8dSchristos 
1134ca32bd8dSchristos 	if (c == NULL) {
113517418e98Schristos 		logit_f("%d: unknown channel id", id);
1136ca32bd8dSchristos 		return;
1137ca32bd8dSchristos 	}
11387a183406Schristos 	if (!c->have_remote_id)
113917418e98Schristos 		fatal_f("channel %d: no remote id", c->self);
11407a183406Schristos 
1141ca32bd8dSchristos 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
11427a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
11437a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
11447a183406Schristos 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
11457a183406Schristos 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
114617418e98Schristos 		fatal_fr(r, "channel %i", c->self);
11477a183406Schristos 	}
1148ca32bd8dSchristos }
1149ca32bd8dSchristos 
1150ca32bd8dSchristos void
11517a183406Schristos channel_register_status_confirm(struct ssh *ssh, int id,
11527a183406Schristos     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
1153ca32bd8dSchristos {
1154ca32bd8dSchristos 	struct channel_confirm *cc;
1155ca32bd8dSchristos 	Channel *c;
1156ca32bd8dSchristos 
11577a183406Schristos 	if ((c = channel_lookup(ssh, id)) == NULL)
115817418e98Schristos 		fatal_f("%d: bad id", id);
1159ca32bd8dSchristos 
116000a838c4Schristos 	cc = xcalloc(1, sizeof(*cc));
1161ca32bd8dSchristos 	cc->cb = cb;
1162ca32bd8dSchristos 	cc->abandon_cb = abandon_cb;
1163ca32bd8dSchristos 	cc->ctx = ctx;
1164ca32bd8dSchristos 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
1165ca32bd8dSchristos }
1166ca32bd8dSchristos 
1167ca32bd8dSchristos void
11687a183406Schristos channel_register_open_confirm(struct ssh *ssh, int id,
11697a183406Schristos     channel_open_fn *fn, void *ctx)
1170ca32bd8dSchristos {
11717a183406Schristos 	Channel *c = channel_lookup(ssh, id);
1172ca32bd8dSchristos 
1173ca32bd8dSchristos 	if (c == NULL) {
117417418e98Schristos 		logit_f("%d: bad id", id);
1175ca32bd8dSchristos 		return;
1176ca32bd8dSchristos 	}
1177ca32bd8dSchristos 	c->open_confirm = fn;
1178ca32bd8dSchristos 	c->open_confirm_ctx = ctx;
1179ca32bd8dSchristos }
1180ca32bd8dSchristos 
1181ca32bd8dSchristos void
11827a183406Schristos channel_register_cleanup(struct ssh *ssh, int id,
11837a183406Schristos     channel_callback_fn *fn, int do_close)
1184ca32bd8dSchristos {
11857a183406Schristos 	Channel *c = channel_by_id(ssh, id);
1186ca32bd8dSchristos 
1187ca32bd8dSchristos 	if (c == NULL) {
118817418e98Schristos 		logit_f("%d: bad id", id);
1189ca32bd8dSchristos 		return;
1190ca32bd8dSchristos 	}
1191ca32bd8dSchristos 	c->detach_user = fn;
1192ca32bd8dSchristos 	c->detach_close = do_close;
1193ca32bd8dSchristos }
1194ca32bd8dSchristos 
1195ca32bd8dSchristos void
11967a183406Schristos channel_cancel_cleanup(struct ssh *ssh, int id)
1197ca32bd8dSchristos {
11987a183406Schristos 	Channel *c = channel_by_id(ssh, id);
1199ca32bd8dSchristos 
1200ca32bd8dSchristos 	if (c == NULL) {
120117418e98Schristos 		logit_f("%d: bad id", id);
1202ca32bd8dSchristos 		return;
1203ca32bd8dSchristos 	}
1204ca32bd8dSchristos 	c->detach_user = NULL;
1205ca32bd8dSchristos 	c->detach_close = 0;
1206ca32bd8dSchristos }
1207ca32bd8dSchristos 
1208ca32bd8dSchristos void
12097a183406Schristos channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
1210ca32bd8dSchristos     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
1211ca32bd8dSchristos {
12127a183406Schristos 	Channel *c = channel_lookup(ssh, id);
1213ca32bd8dSchristos 
1214ca32bd8dSchristos 	if (c == NULL) {
121517418e98Schristos 		logit_f("%d: bad id", id);
1216ca32bd8dSchristos 		return;
1217ca32bd8dSchristos 	}
1218ca32bd8dSchristos 	c->input_filter = ifn;
1219ca32bd8dSchristos 	c->output_filter = ofn;
1220ca32bd8dSchristos 	c->filter_ctx = ctx;
1221ca32bd8dSchristos 	c->filter_cleanup = cfn;
1222ca32bd8dSchristos }
1223ca32bd8dSchristos 
1224ca32bd8dSchristos void
12257a183406Schristos channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
1226ca32bd8dSchristos     int extusage, int nonblock, int is_tty, u_int window_max)
1227ca32bd8dSchristos {
12287a183406Schristos 	Channel *c = channel_lookup(ssh, id);
12297a183406Schristos 	int r;
1230ca32bd8dSchristos 
1231ca32bd8dSchristos 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1232ca32bd8dSchristos 		fatal("channel_activate for non-larval channel %d.", id);
12337a183406Schristos 	if (!c->have_remote_id)
123417418e98Schristos 		fatal_f("channel %d: no remote id", c->self);
12357a183406Schristos 
12367a183406Schristos 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1237ca32bd8dSchristos 	c->type = SSH_CHANNEL_OPEN;
1238c5555919Schristos 	channel_set_used_time(ssh, c);
1239ca32bd8dSchristos 	c->local_window = c->local_window_max = window_max;
12407a183406Schristos 
12417a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
12427a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
12437a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
12447a183406Schristos 	    (r = sshpkt_send(ssh)) != 0)
124517418e98Schristos 		fatal_fr(r, "channel %i", c->self);
1246ca32bd8dSchristos }
1247ca32bd8dSchristos 
1248ca32bd8dSchristos static void
1249a03ec00cSchristos channel_pre_listener(struct ssh *ssh, Channel *c)
1250ca32bd8dSchristos {
1251a03ec00cSchristos 	c->io_want = SSH_CHAN_IO_SOCK_R;
1252ca32bd8dSchristos }
1253ca32bd8dSchristos 
1254ca32bd8dSchristos static void
1255a03ec00cSchristos channel_pre_connecting(struct ssh *ssh, Channel *c)
1256ca32bd8dSchristos {
1257ca32bd8dSchristos 	debug3("channel %d: waiting for connection", c->self);
1258a03ec00cSchristos 	c->io_want = SSH_CHAN_IO_SOCK_W;
1259ca32bd8dSchristos }
1260ca32bd8dSchristos 
1261e8d5d3beSchristos static int
1262e8d5d3beSchristos channel_tcpwinsz(struct ssh *ssh)
1263e8d5d3beSchristos {
1264e8d5d3beSchristos 	u_int32_t tcpwinsz = 0;
1265e8d5d3beSchristos 	socklen_t optsz = sizeof(tcpwinsz);
1266e8d5d3beSchristos 	int ret = -1;
1267e8d5d3beSchristos 
1268e8d5d3beSchristos 	/* if we aren't on a socket return 128KB*/
1269e8d5d3beSchristos 	if(!ssh_packet_connection_is_on_socket(ssh))
1270e8d5d3beSchristos 	    return(128*1024);
1271e8d5d3beSchristos 	ret = getsockopt(ssh_packet_get_connection_in(ssh),
1272e8d5d3beSchristos 			 SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
12737425a2c2Smlelstv 	/* return no more than SSHBUF_SIZE_MAX (currently 256MB) */
12747425a2c2Smlelstv 	if ((ret == 0) && tcpwinsz > SSHBUF_SIZE_MAX)
12757425a2c2Smlelstv 	    tcpwinsz = SSHBUF_SIZE_MAX;
1276e8d5d3beSchristos 	debug2("tcpwinsz: %d for connection: %d", tcpwinsz,
1277e8d5d3beSchristos 	       ssh_packet_get_connection_in(ssh));
1278e8d5d3beSchristos 	return(tcpwinsz);
1279e8d5d3beSchristos }
1280e8d5d3beSchristos 
1281ca32bd8dSchristos static void
1282a03ec00cSchristos channel_pre_open(struct ssh *ssh, Channel *c)
1283ca32bd8dSchristos {
1284a03ec00cSchristos 	c->io_want = 0;
1285ca32bd8dSchristos 	if (c->istate == CHAN_INPUT_OPEN &&
12867a183406Schristos 	    c->remote_window > 0 &&
12877a183406Schristos 	    sshbuf_len(c->input) < c->remote_window &&
12887a183406Schristos 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
1289a03ec00cSchristos 		c->io_want |= SSH_CHAN_IO_RFD;
1290ca32bd8dSchristos 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1291ca32bd8dSchristos 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
12927a183406Schristos 		if (sshbuf_len(c->output) > 0) {
1293a03ec00cSchristos 			c->io_want |= SSH_CHAN_IO_WFD;
1294ca32bd8dSchristos 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1295ca32bd8dSchristos 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
12967a183406Schristos 				debug2("channel %d: "
12977a183406Schristos 				    "obuf_empty delayed efd %d/(%zu)", c->self,
12987a183406Schristos 				    c->efd, sshbuf_len(c->extended));
1299ca32bd8dSchristos 			else
13007a183406Schristos 				chan_obuf_empty(ssh, c);
1301ca32bd8dSchristos 		}
1302ca32bd8dSchristos 	}
1303ca32bd8dSchristos 	/** XXX check close conditions, too */
13047a183406Schristos 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
13057a183406Schristos 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1306ca32bd8dSchristos 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
13077a183406Schristos 		    sshbuf_len(c->extended) > 0)
1308a03ec00cSchristos 			c->io_want |= SSH_CHAN_IO_EFD_W;
130934b27b53Sadam 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
131034b27b53Sadam 		    (c->extended_usage == CHAN_EXTENDED_READ ||
131134b27b53Sadam 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
13127a183406Schristos 		    sshbuf_len(c->extended) < c->remote_window)
1313a03ec00cSchristos 			c->io_want |= SSH_CHAN_IO_EFD_R;
1314ca32bd8dSchristos 	}
1315ca32bd8dSchristos 	/* XXX: What about efd? races? */
1316ca32bd8dSchristos }
1317ca32bd8dSchristos 
1318ca32bd8dSchristos /*
1319ca32bd8dSchristos  * This is a special state for X11 authentication spoofing.  An opened X11
1320ca32bd8dSchristos  * connection (when authentication spoofing is being done) remains in this
1321ca32bd8dSchristos  * state until the first packet has been completely read.  The authentication
1322ca32bd8dSchristos  * data in that packet is then substituted by the real data if it matches the
1323ca32bd8dSchristos  * fake data, and the channel is put into normal mode.
1324ca32bd8dSchristos  * XXX All this happens at the client side.
1325ca32bd8dSchristos  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1326ca32bd8dSchristos  */
1327ca32bd8dSchristos static int
13287a183406Schristos x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1329ca32bd8dSchristos {
13307a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
1331ca32bd8dSchristos 	u_char *ucp;
1332ca32bd8dSchristos 	u_int proto_len, data_len;
1333ca32bd8dSchristos 
13344054ffb0Schristos 	/* Is this being called after the refusal deadline? */
13357a183406Schristos 	if (sc->x11_refuse_time != 0 &&
1336b1066cf3Schristos 	    monotime() >= sc->x11_refuse_time) {
13374054ffb0Schristos 		verbose("Rejected X11 connection after ForwardX11Timeout "
13384054ffb0Schristos 		    "expired");
13394054ffb0Schristos 		return -1;
13404054ffb0Schristos 	}
13414054ffb0Schristos 
1342ca32bd8dSchristos 	/* Check if the fixed size part of the packet is in buffer. */
13437a183406Schristos 	if (sshbuf_len(b) < 12)
1344ca32bd8dSchristos 		return 0;
1345ca32bd8dSchristos 
1346ca32bd8dSchristos 	/* Parse the lengths of variable-length fields. */
13477a183406Schristos 	ucp = sshbuf_mutable_ptr(b);
1348ca32bd8dSchristos 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1349ca32bd8dSchristos 		proto_len = 256 * ucp[6] + ucp[7];
1350ca32bd8dSchristos 		data_len = 256 * ucp[8] + ucp[9];
1351ca32bd8dSchristos 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1352ca32bd8dSchristos 		proto_len = ucp[6] + 256 * ucp[7];
1353ca32bd8dSchristos 		data_len = ucp[8] + 256 * ucp[9];
1354ca32bd8dSchristos 	} else {
1355ca32bd8dSchristos 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1356ca32bd8dSchristos 		    ucp[0]);
1357ca32bd8dSchristos 		return -1;
1358ca32bd8dSchristos 	}
1359ca32bd8dSchristos 
1360ca32bd8dSchristos 	/* Check if the whole packet is in buffer. */
13617a183406Schristos 	if (sshbuf_len(b) <
1362ca32bd8dSchristos 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1363ca32bd8dSchristos 		return 0;
1364ca32bd8dSchristos 
1365ca32bd8dSchristos 	/* Check if authentication protocol matches. */
13667a183406Schristos 	if (proto_len != strlen(sc->x11_saved_proto) ||
13677a183406Schristos 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1368ca32bd8dSchristos 		debug2("X11 connection uses different authentication protocol.");
1369ca32bd8dSchristos 		return -1;
1370ca32bd8dSchristos 	}
1371ca32bd8dSchristos 	/* Check if authentication data matches our fake data. */
13727a183406Schristos 	if (data_len != sc->x11_fake_data_len ||
137334b27b53Sadam 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
13747a183406Schristos 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1375ca32bd8dSchristos 		debug2("X11 auth data does not match fake data.");
1376ca32bd8dSchristos 		return -1;
1377ca32bd8dSchristos 	}
1378ca32bd8dSchristos 	/* Check fake data length */
13797a183406Schristos 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1380ca32bd8dSchristos 		error("X11 fake_data_len %d != saved_data_len %d",
13817a183406Schristos 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1382ca32bd8dSchristos 		return -1;
1383ca32bd8dSchristos 	}
1384ca32bd8dSchristos 	/*
1385ca32bd8dSchristos 	 * Received authentication protocol and data match
1386ca32bd8dSchristos 	 * our fake data. Substitute the fake data with real
1387ca32bd8dSchristos 	 * data.
1388ca32bd8dSchristos 	 */
1389ca32bd8dSchristos 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
13907a183406Schristos 	    sc->x11_saved_data, sc->x11_saved_data_len);
1391ca32bd8dSchristos 	return 1;
1392ca32bd8dSchristos }
1393ca32bd8dSchristos 
1394b1066cf3Schristos void
1395b1066cf3Schristos channel_force_close(struct ssh *ssh, Channel *c, int abandon)
1396b1066cf3Schristos {
1397b1066cf3Schristos 	debug3_f("channel %d: forcibly closing", c->self);
1398b1066cf3Schristos 	if (c->istate == CHAN_INPUT_OPEN)
1399b1066cf3Schristos 		chan_read_failed(ssh, c);
1400b1066cf3Schristos 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1401b1066cf3Schristos 		sshbuf_reset(c->input);
1402b1066cf3Schristos 		chan_ibuf_empty(ssh, c);
1403b1066cf3Schristos 	}
1404b1066cf3Schristos 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1405b1066cf3Schristos 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1406b1066cf3Schristos 		sshbuf_reset(c->output);
1407b1066cf3Schristos 		chan_write_failed(ssh, c);
1408b1066cf3Schristos 	}
1409b1066cf3Schristos 	if (c->detach_user)
1410b1066cf3Schristos 		c->detach_user(ssh, c->self, 1, NULL);
1411b1066cf3Schristos 	if (c->efd != -1)
1412b1066cf3Schristos 		channel_close_fd(ssh, c, &c->efd);
1413b1066cf3Schristos 	if (abandon)
1414b1066cf3Schristos 		c->type = SSH_CHANNEL_ABANDONED;
1415b1066cf3Schristos 	/* exempt from inactivity timeouts */
1416b1066cf3Schristos 	c->inactive_deadline = 0;
1417b1066cf3Schristos 	c->lastused = 0;
1418b1066cf3Schristos }
1419b1066cf3Schristos 
1420ca32bd8dSchristos static void
1421a03ec00cSchristos channel_pre_x11_open(struct ssh *ssh, Channel *c)
1422ca32bd8dSchristos {
14237a183406Schristos 	int ret = x11_open_helper(ssh, c->output);
1424ca32bd8dSchristos 
1425ca32bd8dSchristos 	/* c->force_drain = 1; */
1426ca32bd8dSchristos 
1427ca32bd8dSchristos 	if (ret == 1) {
1428ca32bd8dSchristos 		c->type = SSH_CHANNEL_OPEN;
1429c5555919Schristos 		channel_set_used_time(ssh, c);
1430a03ec00cSchristos 		channel_pre_open(ssh, c);
1431ca32bd8dSchristos 	} else if (ret == -1) {
1432b1066cf3Schristos 		logit("X11 connection rejected because of wrong "
1433b1066cf3Schristos 		    "authentication.");
14347a183406Schristos 		debug2("X11 rejected %d i%d/o%d",
14357a183406Schristos 		    c->self, c->istate, c->ostate);
1436b1066cf3Schristos 		channel_force_close(ssh, c, 0);
1437ca32bd8dSchristos 	}
1438ca32bd8dSchristos }
1439ca32bd8dSchristos 
144034b27b53Sadam static void
1441a03ec00cSchristos channel_pre_mux_client(struct ssh *ssh, Channel *c)
144234b27b53Sadam {
1443a03ec00cSchristos 	c->io_want = 0;
144434b27b53Sadam 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
14457a183406Schristos 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
1446a03ec00cSchristos 		c->io_want |= SSH_CHAN_IO_RFD;
144734b27b53Sadam 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
144834b27b53Sadam 		/* clear buffer immediately (discard any partial packet) */
14497a183406Schristos 		sshbuf_reset(c->input);
14507a183406Schristos 		chan_ibuf_empty(ssh, c);
145134b27b53Sadam 		/* Start output drain. XXX just kill chan? */
14527a183406Schristos 		chan_rcvd_oclose(ssh, c);
145334b27b53Sadam 	}
145434b27b53Sadam 	if (c->ostate == CHAN_OUTPUT_OPEN ||
145534b27b53Sadam 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
14567a183406Schristos 		if (sshbuf_len(c->output) > 0)
1457a03ec00cSchristos 			c->io_want |= SSH_CHAN_IO_WFD;
145834b27b53Sadam 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
14597a183406Schristos 			chan_obuf_empty(ssh, c);
146034b27b53Sadam 	}
146134b27b53Sadam }
146234b27b53Sadam 
1463ca32bd8dSchristos /* try to decode a socks4 header */
1464ca32bd8dSchristos static int
14657a183406Schristos channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1466ca32bd8dSchristos {
1467514b5d45Schristos 	const char *p;
14687a183406Schristos 	char *host;
1469ca32bd8dSchristos 	u_int len, have, i, found, need;
1470ca32bd8dSchristos 	char username[256];
1471ca32bd8dSchristos 	struct {
1472ca32bd8dSchristos 		u_int8_t version;
1473ca32bd8dSchristos 		u_int8_t command;
1474ca32bd8dSchristos 		u_int16_t dest_port;
1475ca32bd8dSchristos 		struct in_addr dest_addr;
1476ca32bd8dSchristos 	} s4_req, s4_rsp;
14777a183406Schristos 	int r;
1478ca32bd8dSchristos 
1479ca32bd8dSchristos 	debug2("channel %d: decode socks4", c->self);
1480ca32bd8dSchristos 
14817a183406Schristos 	have = sshbuf_len(input);
1482ca32bd8dSchristos 	len = sizeof(s4_req);
1483ca32bd8dSchristos 	if (have < len)
1484ca32bd8dSchristos 		return 0;
1485514b5d45Schristos 	p = (const char *)sshbuf_ptr(input);
1486ca32bd8dSchristos 
1487ca32bd8dSchristos 	need = 1;
1488ca32bd8dSchristos 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1489ca32bd8dSchristos 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1490ca32bd8dSchristos 		debug2("channel %d: socks4a request", c->self);
1491ca32bd8dSchristos 		/* ... and needs an extra string (the hostname) */
1492ca32bd8dSchristos 		need = 2;
1493ca32bd8dSchristos 	}
1494ca32bd8dSchristos 	/* Check for terminating NUL on the string(s) */
1495ca32bd8dSchristos 	for (found = 0, i = len; i < have; i++) {
1496ca32bd8dSchristos 		if (p[i] == '\0') {
1497ca32bd8dSchristos 			found++;
1498ca32bd8dSchristos 			if (found == need)
1499ca32bd8dSchristos 				break;
1500ca32bd8dSchristos 		}
1501ca32bd8dSchristos 		if (i > 1024) {
1502ca32bd8dSchristos 			/* the peer is probably sending garbage */
1503ca32bd8dSchristos 			debug("channel %d: decode socks4: too long",
1504ca32bd8dSchristos 			    c->self);
1505ca32bd8dSchristos 			return -1;
1506ca32bd8dSchristos 		}
1507ca32bd8dSchristos 	}
1508ca32bd8dSchristos 	if (found < need)
1509ca32bd8dSchristos 		return 0;
15107a183406Schristos 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
15117a183406Schristos 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
15127a183406Schristos 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
15137a183406Schristos 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
151417418e98Schristos 		debug_r(r, "channels %d: decode socks4", c->self);
15157a183406Schristos 		return -1;
15167a183406Schristos 	}
15177a183406Schristos 	have = sshbuf_len(input);
1518514b5d45Schristos 	p = (const char *)sshbuf_ptr(input);
15197a183406Schristos 	if (memchr(p, '\0', have) == NULL) {
152017418e98Schristos 		error("channel %d: decode socks4: unterminated user", c->self);
15217a183406Schristos 		return -1;
15227a183406Schristos 	}
1523ca32bd8dSchristos 	len = strlen(p);
1524ca32bd8dSchristos 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1525ca32bd8dSchristos 	len++; /* trailing '\0' */
1526ca32bd8dSchristos 	strlcpy(username, p, sizeof(username));
152717418e98Schristos 	if ((r = sshbuf_consume(input, len)) != 0)
152817418e98Schristos 		fatal_fr(r, "channel %d: consume", c->self);
152900a838c4Schristos 	free(c->path);
1530ca32bd8dSchristos 	c->path = NULL;
1531ca32bd8dSchristos 	if (need == 1) {			/* SOCKS4: one string */
1532ca32bd8dSchristos 		host = inet_ntoa(s4_req.dest_addr);
1533ca32bd8dSchristos 		c->path = xstrdup(host);
1534ca32bd8dSchristos 	} else {				/* SOCKS4A: two strings */
15357a183406Schristos 		have = sshbuf_len(input);
1536514b5d45Schristos 		p = (const char *)sshbuf_ptr(input);
15377a183406Schristos 		if (memchr(p, '\0', have) == NULL) {
15387a183406Schristos 			error("channel %d: decode socks4a: host not nul "
15397a183406Schristos 			    "terminated", c->self);
15407a183406Schristos 			return -1;
15417a183406Schristos 		}
1542ca32bd8dSchristos 		len = strlen(p);
1543ca32bd8dSchristos 		debug2("channel %d: decode socks4a: host %s/%d",
1544ca32bd8dSchristos 		    c->self, p, len);
1545ca32bd8dSchristos 		len++;				/* trailing '\0' */
1546ca32bd8dSchristos 		if (len > NI_MAXHOST) {
1547ca32bd8dSchristos 			error("channel %d: hostname \"%.100s\" too long",
1548ca32bd8dSchristos 			    c->self, p);
1549ca32bd8dSchristos 			return -1;
1550ca32bd8dSchristos 		}
1551ca32bd8dSchristos 		c->path = xstrdup(p);
155217418e98Schristos 		if ((r = sshbuf_consume(input, len)) != 0)
155317418e98Schristos 			fatal_fr(r, "channel %d: consume", c->self);
1554ca32bd8dSchristos 	}
1555ca32bd8dSchristos 	c->host_port = ntohs(s4_req.dest_port);
1556ca32bd8dSchristos 
1557ca32bd8dSchristos 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1558ca32bd8dSchristos 	    c->self, c->path, c->host_port, s4_req.command);
1559ca32bd8dSchristos 
1560ca32bd8dSchristos 	if (s4_req.command != 1) {
1561ca32bd8dSchristos 		debug("channel %d: cannot handle: %s cn %d",
1562ca32bd8dSchristos 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1563ca32bd8dSchristos 		return -1;
1564ca32bd8dSchristos 	}
1565ca32bd8dSchristos 	s4_rsp.version = 0;			/* vn: 0 for reply */
1566ca32bd8dSchristos 	s4_rsp.command = 90;			/* cd: req granted */
1567ca32bd8dSchristos 	s4_rsp.dest_port = 0;			/* ignored */
1568ca32bd8dSchristos 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
156917418e98Schristos 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0)
157017418e98Schristos 		fatal_fr(r, "channel %d: append reply", c->self);
1571ca32bd8dSchristos 	return 1;
1572ca32bd8dSchristos }
1573ca32bd8dSchristos 
1574ca32bd8dSchristos /* try to decode a socks5 header */
1575ca32bd8dSchristos #define SSH_SOCKS5_AUTHDONE	0x1000
1576ca32bd8dSchristos #define SSH_SOCKS5_NOAUTH	0x00
1577ca32bd8dSchristos #define SSH_SOCKS5_IPV4		0x01
1578ca32bd8dSchristos #define SSH_SOCKS5_DOMAIN	0x03
1579ca32bd8dSchristos #define SSH_SOCKS5_IPV6		0x04
1580ca32bd8dSchristos #define SSH_SOCKS5_CONNECT	0x01
1581ca32bd8dSchristos #define SSH_SOCKS5_SUCCESS	0x00
1582ca32bd8dSchristos 
1583ca32bd8dSchristos static int
15847a183406Schristos channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1585ca32bd8dSchristos {
15867a183406Schristos 	/* XXX use get/put_u8 instead of trusting struct padding */
1587ca32bd8dSchristos 	struct {
1588ca32bd8dSchristos 		u_int8_t version;
1589ca32bd8dSchristos 		u_int8_t command;
1590ca32bd8dSchristos 		u_int8_t reserved;
1591ca32bd8dSchristos 		u_int8_t atyp;
1592ca32bd8dSchristos 	} s5_req, s5_rsp;
1593ca32bd8dSchristos 	u_int16_t dest_port;
159400a838c4Schristos 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
15957a183406Schristos 	const u_char *p;
1596ca32bd8dSchristos 	u_int have, need, i, found, nmethods, addrlen, af;
15977a183406Schristos 	int r;
1598ca32bd8dSchristos 
1599ca32bd8dSchristos 	debug2("channel %d: decode socks5", c->self);
16007a183406Schristos 	p = sshbuf_ptr(input);
1601ca32bd8dSchristos 	if (p[0] != 0x05)
1602ca32bd8dSchristos 		return -1;
16037a183406Schristos 	have = sshbuf_len(input);
1604ca32bd8dSchristos 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1605ca32bd8dSchristos 		/* format: ver | nmethods | methods */
1606ca32bd8dSchristos 		if (have < 2)
1607ca32bd8dSchristos 			return 0;
1608ca32bd8dSchristos 		nmethods = p[1];
1609ca32bd8dSchristos 		if (have < nmethods + 2)
1610ca32bd8dSchristos 			return 0;
1611ca32bd8dSchristos 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1612ca32bd8dSchristos 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1613ca32bd8dSchristos 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1614ca32bd8dSchristos 				found = 1;
1615ca32bd8dSchristos 				break;
1616ca32bd8dSchristos 			}
1617ca32bd8dSchristos 		}
1618ca32bd8dSchristos 		if (!found) {
1619ca32bd8dSchristos 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1620ca32bd8dSchristos 			    c->self);
1621ca32bd8dSchristos 			return -1;
1622ca32bd8dSchristos 		}
162317418e98Schristos 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0)
162417418e98Schristos 			fatal_fr(r, "channel %d: consume", c->self);
16257a183406Schristos 		/* version, method */
16267a183406Schristos 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
162717418e98Schristos 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0)
162817418e98Schristos 			fatal_fr(r, "channel %d: append reply", c->self);
1629ca32bd8dSchristos 		c->flags |= SSH_SOCKS5_AUTHDONE;
1630ca32bd8dSchristos 		debug2("channel %d: socks5 auth done", c->self);
1631ca32bd8dSchristos 		return 0;				/* need more */
1632ca32bd8dSchristos 	}
1633ca32bd8dSchristos 	debug2("channel %d: socks5 post auth", c->self);
1634ca32bd8dSchristos 	if (have < sizeof(s5_req)+1)
1635ca32bd8dSchristos 		return 0;			/* need more */
1636ca32bd8dSchristos 	memcpy(&s5_req, p, sizeof(s5_req));
1637ca32bd8dSchristos 	if (s5_req.version != 0x05 ||
1638ca32bd8dSchristos 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1639ca32bd8dSchristos 	    s5_req.reserved != 0x00) {
1640ca32bd8dSchristos 		debug2("channel %d: only socks5 connect supported", c->self);
1641ca32bd8dSchristos 		return -1;
1642ca32bd8dSchristos 	}
1643ca32bd8dSchristos 	switch (s5_req.atyp){
1644ca32bd8dSchristos 	case SSH_SOCKS5_IPV4:
1645ca32bd8dSchristos 		addrlen = 4;
1646ca32bd8dSchristos 		af = AF_INET;
1647ca32bd8dSchristos 		break;
1648ca32bd8dSchristos 	case SSH_SOCKS5_DOMAIN:
1649ca32bd8dSchristos 		addrlen = p[sizeof(s5_req)];
1650ca32bd8dSchristos 		af = -1;
1651ca32bd8dSchristos 		break;
1652ca32bd8dSchristos 	case SSH_SOCKS5_IPV6:
1653ca32bd8dSchristos 		addrlen = 16;
1654ca32bd8dSchristos 		af = AF_INET6;
1655ca32bd8dSchristos 		break;
1656ca32bd8dSchristos 	default:
1657ca32bd8dSchristos 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1658ca32bd8dSchristos 		return -1;
1659ca32bd8dSchristos 	}
1660ca32bd8dSchristos 	need = sizeof(s5_req) + addrlen + 2;
1661ca32bd8dSchristos 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1662ca32bd8dSchristos 		need++;
1663ca32bd8dSchristos 	if (have < need)
1664ca32bd8dSchristos 		return 0;
166517418e98Schristos 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0)
166617418e98Schristos 		fatal_fr(r, "channel %d: consume", c->self);
16677a183406Schristos 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
16687a183406Schristos 		/* host string length */
166917418e98Schristos 		if ((r = sshbuf_consume(input, 1)) != 0)
167017418e98Schristos 			fatal_fr(r, "channel %d: consume", c->self);
16717a183406Schristos 	}
16727a183406Schristos 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
16737a183406Schristos 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
167417418e98Schristos 		debug_r(r, "channel %d: parse addr/port", c->self);
16757a183406Schristos 		return -1;
16767a183406Schristos 	}
1677ca32bd8dSchristos 	dest_addr[addrlen] = '\0';
167800a838c4Schristos 	free(c->path);
1679ca32bd8dSchristos 	c->path = NULL;
1680ca32bd8dSchristos 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1681ca32bd8dSchristos 		if (addrlen >= NI_MAXHOST) {
1682ca32bd8dSchristos 			error("channel %d: dynamic request: socks5 hostname "
1683ca32bd8dSchristos 			    "\"%.100s\" too long", c->self, dest_addr);
1684ca32bd8dSchristos 			return -1;
1685ca32bd8dSchristos 		}
1686ca32bd8dSchristos 		c->path = xstrdup(dest_addr);
1687ca32bd8dSchristos 	} else {
1688ca32bd8dSchristos 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1689ca32bd8dSchristos 			return -1;
1690ca32bd8dSchristos 		c->path = xstrdup(ntop);
1691ca32bd8dSchristos 	}
1692ca32bd8dSchristos 	c->host_port = ntohs(dest_port);
1693ca32bd8dSchristos 
1694ca32bd8dSchristos 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1695ca32bd8dSchristos 	    c->self, c->path, c->host_port, s5_req.command);
1696ca32bd8dSchristos 
1697ca32bd8dSchristos 	s5_rsp.version = 0x05;
1698ca32bd8dSchristos 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1699ca32bd8dSchristos 	s5_rsp.reserved = 0;			/* ignored */
1700ca32bd8dSchristos 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1701ca32bd8dSchristos 	dest_port = 0;				/* ignored */
1702ca32bd8dSchristos 
17037a183406Schristos 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
17047a183406Schristos 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
17057a183406Schristos 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
170617418e98Schristos 		fatal_fr(r, "channel %d: append reply", c->self);
1707ca32bd8dSchristos 	return 1;
1708ca32bd8dSchristos }
1709ca32bd8dSchristos 
171034b27b53Sadam Channel *
17117a183406Schristos channel_connect_stdio_fwd(struct ssh *ssh,
1712a629fefcSchristos     const char *host_to_connect, int port_to_connect,
1713b592f463Schristos     int in, int out, int nonblock)
171434b27b53Sadam {
171534b27b53Sadam 	Channel *c;
171634b27b53Sadam 
171717418e98Schristos 	debug_f("%s:%d", host_to_connect, port_to_connect);
171834b27b53Sadam 
17197a183406Schristos 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
172034b27b53Sadam 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
1721b592f463Schristos 	    0, "stdio-forward", nonblock);
172234b27b53Sadam 
172334b27b53Sadam 	c->path = xstrdup(host_to_connect);
172434b27b53Sadam 	c->host_port = port_to_connect;
172534b27b53Sadam 	c->listening_port = 0;
172634b27b53Sadam 	c->force_drain = 1;
172734b27b53Sadam 
17287a183406Schristos 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
1729a629fefcSchristos 	port_open_helper(ssh, c, port_to_connect == PORT_STREAMLOCAL ?
1730a629fefcSchristos 	    "direct-streamlocal@openssh.com" : "direct-tcpip");
173134b27b53Sadam 
173234b27b53Sadam 	return c;
173334b27b53Sadam }
173434b27b53Sadam 
1735ca32bd8dSchristos /* dynamic port forwarding */
1736ca32bd8dSchristos static void
1737a03ec00cSchristos channel_pre_dynamic(struct ssh *ssh, Channel *c)
1738ca32bd8dSchristos {
17397a183406Schristos 	const u_char *p;
1740ca32bd8dSchristos 	u_int have;
1741ca32bd8dSchristos 	int ret;
1742ca32bd8dSchristos 
1743a03ec00cSchristos 	c->io_want = 0;
17447a183406Schristos 	have = sshbuf_len(c->input);
1745ca32bd8dSchristos 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
17467a183406Schristos 	/* sshbuf_dump(c->input, stderr); */
1747ca32bd8dSchristos 	/* check if the fixed size part of the packet is in buffer. */
1748ca32bd8dSchristos 	if (have < 3) {
1749ca32bd8dSchristos 		/* need more */
1750a03ec00cSchristos 		c->io_want |= SSH_CHAN_IO_RFD;
1751ca32bd8dSchristos 		return;
1752ca32bd8dSchristos 	}
1753ca32bd8dSchristos 	/* try to guess the protocol */
17547a183406Schristos 	p = sshbuf_ptr(c->input);
17557a183406Schristos 	/* XXX sshbuf_peek_u8? */
1756ca32bd8dSchristos 	switch (p[0]) {
1757ca32bd8dSchristos 	case 0x04:
17587a183406Schristos 		ret = channel_decode_socks4(c, c->input, c->output);
1759ca32bd8dSchristos 		break;
1760ca32bd8dSchristos 	case 0x05:
17617a183406Schristos 		ret = channel_decode_socks5(c, c->input, c->output);
1762ca32bd8dSchristos 		break;
1763ca32bd8dSchristos 	default:
1764ca32bd8dSchristos 		ret = -1;
1765ca32bd8dSchristos 		break;
1766ca32bd8dSchristos 	}
1767ca32bd8dSchristos 	if (ret < 0) {
17687a183406Schristos 		chan_mark_dead(ssh, c);
1769ca32bd8dSchristos 	} else if (ret == 0) {
1770ca32bd8dSchristos 		debug2("channel %d: pre_dynamic: need more", c->self);
1771ca32bd8dSchristos 		/* need more */
1772a03ec00cSchristos 		c->io_want |= SSH_CHAN_IO_RFD;
17737a183406Schristos 		if (sshbuf_len(c->output))
1774a03ec00cSchristos 			c->io_want |= SSH_CHAN_IO_WFD;
1775ca32bd8dSchristos 	} else {
1776ca32bd8dSchristos 		/* switch to the next state */
1777ca32bd8dSchristos 		c->type = SSH_CHANNEL_OPENING;
17787a183406Schristos 		port_open_helper(ssh, c, "direct-tcpip");
17797a183406Schristos 	}
17807a183406Schristos }
17817a183406Schristos 
17827a183406Schristos /* simulate read-error */
17837a183406Schristos static void
17847a183406Schristos rdynamic_close(struct ssh *ssh, Channel *c)
17857a183406Schristos {
17867a183406Schristos 	c->type = SSH_CHANNEL_OPEN;
1787b1066cf3Schristos 	channel_force_close(ssh, c, 0);
17887a183406Schristos }
17897a183406Schristos 
17907a183406Schristos /* reverse dynamic port forwarding */
17917a183406Schristos static void
1792a03ec00cSchristos channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c)
17937a183406Schristos {
17947a183406Schristos 	const u_char *p;
17957a183406Schristos 	u_int have, len;
17967a183406Schristos 	int r, ret;
17977a183406Schristos 
17987a183406Schristos 	have = sshbuf_len(c->output);
17997a183406Schristos 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
18007a183406Schristos 	/* sshbuf_dump(c->output, stderr); */
18017a183406Schristos 	/* EOF received */
18027a183406Schristos 	if (c->flags & CHAN_EOF_RCVD) {
180317418e98Schristos 		if ((r = sshbuf_consume(c->output, have)) != 0)
180417418e98Schristos 			fatal_fr(r, "channel %d: consume", c->self);
18057a183406Schristos 		rdynamic_close(ssh, c);
18067a183406Schristos 		return;
18077a183406Schristos 	}
18087a183406Schristos 	/* check if the fixed size part of the packet is in buffer. */
18097a183406Schristos 	if (have < 3)
18107a183406Schristos 		return;
18117a183406Schristos 	/* try to guess the protocol */
18127a183406Schristos 	p = sshbuf_ptr(c->output);
18137a183406Schristos 	switch (p[0]) {
18147a183406Schristos 	case 0x04:
18157a183406Schristos 		/* switch input/output for reverse forwarding */
18167a183406Schristos 		ret = channel_decode_socks4(c, c->output, c->input);
18177a183406Schristos 		break;
18187a183406Schristos 	case 0x05:
18197a183406Schristos 		ret = channel_decode_socks5(c, c->output, c->input);
18207a183406Schristos 		break;
18217a183406Schristos 	default:
18227a183406Schristos 		ret = -1;
18237a183406Schristos 		break;
18247a183406Schristos 	}
18257a183406Schristos 	if (ret < 0) {
18267a183406Schristos 		rdynamic_close(ssh, c);
18277a183406Schristos 	} else if (ret == 0) {
18287a183406Schristos 		debug2("channel %d: pre_rdynamic: need more", c->self);
18297a183406Schristos 		/* send socks request to peer */
18307a183406Schristos 		len = sshbuf_len(c->input);
18317a183406Schristos 		if (len > 0 && len < c->remote_window) {
18327a183406Schristos 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
18337a183406Schristos 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
18347a183406Schristos 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
18357a183406Schristos 			    (r = sshpkt_send(ssh)) != 0) {
183617418e98Schristos 				fatal_fr(r, "channel %i: rdynamic", c->self);
18377a183406Schristos 			}
183817418e98Schristos 			if ((r = sshbuf_consume(c->input, len)) != 0)
183917418e98Schristos 				fatal_fr(r, "channel %d: consume", c->self);
18407a183406Schristos 			c->remote_window -= len;
18417a183406Schristos 		}
18427a183406Schristos 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
18437a183406Schristos 		/* the connect failed */
18447a183406Schristos 		rdynamic_close(ssh, c);
1845ca32bd8dSchristos 	}
1846ca32bd8dSchristos }
1847ca32bd8dSchristos 
1848ca32bd8dSchristos /* This is our fake X11 server socket. */
1849ca32bd8dSchristos static void
1850a03ec00cSchristos channel_post_x11_listener(struct ssh *ssh, Channel *c)
1851ca32bd8dSchristos {
1852ca32bd8dSchristos 	Channel *nc;
1853ca32bd8dSchristos 	struct sockaddr_storage addr;
18547a183406Schristos 	int r, newsock, oerrno, remote_port;
1855ca32bd8dSchristos 	socklen_t addrlen;
1856ca32bd8dSchristos 	char buf[16384], *remote_ipaddr;
1857ca32bd8dSchristos 
1858a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
18597a183406Schristos 		return;
18607a183406Schristos 
1861ca32bd8dSchristos 	debug("X11 connection requested.");
1862ca32bd8dSchristos 	addrlen = sizeof(addr);
1863ca32bd8dSchristos 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1864ca32bd8dSchristos 	if (c->single_connection) {
186500a838c4Schristos 		oerrno = errno;
1866ca32bd8dSchristos 		debug2("single_connection: closing X11 listener.");
1867b592f463Schristos 		channel_close_fd(ssh, c, &c->sock);
18687a183406Schristos 		chan_mark_dead(ssh, c);
186900a838c4Schristos 		errno = oerrno;
1870ca32bd8dSchristos 	}
1871cd4ada6aSchristos 	if (newsock == -1) {
187200a838c4Schristos 		if (errno != EINTR && errno != EWOULDBLOCK &&
187300a838c4Schristos 		    errno != ECONNABORTED)
1874ca32bd8dSchristos 			error("accept: %.100s", strerror(errno));
18752649c700Schristos 		if (errno == EMFILE || errno == ENFILE)
187600a838c4Schristos 			c->notbefore = monotime() + 1;
1877ca32bd8dSchristos 		return;
1878ca32bd8dSchristos 	}
1879ca32bd8dSchristos 	set_nodelay(newsock);
1880ca32bd8dSchristos 	remote_ipaddr = get_peer_ipaddr(newsock);
1881ca32bd8dSchristos 	remote_port = get_peer_port(newsock);
1882ca32bd8dSchristos 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1883ca32bd8dSchristos 	    remote_ipaddr, remote_port);
1884ca32bd8dSchristos 
1885b1066cf3Schristos 	nc = channel_new(ssh, "x11-connection",
1886ca32bd8dSchristos 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1887ca32bd8dSchristos 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
18887a183406Schristos 	open_preamble(ssh, __func__, nc, "x11");
1889ffae97bbSchristos 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
1890ffae97bbSchristos 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
189117418e98Schristos 		fatal_fr(r, "channel %i: reply", c->self);
1892ca32bd8dSchristos 	}
18937a183406Schristos 	if ((r = sshpkt_send(ssh)) != 0)
189417418e98Schristos 		fatal_fr(r, "channel %i: send", c->self);
189500a838c4Schristos 	free(remote_ipaddr);
1896ca32bd8dSchristos }
1897ca32bd8dSchristos 
1898ca32bd8dSchristos static void
18997a183406Schristos port_open_helper(struct ssh *ssh, Channel *c, const char *rtype)
1900ca32bd8dSchristos {
19018a4530f9Schristos 	char *local_ipaddr = get_local_ipaddr(c->sock);
19025101d403Schristos 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1903ca32bd8dSchristos 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1904ca32bd8dSchristos 	int remote_port = get_peer_port(c->sock);
19057a183406Schristos 	int r;
1906ca32bd8dSchristos 
190734b27b53Sadam 	if (remote_port == -1) {
190834b27b53Sadam 		/* Fake addr/port to appease peers that validate it (Tectia) */
190900a838c4Schristos 		free(remote_ipaddr);
191034b27b53Sadam 		remote_ipaddr = xstrdup("127.0.0.1");
191134b27b53Sadam 		remote_port = 65535;
191234b27b53Sadam 	}
191334b27b53Sadam 
19147a183406Schristos 	free(c->remote_name);
19157a183406Schristos 	xasprintf(&c->remote_name,
1916ca32bd8dSchristos 	    "%s: listening port %d for %.100s port %d, "
19178a4530f9Schristos 	    "connect from %.200s port %d to %.100s port %d",
1918ca32bd8dSchristos 	    rtype, c->listening_port, c->path, c->host_port,
19198a4530f9Schristos 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1920ca32bd8dSchristos 
19217a183406Schristos 	open_preamble(ssh, __func__, c, rtype);
19228a4530f9Schristos 	if (strcmp(rtype, "direct-tcpip") == 0) {
1923ca32bd8dSchristos 		/* target host, port */
19247a183406Schristos 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
192517418e98Schristos 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0)
192617418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
19278a4530f9Schristos 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
19288a4530f9Schristos 		/* target path */
192917418e98Schristos 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
193017418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
19318a4530f9Schristos 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
19328a4530f9Schristos 		/* listen path */
193317418e98Schristos 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
193417418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
1935ca32bd8dSchristos 	} else {
1936ca32bd8dSchristos 		/* listen address, port */
19377a183406Schristos 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
193817418e98Schristos 		    (r = sshpkt_put_u32(ssh, local_port)) != 0)
193917418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
1940ca32bd8dSchristos 	}
19418a4530f9Schristos 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
19428a4530f9Schristos 		/* reserved for future owner/mode info */
194317418e98Schristos 		if ((r = sshpkt_put_cstring(ssh, "")) != 0)
194417418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
19458a4530f9Schristos 	} else {
1946ca32bd8dSchristos 		/* originator host and port */
19477a183406Schristos 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
194817418e98Schristos 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0)
194917418e98Schristos 			fatal_fr(r, "channel %i: reply", c->self);
1950ca32bd8dSchristos 	}
19517a183406Schristos 	if ((r = sshpkt_send(ssh)) != 0)
195217418e98Schristos 		fatal_fr(r, "channel %i: send", c->self);
195300a838c4Schristos 	free(remote_ipaddr);
19548a4530f9Schristos 	free(local_ipaddr);
1955ca32bd8dSchristos }
1956ca32bd8dSchristos 
19574054ffb0Schristos void
1958b1066cf3Schristos channel_set_x11_refuse_time(struct ssh *ssh, time_t refuse_time)
19594054ffb0Schristos {
19607a183406Schristos 	ssh->chanctxt->x11_refuse_time = refuse_time;
19614054ffb0Schristos }
19624054ffb0Schristos 
1963ca32bd8dSchristos /*
1964ca32bd8dSchristos  * This socket is listening for connections to a forwarded TCP/IP port.
1965ca32bd8dSchristos  */
1966ca32bd8dSchristos static void
1967a03ec00cSchristos channel_post_port_listener(struct ssh *ssh, Channel *c)
1968ca32bd8dSchristos {
1969ca32bd8dSchristos 	Channel *nc;
1970ca32bd8dSchristos 	struct sockaddr_storage addr;
1971ca32bd8dSchristos 	int newsock, nextstate;
1972ca32bd8dSchristos 	socklen_t addrlen;
1973185c8f97Schristos 	const char *rtype;
1974ca32bd8dSchristos 
1975a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
19767a183406Schristos 		return;
19777a183406Schristos 
19787a183406Schristos 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1979ca32bd8dSchristos 	    c->listening_port, c->path, c->host_port);
1980ca32bd8dSchristos 
1981ca32bd8dSchristos 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1982ca32bd8dSchristos 		nextstate = SSH_CHANNEL_OPENING;
1983ca32bd8dSchristos 		rtype = "forwarded-tcpip";
19848a4530f9Schristos 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
19858a4530f9Schristos 		nextstate = SSH_CHANNEL_OPENING;
19868a4530f9Schristos 		rtype = "forwarded-streamlocal@openssh.com";
19878a4530f9Schristos 	} else if (c->host_port == PORT_STREAMLOCAL) {
19888a4530f9Schristos 		nextstate = SSH_CHANNEL_OPENING;
19898a4530f9Schristos 		rtype = "direct-streamlocal@openssh.com";
19908a4530f9Schristos 	} else if (c->host_port == 0) {
1991ca32bd8dSchristos 		nextstate = SSH_CHANNEL_DYNAMIC;
1992ca32bd8dSchristos 		rtype = "dynamic-tcpip";
1993ca32bd8dSchristos 	} else {
1994ca32bd8dSchristos 		nextstate = SSH_CHANNEL_OPENING;
1995ca32bd8dSchristos 		rtype = "direct-tcpip";
1996ca32bd8dSchristos 	}
1997ca32bd8dSchristos 
1998ca32bd8dSchristos 	addrlen = sizeof(addr);
1999ca32bd8dSchristos 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
2000cd4ada6aSchristos 	if (newsock == -1) {
200100a838c4Schristos 		if (errno != EINTR && errno != EWOULDBLOCK &&
200200a838c4Schristos 		    errno != ECONNABORTED)
2003ca32bd8dSchristos 			error("accept: %.100s", strerror(errno));
20042649c700Schristos 		if (errno == EMFILE || errno == ENFILE)
200500a838c4Schristos 			c->notbefore = monotime() + 1;
2006ca32bd8dSchristos 		return;
2007ca32bd8dSchristos 	}
20088a4530f9Schristos 	if (c->host_port != PORT_STREAMLOCAL)
2009ca32bd8dSchristos 		set_nodelay(newsock);
20107a183406Schristos 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
2011ca32bd8dSchristos 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
2012ca32bd8dSchristos 	nc->listening_port = c->listening_port;
2013ca32bd8dSchristos 	nc->host_port = c->host_port;
2014ca32bd8dSchristos 	if (c->path != NULL)
2015ca32bd8dSchristos 		nc->path = xstrdup(c->path);
2016ca32bd8dSchristos 
201734b27b53Sadam 	if (nextstate != SSH_CHANNEL_DYNAMIC)
20187a183406Schristos 		port_open_helper(ssh, nc, rtype);
2019ca32bd8dSchristos }
2020ca32bd8dSchristos 
2021ca32bd8dSchristos /*
2022ca32bd8dSchristos  * This is the authentication agent socket listening for connections from
2023ca32bd8dSchristos  * clients.
2024ca32bd8dSchristos  */
2025ca32bd8dSchristos static void
2026a03ec00cSchristos channel_post_auth_listener(struct ssh *ssh, Channel *c)
2027ca32bd8dSchristos {
2028ca32bd8dSchristos 	Channel *nc;
20297a183406Schristos 	int r, newsock;
2030ca32bd8dSchristos 	struct sockaddr_storage addr;
2031ca32bd8dSchristos 	socklen_t addrlen;
2032ca32bd8dSchristos 
2033a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
20347a183406Schristos 		return;
20357a183406Schristos 
2036ca32bd8dSchristos 	addrlen = sizeof(addr);
2037ca32bd8dSchristos 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
2038cd4ada6aSchristos 	if (newsock == -1) {
20397a183406Schristos 		error("accept from auth socket: %.100s", strerror(errno));
20402649c700Schristos 		if (errno == EMFILE || errno == ENFILE)
204100a838c4Schristos 			c->notbefore = monotime() + 1;
2042ca32bd8dSchristos 		return;
2043ca32bd8dSchristos 	}
2044b1066cf3Schristos 	nc = channel_new(ssh, "agent-connection",
2045ca32bd8dSchristos 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
2046ca32bd8dSchristos 	    c->local_window_max, c->local_maxpacket,
2047ca32bd8dSchristos 	    0, "accepted auth socket", 1);
20487a183406Schristos 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
20497a183406Schristos 	if ((r = sshpkt_send(ssh)) != 0)
205017418e98Schristos 		fatal_fr(r, "channel %i", c->self);
2051ca32bd8dSchristos }
2052ca32bd8dSchristos 
2053ca32bd8dSchristos static void
2054a03ec00cSchristos channel_post_connecting(struct ssh *ssh, Channel *c)
2055ca32bd8dSchristos {
20567a183406Schristos 	int err = 0, sock, isopen, r;
2057ca32bd8dSchristos 	socklen_t sz = sizeof(err);
2058ca32bd8dSchristos 
2059a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
20607a183406Schristos 		return;
20617a183406Schristos 	if (!c->have_remote_id)
206217418e98Schristos 		fatal_f("channel %d: no remote id", c->self);
20637a183406Schristos 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
20647a183406Schristos 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
2065b1066cf3Schristos 
2066cd4ada6aSchristos 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) {
2067ca32bd8dSchristos 		err = errno;
2068ca32bd8dSchristos 		error("getsockopt SO_ERROR failed");
2069ca32bd8dSchristos 	}
2070b1066cf3Schristos 
2071ca32bd8dSchristos 	if (err == 0) {
2072b1066cf3Schristos 		/* Non-blocking connection completed */
2073ca32bd8dSchristos 		debug("channel %d: connected to %s port %d",
2074ca32bd8dSchristos 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
2075ca32bd8dSchristos 		channel_connect_ctx_free(&c->connect_ctx);
2076ca32bd8dSchristos 		c->type = SSH_CHANNEL_OPEN;
2077c5555919Schristos 		channel_set_used_time(ssh, c);
20787a183406Schristos 		if (isopen) {
20797a183406Schristos 			/* no message necessary */
2080ca32bd8dSchristos 		} else {
20817a183406Schristos 			if ((r = sshpkt_start(ssh,
20827a183406Schristos 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
20837a183406Schristos 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
20847a183406Schristos 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
20857a183406Schristos 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
208617418e98Schristos 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
208717418e98Schristos 			    (r = sshpkt_send(ssh)) != 0)
208817418e98Schristos 				fatal_fr(r, "channel %i open confirm", c->self);
2089ca32bd8dSchristos 		}
2090ca32bd8dSchristos 		return;
2091ca32bd8dSchristos 	}
2092b1066cf3Schristos 	if (err == EINTR || err == EAGAIN || err == EINPROGRESS)
2093b1066cf3Schristos 		return;
2094b1066cf3Schristos 
2095b1066cf3Schristos 	/* Non-blocking connection failed */
2096b1066cf3Schristos 	debug("channel %d: connection failed: %s", c->self, strerror(err));
2097b1066cf3Schristos 
2098b1066cf3Schristos 	/* Try next address, if any */
2099b1066cf3Schristos 	if ((sock = connect_next(&c->connect_ctx)) == -1) {
2100b1066cf3Schristos 		/* Exhausted all addresses for this destination */
2101ca32bd8dSchristos 		error("connect_to %.100s port %d: failed.",
2102ca32bd8dSchristos 		    c->connect_ctx.host, c->connect_ctx.port);
2103ca32bd8dSchristos 		channel_connect_ctx_free(&c->connect_ctx);
21047a183406Schristos 		if (isopen) {
21057a183406Schristos 			rdynamic_close(ssh, c);
2106ca32bd8dSchristos 		} else {
21077a183406Schristos 			if ((r = sshpkt_start(ssh,
21087a183406Schristos 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
21097a183406Schristos 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
2110ffae97bbSchristos 			    (r = sshpkt_put_u32(ssh,
2111ffae97bbSchristos 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
2112ffae97bbSchristos 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
211317418e98Schristos 			    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
211417418e98Schristos 			    (r = sshpkt_send(ssh)) != 0)
211517418e98Schristos 				fatal_fr(r, "channel %i: failure", c->self);
21167a183406Schristos 			chan_mark_dead(ssh, c);
2117ca32bd8dSchristos 		}
2118ca32bd8dSchristos 	}
2119b1066cf3Schristos 
2120b1066cf3Schristos 	/* New non-blocking connection in progress */
2121b1066cf3Schristos 	close(c->sock);
2122b1066cf3Schristos 	c->sock = c->rfd = c->wfd = sock;
2123ca32bd8dSchristos }
2124ca32bd8dSchristos 
2125ca32bd8dSchristos static int
2126a03ec00cSchristos channel_handle_rfd(struct ssh *ssh, Channel *c)
2127ca32bd8dSchristos {
2128ca32bd8dSchristos 	char buf[CHAN_RBUF];
21297a183406Schristos 	ssize_t len;
21307a183406Schristos 	int r;
2131b1066cf3Schristos 	size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ;
2132ca32bd8dSchristos 
2133a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
2134a03ec00cSchristos 		return 1; /* Shouldn't happen */
2135a03ec00cSchristos 	if ((avail = sshbuf_avail(c->input)) == 0)
2136a03ec00cSchristos 		return 1; /* Shouldn't happen */
2137a03ec00cSchristos 
2138a03ec00cSchristos 	/*
2139a03ec00cSchristos 	 * For "simple" channels (i.e. not datagram or filtered), we can
2140a03ec00cSchristos 	 * read directly to the channel buffer.
2141a03ec00cSchristos 	 */
2142a03ec00cSchristos 	if (c->input_filter == NULL && !c->datagram) {
2143a03ec00cSchristos 		/* Only OPEN channels have valid rwin */
2144a03ec00cSchristos 		if (c->type == SSH_CHANNEL_OPEN) {
2145a03ec00cSchristos 			if ((have = sshbuf_len(c->input)) >= c->remote_window)
2146a03ec00cSchristos 				return 1; /* shouldn't happen */
2147a03ec00cSchristos 			if (maxlen > c->remote_window - have)
2148a03ec00cSchristos 				maxlen = c->remote_window - have;
2149a03ec00cSchristos 		}
2150a03ec00cSchristos 		if (maxlen > avail)
2151a03ec00cSchristos 			maxlen = avail;
2152b1066cf3Schristos 		if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) {
2153a03ec00cSchristos 			if (errno == EINTR || errno == EAGAIN)
21547a183406Schristos 				return 1;
2155a03ec00cSchristos 			debug2("channel %d: read failed rfd %d maxlen %zu: %s",
2156a03ec00cSchristos 			    c->self, c->rfd, maxlen, ssh_err(r));
2157a03ec00cSchristos 			goto rfail;
2158a03ec00cSchristos 		}
2159b1066cf3Schristos 		if (nr != 0)
2160c5555919Schristos 			channel_set_used_time(ssh, c);
2161a03ec00cSchristos 		return 1;
2162a03ec00cSchristos 	}
21637a183406Schristos 
2164ca32bd8dSchristos 	len = read(c->rfd, buf, sizeof(buf));
2165cd4ada6aSchristos 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
2166ca32bd8dSchristos 		return 1;
2167ca32bd8dSchristos 	if (len <= 0) {
2168a03ec00cSchristos 		debug2("channel %d: read<=0 rfd %d len %zd: %s",
2169a03ec00cSchristos 		    c->self, c->rfd, len,
2170a03ec00cSchristos 		    len == 0 ? "closed" : strerror(errno));
2171a03ec00cSchristos  rfail:
2172ca32bd8dSchristos 		if (c->type != SSH_CHANNEL_OPEN) {
2173ca32bd8dSchristos 			debug2("channel %d: not open", c->self);
21747a183406Schristos 			chan_mark_dead(ssh, c);
2175ca32bd8dSchristos 			return -1;
2176ca32bd8dSchristos 		} else {
21777a183406Schristos 			chan_read_failed(ssh, c);
2178ca32bd8dSchristos 		}
2179ca32bd8dSchristos 		return -1;
2180ca32bd8dSchristos 	}
2181c5555919Schristos 	channel_set_used_time(ssh, c);
2182ca32bd8dSchristos 	if (c->input_filter != NULL) {
21837a183406Schristos 		if (c->input_filter(ssh, c, buf, len) == -1) {
2184ca32bd8dSchristos 			debug2("channel %d: filter stops", c->self);
21857a183406Schristos 			chan_read_failed(ssh, c);
2186ca32bd8dSchristos 		}
2187ca32bd8dSchristos 	} else if (c->datagram) {
21887a183406Schristos 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
218917418e98Schristos 			fatal_fr(r, "channel %i: put datagram", c->self);
2190a03ec00cSchristos 	}
2191ca32bd8dSchristos 	return 1;
2192ca32bd8dSchristos }
2193ca32bd8dSchristos 
2194ca32bd8dSchristos static int
2195a03ec00cSchristos channel_handle_wfd(struct ssh *ssh, Channel *c)
2196ca32bd8dSchristos {
2197ca32bd8dSchristos 	struct termios tio;
21987a183406Schristos 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
21997a183406Schristos 	size_t dlen, olen = 0;
22007a183406Schristos 	int r, len;
22017a183406Schristos 
2202a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
2203a03ec00cSchristos 		return 1;
2204a03ec00cSchristos 	if (sshbuf_len(c->output) == 0)
22057a183406Schristos 		return 1;
2206ca32bd8dSchristos 
2207ca32bd8dSchristos 	/* Send buffered output data to the socket. */
22087a183406Schristos 	olen = sshbuf_len(c->output);
2209ca32bd8dSchristos 	if (c->output_filter != NULL) {
22107a183406Schristos 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
2211ca32bd8dSchristos 			debug2("channel %d: filter stops", c->self);
2212ca32bd8dSchristos 			if (c->type != SSH_CHANNEL_OPEN)
22137a183406Schristos 				chan_mark_dead(ssh, c);
2214ca32bd8dSchristos 			else
22157a183406Schristos 				chan_write_failed(ssh, c);
2216ca32bd8dSchristos 			return -1;
2217ca32bd8dSchristos 		}
2218ca32bd8dSchristos 	} else if (c->datagram) {
22197a183406Schristos 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
222017418e98Schristos 			fatal_fr(r, "channel %i: get datagram", c->self);
22217a183406Schristos 		buf = data;
2222ca32bd8dSchristos 	} else {
22237a183406Schristos 		buf = data = sshbuf_mutable_ptr(c->output);
22247a183406Schristos 		dlen = sshbuf_len(c->output);
2225ca32bd8dSchristos 	}
2226ca32bd8dSchristos 
2227ca32bd8dSchristos 	if (c->datagram) {
2228ca32bd8dSchristos 		/* ignore truncated writes, datagrams might get lost */
2229ca32bd8dSchristos 		len = write(c->wfd, buf, dlen);
223000a838c4Schristos 		free(data);
2231cd4ada6aSchristos 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
2232ca32bd8dSchristos 			return 1;
22337a183406Schristos 		if (len <= 0)
22347a183406Schristos 			goto write_fail;
223534b27b53Sadam 		goto out;
2236ca32bd8dSchristos 	}
2237ca32bd8dSchristos 
2238ca32bd8dSchristos 	len = write(c->wfd, buf, dlen);
2239cd4ada6aSchristos 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
2240ca32bd8dSchristos 		return 1;
2241ca32bd8dSchristos 	if (len <= 0) {
22427a183406Schristos  write_fail:
2243ca32bd8dSchristos 		if (c->type != SSH_CHANNEL_OPEN) {
2244ca32bd8dSchristos 			debug2("channel %d: not open", c->self);
22457a183406Schristos 			chan_mark_dead(ssh, c);
2246ca32bd8dSchristos 			return -1;
2247ca32bd8dSchristos 		} else {
22487a183406Schristos 			chan_write_failed(ssh, c);
2249ca32bd8dSchristos 		}
2250ca32bd8dSchristos 		return -1;
2251ca32bd8dSchristos 	}
2252c5555919Schristos 	channel_set_used_time(ssh, c);
22537a183406Schristos 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2254ca32bd8dSchristos 		if (tcgetattr(c->wfd, &tio) == 0 &&
2255ca32bd8dSchristos 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2256ca32bd8dSchristos 			/*
2257ca32bd8dSchristos 			 * Simulate echo to reduce the impact of
2258ca32bd8dSchristos 			 * traffic analysis. We need to match the
2259ca32bd8dSchristos 			 * size of a SSH2_MSG_CHANNEL_DATA message
2260ca32bd8dSchristos 			 * (4 byte channel id + buf)
2261ca32bd8dSchristos 			 */
22627a183406Schristos 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
22637a183406Schristos 			    (r = sshpkt_send(ssh)) != 0)
226417418e98Schristos 				fatal_fr(r, "channel %i: ignore", c->self);
2265ca32bd8dSchristos 		}
2266ca32bd8dSchristos 	}
226717418e98Schristos 	if ((r = sshbuf_consume(c->output, len)) != 0)
226817418e98Schristos 		fatal_fr(r, "channel %i: consume", c->self);
226934b27b53Sadam  out:
22707a183406Schristos 	c->local_consumed += olen - sshbuf_len(c->output);
22717a183406Schristos 
2272ca32bd8dSchristos 	return 1;
2273ca32bd8dSchristos }
2274ca32bd8dSchristos 
2275ca32bd8dSchristos static int
2276a03ec00cSchristos channel_handle_efd_write(struct ssh *ssh, Channel *c)
2277ca32bd8dSchristos {
22787a183406Schristos 	int r;
22797a183406Schristos 	ssize_t len;
2280ca32bd8dSchristos 
2281a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0)
2282a03ec00cSchristos 		return 1;
2283a03ec00cSchristos 	if (sshbuf_len(c->extended) == 0)
22847a183406Schristos 		return 1;
22857a183406Schristos 
22867a183406Schristos 	len = write(c->efd, sshbuf_ptr(c->extended),
22877a183406Schristos 	    sshbuf_len(c->extended));
22887a183406Schristos 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
2289cd4ada6aSchristos 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
2290ca32bd8dSchristos 		return 1;
2291ca32bd8dSchristos 	if (len <= 0) {
22927a183406Schristos 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
2293b592f463Schristos 		channel_close_fd(ssh, c, &c->efd);
2294ca32bd8dSchristos 	} else {
229517418e98Schristos 		if ((r = sshbuf_consume(c->extended, len)) != 0)
229617418e98Schristos 			fatal_fr(r, "channel %i: consume", c->self);
2297ca32bd8dSchristos 		c->local_consumed += len;
2298c5555919Schristos 		channel_set_used_time(ssh, c);
2299ca32bd8dSchristos 	}
23007a183406Schristos 	return 1;
23017a183406Schristos }
23027a183406Schristos 
23037a183406Schristos static int
2304a03ec00cSchristos channel_handle_efd_read(struct ssh *ssh, Channel *c)
23057a183406Schristos {
23067a183406Schristos 	char buf[CHAN_RBUF];
23077a183406Schristos 	int r;
23087a183406Schristos 	ssize_t len;
23097a183406Schristos 
2310a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
23117a183406Schristos 		return 1;
23127a183406Schristos 
2313ca32bd8dSchristos 	len = read(c->efd, buf, sizeof(buf));
23147a183406Schristos 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
2315cd4ada6aSchristos 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
2316ca32bd8dSchristos 		return 1;
2317ca32bd8dSchristos 	if (len <= 0) {
231817418e98Schristos 		debug2("channel %d: closing read-efd %d", c->self, c->efd);
2319b592f463Schristos 		channel_close_fd(ssh, c, &c->efd);
2320b1066cf3Schristos 		return 1;
2321b1066cf3Schristos 	}
2322c5555919Schristos 	channel_set_used_time(ssh, c);
2323b1066cf3Schristos 	if (c->extended_usage == CHAN_EXTENDED_IGNORE)
232417418e98Schristos 		debug3("channel %d: discard efd", c->self);
232517418e98Schristos 	else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
232617418e98Schristos 		fatal_fr(r, "channel %i: append", c->self);
2327ca32bd8dSchristos 	return 1;
2328ca32bd8dSchristos }
2329ca32bd8dSchristos 
2330ca32bd8dSchristos static int
2331a03ec00cSchristos channel_handle_efd(struct ssh *ssh, Channel *c)
2332ca32bd8dSchristos {
23337a183406Schristos 	if (c->efd == -1)
23347a183406Schristos 		return 1;
23357a183406Schristos 
23367a183406Schristos 	/** XXX handle drain efd, too */
23377a183406Schristos 
23387a183406Schristos 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
2339a03ec00cSchristos 		return channel_handle_efd_write(ssh, c);
23407a183406Schristos 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
23417a183406Schristos 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
2342a03ec00cSchristos 		return channel_handle_efd_read(ssh, c);
23437a183406Schristos 
23447a183406Schristos 	return 1;
23457a183406Schristos }
23467a183406Schristos 
23477a183406Schristos static int
23487a183406Schristos channel_check_window(struct ssh *ssh, Channel *c)
23497a183406Schristos {
23507a183406Schristos 	int r;
23517a183406Schristos 
2352ca32bd8dSchristos 	if (c->type == SSH_CHANNEL_OPEN &&
2353ca32bd8dSchristos 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2354ca32bd8dSchristos 	    ((c->local_window_max - c->local_window >
2355ca32bd8dSchristos 	    c->local_maxpacket*3) ||
2356ca32bd8dSchristos 	    c->local_window < c->local_window_max/2) &&
2357ca32bd8dSchristos 	    c->local_consumed > 0) {
2358313c6c94Schristos 		u_int addition = 0;
23597425a2c2Smlelstv 		u_int32_t tcpwinsz = channel_tcpwinsz(ssh);
2360313c6c94Schristos 		/* adjust max window size if we are in a dynamic environment */
23617425a2c2Smlelstv 		if (c->dynamic_window && (tcpwinsz > c->local_window_max)) {
23627a183406Schristos 			/* grow the window somewhat aggressively to maintain
23637a183406Schristos 			 * pressure */
23647425a2c2Smlelstv 			addition = 1.5*(tcpwinsz - c->local_window_max);
2365313c6c94Schristos 			c->local_window_max += addition;
23667425a2c2Smlelstv 			debug("Channel: Window growth to %d by %d bytes", c->local_window_max, addition);
2367313c6c94Schristos 		}
23687425a2c2Smlelstv 		if (!c->have_remote_id)
23697425a2c2Smlelstv 			fatal_f("channel %d: no remote id", c->self);
23707a183406Schristos 		if ((r = sshpkt_start(ssh,
23717a183406Schristos 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
23727a183406Schristos 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
23737a183406Schristos 		    (r = sshpkt_put_u32(ssh, c->local_consumed + addition)) != 0 ||
23747a183406Schristos 		    (r = sshpkt_send(ssh)) != 0) {
237517418e98Schristos 			fatal_fr(r, "channel %i", c->self);
23767a183406Schristos 		}
237717418e98Schristos 		debug2("channel %d: window %d sent adjust %d", c->self,
23787425a2c2Smlelstv 		    c->local_window,
23797425a2c2Smlelstv 		    c->local_consumed + addition);
2380313c6c94Schristos 		c->local_window += c->local_consumed + addition;
2381ca32bd8dSchristos 		c->local_consumed = 0;
2382ca32bd8dSchristos 	}
2383ca32bd8dSchristos 	return 1;
2384ca32bd8dSchristos }
2385ca32bd8dSchristos 
2386ca32bd8dSchristos static void
2387a03ec00cSchristos channel_post_open(struct ssh *ssh, Channel *c)
2388ca32bd8dSchristos {
2389a03ec00cSchristos 	channel_handle_rfd(ssh, c);
2390a03ec00cSchristos 	channel_handle_wfd(ssh, c);
2391a03ec00cSchristos 	channel_handle_efd(ssh, c);
23927a183406Schristos 	channel_check_window(ssh, c);
2393ca32bd8dSchristos }
2394ca32bd8dSchristos 
239534b27b53Sadam static u_int
23967a183406Schristos read_mux(struct ssh *ssh, Channel *c, u_int need)
239734b27b53Sadam {
239834b27b53Sadam 	char buf[CHAN_RBUF];
23997a183406Schristos 	ssize_t len;
240034b27b53Sadam 	u_int rlen;
24017a183406Schristos 	int r;
240234b27b53Sadam 
24037a183406Schristos 	if (sshbuf_len(c->input) < need) {
24047a183406Schristos 		rlen = need - sshbuf_len(c->input);
2405ee85abc4Schristos 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
2406cd4ada6aSchristos 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
24077a183406Schristos 			return sshbuf_len(c->input);
240834b27b53Sadam 		if (len <= 0) {
24097a183406Schristos 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
241034b27b53Sadam 			    c->self, c->rfd, len);
24117a183406Schristos 			chan_read_failed(ssh, c);
241234b27b53Sadam 			return 0;
241317418e98Schristos 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
241417418e98Schristos 			fatal_fr(r, "channel %i: append", c->self);
24157a183406Schristos 	}
24167a183406Schristos 	return sshbuf_len(c->input);
241734b27b53Sadam }
241834b27b53Sadam 
241934b27b53Sadam static void
2420a03ec00cSchristos channel_post_mux_client_read(struct ssh *ssh, Channel *c)
242134b27b53Sadam {
242234b27b53Sadam 	u_int need;
242334b27b53Sadam 
2424a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
24257a183406Schristos 		return;
24267a183406Schristos 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
24277a183406Schristos 		return;
24287a183406Schristos 	if (c->mux_pause)
24297a183406Schristos 		return;
243034b27b53Sadam 
243134b27b53Sadam 	/*
243234b27b53Sadam 	 * Don't not read past the precise end of packets to
243334b27b53Sadam 	 * avoid disrupting fd passing.
243434b27b53Sadam 	 */
24357a183406Schristos 	if (read_mux(ssh, c, 4) < 4) /* read header */
243634b27b53Sadam 		return;
24377a183406Schristos 	/* XXX sshbuf_peek_u32 */
24387a183406Schristos 	need = PEEK_U32(sshbuf_ptr(c->input));
243934b27b53Sadam #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
244034b27b53Sadam 	if (need > CHANNEL_MUX_MAX_PACKET) {
244134b27b53Sadam 		debug2("channel %d: packet too big %u > %u",
244234b27b53Sadam 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
24437a183406Schristos 		chan_rcvd_oclose(ssh, c);
244434b27b53Sadam 		return;
244534b27b53Sadam 	}
24467a183406Schristos 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
244734b27b53Sadam 		return;
24487a183406Schristos 	if (c->mux_rcb(ssh, c) != 0) {
244934b27b53Sadam 		debug("channel %d: mux_rcb failed", c->self);
24507a183406Schristos 		chan_mark_dead(ssh, c);
245134b27b53Sadam 		return;
245234b27b53Sadam 	}
245334b27b53Sadam }
245434b27b53Sadam 
245534b27b53Sadam static void
2456a03ec00cSchristos channel_post_mux_client_write(struct ssh *ssh, Channel *c)
24577a183406Schristos {
24587a183406Schristos 	ssize_t len;
24597a183406Schristos 	int r;
24607a183406Schristos 
2461a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
2462a03ec00cSchristos 		return;
2463a03ec00cSchristos 	if (sshbuf_len(c->output) == 0)
24647a183406Schristos 		return;
24657a183406Schristos 
24667a183406Schristos 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
2467cd4ada6aSchristos 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
24687a183406Schristos 		return;
24697a183406Schristos 	if (len <= 0) {
24707a183406Schristos 		chan_mark_dead(ssh, c);
24717a183406Schristos 		return;
24727a183406Schristos 	}
24737a183406Schristos 	if ((r = sshbuf_consume(c->output, len)) != 0)
247417418e98Schristos 		fatal_fr(r, "channel %i: consume", c->self);
24757a183406Schristos }
24767a183406Schristos 
24777a183406Schristos static void
2478a03ec00cSchristos channel_post_mux_client(struct ssh *ssh, Channel *c)
24797a183406Schristos {
2480a03ec00cSchristos 	channel_post_mux_client_read(ssh, c);
2481a03ec00cSchristos 	channel_post_mux_client_write(ssh, c);
24827a183406Schristos }
24837a183406Schristos 
24847a183406Schristos static void
2485a03ec00cSchristos channel_post_mux_listener(struct ssh *ssh, Channel *c)
248634b27b53Sadam {
248734b27b53Sadam 	Channel *nc;
248834b27b53Sadam 	struct sockaddr_storage addr;
248934b27b53Sadam 	socklen_t addrlen;
249034b27b53Sadam 	int newsock;
249134b27b53Sadam 	uid_t euid;
249234b27b53Sadam 	gid_t egid;
249334b27b53Sadam 
2494a03ec00cSchristos 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
249534b27b53Sadam 		return;
249634b27b53Sadam 
249734b27b53Sadam 	debug("multiplexing control connection");
249834b27b53Sadam 
249934b27b53Sadam 	/*
250034b27b53Sadam 	 * Accept connection on control socket
250134b27b53Sadam 	 */
250234b27b53Sadam 	memset(&addr, 0, sizeof(addr));
250334b27b53Sadam 	addrlen = sizeof(addr);
250434b27b53Sadam 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
250534b27b53Sadam 	    &addrlen)) == -1) {
250617418e98Schristos 		error_f("accept: %s", strerror(errno));
25072649c700Schristos 		if (errno == EMFILE || errno == ENFILE)
250800a838c4Schristos 			c->notbefore = monotime() + 1;
250934b27b53Sadam 		return;
251034b27b53Sadam 	}
251134b27b53Sadam 
2512cd4ada6aSchristos 	if (getpeereid(newsock, &euid, &egid) == -1) {
251317418e98Schristos 		error_f("getpeereid failed: %s", strerror(errno));
251434b27b53Sadam 		close(newsock);
251534b27b53Sadam 		return;
251634b27b53Sadam 	}
251734b27b53Sadam 	if ((euid != 0) && (getuid() != euid)) {
251834b27b53Sadam 		error("multiplex uid mismatch: peer euid %u != uid %u",
251934b27b53Sadam 		    (u_int)euid, (u_int)getuid());
252034b27b53Sadam 		close(newsock);
252134b27b53Sadam 		return;
252234b27b53Sadam 	}
2523b1066cf3Schristos 	nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT,
252434b27b53Sadam 	    newsock, newsock, -1, c->local_window_max,
252534b27b53Sadam 	    c->local_maxpacket, 0, "mux-control", 1);
252634b27b53Sadam 	nc->mux_rcb = c->mux_rcb;
252717418e98Schristos 	debug3_f("new mux channel %d fd %d", nc->self, nc->sock);
252834b27b53Sadam 	/* establish state */
25297a183406Schristos 	nc->mux_rcb(ssh, nc);
253034b27b53Sadam 	/* mux state transitions must not elicit protocol messages */
253134b27b53Sadam 	nc->flags |= CHAN_LOCAL;
253234b27b53Sadam }
253334b27b53Sadam 
2534ca32bd8dSchristos static void
25357a183406Schristos channel_handler_init(struct ssh_channels *sc)
2536ca32bd8dSchristos {
25377a183406Schristos 	chan_fn **pre, **post;
2538ca32bd8dSchristos 
25397a183406Schristos 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
25407a183406Schristos 	    (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
254117418e98Schristos 		fatal_f("allocation failed");
2542ca32bd8dSchristos 
25437a183406Schristos 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
25447a183406Schristos 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
25457a183406Schristos 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
25467a183406Schristos 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
25477a183406Schristos 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
25487a183406Schristos 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
25497a183406Schristos 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
25507a183406Schristos 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
25517a183406Schristos 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
25527a183406Schristos 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
25537a183406Schristos 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
25547a183406Schristos 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
25557a183406Schristos 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2556ca32bd8dSchristos 
25577a183406Schristos 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
25587a183406Schristos 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
25597a183406Schristos 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
25607a183406Schristos 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
25617a183406Schristos 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
25627a183406Schristos 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
25637a183406Schristos 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
25647a183406Schristos 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
25657a183406Schristos 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
25667a183406Schristos 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
25677a183406Schristos 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
25687a183406Schristos 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2569ca32bd8dSchristos 
25707a183406Schristos 	sc->channel_pre = pre;
25717a183406Schristos 	sc->channel_post = post;
2572ca32bd8dSchristos }
2573ca32bd8dSchristos 
2574ca32bd8dSchristos /* gc dead channels */
2575ca32bd8dSchristos static void
25767a183406Schristos channel_garbage_collect(struct ssh *ssh, Channel *c)
2577ca32bd8dSchristos {
2578ca32bd8dSchristos 	if (c == NULL)
2579ca32bd8dSchristos 		return;
2580ca32bd8dSchristos 	if (c->detach_user != NULL) {
25817a183406Schristos 		if (!chan_is_dead(ssh, c, c->detach_close))
2582ca32bd8dSchristos 			return;
2583aa36fcacSchristos 
2584ca32bd8dSchristos 		debug2("channel %d: gc: notify user", c->self);
2585b1066cf3Schristos 		c->detach_user(ssh, c->self, 0, NULL);
2586ca32bd8dSchristos 		/* if we still have a callback */
2587ca32bd8dSchristos 		if (c->detach_user != NULL)
2588ca32bd8dSchristos 			return;
2589ca32bd8dSchristos 		debug2("channel %d: gc: user detached", c->self);
2590ca32bd8dSchristos 	}
25917a183406Schristos 	if (!chan_is_dead(ssh, c, 1))
2592ca32bd8dSchristos 		return;
2593ca32bd8dSchristos 	debug2("channel %d: garbage collecting", c->self);
25947a183406Schristos 	channel_free(ssh, c);
2595ca32bd8dSchristos }
2596ca32bd8dSchristos 
25977a183406Schristos enum channel_table { CHAN_PRE, CHAN_POST };
25987a183406Schristos 
2599ca32bd8dSchristos static void
2600b1066cf3Schristos channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
2601ca32bd8dSchristos {
26027a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
26037a183406Schristos 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
260434b27b53Sadam 	u_int i, oalloc;
2605ca32bd8dSchristos 	Channel *c;
26062649c700Schristos 	time_t now;
2607ca32bd8dSchristos 
260800a838c4Schristos 	now = monotime();
26097a183406Schristos 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26107a183406Schristos 		c = sc->channels[i];
2611ca32bd8dSchristos 		if (c == NULL)
2612ca32bd8dSchristos 			continue;
2613e160b4e8Schristos 		/* Try to keep IO going while rekeying */
2614e160b4e8Schristos 		if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN)
2615e160b4e8Schristos 			continue;
261634b27b53Sadam 		if (c->delayed) {
26177a183406Schristos 			if (table == CHAN_PRE)
261834b27b53Sadam 				c->delayed = 0;
261934b27b53Sadam 			else
262034b27b53Sadam 				continue;
262134b27b53Sadam 		}
26222649c700Schristos 		if (ftab[c->type] != NULL) {
2623c5555919Schristos 			if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN &&
2624c5555919Schristos 			    channel_get_expiry(ssh, c) != 0 &&
2625c5555919Schristos 			    now >= channel_get_expiry(ssh, c)) {
2626b1066cf3Schristos 				/* channel closed for inactivity */
2627b1066cf3Schristos 				verbose("channel %d: closing after %u seconds "
2628b1066cf3Schristos 				    "of inactivity", c->self,
2629b1066cf3Schristos 				    c->inactive_deadline);
2630b1066cf3Schristos 				channel_force_close(ssh, c, 1);
2631b1066cf3Schristos 			} else if (c->notbefore <= now) {
2632b1066cf3Schristos 				/* Run handlers that are not paused. */
2633a03ec00cSchristos 				(*ftab[c->type])(ssh, c);
2634b1066cf3Schristos 				/* inactivity timeouts must interrupt poll() */
2635b1066cf3Schristos 				if (timeout != NULL &&
2636b1066cf3Schristos 				    c->type == SSH_CHANNEL_OPEN &&
2637c5555919Schristos 				    channel_get_expiry(ssh, c) != 0) {
2638b1066cf3Schristos 					ptimeout_deadline_monotime(timeout,
2639c5555919Schristos 					    channel_get_expiry(ssh, c));
2640b1066cf3Schristos 				}
2641b1066cf3Schristos 			} else if (timeout != NULL) {
26422649c700Schristos 				/*
2643b1066cf3Schristos 				 * Arrange for poll() wakeup when channel pause
2644b1066cf3Schristos 				 * timer expires.
26452649c700Schristos 				 */
2646b1066cf3Schristos 				ptimeout_deadline_monotime(timeout,
2647b1066cf3Schristos 				    c->notbefore);
26482649c700Schristos 			}
26492649c700Schristos 		}
26507a183406Schristos 		channel_garbage_collect(ssh, c);
2651ca32bd8dSchristos 	}
2652ca32bd8dSchristos }
2653ca32bd8dSchristos 
2654ca32bd8dSchristos /*
2655a03ec00cSchristos  * Create sockets before preparing IO.
26567a183406Schristos  * This is necessary for things that need to happen after reading
2657a03ec00cSchristos  * the network-input but need to be completed before IO event setup, e.g.
2658a03ec00cSchristos  * because they may create new channels.
26597a183406Schristos  */
26607a183406Schristos static void
2661a03ec00cSchristos channel_before_prepare_io(struct ssh *ssh)
26627a183406Schristos {
26637a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
26647a183406Schristos 	Channel *c;
26657a183406Schristos 	u_int i, oalloc;
26667a183406Schristos 
26677a183406Schristos 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26687a183406Schristos 		c = sc->channels[i];
26697a183406Schristos 		if (c == NULL)
26707a183406Schristos 			continue;
26717a183406Schristos 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
2672a03ec00cSchristos 			channel_before_prepare_io_rdynamic(ssh, c);
26737a183406Schristos 	}
26747a183406Schristos }
26757a183406Schristos 
2676a03ec00cSchristos static void
2677a03ec00cSchristos dump_channel_poll(const char *func, const char *what, Channel *c,
2678a03ec00cSchristos     u_int pollfd_offset, struct pollfd *pfd)
2679ca32bd8dSchristos {
2680a03ec00cSchristos #ifdef DEBUG_CHANNEL_POLL
2681c4271af5Schristos 	debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] "
2682c4271af5Schristos 	    "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d "
2683c4271af5Schristos 	    "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what,
2684c4271af5Schristos 	    c->rfd, c->wfd, c->efd, c->sock,
2685c4271af5Schristos 	    c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3],
2686c4271af5Schristos 	    c->io_want, c->io_ready,
2687c4271af5Schristos 	    pollfd_offset, pfd->fd, pfd->events, pfd->revents);
2688a03ec00cSchristos #endif
2689ca32bd8dSchristos }
2690ca32bd8dSchristos 
2691a03ec00cSchristos /* Prepare pollfd entries for a single channel */
2692a03ec00cSchristos static void
2693a03ec00cSchristos channel_prepare_pollfd(Channel *c, u_int *next_pollfd,
2694a03ec00cSchristos     struct pollfd *pfd, u_int npfd)
2695a03ec00cSchristos {
2696c4271af5Schristos 	u_int ev, p = *next_pollfd;
2697a03ec00cSchristos 
2698a03ec00cSchristos 	if (c == NULL)
2699a03ec00cSchristos 		return;
2700a03ec00cSchristos 	if (p + 4 > npfd) {
2701a03ec00cSchristos 		/* Shouldn't happen */
2702a03ec00cSchristos 		fatal_f("channel %d: bad pfd offset %u (max %u)",
2703a03ec00cSchristos 		    c->self, p, npfd);
2704a03ec00cSchristos 	}
2705c4271af5Schristos 	c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1;
2706a03ec00cSchristos 	/*
2707a03ec00cSchristos 	 * prepare c->rfd
2708a03ec00cSchristos 	 *
2709a03ec00cSchristos 	 * This is a special case, since c->rfd might be the same as
2710a03ec00cSchristos 	 * c->wfd, c->efd and/or c->sock. Handle those here if they want
2711a03ec00cSchristos 	 * IO too.
2712a03ec00cSchristos 	 */
2713a03ec00cSchristos 	if (c->rfd != -1) {
2714c4271af5Schristos 		ev = 0;
2715a03ec00cSchristos 		if ((c->io_want & SSH_CHAN_IO_RFD) != 0)
2716c4271af5Schristos 			ev |= POLLIN;
2717a03ec00cSchristos 		/* rfd == wfd */
2718c4271af5Schristos 		if (c->wfd == c->rfd) {
2719c4271af5Schristos 			if ((c->io_want & SSH_CHAN_IO_WFD) != 0)
2720c4271af5Schristos 				ev |= POLLOUT;
2721c4271af5Schristos 		}
2722a03ec00cSchristos 		/* rfd == efd */
2723c4271af5Schristos 		if (c->efd == c->rfd) {
2724c4271af5Schristos 			if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
2725c4271af5Schristos 				ev |= POLLIN;
2726c4271af5Schristos 			if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
2727c4271af5Schristos 				ev |= POLLOUT;
2728c4271af5Schristos 		}
2729a03ec00cSchristos 		/* rfd == sock */
2730c4271af5Schristos 		if (c->sock == c->rfd) {
2731c4271af5Schristos 			if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
2732c4271af5Schristos 				ev |= POLLIN;
2733c4271af5Schristos 			if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
2734c4271af5Schristos 				ev |= POLLOUT;
2735c4271af5Schristos 		}
2736c4271af5Schristos 		/* Pack a pfd entry if any event armed for this fd */
2737c4271af5Schristos 		if (ev != 0) {
2738c4271af5Schristos 			c->pfds[0] = p;
2739c4271af5Schristos 			pfd[p].fd = c->rfd;
2740c4271af5Schristos 			pfd[p].events = ev;
2741a03ec00cSchristos 			dump_channel_poll(__func__, "rfd", c, p, &pfd[p]);
2742a03ec00cSchristos 			p++;
2743a03ec00cSchristos 		}
2744c4271af5Schristos 	}
2745c4271af5Schristos 	/* prepare c->wfd if wanting IO and not already handled above */
2746a03ec00cSchristos 	if (c->wfd != -1 && c->rfd != c->wfd) {
2747c4271af5Schristos 		ev = 0;
2748c4271af5Schristos 		if ((c->io_want & SSH_CHAN_IO_WFD))
2749c4271af5Schristos 			ev |= POLLOUT;
2750c4271af5Schristos 		/* Pack a pfd entry if any event armed for this fd */
2751c4271af5Schristos 		if (ev != 0) {
2752c4271af5Schristos 			c->pfds[1] = p;
2753a03ec00cSchristos 			pfd[p].fd = c->wfd;
2754c4271af5Schristos 			pfd[p].events = ev;
2755a03ec00cSchristos 			dump_channel_poll(__func__, "wfd", c, p, &pfd[p]);
2756a03ec00cSchristos 			p++;
2757a03ec00cSchristos 		}
2758c4271af5Schristos 	}
2759c4271af5Schristos 	/* prepare c->efd if wanting IO and not already handled above */
2760a03ec00cSchristos 	if (c->efd != -1 && c->rfd != c->efd) {
2761c4271af5Schristos 		ev = 0;
2762a03ec00cSchristos 		if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
2763c4271af5Schristos 			ev |= POLLIN;
2764a03ec00cSchristos 		if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
2765c4271af5Schristos 			ev |= POLLOUT;
2766c4271af5Schristos 		/* Pack a pfd entry if any event armed for this fd */
2767c4271af5Schristos 		if (ev != 0) {
2768c4271af5Schristos 			c->pfds[2] = p;
2769c4271af5Schristos 			pfd[p].fd = c->efd;
2770c4271af5Schristos 			pfd[p].events = ev;
2771a03ec00cSchristos 			dump_channel_poll(__func__, "efd", c, p, &pfd[p]);
2772a03ec00cSchristos 			p++;
2773a03ec00cSchristos 		}
2774c4271af5Schristos 	}
2775c4271af5Schristos 	/* prepare c->sock if wanting IO and not already handled above */
2776a03ec00cSchristos 	if (c->sock != -1 && c->rfd != c->sock) {
2777c4271af5Schristos 		ev = 0;
2778c4271af5Schristos 		if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
2779c4271af5Schristos 			ev |= POLLIN;
2780c4271af5Schristos 		if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
2781c4271af5Schristos 			ev |= POLLOUT;
2782c4271af5Schristos 		/* Pack a pfd entry if any event armed for this fd */
2783c4271af5Schristos 		if (ev != 0) {
2784c4271af5Schristos 			c->pfds[3] = p;
2785a03ec00cSchristos 			pfd[p].fd = c->sock;
2786a03ec00cSchristos 			pfd[p].events = 0;
2787a03ec00cSchristos 			dump_channel_poll(__func__, "sock", c, p, &pfd[p]);
2788a03ec00cSchristos 			p++;
2789a03ec00cSchristos 		}
2790c4271af5Schristos 	}
2791a03ec00cSchristos 	*next_pollfd = p;
2792a03ec00cSchristos }
2793a03ec00cSchristos 
2794a03ec00cSchristos /* * Allocate/prepare poll structure */
2795a03ec00cSchristos void
2796a03ec00cSchristos channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
2797b1066cf3Schristos     u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout)
2798a03ec00cSchristos {
2799a03ec00cSchristos 	struct ssh_channels *sc = ssh->chanctxt;
2800a03ec00cSchristos 	u_int i, oalloc, p, npfd = npfd_reserved;
2801a03ec00cSchristos 
2802a03ec00cSchristos 	channel_before_prepare_io(ssh); /* might create a new channel */
2803e160b4e8Schristos 	/* clear out I/O flags from last poll */
2804e160b4e8Schristos 	for (i = 0; i < sc->channels_alloc; i++) {
2805e160b4e8Schristos 		if (sc->channels[i] == NULL)
2806e160b4e8Schristos 			continue;
2807e160b4e8Schristos 		sc->channels[i]->io_want = sc->channels[i]->io_ready = 0;
2808e160b4e8Schristos 	}
2809a03ec00cSchristos 	/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */
2810a03ec00cSchristos 	if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved)
2811a03ec00cSchristos 		fatal_f("too many channels"); /* shouldn't happen */
2812a03ec00cSchristos 	npfd += sc->channels_alloc * 4;
2813a03ec00cSchristos 	if (npfd > *npfd_allocp) {
2814a03ec00cSchristos 		*pfdp = xrecallocarray(*pfdp, *npfd_allocp,
2815a03ec00cSchristos 		    npfd, sizeof(**pfdp));
2816a03ec00cSchristos 		*npfd_allocp = npfd;
2817a03ec00cSchristos 	}
2818a03ec00cSchristos 	*npfd_activep = npfd_reserved;
2819a03ec00cSchristos 	oalloc = sc->channels_alloc;
2820a03ec00cSchristos 
2821b1066cf3Schristos 	channel_handler(ssh, CHAN_PRE, timeout);
2822a03ec00cSchristos 
2823a03ec00cSchristos 	if (oalloc != sc->channels_alloc) {
2824a03ec00cSchristos 		/* shouldn't happen */
2825a03ec00cSchristos 		fatal_f("channels_alloc changed during CHAN_PRE "
2826a03ec00cSchristos 		    "(was %u, now %u)", oalloc, sc->channels_alloc);
2827a03ec00cSchristos 	}
2828a03ec00cSchristos 
2829a03ec00cSchristos 	/* Prepare pollfd */
2830a03ec00cSchristos 	p = npfd_reserved;
2831a03ec00cSchristos 	for (i = 0; i < sc->channels_alloc; i++)
2832a03ec00cSchristos 		channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
2833a03ec00cSchristos 	*npfd_activep = p;
2834a03ec00cSchristos }
2835a03ec00cSchristos 
2836a03ec00cSchristos static void
2837c4271af5Schristos fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd,
2838a03ec00cSchristos     const char *what, u_int revents_mask, u_int ready)
2839a03ec00cSchristos {
2840a03ec00cSchristos 	struct pollfd *pfd = &pfds[p];
2841a03ec00cSchristos 
2842a03ec00cSchristos 	if (fd == -1)
2843a03ec00cSchristos 		return;
2844c4271af5Schristos 	if (p == -1 || (u_int)p >= npfd)
2845c4271af5Schristos 		fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd);
2846a03ec00cSchristos 	dump_channel_poll(__func__, what, c, p, pfd);
2847a03ec00cSchristos 	if (pfd->fd != fd) {
2848a03ec00cSchristos 		fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
2849a03ec00cSchristos 		    "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
2850a03ec00cSchristos 		    c->rfd, c->wfd, c->efd, c->sock);
2851a03ec00cSchristos 	}
2852a03ec00cSchristos 	if ((pfd->revents & POLLNVAL) != 0) {
2853a03ec00cSchristos 		fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d",
2854a03ec00cSchristos 		    c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock);
2855a03ec00cSchristos 	}
2856a03ec00cSchristos 	if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0)
2857a03ec00cSchristos 		c->io_ready |= ready & c->io_want;
2858ca32bd8dSchristos }
2859ca32bd8dSchristos 
2860ca32bd8dSchristos /*
2861a03ec00cSchristos  * After poll, perform any appropriate operations for channels which have
2862ca32bd8dSchristos  * events pending.
2863ca32bd8dSchristos  */
2864ca32bd8dSchristos void
2865a03ec00cSchristos channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
2866ca32bd8dSchristos {
2867a03ec00cSchristos 	struct ssh_channels *sc = ssh->chanctxt;
2868c4271af5Schristos 	u_int i;
2869c4271af5Schristos 	int p;
2870a03ec00cSchristos 	Channel *c;
2871a03ec00cSchristos 
2872a03ec00cSchristos #ifdef DEBUG_CHANNEL_POLL
2873c4271af5Schristos 	for (p = 0; p < (int)npfd; p++) {
2874a03ec00cSchristos 		if (pfd[p].revents == 0)
2875a03ec00cSchristos 			continue;
2876a03ec00cSchristos 		debug_f("pfd[%u].fd %d rev 0x%04x",
2877a03ec00cSchristos 		    p, pfd[p].fd, pfd[p].revents);
2878a03ec00cSchristos 	}
2879a03ec00cSchristos #endif
2880a03ec00cSchristos 
2881a03ec00cSchristos 	/* Convert pollfd into c->io_ready */
2882a03ec00cSchristos 	for (i = 0; i < sc->channels_alloc; i++) {
2883a03ec00cSchristos 		c = sc->channels[i];
2884c4271af5Schristos 		if (c == NULL)
2885a03ec00cSchristos 			continue;
2886a03ec00cSchristos 		/* if rfd is shared with efd/sock then wfd should be too */
2887a03ec00cSchristos 		if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd &&
2888a03ec00cSchristos 		    (c->rfd == c->efd || c->rfd == c->sock)) {
2889a03ec00cSchristos 			/* Shouldn't happen */
2890a03ec00cSchristos 			fatal_f("channel %d: unexpected fds r%d w%d e%d s%d",
2891a03ec00cSchristos 			    c->self, c->rfd, c->wfd, c->efd, c->sock);
2892a03ec00cSchristos 		}
2893a03ec00cSchristos 		c->io_ready = 0;
2894a03ec00cSchristos 		/* rfd, potentially shared with wfd, efd and sock */
2895c4271af5Schristos 		if (c->rfd != -1 && (p = c->pfds[0]) != -1) {
2896c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->rfd,
2897c4271af5Schristos 			    "rfd", POLLIN, SSH_CHAN_IO_RFD);
2898a03ec00cSchristos 			if (c->rfd == c->wfd) {
2899c4271af5Schristos 				fd_ready(c, p, pfd, npfd, c->wfd,
2900c4271af5Schristos 				    "wfd/r", POLLOUT, SSH_CHAN_IO_WFD);
2901a03ec00cSchristos 			}
2902a03ec00cSchristos 			if (c->rfd == c->efd) {
2903c4271af5Schristos 				fd_ready(c, p, pfd, npfd, c->efd,
2904c4271af5Schristos 				    "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R);
2905c4271af5Schristos 				fd_ready(c, p, pfd, npfd, c->efd,
2906c4271af5Schristos 				    "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W);
2907a03ec00cSchristos 			}
2908a03ec00cSchristos 			if (c->rfd == c->sock) {
2909c4271af5Schristos 				fd_ready(c, p, pfd, npfd, c->sock,
2910c4271af5Schristos 				    "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R);
2911c4271af5Schristos 				fd_ready(c, p, pfd, npfd, c->sock,
2912c4271af5Schristos 				    "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W);
2913a03ec00cSchristos 			}
2914c4271af5Schristos 			dump_channel_poll(__func__, "rfd", c, p, pfd);
2915a03ec00cSchristos 		}
2916a03ec00cSchristos 		/* wfd */
2917c4271af5Schristos 		if (c->wfd != -1 && c->wfd != c->rfd &&
2918c4271af5Schristos 		    (p = c->pfds[1]) != -1) {
2919c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->wfd,
2920c4271af5Schristos 			    "wfd", POLLOUT, SSH_CHAN_IO_WFD);
2921c4271af5Schristos 			dump_channel_poll(__func__, "wfd", c, p, pfd);
2922a03ec00cSchristos 		}
2923a03ec00cSchristos 		/* efd */
2924c4271af5Schristos 		if (c->efd != -1 && c->efd != c->rfd &&
2925c4271af5Schristos 		    (p = c->pfds[2]) != -1) {
2926c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->efd,
2927c4271af5Schristos 			    "efdr", POLLIN, SSH_CHAN_IO_EFD_R);
2928c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->efd,
2929c4271af5Schristos 			    "efdw", POLLOUT, SSH_CHAN_IO_EFD_W);
2930c4271af5Schristos 			dump_channel_poll(__func__, "efd", c, p, pfd);
2931a03ec00cSchristos 		}
2932a03ec00cSchristos 		/* sock */
2933c4271af5Schristos 		if (c->sock != -1 && c->sock != c->rfd &&
2934c4271af5Schristos 		    (p = c->pfds[3]) != -1) {
2935c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->sock,
2936c4271af5Schristos 			    "sockr", POLLIN, SSH_CHAN_IO_SOCK_R);
2937c4271af5Schristos 			fd_ready(c, p, pfd, npfd, c->sock,
2938c4271af5Schristos 			    "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W);
2939c4271af5Schristos 			dump_channel_poll(__func__, "sock", c, p, pfd);
2940a03ec00cSchristos 		}
2941a03ec00cSchristos 	}
2942a03ec00cSchristos 	channel_handler(ssh, CHAN_POST, NULL);
2943ca32bd8dSchristos }
2944ca32bd8dSchristos 
29457a183406Schristos /*
29467a183406Schristos  * Enqueue data for channels with open or draining c->input.
2947a629fefcSchristos  * Returns non-zero if a packet was enqueued.
29487a183406Schristos  */
2949a629fefcSchristos static int
29507a183406Schristos channel_output_poll_input_open(struct ssh *ssh, Channel *c)
29517a183406Schristos {
29527a183406Schristos 	size_t len, plen;
29537a183406Schristos 	const u_char *pkt;
29547a183406Schristos 	int r;
29557a183406Schristos 
29567a183406Schristos 	if ((len = sshbuf_len(c->input)) == 0) {
29577a183406Schristos 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
29587a183406Schristos 			/*
29597a183406Schristos 			 * input-buffer is empty and read-socket shutdown:
29607a183406Schristos 			 * tell peer, that we will not send more data:
29617a183406Schristos 			 * send IEOF.
29627a183406Schristos 			 * hack for extended data: delay EOF if EFD still
29637a183406Schristos 			 * in use.
29647a183406Schristos 			 */
29657a183406Schristos 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
29667a183406Schristos 				debug2("channel %d: "
29677a183406Schristos 				    "ibuf_empty delayed efd %d/(%zu)",
29687a183406Schristos 				    c->self, c->efd, sshbuf_len(c->extended));
29697a183406Schristos 			else
29707a183406Schristos 				chan_ibuf_empty(ssh, c);
29717a183406Schristos 		}
2972a629fefcSchristos 		return 0;
29737a183406Schristos 	}
29747a183406Schristos 
29757a183406Schristos 	if (!c->have_remote_id)
297617418e98Schristos 		fatal_f("channel %d: no remote id", c->self);
29777a183406Schristos 
29787a183406Schristos 	if (c->datagram) {
29797a183406Schristos 		/* Check datagram will fit; drop if not */
29807a183406Schristos 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
298117418e98Schristos 			fatal_fr(r, "channel %i: get datagram", c->self);
29827a183406Schristos 		/*
29837a183406Schristos 		 * XXX this does tail-drop on the datagram queue which is
29847a183406Schristos 		 * usually suboptimal compared to head-drop. Better to have
29857a183406Schristos 		 * backpressure at read time? (i.e. read + discard)
29867a183406Schristos 		 */
29877a183406Schristos 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
29887a183406Schristos 			debug("channel %d: datagram too big", c->self);
2989a629fefcSchristos 			return 0;
29907a183406Schristos 		}
29917a183406Schristos 		/* Enqueue it */
29927a183406Schristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
29937a183406Schristos 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29947a183406Schristos 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
299517418e98Schristos 		    (r = sshpkt_send(ssh)) != 0)
299617418e98Schristos 			fatal_fr(r, "channel %i: send datagram", c->self);
29977a183406Schristos 		c->remote_window -= plen;
2998a629fefcSchristos 		return 1;
29997a183406Schristos 	}
30007a183406Schristos 
30017a183406Schristos 	/* Enqueue packet for buffered data. */
30027a183406Schristos 	if (len > c->remote_window)
30037a183406Schristos 		len = c->remote_window;
30047a183406Schristos 	if (len > c->remote_maxpacket)
30057a183406Schristos 		len = c->remote_maxpacket;
30067a183406Schristos 	if (len == 0)
3007a629fefcSchristos 		return 0;
30087a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
30097a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30107a183406Schristos 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
301117418e98Schristos 	    (r = sshpkt_send(ssh)) != 0)
301217418e98Schristos 		fatal_fr(r, "channel %i: send data", c->self);
30137a183406Schristos 	if ((r = sshbuf_consume(c->input, len)) != 0)
301417418e98Schristos 		fatal_fr(r, "channel %i: consume", c->self);
30157a183406Schristos 	c->remote_window -= len;
3016a629fefcSchristos 	return 1;
30177a183406Schristos }
30187a183406Schristos 
30197a183406Schristos /*
30207a183406Schristos  * Enqueue data for channels with open c->extended in read mode.
3021a629fefcSchristos  * Returns non-zero if a packet was enqueued.
30227a183406Schristos  */
3023a629fefcSchristos static int
30247a183406Schristos channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
30257a183406Schristos {
30267a183406Schristos 	size_t len;
30277a183406Schristos 	int r;
30287a183406Schristos 
30297a183406Schristos 	if ((len = sshbuf_len(c->extended)) == 0)
3030a629fefcSchristos 		return 0;
30317a183406Schristos 
30327a183406Schristos 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
30337a183406Schristos 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
30347a183406Schristos 	if (len > c->remote_window)
30357a183406Schristos 		len = c->remote_window;
30367a183406Schristos 	if (len > c->remote_maxpacket)
30377a183406Schristos 		len = c->remote_maxpacket;
30387a183406Schristos 	if (len == 0)
3039a629fefcSchristos 		return 0;
30407a183406Schristos 	if (!c->have_remote_id)
304117418e98Schristos 		fatal_f("channel %d: no remote id", c->self);
30427a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
30437a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30447a183406Schristos 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
30457a183406Schristos 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
304617418e98Schristos 	    (r = sshpkt_send(ssh)) != 0)
304717418e98Schristos 		fatal_fr(r, "channel %i: data", c->self);
30487a183406Schristos 	if ((r = sshbuf_consume(c->extended, len)) != 0)
304917418e98Schristos 		fatal_fr(r, "channel %i: consume", c->self);
30507a183406Schristos 	c->remote_window -= len;
30517a183406Schristos 	debug2("channel %d: sent ext data %zu", c->self, len);
3052a629fefcSchristos 	return 1;
30537a183406Schristos }
3054ca32bd8dSchristos 
3055a629fefcSchristos /*
3056a629fefcSchristos  * If there is data to send to the connection, enqueue some of it now.
3057a629fefcSchristos  * Returns non-zero if data was enqueued.
3058a629fefcSchristos  */
3059a629fefcSchristos int
30607a183406Schristos channel_output_poll(struct ssh *ssh)
3061ca32bd8dSchristos {
30627a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
3063ca32bd8dSchristos 	Channel *c;
30647a183406Schristos 	u_int i;
3065a629fefcSchristos 	int ret = 0;
3066ca32bd8dSchristos 
30677a183406Schristos 	for (i = 0; i < sc->channels_alloc; i++) {
30687a183406Schristos 		c = sc->channels[i];
3069ca32bd8dSchristos 		if (c == NULL)
3070ca32bd8dSchristos 			continue;
3071ca32bd8dSchristos 
3072ca32bd8dSchristos 		/*
3073ca32bd8dSchristos 		 * We are only interested in channels that can have buffered
3074ca32bd8dSchristos 		 * incoming data.
3075ca32bd8dSchristos 		 */
3076ca32bd8dSchristos 		if (c->type != SSH_CHANNEL_OPEN)
3077ca32bd8dSchristos 			continue;
30787a183406Schristos 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
3079ca32bd8dSchristos 			/* XXX is this true? */
30807a183406Schristos 			debug3("channel %d: will not send data after close",
30817a183406Schristos 			    c->self);
3082ca32bd8dSchristos 			continue;
3083ca32bd8dSchristos 		}
3084ca32bd8dSchristos 
3085ca32bd8dSchristos 		/* Get the amount of buffered data for this channel. */
30867a183406Schristos 		if (c->istate == CHAN_INPUT_OPEN ||
30877a183406Schristos 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
3088a629fefcSchristos 			ret |= channel_output_poll_input_open(ssh, c);
3089ca32bd8dSchristos 		/* Send extended data, i.e. stderr */
30907a183406Schristos 		if (!(c->flags & CHAN_EOF_SENT) &&
30917a183406Schristos 		    c->extended_usage == CHAN_EXTENDED_READ)
3092a629fefcSchristos 			ret |= channel_output_poll_extended_read(ssh, c);
3093ca32bd8dSchristos 	}
3094a629fefcSchristos 	return ret;
3095ca32bd8dSchristos }
3096ca32bd8dSchristos 
3097ee85abc4Schristos /* -- mux proxy support  */
3098ee85abc4Schristos 
3099ee85abc4Schristos /*
3100ee85abc4Schristos  * When multiplexing channel messages for mux clients we have to deal
3101ee85abc4Schristos  * with downstream messages from the mux client and upstream messages
3102ee85abc4Schristos  * from the ssh server:
3103ee85abc4Schristos  * 1) Handling downstream messages is straightforward and happens
3104ee85abc4Schristos  *    in channel_proxy_downstream():
3105ee85abc4Schristos  *    - We forward all messages (mostly) unmodified to the server.
3106ee85abc4Schristos  *    - However, in order to route messages from upstream to the correct
3107ee85abc4Schristos  *      downstream client, we have to replace the channel IDs used by the
3108ee85abc4Schristos  *      mux clients with a unique channel ID because the mux clients might
3109ee85abc4Schristos  *      use conflicting channel IDs.
3110ee85abc4Schristos  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
3111ee85abc4Schristos  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
3112ee85abc4Schristos  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
3113ee85abc4Schristos  *      with the newly allocated channel ID.
3114ee85abc4Schristos  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
311555a4608bSchristos  *    channels and processed by channel_proxy_upstream(). The local channel ID
3116ee85abc4Schristos  *    is then translated back to the original mux client ID.
3117ee85abc4Schristos  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
3118ee85abc4Schristos  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
3119ee85abc4Schristos  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
3120ee85abc4Schristos  *    downstream mux client are removed.
3121ee85abc4Schristos  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
3122ee85abc4Schristos  *    requires more work, because they are not addressed to a specific
3123ee85abc4Schristos  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
3124ee85abc4Schristos  *    out whether the request is addressed to the local client or a
3125ee85abc4Schristos  *    specific downstream client based on the listen-address/port.
312655a4608bSchristos  * 6) Agent and X11-Forwarding have a similar problem and are currently
3127ee85abc4Schristos  *    not supported as the matching session/channel cannot be identified
3128ee85abc4Schristos  *    easily.
3129ee85abc4Schristos  */
3130ee85abc4Schristos 
3131ee85abc4Schristos /*
3132ee85abc4Schristos  * receive packets from downstream mux clients:
3133ee85abc4Schristos  * channel callback fired on read from mux client, creates
3134ee85abc4Schristos  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
3135ee85abc4Schristos  * on channel creation.
3136ee85abc4Schristos  */
3137ee85abc4Schristos int
31387a183406Schristos channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
3139ee85abc4Schristos {
3140ee85abc4Schristos 	Channel *c = NULL;
3141ee85abc4Schristos 	struct sshbuf *original = NULL, *modified = NULL;
3142ee85abc4Schristos 	const u_char *cp;
3143ee85abc4Schristos 	char *ctype = NULL, *listen_host = NULL;
3144ee85abc4Schristos 	u_char type;
3145ee85abc4Schristos 	size_t have;
31467a183406Schristos 	int ret = -1, r;
3147ee85abc4Schristos 	u_int id, remote_id, listen_port;
3148ee85abc4Schristos 
31497a183406Schristos 	/* sshbuf_dump(downstream->input, stderr); */
31507a183406Schristos 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
3151ee85abc4Schristos 	    != 0) {
315217418e98Schristos 		error_fr(r, "parse");
3153ee85abc4Schristos 		return -1;
3154ee85abc4Schristos 	}
3155ee85abc4Schristos 	if (have < 2) {
315617418e98Schristos 		error_f("short message");
3157ee85abc4Schristos 		return -1;
3158ee85abc4Schristos 	}
3159ee85abc4Schristos 	type = cp[1];
3160ee85abc4Schristos 	/* skip padlen + type */
3161ee85abc4Schristos 	cp += 2;
3162ee85abc4Schristos 	have -= 2;
3163ee85abc4Schristos 	if (ssh_packet_log_type(type))
316417418e98Schristos 		debug3_f("channel %u: down->up: type %u",
3165ee85abc4Schristos 		    downstream->self, type);
3166ee85abc4Schristos 
3167ee85abc4Schristos 	switch (type) {
3168ee85abc4Schristos 	case SSH2_MSG_CHANNEL_OPEN:
3169ee85abc4Schristos 		if ((original = sshbuf_from(cp, have)) == NULL ||
3170ee85abc4Schristos 		    (modified = sshbuf_new()) == NULL) {
317117418e98Schristos 			error_f("alloc");
3172ee85abc4Schristos 			goto out;
3173ee85abc4Schristos 		}
3174ee85abc4Schristos 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
3175ee85abc4Schristos 		    (r = sshbuf_get_u32(original, &id)) != 0) {
317617418e98Schristos 			error_fr(r, "parse");
3177ee85abc4Schristos 			goto out;
3178ee85abc4Schristos 		}
3179b1066cf3Schristos 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3180ee85abc4Schristos 		    -1, -1, -1, 0, 0, 0, ctype, 1);
3181ee85abc4Schristos 		c->mux_ctx = downstream;	/* point to mux client */
3182ee85abc4Schristos 		c->mux_downstream_id = id;	/* original downstream id */
3183ee85abc4Schristos 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
3184ee85abc4Schristos 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3185ee85abc4Schristos 		    (r = sshbuf_putb(modified, original)) != 0) {
318617418e98Schristos 			error_fr(r, "compose");
31877a183406Schristos 			channel_free(ssh, c);
3188ee85abc4Schristos 			goto out;
3189ee85abc4Schristos 		}
3190ee85abc4Schristos 		break;
3191ee85abc4Schristos 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3192ee85abc4Schristos 		/*
3193ee85abc4Schristos 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
3194ee85abc4Schristos 		 * need to parse 'remote_id' instead of 'ctype'.
3195ee85abc4Schristos 		 */
3196ee85abc4Schristos 		if ((original = sshbuf_from(cp, have)) == NULL ||
3197ee85abc4Schristos 		    (modified = sshbuf_new()) == NULL) {
319817418e98Schristos 			error_f("alloc");
3199ee85abc4Schristos 			goto out;
3200ee85abc4Schristos 		}
3201ee85abc4Schristos 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
3202ee85abc4Schristos 		    (r = sshbuf_get_u32(original, &id)) != 0) {
320317418e98Schristos 			error_fr(r, "parse");
3204ee85abc4Schristos 			goto out;
3205ee85abc4Schristos 		}
3206b1066cf3Schristos 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3207ee85abc4Schristos 		    -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
3208ee85abc4Schristos 		c->mux_ctx = downstream;	/* point to mux client */
3209ee85abc4Schristos 		c->mux_downstream_id = id;
3210ee85abc4Schristos 		c->remote_id = remote_id;
32117a183406Schristos 		c->have_remote_id = 1;
3212ee85abc4Schristos 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
3213ee85abc4Schristos 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3214ee85abc4Schristos 		    (r = sshbuf_putb(modified, original)) != 0) {
321517418e98Schristos 			error_fr(r, "compose");
32167a183406Schristos 			channel_free(ssh, c);
3217ee85abc4Schristos 			goto out;
3218ee85abc4Schristos 		}
3219ee85abc4Schristos 		break;
3220ee85abc4Schristos 	case SSH2_MSG_GLOBAL_REQUEST:
3221ee85abc4Schristos 		if ((original = sshbuf_from(cp, have)) == NULL) {
322217418e98Schristos 			error_f("alloc");
3223ee85abc4Schristos 			goto out;
3224ee85abc4Schristos 		}
3225ee85abc4Schristos 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
322617418e98Schristos 			error_fr(r, "parse");
3227ee85abc4Schristos 			goto out;
3228ee85abc4Schristos 		}
3229ee85abc4Schristos 		if (strcmp(ctype, "tcpip-forward") != 0) {
323017418e98Schristos 			error_f("unsupported request %s", ctype);
3231ee85abc4Schristos 			goto out;
3232ee85abc4Schristos 		}
3233ee85abc4Schristos 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
3234ee85abc4Schristos 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
3235ee85abc4Schristos 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
323617418e98Schristos 			error_fr(r, "parse");
3237ee85abc4Schristos 			goto out;
3238ee85abc4Schristos 		}
3239ee85abc4Schristos 		if (listen_port > 65535) {
324017418e98Schristos 			error_f("tcpip-forward for %s: bad port %u",
324117418e98Schristos 			    listen_host, listen_port);
3242ee85abc4Schristos 			goto out;
3243ee85abc4Schristos 		}
3244ee85abc4Schristos 		/* Record that connection to this host/port is permitted. */
3245c5555919Schristos 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>",
3246c5555919Schristos 		    -1, listen_host, NULL, (int)listen_port, downstream);
3247ee85abc4Schristos 		break;
3248ee85abc4Schristos 	case SSH2_MSG_CHANNEL_CLOSE:
3249ee85abc4Schristos 		if (have < 4)
3250ee85abc4Schristos 			break;
3251ee85abc4Schristos 		remote_id = PEEK_U32(cp);
32527a183406Schristos 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
3253ee85abc4Schristos 			if (c->flags & CHAN_CLOSE_RCVD)
32547a183406Schristos 				channel_free(ssh, c);
3255ee85abc4Schristos 			else
3256ee85abc4Schristos 				c->flags |= CHAN_CLOSE_SENT;
3257ee85abc4Schristos 		}
3258ee85abc4Schristos 		break;
3259ee85abc4Schristos 	}
3260ee85abc4Schristos 	if (modified) {
3261ee85abc4Schristos 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3262ee85abc4Schristos 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
3263ee85abc4Schristos 		    (r = sshpkt_send(ssh)) != 0) {
326417418e98Schristos 			error_fr(r, "send");
3265ee85abc4Schristos 			goto out;
3266ee85abc4Schristos 		}
3267ee85abc4Schristos 	} else {
3268ee85abc4Schristos 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3269ee85abc4Schristos 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
3270ee85abc4Schristos 		    (r = sshpkt_send(ssh)) != 0) {
327117418e98Schristos 			error_fr(r, "send");
3272ee85abc4Schristos 			goto out;
3273ee85abc4Schristos 		}
3274ee85abc4Schristos 	}
3275ee85abc4Schristos 	ret = 0;
3276ee85abc4Schristos  out:
3277ee85abc4Schristos 	free(ctype);
3278ee85abc4Schristos 	free(listen_host);
3279ee85abc4Schristos 	sshbuf_free(original);
3280ee85abc4Schristos 	sshbuf_free(modified);
3281ee85abc4Schristos 	return ret;
3282ee85abc4Schristos }
3283ee85abc4Schristos 
3284ee85abc4Schristos /*
3285ee85abc4Schristos  * receive packets from upstream server and de-multiplex packets
3286ee85abc4Schristos  * to correct downstream:
3287ee85abc4Schristos  * implemented as a helper for channel input handlers,
3288ee85abc4Schristos  * replaces local (proxy) channel ID with downstream channel ID.
3289ee85abc4Schristos  */
3290ee85abc4Schristos int
32917a183406Schristos channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
3292ee85abc4Schristos {
3293ee85abc4Schristos 	struct sshbuf *b = NULL;
3294ee85abc4Schristos 	Channel *downstream;
3295ee85abc4Schristos 	const u_char *cp = NULL;
3296ee85abc4Schristos 	size_t len;
3297ee85abc4Schristos 	int r;
3298ee85abc4Schristos 
3299ee85abc4Schristos 	/*
3300ee85abc4Schristos 	 * When receiving packets from the peer we need to check whether we
3301ee85abc4Schristos 	 * need to forward the packets to the mux client. In this case we
330255a4608bSchristos 	 * restore the original channel id and keep track of CLOSE messages,
3303ee85abc4Schristos 	 * so we can cleanup the channel.
3304ee85abc4Schristos 	 */
3305ee85abc4Schristos 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
3306ee85abc4Schristos 		return 0;
3307ee85abc4Schristos 	if ((downstream = c->mux_ctx) == NULL)
3308ee85abc4Schristos 		return 0;
3309ee85abc4Schristos 	switch (type) {
3310ee85abc4Schristos 	case SSH2_MSG_CHANNEL_CLOSE:
3311ee85abc4Schristos 	case SSH2_MSG_CHANNEL_DATA:
3312ee85abc4Schristos 	case SSH2_MSG_CHANNEL_EOF:
3313ee85abc4Schristos 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
3314ee85abc4Schristos 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3315ee85abc4Schristos 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
3316ee85abc4Schristos 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3317ee85abc4Schristos 	case SSH2_MSG_CHANNEL_SUCCESS:
3318ee85abc4Schristos 	case SSH2_MSG_CHANNEL_FAILURE:
3319ee85abc4Schristos 	case SSH2_MSG_CHANNEL_REQUEST:
3320ee85abc4Schristos 		break;
3321ee85abc4Schristos 	default:
332217418e98Schristos 		debug2_f("channel %u: unsupported type %u", c->self, type);
3323ee85abc4Schristos 		return 0;
3324ee85abc4Schristos 	}
3325ee85abc4Schristos 	if ((b = sshbuf_new()) == NULL) {
332617418e98Schristos 		error_f("alloc reply");
3327ee85abc4Schristos 		goto out;
3328ee85abc4Schristos 	}
3329ee85abc4Schristos 	/* get remaining payload (after id) */
3330ee85abc4Schristos 	cp = sshpkt_ptr(ssh, &len);
3331ee85abc4Schristos 	if (cp == NULL) {
333217418e98Schristos 		error_f("no packet");
3333ee85abc4Schristos 		goto out;
3334ee85abc4Schristos 	}
3335ee85abc4Schristos 	/* translate id and send to muxclient */
3336ee85abc4Schristos 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
3337ee85abc4Schristos 	    (r = sshbuf_put_u8(b, type)) != 0 ||
3338ee85abc4Schristos 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
3339ee85abc4Schristos 	    (r = sshbuf_put(b, cp, len)) != 0 ||
33407a183406Schristos 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
334117418e98Schristos 		error_fr(r, "compose muxclient");
3342ee85abc4Schristos 		goto out;
3343ee85abc4Schristos 	}
3344ee85abc4Schristos 	/* sshbuf_dump(b, stderr); */
3345ee85abc4Schristos 	if (ssh_packet_log_type(type))
334617418e98Schristos 		debug3_f("channel %u: up->down: type %u", c->self, type);
3347ee85abc4Schristos  out:
3348ee85abc4Schristos 	/* update state */
3349ee85abc4Schristos 	switch (type) {
3350ee85abc4Schristos 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3351ee85abc4Schristos 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
33527a183406Schristos 		if (cp && len > 4) {
3353ee85abc4Schristos 			c->remote_id = PEEK_U32(cp);
33547a183406Schristos 			c->have_remote_id = 1;
33557a183406Schristos 		}
3356ee85abc4Schristos 		break;
3357ee85abc4Schristos 	case SSH2_MSG_CHANNEL_CLOSE:
3358ee85abc4Schristos 		if (c->flags & CHAN_CLOSE_SENT)
33597a183406Schristos 			channel_free(ssh, c);
3360ee85abc4Schristos 		else
3361ee85abc4Schristos 			c->flags |= CHAN_CLOSE_RCVD;
3362ee85abc4Schristos 		break;
3363ee85abc4Schristos 	}
3364ee85abc4Schristos 	sshbuf_free(b);
3365ee85abc4Schristos 	return 1;
3366ee85abc4Schristos }
3367ca32bd8dSchristos 
3368ca32bd8dSchristos /* -- protocol input */
3369ca32bd8dSchristos 
33707a183406Schristos /* Parse a channel ID from the current packet */
33717a183406Schristos static int
33727a183406Schristos channel_parse_id(struct ssh *ssh, const char *where, const char *what)
3373ca32bd8dSchristos {
33747a183406Schristos 	u_int32_t id;
33757a183406Schristos 	int r;
33767a183406Schristos 
33777a183406Schristos 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
337817418e98Schristos 		error_r(r, "%s: parse id", where);
33797a183406Schristos 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
33807a183406Schristos 	}
33817a183406Schristos 	if (id > INT_MAX) {
338217418e98Schristos 		error_r(r, "%s: bad channel id %u", where, id);
33837a183406Schristos 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
33847a183406Schristos 	}
33857a183406Schristos 	return (int)id;
33867a183406Schristos }
33877a183406Schristos 
33887a183406Schristos /* Lookup a channel from an ID in the current packet */
33897a183406Schristos static Channel *
33907a183406Schristos channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
33917a183406Schristos {
33927a183406Schristos 	int id = channel_parse_id(ssh, where, what);
3393ca32bd8dSchristos 	Channel *c;
3394ca32bd8dSchristos 
33957a183406Schristos 	if ((c = channel_lookup(ssh, id)) == NULL) {
33967a183406Schristos 		ssh_packet_disconnect(ssh,
33977a183406Schristos 		    "%s packet referred to nonexistent channel %d", what, id);
33987a183406Schristos 	}
33997a183406Schristos 	return c;
34007a183406Schristos }
34017a183406Schristos 
34027a183406Schristos int
34037a183406Schristos channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
34047a183406Schristos {
34057a183406Schristos 	const u_char *data;
34067a183406Schristos 	size_t data_len, win_len;
34077a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
34087a183406Schristos 	int r;
34097a183406Schristos 
34107a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3411ee85abc4Schristos 		return 0;
3412ca32bd8dSchristos 
3413ca32bd8dSchristos 	/* Ignore any data for non-open channels (might happen on close) */
3414ca32bd8dSchristos 	if (c->type != SSH_CHANNEL_OPEN &&
34157a183406Schristos 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
34167a183406Schristos 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
3417ca32bd8dSchristos 	    c->type != SSH_CHANNEL_X11_OPEN)
3418e4d43b82Schristos 		return 0;
3419ca32bd8dSchristos 
3420ca32bd8dSchristos 	/* Get the data. */
3421aa36fcacSchristos 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
3422aa36fcacSchristos             (r = sshpkt_get_end(ssh)) != 0)
342317418e98Schristos 		fatal_fr(r, "channel %i: get data", c->self);
34247a183406Schristos 
342534b27b53Sadam 	win_len = data_len;
342634b27b53Sadam 	if (c->datagram)
342734b27b53Sadam 		win_len += 4;  /* string length header */
3428ca32bd8dSchristos 
3429ca32bd8dSchristos 	/*
34307a183406Schristos 	 * The sending side reduces its window as it sends data, so we
34317a183406Schristos 	 * must 'fake' consumption of the data in order to ensure that window
34327a183406Schristos 	 * updates are sent back. Otherwise the connection might deadlock.
3433ca32bd8dSchristos 	 */
34347a183406Schristos 	if (c->ostate != CHAN_OUTPUT_OPEN) {
343534b27b53Sadam 		c->local_window -= win_len;
343634b27b53Sadam 		c->local_consumed += win_len;
3437e4d43b82Schristos 		return 0;
3438ca32bd8dSchristos 	}
3439ca32bd8dSchristos 
344034b27b53Sadam 	if (win_len > c->local_maxpacket) {
34417a183406Schristos 		logit("channel %d: rcvd big packet %zu, maxpack %u",
344234b27b53Sadam 		    c->self, win_len, c->local_maxpacket);
34437a183406Schristos 		return 0;
3444ca32bd8dSchristos 	}
344534b27b53Sadam 	if (win_len > c->local_window) {
3446514b5d45Schristos 		c->local_window_exceeded += win_len - c->local_window;
3447514b5d45Schristos 		logit("channel %d: rcvd too much data %zu, win %u/%u "
3448514b5d45Schristos 		    "(excess %u)", c->self, win_len, c->local_window,
3449514b5d45Schristos 		    c->local_window_max, c->local_window_exceeded);
3450514b5d45Schristos 		c->local_window = 0;
3451514b5d45Schristos 		/* Allow 10% grace before bringing the hammer down */
3452514b5d45Schristos 		if (c->local_window_exceeded > (c->local_window_max / 10)) {
3453514b5d45Schristos 			ssh_packet_disconnect(ssh, "channel %d: peer ignored "
3454514b5d45Schristos 			    "channel window", c->self);
3455ca32bd8dSchristos 		}
3456514b5d45Schristos 	} else {
345734b27b53Sadam 		c->local_window -= win_len;
3458514b5d45Schristos 		c->local_window_exceeded = 0;
3459514b5d45Schristos 	}
34607a183406Schristos 
34617a183406Schristos 	if (c->datagram) {
34627a183406Schristos 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
346317418e98Schristos 			fatal_fr(r, "channel %i: append datagram", c->self);
34647a183406Schristos 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
346517418e98Schristos 		fatal_fr(r, "channel %i: append data", c->self);
34667a183406Schristos 
3467e4d43b82Schristos 	return 0;
3468ca32bd8dSchristos }
3469ca32bd8dSchristos 
3470e4d43b82Schristos int
34717a183406Schristos channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3472ca32bd8dSchristos {
34737a183406Schristos 	const u_char *data;
34747a183406Schristos 	size_t data_len;
34757a183406Schristos 	u_int32_t tcode;
34767a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
34777a183406Schristos 	int r;
3478ca32bd8dSchristos 
34797a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3480ee85abc4Schristos 		return 0;
3481ca32bd8dSchristos 	if (c->type != SSH_CHANNEL_OPEN) {
34827a183406Schristos 		logit("channel %d: ext data for non open", c->self);
3483e4d43b82Schristos 		return 0;
3484ca32bd8dSchristos 	}
3485ca32bd8dSchristos 	if (c->flags & CHAN_EOF_RCVD) {
348617418e98Schristos 		if (ssh->compat & SSH_BUG_EXTEOF)
34877a183406Schristos 			debug("channel %d: accepting ext data after eof",
34887a183406Schristos 			    c->self);
3489ca32bd8dSchristos 		else
34907a183406Schristos 			ssh_packet_disconnect(ssh, "Received extended_data "
34917a183406Schristos 			    "after EOF on channel %d.", c->self);
3492ca32bd8dSchristos 	}
34937a183406Schristos 
34947a183406Schristos 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
349517418e98Schristos 		error_fr(r, "parse tcode");
34967a183406Schristos 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
34977a183406Schristos 	}
3498ca32bd8dSchristos 	if (c->efd == -1 ||
3499ca32bd8dSchristos 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3500ca32bd8dSchristos 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3501ca32bd8dSchristos 		logit("channel %d: bad ext data", c->self);
3502e4d43b82Schristos 		return 0;
3503ca32bd8dSchristos 	}
3504aa36fcacSchristos 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
3505aa36fcacSchristos             (r = sshpkt_get_end(ssh)) != 0) {
350617418e98Schristos 		error_fr(r, "parse data");
35077a183406Schristos 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
35087a183406Schristos 	}
35097a183406Schristos 
3510ca32bd8dSchristos 	if (data_len > c->local_window) {
35117a183406Schristos 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3512ca32bd8dSchristos 		    c->self, data_len, c->local_window);
3513e4d43b82Schristos 		return 0;
3514ca32bd8dSchristos 	}
35157a183406Schristos 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
35167a183406Schristos 	/* XXX sshpkt_getb? */
35177a183406Schristos 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
351817418e98Schristos 		error_fr(r, "append");
3519ca32bd8dSchristos 	c->local_window -= data_len;
3520e4d43b82Schristos 	return 0;
3521ca32bd8dSchristos }
3522ca32bd8dSchristos 
3523e4d43b82Schristos int
35247a183406Schristos channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3525ca32bd8dSchristos {
35267a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
3527aa36fcacSchristos 	int r;
3528ca32bd8dSchristos 
3529aa36fcacSchristos         if ((r = sshpkt_get_end(ssh)) != 0) {
353017418e98Schristos 		error_fr(r, "parse data");
3531aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid ieof message");
3532aa36fcacSchristos 	}
35337a183406Schristos 
35347a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3535ee85abc4Schristos 		return 0;
35367a183406Schristos 	chan_rcvd_ieof(ssh, c);
3537ca32bd8dSchristos 
3538ca32bd8dSchristos 	/* XXX force input close */
3539ca32bd8dSchristos 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3540ca32bd8dSchristos 		debug("channel %d: FORCE input drain", c->self);
3541ca32bd8dSchristos 		c->istate = CHAN_INPUT_WAIT_DRAIN;
35427a183406Schristos 		if (sshbuf_len(c->input) == 0)
35437a183406Schristos 			chan_ibuf_empty(ssh, c);
3544ca32bd8dSchristos 	}
3545e4d43b82Schristos 	return 0;
3546ca32bd8dSchristos }
3547ca32bd8dSchristos 
3548e4d43b82Schristos int
35497a183406Schristos channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3550ca32bd8dSchristos {
35517a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
3552aa36fcacSchristos 	int r;
3553ca32bd8dSchristos 
35547a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3555ee85abc4Schristos 		return 0;
3556aa36fcacSchristos         if ((r = sshpkt_get_end(ssh)) != 0) {
355717418e98Schristos 		error_fr(r, "parse data");
3558aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid oclose message");
3559aa36fcacSchristos 	}
35607a183406Schristos 	chan_rcvd_oclose(ssh, c);
3561e4d43b82Schristos 	return 0;
3562ca32bd8dSchristos }
3563ca32bd8dSchristos 
3564e4d43b82Schristos int
35657a183406Schristos channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3566ca32bd8dSchristos {
35677a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
35687a183406Schristos 	u_int32_t remote_window, remote_maxpacket;
35697a183406Schristos 	int r;
3570ca32bd8dSchristos 
35717a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3572ee85abc4Schristos 		return 0;
3573ee85abc4Schristos 	if (c->type != SSH_CHANNEL_OPENING)
3574aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Received open confirmation for "
35757a183406Schristos 		    "non-opening channel %d.", c->self);
35767a183406Schristos 	/*
35777a183406Schristos 	 * Record the remote channel number and mark that the channel
35787a183406Schristos 	 * is now open.
35797a183406Schristos 	 */
35807a183406Schristos 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
35817a183406Schristos 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
3582aa36fcacSchristos 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 ||
3583aa36fcacSchristos             (r = sshpkt_get_end(ssh)) != 0) {
358417418e98Schristos 		error_fr(r, "window/maxpacket");
3585aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid open confirmation message");
35867a183406Schristos 	}
3587ca32bd8dSchristos 
35887a183406Schristos 	c->have_remote_id = 1;
35897a183406Schristos 	c->remote_window = remote_window;
35907a183406Schristos 	c->remote_maxpacket = remote_maxpacket;
35917a183406Schristos 	c->type = SSH_CHANNEL_OPEN;
3592ca32bd8dSchristos 	if (c->open_confirm) {
359317418e98Schristos 		debug2_f("channel %d: callback start", c->self);
35947a183406Schristos 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
359517418e98Schristos 		debug2_f("channel %d: callback done", c->self);
3596ca32bd8dSchristos 	}
3597c5555919Schristos 	channel_set_used_time(ssh, c);
3598ca32bd8dSchristos 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3599ca32bd8dSchristos 	    c->remote_window, c->remote_maxpacket);
3600e4d43b82Schristos 	return 0;
3601ca32bd8dSchristos }
3602ca32bd8dSchristos 
3603185c8f97Schristos static const char *
3604ca32bd8dSchristos reason2txt(int reason)
3605ca32bd8dSchristos {
3606ca32bd8dSchristos 	switch (reason) {
3607ca32bd8dSchristos 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3608ca32bd8dSchristos 		return "administratively prohibited";
3609ca32bd8dSchristos 	case SSH2_OPEN_CONNECT_FAILED:
3610ca32bd8dSchristos 		return "connect failed";
3611ca32bd8dSchristos 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3612ca32bd8dSchristos 		return "unknown channel type";
3613ca32bd8dSchristos 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3614ca32bd8dSchristos 		return "resource shortage";
3615ca32bd8dSchristos 	}
3616ca32bd8dSchristos 	return "unknown reason";
3617ca32bd8dSchristos }
3618ca32bd8dSchristos 
3619e4d43b82Schristos int
36207a183406Schristos channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3621ca32bd8dSchristos {
36227a183406Schristos 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
36237a183406Schristos 	u_int32_t reason;
36247a183406Schristos 	char *msg = NULL;
36257a183406Schristos 	int r;
3626ca32bd8dSchristos 
36277a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3628ee85abc4Schristos 		return 0;
3629ee85abc4Schristos 	if (c->type != SSH_CHANNEL_OPENING)
3630aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Received open failure for "
36317a183406Schristos 		    "non-opening channel %d.", c->self);
36327a183406Schristos 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
363317418e98Schristos 		error_fr(r, "parse reason");
3634aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid open failure message");
3635ca32bd8dSchristos 	}
36367a183406Schristos 	/* skip language */
36377a183406Schristos 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
3638aa36fcacSchristos 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 ||
3639aa36fcacSchristos             (r = sshpkt_get_end(ssh)) != 0) {
364017418e98Schristos 		error_fr(r, "parse msg/lang");
3641aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid open failure message");
36427a183406Schristos 	}
36437a183406Schristos 	logit("channel %d: open failed: %s%s%s", c->self,
3644ca32bd8dSchristos 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
364500a838c4Schristos 	free(msg);
364634b27b53Sadam 	if (c->open_confirm) {
364717418e98Schristos 		debug2_f("channel %d: callback start", c->self);
36487a183406Schristos 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
364917418e98Schristos 		debug2_f("channel %d: callback done", c->self);
365034b27b53Sadam 	}
3651ca32bd8dSchristos 	/* Schedule the channel for cleanup/deletion. */
36527a183406Schristos 	chan_mark_dead(ssh, c);
3653e4d43b82Schristos 	return 0;
3654ca32bd8dSchristos }
3655ca32bd8dSchristos 
3656e4d43b82Schristos int
36577a183406Schristos channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3658ca32bd8dSchristos {
36597a183406Schristos 	int id = channel_parse_id(ssh, __func__, "window adjust");
3660ca32bd8dSchristos 	Channel *c;
36617a183406Schristos 	u_int32_t adjust;
36627a183406Schristos 	u_int new_rwin;
36637a183406Schristos 	int r;
3664ca32bd8dSchristos 
36657a183406Schristos 	if ((c = channel_lookup(ssh, id)) == NULL) {
3666ca32bd8dSchristos 		logit("Received window adjust for non-open channel %d.", id);
3667e4d43b82Schristos 		return 0;
3668ca32bd8dSchristos 	}
36697a183406Schristos 
36707a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3671ee85abc4Schristos 		return 0;
3672aa36fcacSchristos 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 ||
3673aa36fcacSchristos             (r = sshpkt_get_end(ssh)) != 0) {
367417418e98Schristos 		error_fr(r, "parse adjust");
3675aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid window adjust message");
36767a183406Schristos 	}
36777a183406Schristos 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
36787a183406Schristos 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
36794054ffb0Schristos 		fatal("channel %d: adjust %u overflows remote window %u",
36807a183406Schristos 		    c->self, adjust, c->remote_window);
36817a183406Schristos 	}
36827a183406Schristos 	c->remote_window = new_rwin;
3683e4d43b82Schristos 	return 0;
3684ca32bd8dSchristos }
3685ca32bd8dSchristos 
3686e4d43b82Schristos int
36877a183406Schristos channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3688ca32bd8dSchristos {
36897a183406Schristos 	int id = channel_parse_id(ssh, __func__, "status confirm");
3690ca32bd8dSchristos 	Channel *c;
3691ca32bd8dSchristos 	struct channel_confirm *cc;
3692ca32bd8dSchristos 
3693ca32bd8dSchristos 	/* Reset keepalive timeout */
3694aa36fcacSchristos 	ssh_packet_set_alive_timeouts(ssh, 0);
3695ca32bd8dSchristos 
369617418e98Schristos 	debug2_f("type %d id %d", type, id);
3697ca32bd8dSchristos 
36987a183406Schristos 	if ((c = channel_lookup(ssh, id)) == NULL) {
369917418e98Schristos 		logit_f("%d: unknown", id);
3700e4d43b82Schristos 		return 0;
3701ca32bd8dSchristos 	}
37027a183406Schristos 	if (channel_proxy_upstream(c, type, seq, ssh))
3703ee85abc4Schristos 		return 0;
3704cd4ada6aSchristos         if (sshpkt_get_end(ssh) != 0)
3705aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Invalid status confirm message");
3706ca32bd8dSchristos 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3707e4d43b82Schristos 		return 0;
37087a183406Schristos 	cc->cb(ssh, type, c, cc->ctx);
3709ca32bd8dSchristos 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
37108db691beSchristos 	freezero(cc, sizeof(*cc));
3711e4d43b82Schristos 	return 0;
3712ca32bd8dSchristos }
3713ca32bd8dSchristos 
3714ca32bd8dSchristos /* -- tcp forwarding */
3715ca32bd8dSchristos 
3716ca32bd8dSchristos void
37177a183406Schristos channel_set_af(struct ssh *ssh, int af)
3718ca32bd8dSchristos {
37197a183406Schristos 	ssh->chanctxt->IPv4or6 = af;
3720ca32bd8dSchristos }
3721ca32bd8dSchristos 
3722313c6c94Schristos void
3723313c6c94Schristos channel_set_hpn(int external_hpn_disabled, int external_hpn_buffer_size)
3724313c6c94Schristos {
3725313c6c94Schristos       	hpn_disabled = external_hpn_disabled;
3726313c6c94Schristos 	hpn_buffer_size = external_hpn_buffer_size;
3727313c6c94Schristos 	debug("HPN Disabled: %d, HPN Buffer Size: %d", hpn_disabled, hpn_buffer_size);
3728313c6c94Schristos }
3729313c6c94Schristos 
3730091c4109Schristos /*
3731091c4109Schristos  * Determine whether or not a port forward listens to loopback, the
3732091c4109Schristos  * specified address or wildcard. On the client, a specified bind
3733091c4109Schristos  * address will always override gateway_ports. On the server, a
3734091c4109Schristos  * gateway_ports of 1 (``yes'') will override the client's specification
3735091c4109Schristos  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3736091c4109Schristos  * will bind to whatever address the client asked for.
3737091c4109Schristos  *
3738091c4109Schristos  * Special-case listen_addrs are:
3739091c4109Schristos  *
3740091c4109Schristos  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3741091c4109Schristos  * "" (empty string), "*"  -> wildcard v4/v6
3742091c4109Schristos  * "localhost"             -> loopback v4/v6
37438a4530f9Schristos  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3744091c4109Schristos  */
3745091c4109Schristos static const char *
3746aa36fcacSchristos channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp,
37478a4530f9Schristos     int is_client, struct ForwardOptions *fwd_opts)
3748091c4109Schristos {
3749091c4109Schristos 	const char *addr = NULL;
3750091c4109Schristos 	int wildcard = 0;
3751091c4109Schristos 
3752091c4109Schristos 	if (listen_addr == NULL) {
3753091c4109Schristos 		/* No address specified: default to gateway_ports setting */
37548a4530f9Schristos 		if (fwd_opts->gateway_ports)
3755091c4109Schristos 			wildcard = 1;
37568a4530f9Schristos 	} else if (fwd_opts->gateway_ports || is_client) {
375717418e98Schristos 		if (((ssh->compat & SSH_OLD_FORWARD_ADDR) &&
3758091c4109Schristos 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3759091c4109Schristos 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
37608a4530f9Schristos 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3761091c4109Schristos 			wildcard = 1;
37628a4530f9Schristos 			/*
37638a4530f9Schristos 			 * Notify client if they requested a specific listen
37648a4530f9Schristos 			 * address and it was overridden.
37658a4530f9Schristos 			 */
37668a4530f9Schristos 			if (*listen_addr != '\0' &&
37678a4530f9Schristos 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
37688a4530f9Schristos 			    strcmp(listen_addr, "*") != 0) {
3769aa36fcacSchristos 				ssh_packet_send_debug(ssh,
3770aa36fcacSchristos 				    "Forwarding listen address "
37718a4530f9Schristos 				    "\"%s\" overridden by server "
37728a4530f9Schristos 				    "GatewayPorts", listen_addr);
37738a4530f9Schristos 			}
37748a4530f9Schristos 		} else if (strcmp(listen_addr, "localhost") != 0 ||
37758a4530f9Schristos 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
37768a4530f9Schristos 		    strcmp(listen_addr, "::1") == 0) {
3777ed75d7a8Schristos 			/*
3778ed75d7a8Schristos 			 * Accept explicit localhost address when
3779ed75d7a8Schristos 			 * GatewayPorts=yes. The "localhost" hostname is
3780ed75d7a8Schristos 			 * deliberately skipped here so it will listen on all
3781ed75d7a8Schristos 			 * available local address families.
3782ed75d7a8Schristos 			 */
37838a4530f9Schristos 			addr = listen_addr;
37848a4530f9Schristos 		}
37858a4530f9Schristos 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
37868a4530f9Schristos 	    strcmp(listen_addr, "::1") == 0) {
37878a4530f9Schristos 		/*
37888a4530f9Schristos 		 * If a specific IPv4/IPv6 localhost address has been
37898a4530f9Schristos 		 * requested then accept it even if gateway_ports is in
37908a4530f9Schristos 		 * effect. This allows the client to prefer IPv4 or IPv6.
37918a4530f9Schristos 		 */
3792091c4109Schristos 		addr = listen_addr;
3793091c4109Schristos 	}
3794091c4109Schristos 	if (wildcardp != NULL)
3795091c4109Schristos 		*wildcardp = wildcard;
3796091c4109Schristos 	return addr;
3797091c4109Schristos }
3798091c4109Schristos 
3799ca32bd8dSchristos static int
38007a183406Schristos channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
38017a183406Schristos     struct Forward *fwd, int *allocated_listen_port,
38027a183406Schristos     struct ForwardOptions *fwd_opts)
3803ca32bd8dSchristos {
3804ca32bd8dSchristos 	Channel *c;
3805ca32bd8dSchristos 	int sock, r, success = 0, wildcard = 0, is_client;
3806ca32bd8dSchristos 	struct addrinfo hints, *ai, *aitop;
3807ca32bd8dSchristos 	const char *host, *addr;
3808ca32bd8dSchristos 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3809ca32bd8dSchristos 	in_port_t *lport_p;
3810ca32bd8dSchristos 
3811ca32bd8dSchristos 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3812ca32bd8dSchristos 
38134054ffb0Schristos 	if (is_client && fwd->connect_path != NULL) {
38144054ffb0Schristos 		host = fwd->connect_path;
38154054ffb0Schristos 	} else {
38164054ffb0Schristos 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
38174054ffb0Schristos 		    fwd->listen_host : fwd->connect_host;
3818ca32bd8dSchristos 		if (host == NULL) {
3819ca32bd8dSchristos 			error("No forward host name.");
3820ca32bd8dSchristos 			return 0;
3821ca32bd8dSchristos 		}
3822ca32bd8dSchristos 		if (strlen(host) >= NI_MAXHOST) {
3823ca32bd8dSchristos 			error("Forward host name too long.");
3824ca32bd8dSchristos 			return 0;
3825ca32bd8dSchristos 		}
38264054ffb0Schristos 	}
3827ca32bd8dSchristos 
3828091c4109Schristos 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
3829aa36fcacSchristos 	addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard,
38308a4530f9Schristos 	    is_client, fwd_opts);
383117418e98Schristos 	debug3_f("type %d wildcard %d addr %s", type, wildcard,
383217418e98Schristos 	    (addr == NULL) ? "NULL" : addr);
3833ca32bd8dSchristos 
3834ca32bd8dSchristos 	/*
3835ca32bd8dSchristos 	 * getaddrinfo returns a loopback address if the hostname is
3836ca32bd8dSchristos 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3837ca32bd8dSchristos 	 */
3838ca32bd8dSchristos 	memset(&hints, 0, sizeof(hints));
38397a183406Schristos 	hints.ai_family = ssh->chanctxt->IPv4or6;
3840ca32bd8dSchristos 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3841ca32bd8dSchristos 	hints.ai_socktype = SOCK_STREAM;
38428a4530f9Schristos 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3843ca32bd8dSchristos 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3844ca32bd8dSchristos 		if (addr == NULL) {
3845ca32bd8dSchristos 			/* This really shouldn't happen */
3846aa36fcacSchristos 			ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s",
3847ca32bd8dSchristos 			    ssh_gai_strerror(r));
3848ca32bd8dSchristos 		} else {
384917418e98Schristos 			error_f("getaddrinfo(%.64s): %s", addr,
3850ca32bd8dSchristos 			    ssh_gai_strerror(r));
3851ca32bd8dSchristos 		}
3852ca32bd8dSchristos 		return 0;
3853ca32bd8dSchristos 	}
3854ca32bd8dSchristos 	if (allocated_listen_port != NULL)
3855ca32bd8dSchristos 		*allocated_listen_port = 0;
3856ca32bd8dSchristos 	for (ai = aitop; ai; ai = ai->ai_next) {
3857ca32bd8dSchristos 		switch (ai->ai_family) {
3858ca32bd8dSchristos 		case AF_INET:
3859ca32bd8dSchristos 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3860ca32bd8dSchristos 			    sin_port;
3861ca32bd8dSchristos 			break;
3862ca32bd8dSchristos 		case AF_INET6:
3863ca32bd8dSchristos 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3864ca32bd8dSchristos 			    sin6_port;
3865ca32bd8dSchristos 			break;
3866ca32bd8dSchristos 		default:
3867ca32bd8dSchristos 			continue;
3868ca32bd8dSchristos 		}
3869ca32bd8dSchristos 		/*
3870ca32bd8dSchristos 		 * If allocating a port for -R forwards, then use the
3871ca32bd8dSchristos 		 * same port for all address families.
3872ca32bd8dSchristos 		 */
38737a183406Schristos 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
38747a183406Schristos 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
38757a183406Schristos 		    *allocated_listen_port > 0)
3876ca32bd8dSchristos 			*lport_p = htons(*allocated_listen_port);
3877ca32bd8dSchristos 
3878ca32bd8dSchristos 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
38797a183406Schristos 		    strport, sizeof(strport),
38807a183406Schristos 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
388117418e98Schristos 			error_f("getnameinfo failed");
3882ca32bd8dSchristos 			continue;
3883ca32bd8dSchristos 		}
3884ca32bd8dSchristos 		/* Create a port to listen for the host. */
3885ca32bd8dSchristos 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
3886cd4ada6aSchristos 		if (sock == -1) {
3887ca32bd8dSchristos 			/* this is no error since kernel may not support ipv6 */
3888ffae97bbSchristos 			verbose("socket [%s]:%s: %.100s", ntop, strport,
3889ffae97bbSchristos 			    strerror(errno));
3890ca32bd8dSchristos 			continue;
3891ca32bd8dSchristos 		}
3892ca32bd8dSchristos 
3893ffae97bbSchristos 		set_reuseaddr(sock);
3894ca32bd8dSchristos 
3895ca32bd8dSchristos 		debug("Local forwarding listening on %s port %s.",
3896ca32bd8dSchristos 		    ntop, strport);
3897ca32bd8dSchristos 
3898ca32bd8dSchristos 		/* Bind the socket to the address. */
3899cd4ada6aSchristos 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
39007a183406Schristos 			/*
39017a183406Schristos 			 * address can be in if use ipv6 address is
39027a183406Schristos 			 * already bound
39037a183406Schristos 			 */
3904ffae97bbSchristos 			verbose("bind [%s]:%s: %.100s",
3905ffae97bbSchristos 			    ntop, strport, strerror(errno));
3906ca32bd8dSchristos 			close(sock);
3907ca32bd8dSchristos 			continue;
3908ca32bd8dSchristos 		}
3909ca32bd8dSchristos 		/* Start listening for connections on the socket. */
3910cd4ada6aSchristos 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
3911ffae97bbSchristos 			error("listen [%s]:%s: %.100s", ntop, strport,
3912ffae97bbSchristos 			    strerror(errno));
3913ca32bd8dSchristos 			close(sock);
3914ca32bd8dSchristos 			continue;
3915ca32bd8dSchristos 		}
3916ca32bd8dSchristos 
3917ca32bd8dSchristos 		/*
39188a4530f9Schristos 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3919ca32bd8dSchristos 		 * record what we got.
3920ca32bd8dSchristos 		 */
39217a183406Schristos 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
39227a183406Schristos 		    fwd->listen_port == 0 &&
3923ca32bd8dSchristos 		    allocated_listen_port != NULL &&
3924ca32bd8dSchristos 		    *allocated_listen_port == 0) {
39255101d403Schristos 			*allocated_listen_port = get_local_port(sock);
3926ca32bd8dSchristos 			debug("Allocated listen port %d",
3927ca32bd8dSchristos 			    *allocated_listen_port);
3928ca32bd8dSchristos 		}
3929ca32bd8dSchristos 
3930ca32bd8dSchristos 		/* Allocate a channel number for the socket. */
3931313c6c94Schristos 		/* explicitly test for hpn disabled option. if true use smaller window size */
3932313c6c94Schristos 		if (hpn_disabled)
3933b1066cf3Schristos 		c = channel_new(ssh, "port-listener", type, sock, sock, -1,
3934ca32bd8dSchristos 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3935ca32bd8dSchristos 		    0, "port listener", 1);
3936313c6c94Schristos 		else
39377a183406Schristos 			c = channel_new(ssh, "port listener", type, sock, sock,
39387a183406Schristos 			  -1, hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
3939313c6c94Schristos 		    	  0, "port listener", 1);
3940ca32bd8dSchristos 		c->path = xstrdup(host);
39418a4530f9Schristos 		c->host_port = fwd->connect_port;
3942091c4109Schristos 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
39438a4530f9Schristos 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
394417418e98Schristos 		    !(ssh->compat & SSH_BUG_DYNAMIC_RPORT))
3945091c4109Schristos 			c->listening_port = *allocated_listen_port;
3946091c4109Schristos 		else
39478a4530f9Schristos 			c->listening_port = fwd->listen_port;
3948ca32bd8dSchristos 		success = 1;
3949ca32bd8dSchristos 	}
3950ca32bd8dSchristos 	if (success == 0)
395117418e98Schristos 		error_f("cannot listen to port: %d", fwd->listen_port);
3952ca32bd8dSchristos 	freeaddrinfo(aitop);
3953ca32bd8dSchristos 	return success;
3954ca32bd8dSchristos }
3955ca32bd8dSchristos 
39568a4530f9Schristos static int
39577a183406Schristos channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
39587a183406Schristos     struct Forward *fwd, struct ForwardOptions *fwd_opts)
39598a4530f9Schristos {
39608a4530f9Schristos 	struct sockaddr_un sunaddr;
39618a4530f9Schristos 	const char *path;
39628a4530f9Schristos 	Channel *c;
39638a4530f9Schristos 	int port, sock;
39648a4530f9Schristos 	mode_t omask;
39658a4530f9Schristos 
39668a4530f9Schristos 	switch (type) {
39678a4530f9Schristos 	case SSH_CHANNEL_UNIX_LISTENER:
39688a4530f9Schristos 		if (fwd->connect_path != NULL) {
39698a4530f9Schristos 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
39708a4530f9Schristos 				error("Local connecting path too long: %s",
39718a4530f9Schristos 				    fwd->connect_path);
39728a4530f9Schristos 				return 0;
39738a4530f9Schristos 			}
39748a4530f9Schristos 			path = fwd->connect_path;
39758a4530f9Schristos 			port = PORT_STREAMLOCAL;
39768a4530f9Schristos 		} else {
39778a4530f9Schristos 			if (fwd->connect_host == NULL) {
39788a4530f9Schristos 				error("No forward host name.");
39798a4530f9Schristos 				return 0;
39808a4530f9Schristos 			}
39818a4530f9Schristos 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
39828a4530f9Schristos 				error("Forward host name too long.");
39838a4530f9Schristos 				return 0;
39848a4530f9Schristos 			}
39858a4530f9Schristos 			path = fwd->connect_host;
39868a4530f9Schristos 			port = fwd->connect_port;
39878a4530f9Schristos 		}
39888a4530f9Schristos 		break;
39898a4530f9Schristos 	case SSH_CHANNEL_RUNIX_LISTENER:
39908a4530f9Schristos 		path = fwd->listen_path;
39918a4530f9Schristos 		port = PORT_STREAMLOCAL;
39928a4530f9Schristos 		break;
39938a4530f9Schristos 	default:
399417418e98Schristos 		error_f("unexpected channel type %d", type);
39958a4530f9Schristos 		return 0;
39968a4530f9Schristos 	}
39978a4530f9Schristos 
39988a4530f9Schristos 	if (fwd->listen_path == NULL) {
39998a4530f9Schristos 		error("No forward path name.");
40008a4530f9Schristos 		return 0;
40018a4530f9Schristos 	}
40028a4530f9Schristos 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
40038a4530f9Schristos 		error("Local listening path too long: %s", fwd->listen_path);
40048a4530f9Schristos 		return 0;
40058a4530f9Schristos 	}
40068a4530f9Schristos 
400717418e98Schristos 	debug3_f("type %d path %s", type, fwd->listen_path);
40088a4530f9Schristos 
40098a4530f9Schristos 	/* Start a Unix domain listener. */
40108a4530f9Schristos 	omask = umask(fwd_opts->streamlocal_bind_mask);
40118a4530f9Schristos 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
40128a4530f9Schristos 	    fwd_opts->streamlocal_bind_unlink);
40138a4530f9Schristos 	umask(omask);
40148a4530f9Schristos 	if (sock < 0)
40158a4530f9Schristos 		return 0;
40168a4530f9Schristos 
40178a4530f9Schristos 	debug("Local forwarding listening on path %s.", fwd->listen_path);
40188a4530f9Schristos 
40198a4530f9Schristos 	/* Allocate a channel number for the socket. */
4020b1066cf3Schristos 	c = channel_new(ssh, "unix-listener", type, sock, sock, -1,
40218a4530f9Schristos 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
40228a4530f9Schristos 	    0, "unix listener", 1);
40238a4530f9Schristos 	c->path = xstrdup(path);
40248a4530f9Schristos 	c->host_port = port;
40258a4530f9Schristos 	c->listening_port = PORT_STREAMLOCAL;
40268a4530f9Schristos 	c->listening_addr = xstrdup(fwd->listen_path);
40278a4530f9Schristos 	return 1;
40288a4530f9Schristos }
40298a4530f9Schristos 
40308a4530f9Schristos static int
40317a183406Schristos channel_cancel_rport_listener_tcpip(struct ssh *ssh,
40327a183406Schristos     const char *host, u_short port)
4033ca32bd8dSchristos {
4034ca32bd8dSchristos 	u_int i;
4035ca32bd8dSchristos 	int found = 0;
4036ca32bd8dSchristos 
40377a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40387a183406Schristos 		Channel *c = ssh->chanctxt->channels[i];
4039091c4109Schristos 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
4040091c4109Schristos 			continue;
4041091c4109Schristos 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
404217418e98Schristos 			debug2_f("close channel %d", i);
40437a183406Schristos 			channel_free(ssh, c);
4044091c4109Schristos 			found = 1;
4045091c4109Schristos 		}
4046091c4109Schristos 	}
4047ca32bd8dSchristos 
40487a183406Schristos 	return found;
4049091c4109Schristos }
4050091c4109Schristos 
40518a4530f9Schristos static int
40527a183406Schristos channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
4053091c4109Schristos {
4054091c4109Schristos 	u_int i;
4055091c4109Schristos 	int found = 0;
40568a4530f9Schristos 
40577a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40587a183406Schristos 		Channel *c = ssh->chanctxt->channels[i];
40598a4530f9Schristos 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
40608a4530f9Schristos 			continue;
40618a4530f9Schristos 		if (c->path == NULL)
40628a4530f9Schristos 			continue;
40638a4530f9Schristos 		if (strcmp(c->path, path) == 0) {
406417418e98Schristos 			debug2_f("close channel %d", i);
40657a183406Schristos 			channel_free(ssh, c);
40668a4530f9Schristos 			found = 1;
40678a4530f9Schristos 		}
40688a4530f9Schristos 	}
40698a4530f9Schristos 
40707a183406Schristos 	return found;
40718a4530f9Schristos }
40728a4530f9Schristos 
40738a4530f9Schristos int
40747a183406Schristos channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
40758a4530f9Schristos {
40767a183406Schristos 	if (fwd->listen_path != NULL) {
40777a183406Schristos 		return channel_cancel_rport_listener_streamlocal(ssh,
40787a183406Schristos 		    fwd->listen_path);
40797a183406Schristos 	} else {
40807a183406Schristos 		return channel_cancel_rport_listener_tcpip(ssh,
40817a183406Schristos 		    fwd->listen_host, fwd->listen_port);
40827a183406Schristos 	}
40838a4530f9Schristos }
40848a4530f9Schristos 
40858a4530f9Schristos static int
40867a183406Schristos channel_cancel_lport_listener_tcpip(struct ssh *ssh,
40877a183406Schristos     const char *lhost, u_short lport, int cport,
40887a183406Schristos     struct ForwardOptions *fwd_opts)
40898a4530f9Schristos {
40908a4530f9Schristos 	u_int i;
40918a4530f9Schristos 	int found = 0;
4092aa36fcacSchristos 	const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts);
4093091c4109Schristos 
40947a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40957a183406Schristos 		Channel *c = ssh->chanctxt->channels[i];
4096091c4109Schristos 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
4097091c4109Schristos 			continue;
4098091c4109Schristos 		if (c->listening_port != lport)
4099091c4109Schristos 			continue;
4100091c4109Schristos 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
4101091c4109Schristos 			/* skip dynamic forwardings */
4102091c4109Schristos 			if (c->host_port == 0)
4103091c4109Schristos 				continue;
4104091c4109Schristos 		} else {
4105091c4109Schristos 			if (c->host_port != cport)
4106091c4109Schristos 				continue;
4107091c4109Schristos 		}
4108091c4109Schristos 		if ((c->listening_addr == NULL && addr != NULL) ||
4109091c4109Schristos 		    (c->listening_addr != NULL && addr == NULL))
4110091c4109Schristos 			continue;
4111091c4109Schristos 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
411217418e98Schristos 			debug2_f("close channel %d", i);
41137a183406Schristos 			channel_free(ssh, c);
4114ca32bd8dSchristos 			found = 1;
4115ca32bd8dSchristos 		}
4116ca32bd8dSchristos 	}
4117ca32bd8dSchristos 
41187a183406Schristos 	return found;
4119ca32bd8dSchristos }
4120ca32bd8dSchristos 
41218a4530f9Schristos static int
41227a183406Schristos channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
41238a4530f9Schristos {
41248a4530f9Schristos 	u_int i;
41258a4530f9Schristos 	int found = 0;
41268a4530f9Schristos 
41278a4530f9Schristos 	if (path == NULL) {
412817418e98Schristos 		error_f("no path specified.");
41298a4530f9Schristos 		return 0;
41308a4530f9Schristos 	}
41318a4530f9Schristos 
41327a183406Schristos 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
41337a183406Schristos 		Channel *c = ssh->chanctxt->channels[i];
41348a4530f9Schristos 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
41358a4530f9Schristos 			continue;
41368a4530f9Schristos 		if (c->listening_addr == NULL)
41378a4530f9Schristos 			continue;
41388a4530f9Schristos 		if (strcmp(c->listening_addr, path) == 0) {
413917418e98Schristos 			debug2_f("close channel %d", i);
41407a183406Schristos 			channel_free(ssh, c);
41418a4530f9Schristos 			found = 1;
41428a4530f9Schristos 		}
41438a4530f9Schristos 	}
41448a4530f9Schristos 
41457a183406Schristos 	return found;
41468a4530f9Schristos }
41478a4530f9Schristos 
41488a4530f9Schristos int
41497a183406Schristos channel_cancel_lport_listener(struct ssh *ssh,
41507a183406Schristos     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
4151ca32bd8dSchristos {
41528a4530f9Schristos 	if (fwd->listen_path != NULL) {
41537a183406Schristos 		return channel_cancel_lport_listener_streamlocal(ssh,
41547a183406Schristos 		    fwd->listen_path);
41557a183406Schristos 	} else {
41567a183406Schristos 		return channel_cancel_lport_listener_tcpip(ssh,
41577a183406Schristos 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
41587a183406Schristos 	}
41597a183406Schristos }
41607a183406Schristos 
41617a183406Schristos /* protocol local port fwd, used by ssh */
41627a183406Schristos int
41637a183406Schristos channel_setup_local_fwd_listener(struct ssh *ssh,
41647a183406Schristos     struct Forward *fwd, struct ForwardOptions *fwd_opts)
41657a183406Schristos {
41667a183406Schristos 	if (fwd->listen_path != NULL) {
41677a183406Schristos 		return channel_setup_fwd_listener_streamlocal(ssh,
41688a4530f9Schristos 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
41698a4530f9Schristos 	} else {
41707a183406Schristos 		return channel_setup_fwd_listener_tcpip(ssh,
41717a183406Schristos 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
41728a4530f9Schristos 	}
4173ca32bd8dSchristos }
4174ca32bd8dSchristos 
417555a4608bSchristos /* Matches a remote forwarding permission against a requested forwarding */
417655a4608bSchristos static int
417755a4608bSchristos remote_open_match(struct permission *allowed_open, struct Forward *fwd)
417855a4608bSchristos {
417955a4608bSchristos 	int ret;
418055a4608bSchristos 	char *lhost;
418155a4608bSchristos 
418255a4608bSchristos 	/* XXX add ACLs for streamlocal */
418355a4608bSchristos 	if (fwd->listen_path != NULL)
418455a4608bSchristos 		return 1;
418555a4608bSchristos 
418655a4608bSchristos 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
418755a4608bSchristos 		return 0;
418855a4608bSchristos 
418955a4608bSchristos 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
419055a4608bSchristos 	    allowed_open->listen_port != fwd->listen_port)
419155a4608bSchristos 		return 0;
419255a4608bSchristos 
419355a4608bSchristos 	/* Match hostnames case-insensitively */
419455a4608bSchristos 	lhost = xstrdup(fwd->listen_host);
419555a4608bSchristos 	lowercase(lhost);
419655a4608bSchristos 	ret = match_pattern(lhost, allowed_open->listen_host);
419755a4608bSchristos 	free(lhost);
419855a4608bSchristos 
419955a4608bSchristos 	return ret;
420055a4608bSchristos }
420155a4608bSchristos 
420255a4608bSchristos /* Checks whether a requested remote forwarding is permitted */
420355a4608bSchristos static int
420455a4608bSchristos check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
420555a4608bSchristos {
420655a4608bSchristos 	struct ssh_channels *sc = ssh->chanctxt;
420755a4608bSchristos 	struct permission_set *pset = &sc->remote_perms;
420855a4608bSchristos 	u_int i, permit, permit_adm = 1;
420955a4608bSchristos 	struct permission *perm;
421055a4608bSchristos 
421155a4608bSchristos 	/* XXX apply GatewayPorts override before checking? */
421255a4608bSchristos 
421355a4608bSchristos 	permit = pset->all_permitted;
421455a4608bSchristos 	if (!permit) {
421555a4608bSchristos 		for (i = 0; i < pset->num_permitted_user; i++) {
421655a4608bSchristos 			perm = &pset->permitted_user[i];
421755a4608bSchristos 			if (remote_open_match(perm, fwd)) {
421855a4608bSchristos 				permit = 1;
421955a4608bSchristos 				break;
422055a4608bSchristos 			}
422155a4608bSchristos 		}
422255a4608bSchristos 	}
422355a4608bSchristos 
422455a4608bSchristos 	if (pset->num_permitted_admin > 0) {
422555a4608bSchristos 		permit_adm = 0;
422655a4608bSchristos 		for (i = 0; i < pset->num_permitted_admin; i++) {
422755a4608bSchristos 			perm = &pset->permitted_admin[i];
422855a4608bSchristos 			if (remote_open_match(perm, fwd)) {
422955a4608bSchristos 				permit_adm = 1;
423055a4608bSchristos 				break;
423155a4608bSchristos 			}
423255a4608bSchristos 		}
423355a4608bSchristos 	}
423455a4608bSchristos 
423555a4608bSchristos 	return permit && permit_adm;
423655a4608bSchristos }
423755a4608bSchristos 
4238ca32bd8dSchristos /* protocol v2 remote port fwd, used by sshd */
4239ca32bd8dSchristos int
42407a183406Schristos channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
42418a4530f9Schristos     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
4242ca32bd8dSchristos {
424355a4608bSchristos 	if (!check_rfwd_permission(ssh, fwd)) {
4244aa36fcacSchristos 		ssh_packet_send_debug(ssh, "port forwarding refused");
4245cd4ada6aSchristos 		if (fwd->listen_path != NULL)
4246cd4ada6aSchristos 			/* XXX always allowed, see remote_open_match() */
4247cd4ada6aSchristos 			logit("Received request from %.100s port %d to "
4248cd4ada6aSchristos 			    "remote forward to path \"%.100s\", "
4249cd4ada6aSchristos 			    "but the request was denied.",
4250cd4ada6aSchristos 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
4251cd4ada6aSchristos 			    fwd->listen_path);
4252cd4ada6aSchristos 		else if(fwd->listen_host != NULL)
4253cd4ada6aSchristos 			logit("Received request from %.100s port %d to "
4254cd4ada6aSchristos 			    "remote forward to host %.100s port %d, "
4255cd4ada6aSchristos 			    "but the request was denied.",
4256cd4ada6aSchristos 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
4257cd4ada6aSchristos 			    fwd->listen_host, fwd->listen_port );
4258cd4ada6aSchristos 		else
4259cd4ada6aSchristos 			logit("Received request from %.100s port %d to remote "
4260cd4ada6aSchristos 			    "forward, but the request was denied.",
4261cd4ada6aSchristos 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
426255a4608bSchristos 		return 0;
426355a4608bSchristos 	}
42648a4530f9Schristos 	if (fwd->listen_path != NULL) {
42657a183406Schristos 		return channel_setup_fwd_listener_streamlocal(ssh,
42668a4530f9Schristos 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
42678a4530f9Schristos 	} else {
42687a183406Schristos 		return channel_setup_fwd_listener_tcpip(ssh,
42698a4530f9Schristos 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
42708a4530f9Schristos 		    fwd_opts);
42718a4530f9Schristos 	}
4272ca32bd8dSchristos }
4273ca32bd8dSchristos 
4274ca32bd8dSchristos /*
4275091c4109Schristos  * Translate the requested rfwd listen host to something usable for
4276091c4109Schristos  * this server.
4277091c4109Schristos  */
4278091c4109Schristos static const char *
4279091c4109Schristos channel_rfwd_bind_host(const char *listen_host)
4280091c4109Schristos {
4281091c4109Schristos 	if (listen_host == NULL) {
4282091c4109Schristos 		return "localhost";
4283091c4109Schristos 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
4284091c4109Schristos 		return "";
4285091c4109Schristos 	} else
4286091c4109Schristos 		return listen_host;
4287091c4109Schristos }
4288091c4109Schristos 
4289091c4109Schristos /*
4290ca32bd8dSchristos  * Initiate forwarding of connections to port "port" on remote host through
4291ca32bd8dSchristos  * the secure channel to host:port from local side.
4292091c4109Schristos  * Returns handle (index) for updating the dynamic listen port with
429355a4608bSchristos  * channel_update_permission().
4294ca32bd8dSchristos  */
4295ca32bd8dSchristos int
42967a183406Schristos channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
4297ca32bd8dSchristos {
42987a183406Schristos 	int r, success = 0, idx = -1;
4299b1066cf3Schristos 	const char *host_to_connect, *listen_host, *listen_path;
43007a183406Schristos 	int port_to_connect, listen_port;
4301ca32bd8dSchristos 
4302ca32bd8dSchristos 	/* Send the forward request to the remote side. */
43038a4530f9Schristos 	if (fwd->listen_path != NULL) {
43047a183406Schristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43057a183406Schristos 		    (r = sshpkt_put_cstring(ssh,
43067a183406Schristos 		    "streamlocal-forward@openssh.com")) != 0 ||
43077a183406Schristos 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43087a183406Schristos 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
4309aa36fcacSchristos 		    (r = sshpkt_send(ssh)) != 0 ||
4310aa36fcacSchristos 		    (r = ssh_packet_write_wait(ssh)) < 0)
431117418e98Schristos 			fatal_fr(r, "request streamlocal");
43128a4530f9Schristos 	} else {
43137a183406Schristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43147a183406Schristos 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
43157a183406Schristos 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43167a183406Schristos 		    (r = sshpkt_put_cstring(ssh,
43177a183406Schristos 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
43187a183406Schristos 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
4319aa36fcacSchristos 		    (r = sshpkt_send(ssh)) != 0 ||
4320aa36fcacSchristos 		    (r = ssh_packet_write_wait(ssh)) < 0)
432117418e98Schristos 			fatal_fr(r, "request tcpip-forward");
43228a4530f9Schristos 	}
4323ca32bd8dSchristos 	/* Assume that server accepts the request */
4324ca32bd8dSchristos 	success = 1;
4325ca32bd8dSchristos 	if (success) {
432634b27b53Sadam 		/* Record that connection to this host/port is permitted. */
43277a183406Schristos 		host_to_connect = listen_host = listen_path = NULL;
43287a183406Schristos 		port_to_connect = listen_port = 0;
43298a4530f9Schristos 		if (fwd->connect_path != NULL) {
4330b1066cf3Schristos 			host_to_connect = fwd->connect_path;
43317a183406Schristos 			port_to_connect = PORT_STREAMLOCAL;
43328a4530f9Schristos 		} else {
4333b1066cf3Schristos 			host_to_connect = fwd->connect_host;
43347a183406Schristos 			port_to_connect = fwd->connect_port;
43358a4530f9Schristos 		}
43368a4530f9Schristos 		if (fwd->listen_path != NULL) {
4337b1066cf3Schristos 			listen_path = fwd->listen_path;
43387a183406Schristos 			listen_port = PORT_STREAMLOCAL;
43398a4530f9Schristos 		} else {
4340b1066cf3Schristos 			listen_host = fwd->listen_host;
43417a183406Schristos 			listen_port = fwd->listen_port;
43428a4530f9Schristos 		}
434355a4608bSchristos 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
43447a183406Schristos 		    host_to_connect, port_to_connect,
43457a183406Schristos 		    listen_host, listen_path, listen_port, NULL);
4346ca32bd8dSchristos 	}
43477a183406Schristos 	return idx;
4348ca32bd8dSchristos }
4349ca32bd8dSchristos 
43508a4530f9Schristos static int
435155a4608bSchristos open_match(struct permission *allowed_open, const char *requestedhost,
43528a4530f9Schristos     int requestedport)
43538a4530f9Schristos {
43548a4530f9Schristos 	if (allowed_open->host_to_connect == NULL)
43558a4530f9Schristos 		return 0;
43568a4530f9Schristos 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
43578a4530f9Schristos 	    allowed_open->port_to_connect != requestedport)
43588a4530f9Schristos 		return 0;
43595101d403Schristos 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
43605101d403Schristos 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
43618a4530f9Schristos 		return 0;
43628a4530f9Schristos 	return 1;
43638a4530f9Schristos }
43648a4530f9Schristos 
43658a4530f9Schristos /*
43668a4530f9Schristos  * Note that in the listen host/port case
43678a4530f9Schristos  * we don't support FWD_PERMIT_ANY_PORT and
43688a4530f9Schristos  * need to translate between the configured-host (listen_host)
43698a4530f9Schristos  * and what we've sent to the remote server (channel_rfwd_bind_host)
43708a4530f9Schristos  */
43718a4530f9Schristos static int
437255a4608bSchristos open_listen_match_tcpip(struct permission *allowed_open,
43738a4530f9Schristos     const char *requestedhost, u_short requestedport, int translate)
43748a4530f9Schristos {
43758a4530f9Schristos 	const char *allowed_host;
43768a4530f9Schristos 
43778a4530f9Schristos 	if (allowed_open->host_to_connect == NULL)
43788a4530f9Schristos 		return 0;
43798a4530f9Schristos 	if (allowed_open->listen_port != requestedport)
43808a4530f9Schristos 		return 0;
43818a4530f9Schristos 	if (!translate && allowed_open->listen_host == NULL &&
43828a4530f9Schristos 	    requestedhost == NULL)
43838a4530f9Schristos 		return 1;
43848a4530f9Schristos 	allowed_host = translate ?
43858a4530f9Schristos 	    channel_rfwd_bind_host(allowed_open->listen_host) :
43868a4530f9Schristos 	    allowed_open->listen_host;
438755a4608bSchristos 	if (allowed_host == NULL || requestedhost == NULL ||
43888a4530f9Schristos 	    strcmp(allowed_host, requestedhost) != 0)
43898a4530f9Schristos 		return 0;
43908a4530f9Schristos 	return 1;
43918a4530f9Schristos }
43928a4530f9Schristos 
43938a4530f9Schristos static int
439455a4608bSchristos open_listen_match_streamlocal(struct permission *allowed_open,
43958a4530f9Schristos     const char *requestedpath)
43968a4530f9Schristos {
43978a4530f9Schristos 	if (allowed_open->host_to_connect == NULL)
43988a4530f9Schristos 		return 0;
43998a4530f9Schristos 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
44008a4530f9Schristos 		return 0;
44018a4530f9Schristos 	if (allowed_open->listen_path == NULL ||
44028a4530f9Schristos 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
44038a4530f9Schristos 		return 0;
44048a4530f9Schristos 	return 1;
44058a4530f9Schristos }
44068a4530f9Schristos 
4407ca32bd8dSchristos /*
4408ca32bd8dSchristos  * Request cancellation of remote forwarding of connection host:port from
4409ca32bd8dSchristos  * local side.
4410ca32bd8dSchristos  */
44118a4530f9Schristos static int
44127a183406Schristos channel_request_rforward_cancel_tcpip(struct ssh *ssh,
44137a183406Schristos     const char *host, u_short port)
4414ca32bd8dSchristos {
44157a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
441655a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
44177a183406Schristos 	int r;
44187a183406Schristos 	u_int i;
44198db691beSchristos 	struct permission *perm = NULL;
4420ca32bd8dSchristos 
442155a4608bSchristos 	for (i = 0; i < pset->num_permitted_user; i++) {
442255a4608bSchristos 		perm = &pset->permitted_user[i];
442355a4608bSchristos 		if (open_listen_match_tcpip(perm, host, port, 0))
4424ca32bd8dSchristos 			break;
442555a4608bSchristos 		perm = NULL;
4426ca32bd8dSchristos 	}
442755a4608bSchristos 	if (perm == NULL) {
442817418e98Schristos 		debug_f("requested forward not found");
4429091c4109Schristos 		return -1;
4430ca32bd8dSchristos 	}
44317a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44327a183406Schristos 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
44337a183406Schristos 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44347a183406Schristos 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
44357a183406Schristos 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
44367a183406Schristos 	    (r = sshpkt_send(ssh)) != 0)
443717418e98Schristos 		fatal_fr(r, "send cancel");
4438ca32bd8dSchristos 
443955a4608bSchristos 	fwd_perm_clear(perm); /* unregister */
4440091c4109Schristos 
4441091c4109Schristos 	return 0;
4442ca32bd8dSchristos }
4443ca32bd8dSchristos 
4444ca32bd8dSchristos /*
44458a4530f9Schristos  * Request cancellation of remote forwarding of Unix domain socket
44468a4530f9Schristos  * path from local side.
44478a4530f9Schristos  */
44488a4530f9Schristos static int
44497a183406Schristos channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
44508a4530f9Schristos {
44517a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
445255a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
44537a183406Schristos 	int r;
44547a183406Schristos 	u_int i;
44558db691beSchristos 	struct permission *perm = NULL;
44568a4530f9Schristos 
445755a4608bSchristos 	for (i = 0; i < pset->num_permitted_user; i++) {
445855a4608bSchristos 		perm = &pset->permitted_user[i];
445955a4608bSchristos 		if (open_listen_match_streamlocal(perm, path))
44608a4530f9Schristos 			break;
446155a4608bSchristos 		perm = NULL;
44628a4530f9Schristos 	}
446355a4608bSchristos 	if (perm == NULL) {
446417418e98Schristos 		debug_f("requested forward not found");
44658a4530f9Schristos 		return -1;
44668a4530f9Schristos 	}
44677a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44687a183406Schristos 	    (r = sshpkt_put_cstring(ssh,
44697a183406Schristos 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
44707a183406Schristos 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44717a183406Schristos 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
44727a183406Schristos 	    (r = sshpkt_send(ssh)) != 0)
447317418e98Schristos 		fatal_fr(r, "send cancel");
44748a4530f9Schristos 
447555a4608bSchristos 	fwd_perm_clear(perm); /* unregister */
44768a4530f9Schristos 
44778a4530f9Schristos 	return 0;
44788a4530f9Schristos }
44798a4530f9Schristos 
44808a4530f9Schristos /*
44818a4530f9Schristos  * Request cancellation of remote forwarding of a connection from local side.
44828a4530f9Schristos  */
44838a4530f9Schristos int
44847a183406Schristos channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
44858a4530f9Schristos {
44868a4530f9Schristos 	if (fwd->listen_path != NULL) {
44877a183406Schristos 		return channel_request_rforward_cancel_streamlocal(ssh,
44887a183406Schristos 		    fwd->listen_path);
44898a4530f9Schristos 	} else {
44907a183406Schristos 		return channel_request_rforward_cancel_tcpip(ssh,
44917a183406Schristos 		    fwd->listen_host,
44927a183406Schristos 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
44938a4530f9Schristos 	}
44948a4530f9Schristos }
44958a4530f9Schristos 
44968a4530f9Schristos /*
449755a4608bSchristos  * Permits opening to any host/port if permitted_user[] is empty.  This is
4498ca32bd8dSchristos  * usually called by the server, because the user could connect to any port
4499ca32bd8dSchristos  * anyway, and the server has no way to know but to trust the client anyway.
4500ca32bd8dSchristos  */
4501ca32bd8dSchristos void
450255a4608bSchristos channel_permit_all(struct ssh *ssh, int where)
4503ca32bd8dSchristos {
450455a4608bSchristos 	struct permission_set *pset = permission_set_get(ssh, where);
450555a4608bSchristos 
450655a4608bSchristos 	if (pset->num_permitted_user == 0)
450755a4608bSchristos 		pset->all_permitted = 1;
4508ca32bd8dSchristos }
4509ca32bd8dSchristos 
451055a4608bSchristos /*
451155a4608bSchristos  * Permit the specified host/port for forwarding.
451255a4608bSchristos  */
4513ca32bd8dSchristos void
451455a4608bSchristos channel_add_permission(struct ssh *ssh, int who, int where,
451555a4608bSchristos     char *host, int port)
4516ca32bd8dSchristos {
451755a4608bSchristos 	int local = where == FORWARD_LOCAL;
451855a4608bSchristos 	struct permission_set *pset = permission_set_get(ssh, where);
45197a183406Schristos 
452055a4608bSchristos 	debug("allow %s forwarding to host %s port %d",
452155a4608bSchristos 	    fwd_ident(who, where), host, port);
452255a4608bSchristos 	/*
452355a4608bSchristos 	 * Remote forwards set listen_host/port, local forwards set
452455a4608bSchristos 	 * host/port_to_connect.
452555a4608bSchristos 	 */
452655a4608bSchristos 	permission_set_add(ssh, who, where,
452755a4608bSchristos 	    local ? host : 0, local ? port : 0,
452855a4608bSchristos 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
452955a4608bSchristos 	pset->all_permitted = 0;
453055a4608bSchristos }
453155a4608bSchristos 
453255a4608bSchristos /*
453355a4608bSchristos  * Administratively disable forwarding.
453455a4608bSchristos  */
453555a4608bSchristos void
453655a4608bSchristos channel_disable_admin(struct ssh *ssh, int where)
453755a4608bSchristos {
453855a4608bSchristos 	channel_clear_permission(ssh, FORWARD_ADM, where);
453955a4608bSchristos 	permission_set_add(ssh, FORWARD_ADM, where,
454055a4608bSchristos 	    NULL, 0, NULL, NULL, 0, NULL);
454155a4608bSchristos }
454255a4608bSchristos 
454355a4608bSchristos /*
454455a4608bSchristos  * Clear a list of permitted opens.
454555a4608bSchristos  */
454655a4608bSchristos void
454755a4608bSchristos channel_clear_permission(struct ssh *ssh, int who, int where)
454855a4608bSchristos {
454955a4608bSchristos 	struct permission **permp;
455055a4608bSchristos 	u_int *npermp;
455155a4608bSchristos 
455255a4608bSchristos 	permission_set_get_array(ssh, who, where, &permp, &npermp);
455355a4608bSchristos 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
455455a4608bSchristos 	*npermp = 0;
4555ca32bd8dSchristos }
4556ca32bd8dSchristos 
4557091c4109Schristos /*
4558091c4109Schristos  * Update the listen port for a dynamic remote forward, after
4559091c4109Schristos  * the actual 'newport' has been allocated. If 'newport' < 0 is
4560091c4109Schristos  * passed then they entry will be invalidated.
4561091c4109Schristos  */
4562091c4109Schristos void
456355a4608bSchristos channel_update_permission(struct ssh *ssh, int idx, int newport)
4564091c4109Schristos {
456555a4608bSchristos 	struct permission_set *pset = &ssh->chanctxt->local_perms;
45667a183406Schristos 
456755a4608bSchristos 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
456817418e98Schristos 		debug_f("index out of range: %d num_permitted_user %d",
456917418e98Schristos 		    idx, pset->num_permitted_user);
4570091c4109Schristos 		return;
4571091c4109Schristos 	}
4572091c4109Schristos 	debug("%s allowed port %d for forwarding to host %s port %d",
4573091c4109Schristos 	    newport > 0 ? "Updating" : "Removing",
4574091c4109Schristos 	    newport,
457555a4608bSchristos 	    pset->permitted_user[idx].host_to_connect,
457655a4608bSchristos 	    pset->permitted_user[idx].port_to_connect);
45777a183406Schristos 	if (newport <= 0)
457855a4608bSchristos 		fwd_perm_clear(&pset->permitted_user[idx]);
45797a183406Schristos 	else {
458055a4608bSchristos 		pset->permitted_user[idx].listen_port =
458117418e98Schristos 		    (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4582091c4109Schristos 	}
4583091c4109Schristos }
4584091c4109Schristos 
4585ca32bd8dSchristos /* Try to start non-blocking connect to next host in cctx list */
4586ca32bd8dSchristos static int
4587ca32bd8dSchristos connect_next(struct channel_connect *cctx)
4588ca32bd8dSchristos {
4589ca32bd8dSchristos 	int sock, saved_errno;
45908a4530f9Schristos 	struct sockaddr_un *sunaddr;
45917a183406Schristos 	char ntop[NI_MAXHOST];
45927a183406Schristos 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4593ca32bd8dSchristos 
4594ca32bd8dSchristos 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
45958a4530f9Schristos 		switch (cctx->ai->ai_family) {
45968a4530f9Schristos 		case AF_UNIX:
45978a4530f9Schristos 			/* unix:pathname instead of host:port */
45988a4530f9Schristos 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
45998a4530f9Schristos 			strlcpy(ntop, "unix", sizeof(ntop));
46008a4530f9Schristos 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
46018a4530f9Schristos 			break;
46028a4530f9Schristos 		case AF_INET:
46038a4530f9Schristos 		case AF_INET6:
4604ca32bd8dSchristos 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4605ca32bd8dSchristos 			    ntop, sizeof(ntop), strport, sizeof(strport),
4606ca32bd8dSchristos 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
4607e160b4e8Schristos 				error_f("getnameinfo failed");
4608ca32bd8dSchristos 				continue;
4609ca32bd8dSchristos 			}
46108a4530f9Schristos 			break;
46118a4530f9Schristos 		default:
46128a4530f9Schristos 			continue;
46138a4530f9Schristos 		}
4614e160b4e8Schristos 		debug_f("start for host %.100s ([%.100s]:%s)",
4615e160b4e8Schristos 		    cctx->host, ntop, strport);
4616ca32bd8dSchristos 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4617ca32bd8dSchristos 		    cctx->ai->ai_protocol)) == -1) {
4618ca32bd8dSchristos 			if (cctx->ai->ai_next == NULL)
4619ca32bd8dSchristos 				error("socket: %.100s", strerror(errno));
4620ca32bd8dSchristos 			else
4621ca32bd8dSchristos 				verbose("socket: %.100s", strerror(errno));
4622ca32bd8dSchristos 			continue;
4623ca32bd8dSchristos 		}
4624ca32bd8dSchristos 		if (set_nonblock(sock) == -1)
462517418e98Schristos 			fatal_f("set_nonblock(%d)", sock);
4626ca32bd8dSchristos 		if (connect(sock, cctx->ai->ai_addr,
4627ca32bd8dSchristos 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
4628e160b4e8Schristos 			debug_f("host %.100s ([%.100s]:%s): %.100s",
4629e160b4e8Schristos 			    cctx->host, ntop, strport, strerror(errno));
4630ca32bd8dSchristos 			saved_errno = errno;
4631ca32bd8dSchristos 			close(sock);
4632ca32bd8dSchristos 			errno = saved_errno;
4633ca32bd8dSchristos 			continue;	/* fail -- try next */
4634ca32bd8dSchristos 		}
46358a4530f9Schristos 		if (cctx->ai->ai_family != AF_UNIX)
46368a4530f9Schristos 			set_nodelay(sock);
4637e160b4e8Schristos 		debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d",
4638e160b4e8Schristos 		    cctx->host, ntop, strport, sock);
4639ca32bd8dSchristos 		cctx->ai = cctx->ai->ai_next;
4640ca32bd8dSchristos 		return sock;
4641ca32bd8dSchristos 	}
4642ca32bd8dSchristos 	return -1;
4643ca32bd8dSchristos }
4644ca32bd8dSchristos 
4645ca32bd8dSchristos static void
4646ca32bd8dSchristos channel_connect_ctx_free(struct channel_connect *cctx)
4647ca32bd8dSchristos {
464800a838c4Schristos 	free(cctx->host);
46498a4530f9Schristos 	if (cctx->aitop) {
46508a4530f9Schristos 		if (cctx->aitop->ai_family == AF_UNIX)
46518a4530f9Schristos 			free(cctx->aitop);
46528a4530f9Schristos 		else
4653ca32bd8dSchristos 			freeaddrinfo(cctx->aitop);
46548a4530f9Schristos 	}
46558a4530f9Schristos 	memset(cctx, 0, sizeof(*cctx));
4656ca32bd8dSchristos }
4657ca32bd8dSchristos 
465841768fc1Schristos /*
46597a183406Schristos  * Return connecting socket to remote host:port or local socket path,
466041768fc1Schristos  * passing back the failure reason if appropriate.
466141768fc1Schristos  */
46627a183406Schristos static int
46637a183406Schristos connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
46647a183406Schristos     const char *ctype, const char *rname, struct channel_connect *cctx,
46657a183406Schristos     int *reason, const char **errmsg)
4666ca32bd8dSchristos {
4667ca32bd8dSchristos 	struct addrinfo hints;
4668ca32bd8dSchristos 	int gaierr;
4669ca32bd8dSchristos 	int sock = -1;
4670ca32bd8dSchristos 	char strport[NI_MAXSERV];
46718a4530f9Schristos 
46728a4530f9Schristos 	if (port == PORT_STREAMLOCAL) {
46738a4530f9Schristos 		struct sockaddr_un *sunaddr;
46748a4530f9Schristos 		struct addrinfo *ai;
46758a4530f9Schristos 
46768a4530f9Schristos 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
46778a4530f9Schristos 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
46787a183406Schristos 			return -1;
46798a4530f9Schristos 		}
46808a4530f9Schristos 
46818a4530f9Schristos 		/*
46828a4530f9Schristos 		 * Fake up a struct addrinfo for AF_UNIX connections.
46838a4530f9Schristos 		 * channel_connect_ctx_free() must check ai_family
46848a4530f9Schristos 		 * and use free() not freeaddirinfo() for AF_UNIX.
46858a4530f9Schristos 		 */
46868a4530f9Schristos 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
46878a4530f9Schristos 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
46888a4530f9Schristos 		ai->ai_addr = (struct sockaddr *)(ai + 1);
46898a4530f9Schristos 		ai->ai_addrlen = sizeof(*sunaddr);
46908a4530f9Schristos 		ai->ai_family = AF_UNIX;
46917a183406Schristos 		ai->ai_socktype = socktype;
46928a4530f9Schristos 		ai->ai_protocol = PF_UNSPEC;
46938a4530f9Schristos 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
46948a4530f9Schristos 		sunaddr->sun_family = AF_UNIX;
46958a4530f9Schristos 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
46967a183406Schristos 		cctx->aitop = ai;
46978a4530f9Schristos 	} else {
4698ca32bd8dSchristos 		memset(&hints, 0, sizeof(hints));
46997a183406Schristos 		hints.ai_family = ssh->chanctxt->IPv4or6;
47007a183406Schristos 		hints.ai_socktype = socktype;
4701ca32bd8dSchristos 		snprintf(strport, sizeof strport, "%d", port);
47027a183406Schristos 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
470341768fc1Schristos 		    != 0) {
470441768fc1Schristos 			if (errmsg != NULL)
470541768fc1Schristos 				*errmsg = ssh_gai_strerror(gaierr);
470641768fc1Schristos 			if (reason != NULL)
470741768fc1Schristos 				*reason = SSH2_OPEN_CONNECT_FAILED;
47088a4530f9Schristos 			error("connect_to %.100s: unknown host (%s)", name,
4709ca32bd8dSchristos 			    ssh_gai_strerror(gaierr));
47107a183406Schristos 			return -1;
4711ca32bd8dSchristos 		}
47128a4530f9Schristos 	}
4713ca32bd8dSchristos 
47147a183406Schristos 	cctx->host = xstrdup(name);
47157a183406Schristos 	cctx->port = port;
47167a183406Schristos 	cctx->ai = cctx->aitop;
4717ca32bd8dSchristos 
47187a183406Schristos 	if ((sock = connect_next(cctx)) == -1) {
4719ca32bd8dSchristos 		error("connect to %.100s port %d failed: %s",
47208a4530f9Schristos 		    name, port, strerror(errno));
47217a183406Schristos 		return -1;
4722ca32bd8dSchristos 	}
47237a183406Schristos 
47247a183406Schristos 	return sock;
4725ca32bd8dSchristos }
4726ca32bd8dSchristos 
472741768fc1Schristos /* Return CONNECTING channel to remote host:port or local socket path */
472841768fc1Schristos static Channel *
47297a183406Schristos connect_to(struct ssh *ssh, const char *host, int port,
47307a183406Schristos     const char *ctype, const char *rname)
473141768fc1Schristos {
47327a183406Schristos 	struct channel_connect cctx;
47337a183406Schristos 	Channel *c;
47347a183406Schristos 	int sock;
47357a183406Schristos 
47367a183406Schristos 	memset(&cctx, 0, sizeof(cctx));
47377a183406Schristos 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
47387a183406Schristos 	    &cctx, NULL, NULL);
47397a183406Schristos 	if (sock == -1) {
47407a183406Schristos 		channel_connect_ctx_free(&cctx);
47417a183406Schristos 		return NULL;
47427a183406Schristos 	}
47437a183406Schristos 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
47447a183406Schristos 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
47457a183406Schristos 	c->host_port = port;
47467a183406Schristos 	c->path = xstrdup(host);
47477a183406Schristos 	c->connect_ctx = cctx;
47487a183406Schristos 
47497a183406Schristos 	return c;
475041768fc1Schristos }
475141768fc1Schristos 
4752ee85abc4Schristos /*
4753ee85abc4Schristos  * returns either the newly connected channel or the downstream channel
4754ee85abc4Schristos  * that needs to deal with this connection.
4755ee85abc4Schristos  */
4756ca32bd8dSchristos Channel *
47577a183406Schristos channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
47587a183406Schristos     u_short listen_port, const char *ctype, const char *rname)
4759ca32bd8dSchristos {
47607a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
476155a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
47627a183406Schristos 	u_int i;
476355a4608bSchristos 	struct permission *perm;
4764ca32bd8dSchristos 
476555a4608bSchristos 	for (i = 0; i < pset->num_permitted_user; i++) {
476655a4608bSchristos 		perm = &pset->permitted_user[i];
476755a4608bSchristos 		if (open_listen_match_tcpip(perm,
476855a4608bSchristos 		    listen_host, listen_port, 1)) {
476955a4608bSchristos 			if (perm->downstream)
477055a4608bSchristos 				return perm->downstream;
477155a4608bSchristos 			if (perm->port_to_connect == 0)
47727a183406Schristos 				return rdynamic_connect_prepare(ssh,
47737a183406Schristos 				    ctype, rname);
47747a183406Schristos 			return connect_to(ssh,
477555a4608bSchristos 			    perm->host_to_connect, perm->port_to_connect,
47767a183406Schristos 			    ctype, rname);
4777ca32bd8dSchristos 		}
4778ca32bd8dSchristos 	}
4779ca32bd8dSchristos 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4780ca32bd8dSchristos 	    listen_port);
4781ca32bd8dSchristos 	return NULL;
4782ca32bd8dSchristos }
4783ca32bd8dSchristos 
47848a4530f9Schristos Channel *
47857a183406Schristos channel_connect_by_listen_path(struct ssh *ssh, const char *path,
47867a183406Schristos     const char *ctype, const char *rname)
47878a4530f9Schristos {
47887a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
478955a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
47907a183406Schristos 	u_int i;
479155a4608bSchristos 	struct permission *perm;
47928a4530f9Schristos 
479355a4608bSchristos 	for (i = 0; i < pset->num_permitted_user; i++) {
479455a4608bSchristos 		perm = &pset->permitted_user[i];
479555a4608bSchristos 		if (open_listen_match_streamlocal(perm, path)) {
47967a183406Schristos 			return connect_to(ssh,
479755a4608bSchristos 			    perm->host_to_connect, perm->port_to_connect,
47987a183406Schristos 			    ctype, rname);
47998a4530f9Schristos 		}
48008a4530f9Schristos 	}
48018a4530f9Schristos 	error("WARNING: Server requests forwarding for unknown path %.100s",
48028a4530f9Schristos 	    path);
48038a4530f9Schristos 	return NULL;
48048a4530f9Schristos }
48058a4530f9Schristos 
4806ca32bd8dSchristos /* Check if connecting to that port is permitted and connect. */
4807ca32bd8dSchristos Channel *
48087a183406Schristos channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
48097a183406Schristos     const char *ctype, const char *rname, int *reason, const char **errmsg)
4810ca32bd8dSchristos {
48117a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
481255a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
48137a183406Schristos 	struct channel_connect cctx;
48147a183406Schristos 	Channel *c;
48157a183406Schristos 	u_int i, permit, permit_adm = 1;
48167a183406Schristos 	int sock;
481755a4608bSchristos 	struct permission *perm;
4818ca32bd8dSchristos 
481955a4608bSchristos 	permit = pset->all_permitted;
4820ca32bd8dSchristos 	if (!permit) {
482155a4608bSchristos 		for (i = 0; i < pset->num_permitted_user; i++) {
482255a4608bSchristos 			perm = &pset->permitted_user[i];
482355a4608bSchristos 			if (open_match(perm, host, port)) {
4824ca32bd8dSchristos 				permit = 1;
48258a4530f9Schristos 				break;
48268a4530f9Schristos 			}
4827ca32bd8dSchristos 		}
48287a183406Schristos 	}
4829ca32bd8dSchristos 
483055a4608bSchristos 	if (pset->num_permitted_admin > 0) {
4831ca32bd8dSchristos 		permit_adm = 0;
483255a4608bSchristos 		for (i = 0; i < pset->num_permitted_admin; i++) {
483355a4608bSchristos 			perm = &pset->permitted_admin[i];
483455a4608bSchristos 			if (open_match(perm, host, port)) {
4835ca32bd8dSchristos 				permit_adm = 1;
48368a4530f9Schristos 				break;
48378a4530f9Schristos 			}
4838ca32bd8dSchristos 		}
48397a183406Schristos 	}
4840ca32bd8dSchristos 
4841ca32bd8dSchristos 	if (!permit || !permit_adm) {
4842cd4ada6aSchristos 		logit("Received request from %.100s port %d to connect to "
4843cd4ada6aSchristos 		    "host %.100s port %d, but the request was denied.",
4844cd4ada6aSchristos 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port);
484541768fc1Schristos 		if (reason != NULL)
484641768fc1Schristos 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4847ca32bd8dSchristos 		return NULL;
4848ca32bd8dSchristos 	}
48497a183406Schristos 
48507a183406Schristos 	memset(&cctx, 0, sizeof(cctx));
48517a183406Schristos 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
48527a183406Schristos 	    &cctx, reason, errmsg);
48537a183406Schristos 	if (sock == -1) {
48547a183406Schristos 		channel_connect_ctx_free(&cctx);
48557a183406Schristos 		return NULL;
48567a183406Schristos 	}
48577a183406Schristos 
48587a183406Schristos 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
48597a183406Schristos 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
48607a183406Schristos 	c->host_port = port;
48617a183406Schristos 	c->path = xstrdup(host);
48627a183406Schristos 	c->connect_ctx = cctx;
48637a183406Schristos 
48647a183406Schristos 	return c;
4865ca32bd8dSchristos }
4866ca32bd8dSchristos 
48678a4530f9Schristos /* Check if connecting to that path is permitted and connect. */
48688a4530f9Schristos Channel *
48697a183406Schristos channel_connect_to_path(struct ssh *ssh, const char *path, const char *ctype,
48707a183406Schristos     const char *rname)
48718a4530f9Schristos {
48727a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
487355a4608bSchristos 	struct permission_set *pset = &sc->local_perms;
48747a183406Schristos 	u_int i, permit, permit_adm = 1;
487555a4608bSchristos 	struct permission *perm;
48768a4530f9Schristos 
487755a4608bSchristos 	permit = pset->all_permitted;
48788a4530f9Schristos 	if (!permit) {
487955a4608bSchristos 		for (i = 0; i < pset->num_permitted_user; i++) {
488055a4608bSchristos 			perm = &pset->permitted_user[i];
488155a4608bSchristos 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
48828a4530f9Schristos 				permit = 1;
48838a4530f9Schristos 				break;
48848a4530f9Schristos 			}
48858a4530f9Schristos 		}
48867a183406Schristos 	}
48878a4530f9Schristos 
488855a4608bSchristos 	if (pset->num_permitted_admin > 0) {
48898a4530f9Schristos 		permit_adm = 0;
489055a4608bSchristos 		for (i = 0; i < pset->num_permitted_admin; i++) {
489155a4608bSchristos 			perm = &pset->permitted_admin[i];
489255a4608bSchristos 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
48938a4530f9Schristos 				permit_adm = 1;
48948a4530f9Schristos 				break;
48958a4530f9Schristos 			}
48968a4530f9Schristos 		}
48977a183406Schristos 	}
48988a4530f9Schristos 
48998a4530f9Schristos 	if (!permit || !permit_adm) {
49008a4530f9Schristos 		logit("Received request to connect to path %.100s, "
49018a4530f9Schristos 		    "but the request was denied.", path);
49028a4530f9Schristos 		return NULL;
49038a4530f9Schristos 	}
49047a183406Schristos 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
49058a4530f9Schristos }
49068a4530f9Schristos 
4907ca32bd8dSchristos void
49087a183406Schristos channel_send_window_changes(struct ssh *ssh)
4909ca32bd8dSchristos {
49107a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
4911ca32bd8dSchristos 	struct winsize ws;
49127a183406Schristos 	int r;
49137a183406Schristos 	u_int i;
4914ca32bd8dSchristos 
49157a183406Schristos 	for (i = 0; i < sc->channels_alloc; i++) {
49167a183406Schristos 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
49177a183406Schristos 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
4918ca32bd8dSchristos 			continue;
4919cd4ada6aSchristos 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1)
4920ca32bd8dSchristos 			continue;
49217a183406Schristos 		channel_request_start(ssh, i, "window-change", 0);
49227a183406Schristos 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
49237a183406Schristos 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
49247a183406Schristos 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
49257a183406Schristos 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
49267a183406Schristos 		    (r = sshpkt_send(ssh)) != 0)
492717418e98Schristos 			fatal_fr(r, "channel %u; send window-change", i);
4928ca32bd8dSchristos 	}
4929ca32bd8dSchristos }
4930ca32bd8dSchristos 
49317a183406Schristos /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
49327a183406Schristos static Channel *
49337a183406Schristos rdynamic_connect_prepare(struct ssh *ssh, const char *ctype, const char *rname)
49347a183406Schristos {
49357a183406Schristos 	Channel *c;
49367a183406Schristos 	int r;
49377a183406Schristos 
49387a183406Schristos 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
49397a183406Schristos 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
49407a183406Schristos 	c->host_port = 0;
49417a183406Schristos 	c->path = NULL;
49427a183406Schristos 
49437a183406Schristos 	/*
49447a183406Schristos 	 * We need to open the channel before we have a FD,
49457a183406Schristos 	 * so that we can get SOCKS header from peer.
49467a183406Schristos 	 */
49477a183406Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
49487a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
49497a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
49507a183406Schristos 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
495117418e98Schristos 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0)
495217418e98Schristos 		fatal_fr(r, "channel %i; confirm", c->self);
49537a183406Schristos 	return c;
49547a183406Schristos }
49557a183406Schristos 
49567a183406Schristos /* Return CONNECTING socket to remote host:port or local socket path */
49577a183406Schristos static int
49587a183406Schristos rdynamic_connect_finish(struct ssh *ssh, Channel *c)
49597a183406Schristos {
496017418e98Schristos 	struct ssh_channels *sc = ssh->chanctxt;
496117418e98Schristos 	struct permission_set *pset = &sc->local_perms;
496217418e98Schristos 	struct permission *perm;
49637a183406Schristos 	struct channel_connect cctx;
496417418e98Schristos 	u_int i, permit_adm = 1;
49657a183406Schristos 	int sock;
49667a183406Schristos 
496717418e98Schristos 	if (pset->num_permitted_admin > 0) {
496817418e98Schristos 		permit_adm = 0;
496917418e98Schristos 		for (i = 0; i < pset->num_permitted_admin; i++) {
497017418e98Schristos 			perm = &pset->permitted_admin[i];
497117418e98Schristos 			if (open_match(perm, c->path, c->host_port)) {
497217418e98Schristos 				permit_adm = 1;
497317418e98Schristos 				break;
497417418e98Schristos 			}
497517418e98Schristos 		}
497617418e98Schristos 	}
497717418e98Schristos 	if (!permit_adm) {
497817418e98Schristos 		debug_f("requested forward not permitted");
497917418e98Schristos 		return -1;
498017418e98Schristos 	}
498117418e98Schristos 
49827a183406Schristos 	memset(&cctx, 0, sizeof(cctx));
49837a183406Schristos 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
49847a183406Schristos 	    NULL, &cctx, NULL, NULL);
49857a183406Schristos 	if (sock == -1)
49867a183406Schristos 		channel_connect_ctx_free(&cctx);
49877a183406Schristos 	else {
49887a183406Schristos 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
49897a183406Schristos 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
49907a183406Schristos 		c->connect_ctx = cctx;
49917a183406Schristos 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
49927a183406Schristos 	}
49937a183406Schristos 	return sock;
49947a183406Schristos }
49957a183406Schristos 
4996ca32bd8dSchristos /* -- X11 forwarding */
4997ca32bd8dSchristos 
4998ca32bd8dSchristos /*
4999ca32bd8dSchristos  * Creates an internet domain socket for listening for X11 connections.
5000ca32bd8dSchristos  * Returns 0 and a suitable display number for the DISPLAY variable
5001ca32bd8dSchristos  * stored in display_numberp , or -1 if an error occurs.
5002ca32bd8dSchristos  */
5003ca32bd8dSchristos int
50047a183406Schristos x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
50057a183406Schristos     int x11_use_localhost, int single_connection,
50067a183406Schristos     u_int *display_numberp, int **chanids)
5007ca32bd8dSchristos {
5008ca32bd8dSchristos 	Channel *nc = NULL;
5009ca32bd8dSchristos 	int display_number, sock;
5010ca32bd8dSchristos 	u_short port;
5011ca32bd8dSchristos 	struct addrinfo hints, *ai, *aitop;
5012ca32bd8dSchristos 	char strport[NI_MAXSERV];
5013ca32bd8dSchristos 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
5014ca32bd8dSchristos 
5015ca32bd8dSchristos 	if (chanids == NULL)
5016ca32bd8dSchristos 		return -1;
5017ca32bd8dSchristos 
5018ca32bd8dSchristos 	for (display_number = x11_display_offset;
5019ca32bd8dSchristos 	    display_number < MAX_DISPLAYS;
5020ca32bd8dSchristos 	    display_number++) {
5021ca32bd8dSchristos 		port = 6000 + display_number;
5022ca32bd8dSchristos 		memset(&hints, 0, sizeof(hints));
50237a183406Schristos 		hints.ai_family = ssh->chanctxt->IPv4or6;
5024ca32bd8dSchristos 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
5025ca32bd8dSchristos 		hints.ai_socktype = SOCK_STREAM;
5026ca32bd8dSchristos 		snprintf(strport, sizeof strport, "%d", port);
50277a183406Schristos 		if ((gaierr = getaddrinfo(NULL, strport,
50287a183406Schristos 		    &hints, &aitop)) != 0) {
5029ca32bd8dSchristos 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
5030ca32bd8dSchristos 			return -1;
5031ca32bd8dSchristos 		}
5032ca32bd8dSchristos 		for (ai = aitop; ai; ai = ai->ai_next) {
50337a183406Schristos 			if (ai->ai_family != AF_INET &&
50347a183406Schristos 			    ai->ai_family != AF_INET6)
5035ca32bd8dSchristos 				continue;
5036ca32bd8dSchristos 			sock = socket(ai->ai_family, ai->ai_socktype,
5037ca32bd8dSchristos 			    ai->ai_protocol);
5038cd4ada6aSchristos 			if (sock == -1) {
5039ca32bd8dSchristos 				error("socket: %.100s", strerror(errno));
5040ca32bd8dSchristos 				freeaddrinfo(aitop);
5041ca32bd8dSchristos 				return -1;
5042ca32bd8dSchristos 			}
5043ffae97bbSchristos 			set_reuseaddr(sock);
5044cd4ada6aSchristos 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
504517418e98Schristos 				debug2_f("bind port %d: %.100s", port,
504617418e98Schristos 				    strerror(errno));
5047ca32bd8dSchristos 				close(sock);
50487a183406Schristos 				for (n = 0; n < num_socks; n++)
5049ca32bd8dSchristos 					close(socks[n]);
5050ca32bd8dSchristos 				num_socks = 0;
5051ca32bd8dSchristos 				break;
5052ca32bd8dSchristos 			}
5053ca32bd8dSchristos 			socks[num_socks++] = sock;
5054ca32bd8dSchristos 			if (num_socks == NUM_SOCKS)
5055ca32bd8dSchristos 				break;
5056ca32bd8dSchristos 		}
5057ca32bd8dSchristos 		freeaddrinfo(aitop);
5058ca32bd8dSchristos 		if (num_socks > 0)
5059ca32bd8dSchristos 			break;
5060ca32bd8dSchristos 	}
5061ca32bd8dSchristos 	if (display_number >= MAX_DISPLAYS) {
5062ca32bd8dSchristos 		error("Failed to allocate internet-domain X11 display socket.");
5063ca32bd8dSchristos 		return -1;
5064ca32bd8dSchristos 	}
5065ca32bd8dSchristos 	/* Start listening for connections on the socket. */
5066ca32bd8dSchristos 	for (n = 0; n < num_socks; n++) {
5067ca32bd8dSchristos 		sock = socks[n];
5068cd4ada6aSchristos 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
5069ca32bd8dSchristos 			error("listen: %.100s", strerror(errno));
5070ca32bd8dSchristos 			close(sock);
5071ca32bd8dSchristos 			return -1;
5072ca32bd8dSchristos 		}
5073ca32bd8dSchristos 	}
5074ca32bd8dSchristos 
5075ca32bd8dSchristos 	/* Allocate a channel for each socket. */
5076ca32bd8dSchristos 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
5077ca32bd8dSchristos 	for (n = 0; n < num_socks; n++) {
5078ca32bd8dSchristos 		sock = socks[n];
5079313c6c94Schristos 		/* Is this really necassary? */
5080313c6c94Schristos 		if (hpn_disabled)
5081b1066cf3Schristos 		nc = channel_new(ssh, "x11-listener",
5082ca32bd8dSchristos 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
5083ca32bd8dSchristos 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
5084ca32bd8dSchristos 		    0, "X11 inet listener", 1);
5085313c6c94Schristos 		else
50867a183406Schristos 			nc = channel_new(ssh, "x11 listener",
5087313c6c94Schristos 			    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
5088313c6c94Schristos 			    hpn_buffer_size, CHAN_X11_PACKET_DEFAULT,
5089313c6c94Schristos 			    0, "X11 inet listener", 1);
5090ca32bd8dSchristos 		nc->single_connection = single_connection;
5091ca32bd8dSchristos 		(*chanids)[n] = nc->self;
5092ca32bd8dSchristos 	}
5093ca32bd8dSchristos 	(*chanids)[n] = -1;
5094ca32bd8dSchristos 
5095ca32bd8dSchristos 	/* Return the display number for the DISPLAY environment variable. */
5096ca32bd8dSchristos 	*display_numberp = display_number;
50977a183406Schristos 	return 0;
5098ca32bd8dSchristos }
5099ca32bd8dSchristos 
5100ca32bd8dSchristos static int
5101ca32bd8dSchristos connect_local_xsocket(u_int dnr)
5102ca32bd8dSchristos {
5103ca32bd8dSchristos 	int sock;
5104ca32bd8dSchristos 	struct sockaddr_un addr;
5105ca32bd8dSchristos 
5106ca32bd8dSchristos 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
5107cd4ada6aSchristos 	if (sock == -1)
5108ca32bd8dSchristos 		error("socket: %.100s", strerror(errno));
5109ca32bd8dSchristos 	memset(&addr, 0, sizeof(addr));
5110ca32bd8dSchristos 	addr.sun_family = AF_UNIX;
5111ca32bd8dSchristos 	snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
5112ca32bd8dSchristos 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
5113ca32bd8dSchristos 		return sock;
5114ca32bd8dSchristos 	close(sock);
5115ca32bd8dSchristos 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
5116ca32bd8dSchristos 	return -1;
5117ca32bd8dSchristos }
5118ca32bd8dSchristos 
5119ca32bd8dSchristos int
51207a183406Schristos x11_connect_display(struct ssh *ssh)
5121ca32bd8dSchristos {
5122ca32bd8dSchristos 	u_int display_number;
5123ca32bd8dSchristos 	const char *display;
5124ca32bd8dSchristos 	char buf[1024], *cp;
5125ca32bd8dSchristos 	struct addrinfo hints, *ai, *aitop;
5126ca32bd8dSchristos 	char strport[NI_MAXSERV];
5127ca32bd8dSchristos 	int gaierr, sock = 0;
5128ca32bd8dSchristos 
5129ca32bd8dSchristos 	/* Try to open a socket for the local X server. */
5130ca32bd8dSchristos 	display = getenv("DISPLAY");
5131ca32bd8dSchristos 	if (!display) {
5132ca32bd8dSchristos 		error("DISPLAY not set.");
5133ca32bd8dSchristos 		return -1;
5134ca32bd8dSchristos 	}
5135ca32bd8dSchristos 	/*
5136ca32bd8dSchristos 	 * Now we decode the value of the DISPLAY variable and make a
5137ca32bd8dSchristos 	 * connection to the real X server.
5138ca32bd8dSchristos 	 */
5139ca32bd8dSchristos 
5140ca32bd8dSchristos 	/*
5141ca32bd8dSchristos 	 * Check if it is a unix domain socket.  Unix domain displays are in
5142ca32bd8dSchristos 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
5143ca32bd8dSchristos 	 */
5144ca32bd8dSchristos 	if (strncmp(display, "unix:", 5) == 0 ||
5145ca32bd8dSchristos 	    display[0] == ':') {
5146ca32bd8dSchristos 		/* Connect to the unix domain socket. */
51477a183406Schristos 		if (sscanf(strrchr(display, ':') + 1, "%u",
51487a183406Schristos 		    &display_number) != 1) {
51497a183406Schristos 			error("Could not parse display number from DISPLAY: "
51507a183406Schristos 			    "%.100s", display);
5151ca32bd8dSchristos 			return -1;
5152ca32bd8dSchristos 		}
5153ca32bd8dSchristos 		/* Create a socket. */
5154ca32bd8dSchristos 		sock = connect_local_xsocket(display_number);
5155ca32bd8dSchristos 		if (sock < 0)
5156ca32bd8dSchristos 			return -1;
5157ca32bd8dSchristos 
5158ca32bd8dSchristos 		/* OK, we now have a connection to the display. */
5159ca32bd8dSchristos 		return sock;
5160ca32bd8dSchristos 	}
5161ca32bd8dSchristos 	/*
5162ca32bd8dSchristos 	 * Connect to an inet socket.  The DISPLAY value is supposedly
5163ca32bd8dSchristos 	 * hostname:d[.s], where hostname may also be numeric IP address.
5164ca32bd8dSchristos 	 */
5165ca32bd8dSchristos 	strlcpy(buf, display, sizeof(buf));
5166ca32bd8dSchristos 	cp = strchr(buf, ':');
5167ca32bd8dSchristos 	if (!cp) {
5168ca32bd8dSchristos 		error("Could not find ':' in DISPLAY: %.100s", display);
5169ca32bd8dSchristos 		return -1;
5170ca32bd8dSchristos 	}
5171ca32bd8dSchristos 	*cp = 0;
51727a183406Schristos 	/*
51737a183406Schristos 	 * buf now contains the host name.  But first we parse the
51747a183406Schristos 	 * display number.
51757a183406Schristos 	 */
5176ca32bd8dSchristos 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
5177ca32bd8dSchristos 		error("Could not parse display number from DISPLAY: %.100s",
5178ca32bd8dSchristos 		    display);
5179ca32bd8dSchristos 		return -1;
5180ca32bd8dSchristos 	}
5181ca32bd8dSchristos 
5182ca32bd8dSchristos 	/* Look up the host address */
5183ca32bd8dSchristos 	memset(&hints, 0, sizeof(hints));
51847a183406Schristos 	hints.ai_family = ssh->chanctxt->IPv4or6;
5185ca32bd8dSchristos 	hints.ai_socktype = SOCK_STREAM;
5186ca32bd8dSchristos 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
5187ca32bd8dSchristos 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
5188ca32bd8dSchristos 		error("%.100s: unknown host. (%s)", buf,
5189ca32bd8dSchristos 		ssh_gai_strerror(gaierr));
5190ca32bd8dSchristos 		return -1;
5191ca32bd8dSchristos 	}
5192ca32bd8dSchristos 	for (ai = aitop; ai; ai = ai->ai_next) {
5193ca32bd8dSchristos 		/* Create a socket. */
5194ca32bd8dSchristos 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
5195cd4ada6aSchristos 		if (sock == -1) {
5196ca32bd8dSchristos 			debug2("socket: %.100s", strerror(errno));
5197ca32bd8dSchristos 			continue;
5198ca32bd8dSchristos 		}
5199ca32bd8dSchristos 		/* Connect it to the display. */
5200cd4ada6aSchristos 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
5201ca32bd8dSchristos 			debug2("connect %.100s port %u: %.100s", buf,
5202ca32bd8dSchristos 			    6000 + display_number, strerror(errno));
5203ca32bd8dSchristos 			close(sock);
5204ca32bd8dSchristos 			continue;
5205ca32bd8dSchristos 		}
5206ca32bd8dSchristos 		/* Success */
5207ca32bd8dSchristos 		break;
5208ca32bd8dSchristos 	}
5209ca32bd8dSchristos 	freeaddrinfo(aitop);
5210ca32bd8dSchristos 	if (!ai) {
52117a183406Schristos 		error("connect %.100s port %u: %.100s", buf,
52127a183406Schristos 		    6000 + display_number, strerror(errno));
5213ca32bd8dSchristos 		return -1;
5214ca32bd8dSchristos 	}
5215ca32bd8dSchristos 	set_nodelay(sock);
5216ca32bd8dSchristos 	return sock;
5217ca32bd8dSchristos }
5218ca32bd8dSchristos 
5219ca32bd8dSchristos /*
5220ca32bd8dSchristos  * Requests forwarding of X11 connections, generates fake authentication
5221ca32bd8dSchristos  * data, and enables authentication spoofing.
5222ca32bd8dSchristos  * This should be called in the client only.
5223ca32bd8dSchristos  */
5224ca32bd8dSchristos void
52257a183406Schristos x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
52267a183406Schristos     const char *disp, const char *proto, const char *data, int want_reply)
5227ca32bd8dSchristos {
52287a183406Schristos 	struct ssh_channels *sc = ssh->chanctxt;
5229ca32bd8dSchristos 	u_int data_len = (u_int) strlen(data) / 2;
5230ca32bd8dSchristos 	u_int i, value;
5231ca32bd8dSchristos 	const char *cp;
52327a183406Schristos 	char *new_data;
52337a183406Schristos 	int r, screen_number;
5234ca32bd8dSchristos 
52357a183406Schristos 	if (sc->x11_saved_display == NULL)
52367a183406Schristos 		sc->x11_saved_display = xstrdup(disp);
52377a183406Schristos 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
5238ca32bd8dSchristos 		error("x11_request_forwarding_with_spoofing: different "
5239ca32bd8dSchristos 		    "$DISPLAY already forwarded");
5240ca32bd8dSchristos 		return;
5241ca32bd8dSchristos 	}
5242ca32bd8dSchristos 
5243ca32bd8dSchristos 	cp = strchr(disp, ':');
5244ca32bd8dSchristos 	if (cp)
5245ca32bd8dSchristos 		cp = strchr(cp, '.');
5246ca32bd8dSchristos 	if (cp)
5247ca32bd8dSchristos 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
5248ca32bd8dSchristos 	else
5249ca32bd8dSchristos 		screen_number = 0;
5250ca32bd8dSchristos 
52517a183406Schristos 	if (sc->x11_saved_proto == NULL) {
5252ca32bd8dSchristos 		/* Save protocol name. */
52537a183406Schristos 		sc->x11_saved_proto = xstrdup(proto);
5254ee85abc4Schristos 
5255ee85abc4Schristos 		/* Extract real authentication data. */
52567a183406Schristos 		sc->x11_saved_data = xmalloc(data_len);
5257ca32bd8dSchristos 		for (i = 0; i < data_len; i++) {
525817418e98Schristos 			if (sscanf(data + 2 * i, "%2x", &value) != 1) {
5259ca32bd8dSchristos 				fatal("x11_request_forwarding: bad "
5260ca32bd8dSchristos 				    "authentication data: %.100s", data);
526117418e98Schristos 			}
52627a183406Schristos 			sc->x11_saved_data[i] = value;
5263ca32bd8dSchristos 		}
52647a183406Schristos 		sc->x11_saved_data_len = data_len;
5265ee85abc4Schristos 
5266ee85abc4Schristos 		/* Generate fake data of the same length. */
52677a183406Schristos 		sc->x11_fake_data = xmalloc(data_len);
52687a183406Schristos 		arc4random_buf(sc->x11_fake_data, data_len);
52697a183406Schristos 		sc->x11_fake_data_len = data_len;
5270ca32bd8dSchristos 	}
5271ca32bd8dSchristos 
5272ca32bd8dSchristos 	/* Convert the fake data into hex. */
52737a183406Schristos 	new_data = tohex(sc->x11_fake_data, data_len);
5274ca32bd8dSchristos 
5275ca32bd8dSchristos 	/* Send the request packet. */
52767a183406Schristos 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
52777a183406Schristos 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
52787a183406Schristos 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
52797a183406Schristos 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
52807a183406Schristos 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
5281aa36fcacSchristos 	    (r = sshpkt_send(ssh)) != 0 ||
5282aa36fcacSchristos 	    (r = ssh_packet_write_wait(ssh)) < 0)
528317418e98Schristos 		fatal_fr(r, "send x11-req");
528400a838c4Schristos 	free(new_data);
5285ca32bd8dSchristos }
5286