xref: /freebsd/crypto/openssh/channels.c (revision 190cef3d52236565eb22e18b33e9e865ec634aa3)
1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: channels.c,v 1.384 2018/07/27 12:03:17 markus Exp $ */
2511b41d2SMark Murray /*
3511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5511b41d2SMark Murray  *                    All rights reserved
6511b41d2SMark Murray  * This file contains functions for generic socket connection forwarding.
7511b41d2SMark Murray  * There is also code for initiating connection forwarding for X11 connections,
8511b41d2SMark Murray  * arbitrary tcp/ip connections, and the authentication agent connection.
9511b41d2SMark Murray  *
10b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
11b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
12b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
13b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
14b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
15b66f2d16SKris Kennaway  *
16a04a10f8SKris Kennaway  * SSH2 support added by Markus Friedl.
17af12a3e7SDag-Erling Smørgrav  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
18b66f2d16SKris Kennaway  * Copyright (c) 1999 Dug Song.  All rights reserved.
19b66f2d16SKris Kennaway  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
20b66f2d16SKris Kennaway  *
21b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
22b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
23b66f2d16SKris Kennaway  * are met:
24b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
25b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
26b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
27b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
28b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
29b66f2d16SKris Kennaway  *
30b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40511b41d2SMark Murray  */
41511b41d2SMark Murray 
42511b41d2SMark Murray #include "includes.h"
43a04a10f8SKris Kennaway 
44333ee039SDag-Erling Smørgrav #include <sys/types.h>
45a0ee8cc6SDag-Erling Smørgrav #include <sys/stat.h>
46333ee039SDag-Erling Smørgrav #include <sys/ioctl.h>
47333ee039SDag-Erling Smørgrav #include <sys/un.h>
48333ee039SDag-Erling Smørgrav #include <sys/socket.h>
49333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
50333ee039SDag-Erling Smørgrav # include <sys/time.h>
51333ee039SDag-Erling Smørgrav #endif
52333ee039SDag-Erling Smørgrav 
53333ee039SDag-Erling Smørgrav #include <netinet/in.h>
54333ee039SDag-Erling Smørgrav #include <arpa/inet.h>
55333ee039SDag-Erling Smørgrav 
56333ee039SDag-Erling Smørgrav #include <errno.h>
57b15c8340SDag-Erling Smørgrav #include <fcntl.h>
584f52dfbbSDag-Erling Smørgrav #include <limits.h>
59333ee039SDag-Erling Smørgrav #include <netdb.h>
604f52dfbbSDag-Erling Smørgrav #include <stdarg.h>
61bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H
62bc5531deSDag-Erling Smørgrav  #include <stdint.h>
63bc5531deSDag-Erling Smørgrav #endif
64333ee039SDag-Erling Smørgrav #include <stdio.h>
65333ee039SDag-Erling Smørgrav #include <stdlib.h>
66333ee039SDag-Erling Smørgrav #include <string.h>
67333ee039SDag-Erling Smørgrav #include <termios.h>
68333ee039SDag-Erling Smørgrav #include <unistd.h>
69333ee039SDag-Erling Smørgrav 
70d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
71333ee039SDag-Erling Smørgrav #include "xmalloc.h"
72ca3176e7SBrian Feldman #include "ssh.h"
73ca3176e7SBrian Feldman #include "ssh2.h"
74ca86bcf2SDag-Erling Smørgrav #include "ssherr.h"
754f52dfbbSDag-Erling Smørgrav #include "sshbuf.h"
76ca3176e7SBrian Feldman #include "packet.h"
77ca3176e7SBrian Feldman #include "log.h"
78ca3176e7SBrian Feldman #include "misc.h"
79ca3176e7SBrian Feldman #include "channels.h"
80ca3176e7SBrian Feldman #include "compat.h"
81ca3176e7SBrian Feldman #include "canohost.h"
82*190cef3dSDag-Erling Smørgrav #include "sshkey.h"
83b66f2d16SKris Kennaway #include "authfd.h"
84af12a3e7SDag-Erling Smørgrav #include "pathnames.h"
85*190cef3dSDag-Erling Smørgrav #include "match.h"
86511b41d2SMark Murray 
874f52dfbbSDag-Erling Smørgrav /* -- agent forwarding */
884f52dfbbSDag-Erling Smørgrav #define	NUM_SOCKS	10
89511b41d2SMark Murray 
90af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
914f52dfbbSDag-Erling Smørgrav /* special-case port number meaning allow any port */
924f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT	0
934f52dfbbSDag-Erling Smørgrav 
944f52dfbbSDag-Erling Smørgrav /* special-case wildcard meaning allow any host */
954f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST	"*"
964f52dfbbSDag-Erling Smørgrav 
974f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */
984f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */
994f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS  1000
100511b41d2SMark Murray 
101*190cef3dSDag-Erling Smørgrav /* Per-channel callback for pre/post select() actions */
102*190cef3dSDag-Erling Smørgrav typedef void chan_fn(struct ssh *, Channel *c,
103*190cef3dSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset);
104*190cef3dSDag-Erling Smørgrav 
105511b41d2SMark Murray /*
106511b41d2SMark Murray  * Data structure for storing which hosts are permitted for forward requests.
107511b41d2SMark Murray  * The local sides of any remote forwards are stored in this array to prevent
108511b41d2SMark Murray  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
109511b41d2SMark Murray  * network (which might be behind a firewall).
110511b41d2SMark Murray  */
111a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */
112a0ee8cc6SDag-Erling Smørgrav /*      Overload host_to_connect; we could just make this match Forward */
113a0ee8cc6SDag-Erling Smørgrav /*	XXX - can we use listen_host instead of listen_path? */
114*190cef3dSDag-Erling Smørgrav struct permission {
115a04a10f8SKris Kennaway 	char *host_to_connect;		/* Connect to 'host'. */
116a0ee8cc6SDag-Erling Smørgrav 	int port_to_connect;		/* Connect to 'port'. */
117a0ee8cc6SDag-Erling Smørgrav 	char *listen_host;		/* Remote side should listen address. */
118a0ee8cc6SDag-Erling Smørgrav 	char *listen_path;		/* Remote side should listen path. */
119a0ee8cc6SDag-Erling Smørgrav 	int listen_port;		/* Remote side should listen port. */
120ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;		/* Downstream mux*/
121*190cef3dSDag-Erling Smørgrav };
122511b41d2SMark Murray 
123*190cef3dSDag-Erling Smørgrav /*
124*190cef3dSDag-Erling Smørgrav  * Stores the forwarding permission state for a single direction (local or
125*190cef3dSDag-Erling Smørgrav  * remote).
126*190cef3dSDag-Erling Smørgrav  */
127*190cef3dSDag-Erling Smørgrav struct permission_set {
128*190cef3dSDag-Erling Smørgrav 	/*
129*190cef3dSDag-Erling Smørgrav 	 * List of all local permitted host/port pairs to allow for the
130*190cef3dSDag-Erling Smørgrav 	 * user.
131*190cef3dSDag-Erling Smørgrav 	 */
132*190cef3dSDag-Erling Smørgrav 	u_int num_permitted_user;
133*190cef3dSDag-Erling Smørgrav 	struct permission *permitted_user;
134*190cef3dSDag-Erling Smørgrav 
135*190cef3dSDag-Erling Smørgrav 	/*
136*190cef3dSDag-Erling Smørgrav 	 * List of all permitted host/port pairs to allow for the admin.
137*190cef3dSDag-Erling Smørgrav 	 */
138*190cef3dSDag-Erling Smørgrav 	u_int num_permitted_admin;
139*190cef3dSDag-Erling Smørgrav 	struct permission *permitted_admin;
140*190cef3dSDag-Erling Smørgrav 
141*190cef3dSDag-Erling Smørgrav 	/*
142*190cef3dSDag-Erling Smørgrav 	 * If this is true, all opens/listens are permitted.  This is the
143*190cef3dSDag-Erling Smørgrav 	 * case on the server on which we have to trust the client anyway,
144*190cef3dSDag-Erling Smørgrav 	 * and the user could do anything after logging in.
145*190cef3dSDag-Erling Smørgrav 	 */
146*190cef3dSDag-Erling Smørgrav 	int all_permitted;
147*190cef3dSDag-Erling Smørgrav };
148af12a3e7SDag-Erling Smørgrav 
1494f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */
1504f52dfbbSDag-Erling Smørgrav struct ssh_channels {
1514f52dfbbSDag-Erling Smørgrav 	/*
1524f52dfbbSDag-Erling Smørgrav 	 * Pointer to an array containing all allocated channels.  The array
1534f52dfbbSDag-Erling Smørgrav 	 * is dynamically extended as needed.
1544f52dfbbSDag-Erling Smørgrav 	 */
1554f52dfbbSDag-Erling Smørgrav 	Channel **channels;
156076ad2f8SDag-Erling Smørgrav 
157511b41d2SMark Murray 	/*
1584f52dfbbSDag-Erling Smørgrav 	 * Size of the channel array.  All slots of the array must always be
1594f52dfbbSDag-Erling Smørgrav 	 * initialized (at least the type field); unused slots set to NULL
160511b41d2SMark Murray 	 */
1614f52dfbbSDag-Erling Smørgrav 	u_int channels_alloc;
162511b41d2SMark Murray 
1634f52dfbbSDag-Erling Smørgrav 	/*
1644f52dfbbSDag-Erling Smørgrav 	 * Maximum file descriptor value used in any of the channels.  This is
1654f52dfbbSDag-Erling Smørgrav 	 * updated in channel_new.
1664f52dfbbSDag-Erling Smørgrav 	 */
1674f52dfbbSDag-Erling Smørgrav 	int channel_max_fd;
1684f52dfbbSDag-Erling Smørgrav 
1694f52dfbbSDag-Erling Smørgrav 	/*
1704f52dfbbSDag-Erling Smørgrav 	 * 'channel_pre*' are called just before select() to add any bits
1714f52dfbbSDag-Erling Smørgrav 	 * relevant to channels in the select bitmasks.
1724f52dfbbSDag-Erling Smørgrav 	 *
1734f52dfbbSDag-Erling Smørgrav 	 * 'channel_post*': perform any appropriate operations for
1744f52dfbbSDag-Erling Smørgrav 	 * channels which have events pending.
1754f52dfbbSDag-Erling Smørgrav 	 */
1764f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_pre;
1774f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_post;
1784f52dfbbSDag-Erling Smørgrav 
1794f52dfbbSDag-Erling Smørgrav 	/* -- tcp forwarding */
180*190cef3dSDag-Erling Smørgrav 	struct permission_set local_perms;
181*190cef3dSDag-Erling Smørgrav 	struct permission_set remote_perms;
182af12a3e7SDag-Erling Smørgrav 
183af12a3e7SDag-Erling Smørgrav 	/* -- X11 forwarding */
184af12a3e7SDag-Erling Smørgrav 
185d4ecd108SDag-Erling Smørgrav 	/* Saved X11 local (client) display. */
1864f52dfbbSDag-Erling Smørgrav 	char *x11_saved_display;
187d4ecd108SDag-Erling Smørgrav 
188af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication protocol name. */
1894f52dfbbSDag-Erling Smørgrav 	char *x11_saved_proto;
190af12a3e7SDag-Erling Smørgrav 
191af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication data.  This is the real data. */
1924f52dfbbSDag-Erling Smørgrav 	char *x11_saved_data;
1934f52dfbbSDag-Erling Smørgrav 	u_int x11_saved_data_len;
194af12a3e7SDag-Erling Smørgrav 
195557f75e5SDag-Erling Smørgrav 	/* Deadline after which all X11 connections are refused */
1964f52dfbbSDag-Erling Smørgrav 	u_int x11_refuse_time;
197557f75e5SDag-Erling Smørgrav 
198af12a3e7SDag-Erling Smørgrav 	/*
1994f52dfbbSDag-Erling Smørgrav 	 * Fake X11 authentication data.  This is what the server will be
2004f52dfbbSDag-Erling Smørgrav 	 * sending us; we should replace any occurrences of this by the
2014f52dfbbSDag-Erling Smørgrav 	 * real data.
202af12a3e7SDag-Erling Smørgrav 	 */
2034f52dfbbSDag-Erling Smørgrav 	u_char *x11_fake_data;
2044f52dfbbSDag-Erling Smørgrav 	u_int x11_fake_data_len;
205af12a3e7SDag-Erling Smørgrav 
206ca3176e7SBrian Feldman 	/* AF_UNSPEC or AF_INET or AF_INET6 */
2074f52dfbbSDag-Erling Smørgrav 	int IPv4or6;
2084f52dfbbSDag-Erling Smørgrav };
209ca3176e7SBrian Feldman 
210af12a3e7SDag-Erling Smørgrav /* helper */
2114f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
212ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host);
213ca3176e7SBrian Feldman 
214d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */
215d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *);
216d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *);
2174f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
2184f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *);
2194f52dfbbSDag-Erling Smørgrav 
2204f52dfbbSDag-Erling Smørgrav /* Setup helper */
2214f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc);
222d4af9e69SDag-Erling Smørgrav 
223af12a3e7SDag-Erling Smørgrav /* -- channel core */
224a04a10f8SKris Kennaway 
2254f52dfbbSDag-Erling Smørgrav void
2264f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh)
2274f52dfbbSDag-Erling Smørgrav {
2284f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc;
2294f52dfbbSDag-Erling Smørgrav 
2304f52dfbbSDag-Erling Smørgrav 	if ((sc = calloc(1, sizeof(*sc))) == NULL ||
2314f52dfbbSDag-Erling Smørgrav 	    (sc->channel_pre = calloc(SSH_CHANNEL_MAX_TYPE,
2324f52dfbbSDag-Erling Smørgrav 	    sizeof(*sc->channel_pre))) == NULL ||
2334f52dfbbSDag-Erling Smørgrav 	    (sc->channel_post = calloc(SSH_CHANNEL_MAX_TYPE,
2344f52dfbbSDag-Erling Smørgrav 	    sizeof(*sc->channel_post))) == NULL)
2354f52dfbbSDag-Erling Smørgrav 		fatal("%s: allocation failed", __func__);
2364f52dfbbSDag-Erling Smørgrav 	sc->channels_alloc = 10;
2374f52dfbbSDag-Erling Smørgrav 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2384f52dfbbSDag-Erling Smørgrav 	sc->IPv4or6 = AF_UNSPEC;
2394f52dfbbSDag-Erling Smørgrav 	channel_handler_init(sc);
2404f52dfbbSDag-Erling Smørgrav 
2414f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt = sc;
2424f52dfbbSDag-Erling Smørgrav }
2434f52dfbbSDag-Erling Smørgrav 
244a04a10f8SKris Kennaway Channel *
2454f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id)
246a04a10f8SKris Kennaway {
247a04a10f8SKris Kennaway 	Channel *c;
248af12a3e7SDag-Erling Smørgrav 
2494f52dfbbSDag-Erling Smørgrav 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
2504f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id", __func__, id);
251a04a10f8SKris Kennaway 		return NULL;
252a04a10f8SKris Kennaway 	}
2534f52dfbbSDag-Erling Smørgrav 	c = ssh->chanctxt->channels[id];
254af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
2554f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id: channel free", __func__, id);
256a04a10f8SKris Kennaway 		return NULL;
257a04a10f8SKris Kennaway 	}
258a04a10f8SKris Kennaway 	return c;
259a04a10f8SKris Kennaway }
260a04a10f8SKris Kennaway 
261ca86bcf2SDag-Erling Smørgrav Channel *
2624f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id)
263ca86bcf2SDag-Erling Smørgrav {
264ca86bcf2SDag-Erling Smørgrav 	Channel *c;
265ca86bcf2SDag-Erling Smørgrav 	u_int i;
266ca86bcf2SDag-Erling Smørgrav 
2674f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2684f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
2694f52dfbbSDag-Erling Smørgrav 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
270ca86bcf2SDag-Erling Smørgrav 			return c;
271ca86bcf2SDag-Erling Smørgrav 	}
272ca86bcf2SDag-Erling Smørgrav 	return NULL;
273ca86bcf2SDag-Erling Smørgrav }
274ca86bcf2SDag-Erling Smørgrav 
275a04a10f8SKris Kennaway /*
276b74df5b2SDag-Erling Smørgrav  * Returns the channel if it is allowed to receive protocol messages.
277b74df5b2SDag-Erling Smørgrav  * Private channels, like listening sockets, may not receive messages.
278b74df5b2SDag-Erling Smørgrav  */
279b74df5b2SDag-Erling Smørgrav Channel *
2804f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id)
281b74df5b2SDag-Erling Smørgrav {
282b74df5b2SDag-Erling Smørgrav 	Channel *c;
283b74df5b2SDag-Erling Smørgrav 
2844f52dfbbSDag-Erling Smørgrav 	if ((c = channel_by_id(ssh, id)) == NULL)
2854f52dfbbSDag-Erling Smørgrav 		return NULL;
286b74df5b2SDag-Erling Smørgrav 
287b74df5b2SDag-Erling Smørgrav 	switch (c->type) {
288b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_X11_OPEN:
289b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_LARVAL:
290b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_CONNECTING:
291b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_DYNAMIC:
2924f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_OPEN:
2934f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_FINISH:
294b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPENING:
295b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPEN:
296e4a9863fSDag-Erling Smørgrav 	case SSH_CHANNEL_ABANDONED:
297ca86bcf2SDag-Erling Smørgrav 	case SSH_CHANNEL_MUX_PROXY:
2984f52dfbbSDag-Erling Smørgrav 		return c;
299b74df5b2SDag-Erling Smørgrav 	}
300b74df5b2SDag-Erling Smørgrav 	logit("Non-public channel %d, type %d.", id, c->type);
3014f52dfbbSDag-Erling Smørgrav 	return NULL;
302b74df5b2SDag-Erling Smørgrav }
303b74df5b2SDag-Erling Smørgrav 
304b74df5b2SDag-Erling Smørgrav /*
305a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
306a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
307a04a10f8SKris Kennaway  */
308af12a3e7SDag-Erling Smørgrav static void
3094f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
310d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty)
311a04a10f8SKris Kennaway {
3124f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3134f52dfbbSDag-Erling Smørgrav 
314a04a10f8SKris Kennaway 	/* Update the maximum file descriptor value. */
3154f52dfbbSDag-Erling Smørgrav 	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, rfd);
3164f52dfbbSDag-Erling Smørgrav 	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, wfd);
3174f52dfbbSDag-Erling Smørgrav 	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, efd);
318ca3176e7SBrian Feldman 
319b15c8340SDag-Erling Smørgrav 	if (rfd != -1)
320b15c8340SDag-Erling Smørgrav 		fcntl(rfd, F_SETFD, FD_CLOEXEC);
321b15c8340SDag-Erling Smørgrav 	if (wfd != -1 && wfd != rfd)
322b15c8340SDag-Erling Smørgrav 		fcntl(wfd, F_SETFD, FD_CLOEXEC);
323b15c8340SDag-Erling Smørgrav 	if (efd != -1 && efd != rfd && efd != wfd)
324b15c8340SDag-Erling Smørgrav 		fcntl(efd, F_SETFD, FD_CLOEXEC);
325a04a10f8SKris Kennaway 
326a04a10f8SKris Kennaway 	c->rfd = rfd;
327a04a10f8SKris Kennaway 	c->wfd = wfd;
328a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
329a04a10f8SKris Kennaway 	c->efd = efd;
330a04a10f8SKris Kennaway 	c->extended_usage = extusage;
3315b9b2fafSBrian Feldman 
332d4af9e69SDag-Erling Smørgrav 	if ((c->isatty = is_tty) != 0)
333221552e4SDag-Erling Smørgrav 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
334e4a9863fSDag-Erling Smørgrav #ifdef _AIX
335e4a9863fSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
336d4af9e69SDag-Erling Smørgrav 	c->wfd_isatty = is_tty || isatty(c->wfd);
337e4a9863fSDag-Erling Smørgrav #endif
338e0fbb1d2SBrian Feldman 
3395b9b2fafSBrian Feldman 	/* enable nonblocking mode */
3405b9b2fafSBrian Feldman 	if (nonblock) {
341a04a10f8SKris Kennaway 		if (rfd != -1)
342a04a10f8SKris Kennaway 			set_nonblock(rfd);
343a04a10f8SKris Kennaway 		if (wfd != -1)
344a04a10f8SKris Kennaway 			set_nonblock(wfd);
345a04a10f8SKris Kennaway 		if (efd != -1)
346a04a10f8SKris Kennaway 			set_nonblock(efd);
347a04a10f8SKris Kennaway 	}
3485b9b2fafSBrian Feldman }
349a04a10f8SKris Kennaway 
350511b41d2SMark Murray /*
351511b41d2SMark Murray  * Allocate a new channel object and set its type and socket. This will cause
352511b41d2SMark Murray  * remote_name to be freed.
353511b41d2SMark Murray  */
354af12a3e7SDag-Erling Smørgrav Channel *
3554f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
356a82e551fSDag-Erling Smørgrav     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
357511b41d2SMark Murray {
3584f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3594f52dfbbSDag-Erling Smørgrav 	u_int i, found;
360511b41d2SMark Murray 	Channel *c;
361511b41d2SMark Murray 
362511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
3634f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
3644f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL) {
365511b41d2SMark Murray 			/* Found a free slot. */
3664f52dfbbSDag-Erling Smørgrav 			found = i;
367511b41d2SMark Murray 			break;
368511b41d2SMark Murray 		}
3694f52dfbbSDag-Erling Smørgrav 	}
3704f52dfbbSDag-Erling Smørgrav 	if (i >= sc->channels_alloc) {
3714f52dfbbSDag-Erling Smørgrav 		/*
3724f52dfbbSDag-Erling Smørgrav 		 * There are no free slots. Take last+1 slot and expand
3734f52dfbbSDag-Erling Smørgrav 		 * the array.
3744f52dfbbSDag-Erling Smørgrav 		 */
3754f52dfbbSDag-Erling Smørgrav 		found = sc->channels_alloc;
3764f52dfbbSDag-Erling Smørgrav 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
3774f52dfbbSDag-Erling Smørgrav 			fatal("%s: internal error: channels_alloc %d too big",
3784f52dfbbSDag-Erling Smørgrav 			    __func__, sc->channels_alloc);
3794f52dfbbSDag-Erling Smørgrav 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
3804f52dfbbSDag-Erling Smørgrav 		    sc->channels_alloc + 10, sizeof(*sc->channels));
3814f52dfbbSDag-Erling Smørgrav 		sc->channels_alloc += 10;
3824f52dfbbSDag-Erling Smørgrav 		debug2("channel: expanding %d", sc->channels_alloc);
383511b41d2SMark Murray 	}
384af12a3e7SDag-Erling Smørgrav 	/* Initialize and return new channel. */
3854f52dfbbSDag-Erling Smørgrav 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
3864f52dfbbSDag-Erling Smørgrav 	if ((c->input = sshbuf_new()) == NULL ||
3874f52dfbbSDag-Erling Smørgrav 	    (c->output = sshbuf_new()) == NULL ||
3884f52dfbbSDag-Erling Smørgrav 	    (c->extended = sshbuf_new()) == NULL)
3894f52dfbbSDag-Erling Smørgrav 		fatal("%s: sshbuf_new failed", __func__);
390af12a3e7SDag-Erling Smørgrav 	c->ostate = CHAN_OUTPUT_OPEN;
391af12a3e7SDag-Erling Smørgrav 	c->istate = CHAN_INPUT_OPEN;
3924f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
393511b41d2SMark Murray 	c->self = found;
394511b41d2SMark Murray 	c->type = type;
395a04a10f8SKris Kennaway 	c->ctype = ctype;
396a04a10f8SKris Kennaway 	c->local_window = window;
397a04a10f8SKris Kennaway 	c->local_window_max = window;
398a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
399221552e4SDag-Erling Smørgrav 	c->remote_name = xstrdup(remote_name);
400b15c8340SDag-Erling Smørgrav 	c->ctl_chan = -1;
401b15c8340SDag-Erling Smørgrav 	c->delayed = 1;		/* prevent call to channel_post handler */
402d4af9e69SDag-Erling Smørgrav 	TAILQ_INIT(&c->status_confirms);
403511b41d2SMark Murray 	debug("channel %d: new [%s]", found, remote_name);
404af12a3e7SDag-Erling Smørgrav 	return c;
405a04a10f8SKris Kennaway }
406511b41d2SMark Murray 
4074f52dfbbSDag-Erling Smørgrav static void
4084f52dfbbSDag-Erling Smørgrav channel_find_maxfd(struct ssh_channels *sc)
409af12a3e7SDag-Erling Smørgrav {
41021e764dfSDag-Erling Smørgrav 	u_int i;
41121e764dfSDag-Erling Smørgrav 	int max = 0;
412af12a3e7SDag-Erling Smørgrav 	Channel *c;
413af12a3e7SDag-Erling Smørgrav 
4144f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
4154f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
416af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
417ca86bcf2SDag-Erling Smørgrav 			max = MAXIMUM(max, c->rfd);
418ca86bcf2SDag-Erling Smørgrav 			max = MAXIMUM(max, c->wfd);
419ca86bcf2SDag-Erling Smørgrav 			max = MAXIMUM(max, c->efd);
420af12a3e7SDag-Erling Smørgrav 		}
421af12a3e7SDag-Erling Smørgrav 	}
4224f52dfbbSDag-Erling Smørgrav 	sc->channel_max_fd = max;
423af12a3e7SDag-Erling Smørgrav }
424af12a3e7SDag-Erling Smørgrav 
425af12a3e7SDag-Erling Smørgrav int
4264f52dfbbSDag-Erling Smørgrav channel_close_fd(struct ssh *ssh, int *fdp)
427af12a3e7SDag-Erling Smørgrav {
4284f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
429af12a3e7SDag-Erling Smørgrav 	int ret = 0, fd = *fdp;
430af12a3e7SDag-Erling Smørgrav 
431af12a3e7SDag-Erling Smørgrav 	if (fd != -1) {
432af12a3e7SDag-Erling Smørgrav 		ret = close(fd);
433af12a3e7SDag-Erling Smørgrav 		*fdp = -1;
4344f52dfbbSDag-Erling Smørgrav 		if (fd == sc->channel_max_fd)
4354f52dfbbSDag-Erling Smørgrav 			channel_find_maxfd(sc);
436af12a3e7SDag-Erling Smørgrav 	}
437af12a3e7SDag-Erling Smørgrav 	return ret;
438af12a3e7SDag-Erling Smørgrav }
439a04a10f8SKris Kennaway 
440a04a10f8SKris Kennaway /* Close all channel fd/socket. */
441af12a3e7SDag-Erling Smørgrav static void
4424f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c)
443511b41d2SMark Murray {
44447dd1d1bSDag-Erling Smørgrav 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
44547dd1d1bSDag-Erling Smørgrav 
4464f52dfbbSDag-Erling Smørgrav 	channel_close_fd(ssh, &c->sock);
44747dd1d1bSDag-Erling Smørgrav 	if (rfd != sock)
4484f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->rfd);
44947dd1d1bSDag-Erling Smørgrav 	if (wfd != sock && wfd != rfd)
4504f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->wfd);
45147dd1d1bSDag-Erling Smørgrav 	if (efd != sock && efd != rfd && efd != wfd)
4524f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->efd);
4534f52dfbbSDag-Erling Smørgrav }
4544f52dfbbSDag-Erling Smørgrav 
4554f52dfbbSDag-Erling Smørgrav static void
456*190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm)
4574f52dfbbSDag-Erling Smørgrav {
458*190cef3dSDag-Erling Smørgrav 	free(perm->host_to_connect);
459*190cef3dSDag-Erling Smørgrav 	free(perm->listen_host);
460*190cef3dSDag-Erling Smørgrav 	free(perm->listen_path);
461*190cef3dSDag-Erling Smørgrav 	bzero(perm, sizeof(*perm));
4624f52dfbbSDag-Erling Smørgrav }
4634f52dfbbSDag-Erling Smørgrav 
464*190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */
465*190cef3dSDag-Erling Smørgrav static const char *
466*190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where)
467*190cef3dSDag-Erling Smørgrav {
468*190cef3dSDag-Erling Smørgrav 	if (who == FORWARD_ADM) {
469*190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
470*190cef3dSDag-Erling Smørgrav 			return "admin local";
471*190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
472*190cef3dSDag-Erling Smørgrav 			return "admin remote";
473*190cef3dSDag-Erling Smørgrav 	} else if (who == FORWARD_USER) {
474*190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
475*190cef3dSDag-Erling Smørgrav 			return "user local";
476*190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
477*190cef3dSDag-Erling Smørgrav 			return "user remote";
478*190cef3dSDag-Erling Smørgrav 	}
479*190cef3dSDag-Erling Smørgrav 	fatal("Unknown forward permission list %d/%d", who, where);
480*190cef3dSDag-Erling Smørgrav }
4814f52dfbbSDag-Erling Smørgrav 
482*190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */
483*190cef3dSDag-Erling Smørgrav static struct permission_set *
484*190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where)
485*190cef3dSDag-Erling Smørgrav {
486*190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
487*190cef3dSDag-Erling Smørgrav 
488*190cef3dSDag-Erling Smørgrav 	switch (where) {
489*190cef3dSDag-Erling Smørgrav 	case FORWARD_LOCAL:
490*190cef3dSDag-Erling Smørgrav 		return &sc->local_perms;
491*190cef3dSDag-Erling Smørgrav 		break;
492*190cef3dSDag-Erling Smørgrav 	case FORWARD_REMOTE:
493*190cef3dSDag-Erling Smørgrav 		return &sc->remote_perms;
494*190cef3dSDag-Erling Smørgrav 		break;
495*190cef3dSDag-Erling Smørgrav 	default:
496*190cef3dSDag-Erling Smørgrav 		fatal("%s: invalid forwarding direction %d", __func__, where);
497*190cef3dSDag-Erling Smørgrav 	}
498*190cef3dSDag-Erling Smørgrav }
499*190cef3dSDag-Erling Smørgrav 
500*190cef3dSDag-Erling Smørgrav /* Reutrns pointers to the specified forwarding list and its element count */
501*190cef3dSDag-Erling Smørgrav static void
502*190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where,
503*190cef3dSDag-Erling Smørgrav     struct permission ***permpp, u_int **npermpp)
504*190cef3dSDag-Erling Smørgrav {
505*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
506*190cef3dSDag-Erling Smørgrav 
507*190cef3dSDag-Erling Smørgrav 	switch (who) {
508*190cef3dSDag-Erling Smørgrav 	case FORWARD_USER:
509*190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_user;
510*190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_user;
511*190cef3dSDag-Erling Smørgrav 		break;
512*190cef3dSDag-Erling Smørgrav 	case FORWARD_ADM:
513*190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_admin;
514*190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_admin;
515*190cef3dSDag-Erling Smørgrav 		break;
516*190cef3dSDag-Erling Smørgrav 	default:
517*190cef3dSDag-Erling Smørgrav 		fatal("%s: invalid forwarding client %d", __func__, who);
518*190cef3dSDag-Erling Smørgrav 	}
519*190cef3dSDag-Erling Smørgrav }
520*190cef3dSDag-Erling Smørgrav 
521*190cef3dSDag-Erling Smørgrav /* Adds an entry to the spcified forwarding list */
5224f52dfbbSDag-Erling Smørgrav static int
523*190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where,
5244f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, int port_to_connect,
5254f52dfbbSDag-Erling Smørgrav     const char *listen_host, const char *listen_path, int listen_port,
5264f52dfbbSDag-Erling Smørgrav     Channel *downstream)
5274f52dfbbSDag-Erling Smørgrav {
528*190cef3dSDag-Erling Smørgrav 	struct permission **permp;
529*190cef3dSDag-Erling Smørgrav 	u_int n, *npermp;
5304f52dfbbSDag-Erling Smørgrav 
531*190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
5324f52dfbbSDag-Erling Smørgrav 
533*190cef3dSDag-Erling Smørgrav 	if (*npermp >= INT_MAX)
534*190cef3dSDag-Erling Smørgrav 		fatal("%s: %s overflow", __func__, fwd_ident(who, where));
5354f52dfbbSDag-Erling Smørgrav 
536*190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
537*190cef3dSDag-Erling Smørgrav 	n = (*npermp)++;
5384f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
539*190cef3dSDag-Erling Smørgrav 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
540*190cef3dSDag-Erling Smørgrav 	(*permp)[n].port_to_connect = port_to_connect;
541*190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
542*190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
543*190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_port = listen_port;
544*190cef3dSDag-Erling Smørgrav 	(*permp)[n].downstream = downstream;
5454f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP
5464f52dfbbSDag-Erling Smørgrav 	return (int)n;
5474f52dfbbSDag-Erling Smørgrav }
5484f52dfbbSDag-Erling Smørgrav 
5494f52dfbbSDag-Erling Smørgrav static void
5504f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
5514f52dfbbSDag-Erling Smørgrav {
5524f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
553*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
554*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
5554f52dfbbSDag-Erling Smørgrav 	int r;
5564f52dfbbSDag-Erling Smørgrav 	u_int i;
5574f52dfbbSDag-Erling Smørgrav 
558*190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
559*190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
560*190cef3dSDag-Erling Smørgrav 		if (perm->downstream != c)
5614f52dfbbSDag-Erling Smørgrav 			continue;
5624f52dfbbSDag-Erling Smørgrav 
5634f52dfbbSDag-Erling Smørgrav 		/* cancel on the server, since mux client is gone */
5644f52dfbbSDag-Erling Smørgrav 		debug("channel %d: cleanup remote forward for %s:%u",
565*190cef3dSDag-Erling Smørgrav 		    c->self, perm->listen_host, perm->listen_port);
5664f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
5674f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
5684f52dfbbSDag-Erling Smørgrav 		    "cancel-tcpip-forward")) != 0 ||
5694f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
5704f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
571*190cef3dSDag-Erling Smørgrav 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
572*190cef3dSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
5734f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
5744f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: %s", __func__,
5754f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
5764f52dfbbSDag-Erling Smørgrav 		}
577*190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(perm); /* unregister */
5784f52dfbbSDag-Erling Smørgrav 	}
579a04a10f8SKris Kennaway }
580511b41d2SMark Murray 
581a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
582a04a10f8SKris Kennaway void
5834f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c)
584a04a10f8SKris Kennaway {
5854f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
586af12a3e7SDag-Erling Smørgrav 	char *s;
58721e764dfSDag-Erling Smørgrav 	u_int i, n;
588ca86bcf2SDag-Erling Smørgrav 	Channel *other;
589d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
590ca3176e7SBrian Feldman 
5914f52dfbbSDag-Erling Smørgrav 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
5924f52dfbbSDag-Erling Smørgrav 		if ((other = sc->channels[i]) == NULL)
5934f52dfbbSDag-Erling Smørgrav 			continue;
594af12a3e7SDag-Erling Smørgrav 		n++;
595ca86bcf2SDag-Erling Smørgrav 		/* detach from mux client and prepare for closing */
596ca86bcf2SDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
597ca86bcf2SDag-Erling Smørgrav 		    other->type == SSH_CHANNEL_MUX_PROXY &&
598ca86bcf2SDag-Erling Smørgrav 		    other->mux_ctx == c) {
599ca86bcf2SDag-Erling Smørgrav 			other->mux_ctx = NULL;
600ca86bcf2SDag-Erling Smørgrav 			other->type = SSH_CHANNEL_OPEN;
601ca86bcf2SDag-Erling Smørgrav 			other->istate = CHAN_INPUT_CLOSED;
602ca86bcf2SDag-Erling Smørgrav 			other->ostate = CHAN_OUTPUT_CLOSED;
603ca86bcf2SDag-Erling Smørgrav 		}
604ca86bcf2SDag-Erling Smørgrav 	}
60521e764dfSDag-Erling Smørgrav 	debug("channel %d: free: %s, nchannels %u", c->self,
606af12a3e7SDag-Erling Smørgrav 	    c->remote_name ? c->remote_name : "???", n);
607af12a3e7SDag-Erling Smørgrav 
6084f52dfbbSDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_MUX_CLIENT)
6094f52dfbbSDag-Erling Smørgrav 		mux_remove_remote_forwardings(ssh, c);
610ca86bcf2SDag-Erling Smørgrav 
611*190cef3dSDag-Erling Smørgrav 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
6124f52dfbbSDag-Erling Smørgrav 		s = channel_open_message(ssh);
613221552e4SDag-Erling Smørgrav 		debug3("channel %d: status: %s", c->self, s);
614e4a9863fSDag-Erling Smørgrav 		free(s);
615*190cef3dSDag-Erling Smørgrav 	}
616ca3176e7SBrian Feldman 
6174f52dfbbSDag-Erling Smørgrav 	channel_close_fds(ssh, c);
6184f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->input);
6194f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->output);
6204f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->extended);
6214f52dfbbSDag-Erling Smørgrav 	c->input = c->output = c->extended = NULL;
622e4a9863fSDag-Erling Smørgrav 	free(c->remote_name);
623a04a10f8SKris Kennaway 	c->remote_name = NULL;
624e4a9863fSDag-Erling Smørgrav 	free(c->path);
625cce7d346SDag-Erling Smørgrav 	c->path = NULL;
626e4a9863fSDag-Erling Smørgrav 	free(c->listening_addr);
627462c32cbSDag-Erling Smørgrav 	c->listening_addr = NULL;
628d4af9e69SDag-Erling Smørgrav 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
629d4af9e69SDag-Erling Smørgrav 		if (cc->abandon_cb != NULL)
6304f52dfbbSDag-Erling Smørgrav 			cc->abandon_cb(ssh, c, cc->ctx);
631d4af9e69SDag-Erling Smørgrav 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
632b83788ffSDag-Erling Smørgrav 		explicit_bzero(cc, sizeof(*cc));
633e4a9863fSDag-Erling Smørgrav 		free(cc);
634d4af9e69SDag-Erling Smørgrav 	}
635d4af9e69SDag-Erling Smørgrav 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
6364f52dfbbSDag-Erling Smørgrav 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
6374f52dfbbSDag-Erling Smørgrav 	sc->channels[c->self] = NULL;
6384f52dfbbSDag-Erling Smørgrav 	explicit_bzero(c, sizeof(*c));
639e4a9863fSDag-Erling Smørgrav 	free(c);
640af12a3e7SDag-Erling Smørgrav }
641af12a3e7SDag-Erling Smørgrav 
642af12a3e7SDag-Erling Smørgrav void
6434f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh)
644af12a3e7SDag-Erling Smørgrav {
64521e764dfSDag-Erling Smørgrav 	u_int i;
646af12a3e7SDag-Erling Smørgrav 
6474f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
6484f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
6494f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, ssh->chanctxt->channels[i]);
650af12a3e7SDag-Erling Smørgrav }
651af12a3e7SDag-Erling Smørgrav 
652af12a3e7SDag-Erling Smørgrav /*
653af12a3e7SDag-Erling Smørgrav  * Closes the sockets/fds of all channels.  This is used to close extra file
654af12a3e7SDag-Erling Smørgrav  * descriptors after a fork.
655af12a3e7SDag-Erling Smørgrav  */
656af12a3e7SDag-Erling Smørgrav void
6574f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh)
658af12a3e7SDag-Erling Smørgrav {
65921e764dfSDag-Erling Smørgrav 	u_int i;
660af12a3e7SDag-Erling Smørgrav 
6614f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
6624f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
6634f52dfbbSDag-Erling Smørgrav 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
664af12a3e7SDag-Erling Smørgrav }
665af12a3e7SDag-Erling Smørgrav 
666af12a3e7SDag-Erling Smørgrav /*
667af12a3e7SDag-Erling Smørgrav  * Stop listening to channels.
668af12a3e7SDag-Erling Smørgrav  */
669af12a3e7SDag-Erling Smørgrav void
6704f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh)
671af12a3e7SDag-Erling Smørgrav {
67221e764dfSDag-Erling Smørgrav 	u_int i;
673af12a3e7SDag-Erling Smørgrav 	Channel *c;
674af12a3e7SDag-Erling Smørgrav 
6754f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
6764f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
677af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
678af12a3e7SDag-Erling Smørgrav 			switch (c->type) {
679af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_AUTH_SOCKET:
680af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_PORT_LISTENER:
681af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_RPORT_LISTENER:
682af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_X11_LISTENER:
683a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_UNIX_LISTENER:
684a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_RUNIX_LISTENER:
6854f52dfbbSDag-Erling Smørgrav 				channel_close_fd(ssh, &c->sock);
6864f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
687af12a3e7SDag-Erling Smørgrav 				break;
688af12a3e7SDag-Erling Smørgrav 			}
689af12a3e7SDag-Erling Smørgrav 		}
690af12a3e7SDag-Erling Smørgrav 	}
691af12a3e7SDag-Erling Smørgrav }
692af12a3e7SDag-Erling Smørgrav 
693af12a3e7SDag-Erling Smørgrav /*
694af12a3e7SDag-Erling Smørgrav  * Returns true if no channel has too much buffered data, and false if one or
695af12a3e7SDag-Erling Smørgrav  * more channel is overfull.
696af12a3e7SDag-Erling Smørgrav  */
697af12a3e7SDag-Erling Smørgrav int
6984f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh)
699af12a3e7SDag-Erling Smørgrav {
700af12a3e7SDag-Erling Smørgrav 	u_int i;
7014f52dfbbSDag-Erling Smørgrav 	u_int maxsize = ssh_packet_get_maxsize(ssh);
702af12a3e7SDag-Erling Smørgrav 	Channel *c;
703af12a3e7SDag-Erling Smørgrav 
7044f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7054f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
7064f52dfbbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
7074f52dfbbSDag-Erling Smørgrav 			continue;
7084f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > maxsize) {
7094f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: big output buffer %zu > %u",
7104f52dfbbSDag-Erling Smørgrav 			    c->self, sshbuf_len(c->output), maxsize);
711af12a3e7SDag-Erling Smørgrav 			return 0;
712af12a3e7SDag-Erling Smørgrav 		}
713af12a3e7SDag-Erling Smørgrav 	}
714af12a3e7SDag-Erling Smørgrav 	return 1;
715af12a3e7SDag-Erling Smørgrav }
716af12a3e7SDag-Erling Smørgrav 
717af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */
718af12a3e7SDag-Erling Smørgrav int
7194f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh)
720af12a3e7SDag-Erling Smørgrav {
72121e764dfSDag-Erling Smørgrav 	u_int i;
722af12a3e7SDag-Erling Smørgrav 	Channel *c;
723af12a3e7SDag-Erling Smørgrav 
7244f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7254f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
726af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
727af12a3e7SDag-Erling Smørgrav 			continue;
728af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
729af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
730af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
731af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
732b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
733af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
734af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
735af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
7364f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
737af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
738af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
739e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
740a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
741a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
742af12a3e7SDag-Erling Smørgrav 			continue;
743af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
744af12a3e7SDag-Erling Smørgrav 			continue;
745af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
746af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
7474f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
748af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
749b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
750ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
751af12a3e7SDag-Erling Smørgrav 			return 1;
752af12a3e7SDag-Erling Smørgrav 		default:
7534f52dfbbSDag-Erling Smørgrav 			fatal("%s: bad channel type %d", __func__, c->type);
754af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
755af12a3e7SDag-Erling Smørgrav 		}
756af12a3e7SDag-Erling Smørgrav 	}
757af12a3e7SDag-Erling Smørgrav 	return 0;
758af12a3e7SDag-Erling Smørgrav }
759af12a3e7SDag-Erling Smørgrav 
760af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */
761af12a3e7SDag-Erling Smørgrav int
7624f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh)
763af12a3e7SDag-Erling Smørgrav {
76421e764dfSDag-Erling Smørgrav 	u_int i;
765af12a3e7SDag-Erling Smørgrav 	Channel *c;
766af12a3e7SDag-Erling Smørgrav 
7674f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7684f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
7694f52dfbbSDag-Erling Smørgrav 		if (c == NULL || !c->have_remote_id)
770af12a3e7SDag-Erling Smørgrav 			continue;
771af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
772af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
773af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
7744f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
7754f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
776af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
777af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
778af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
779b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
780b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
781ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
782af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
783af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
784af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
785e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
786a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
787a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
788af12a3e7SDag-Erling Smørgrav 			continue;
789af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
790af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
791af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
792af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
793af12a3e7SDag-Erling Smørgrav 			return i;
794af12a3e7SDag-Erling Smørgrav 		default:
7954f52dfbbSDag-Erling Smørgrav 			fatal("%s: bad channel type %d", __func__, c->type);
796af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
797af12a3e7SDag-Erling Smørgrav 		}
798af12a3e7SDag-Erling Smørgrav 	}
799af12a3e7SDag-Erling Smørgrav 	return -1;
800af12a3e7SDag-Erling Smørgrav }
801af12a3e7SDag-Erling Smørgrav 
802af12a3e7SDag-Erling Smørgrav /*
803af12a3e7SDag-Erling Smørgrav  * Returns a message describing the currently open forwarded connections,
804af12a3e7SDag-Erling Smørgrav  * suitable for sending to the client.  The message contains crlf pairs for
805af12a3e7SDag-Erling Smørgrav  * newlines.
806af12a3e7SDag-Erling Smørgrav  */
807af12a3e7SDag-Erling Smørgrav char *
8084f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh)
809af12a3e7SDag-Erling Smørgrav {
8104f52dfbbSDag-Erling Smørgrav 	struct sshbuf *buf;
811af12a3e7SDag-Erling Smørgrav 	Channel *c;
81221e764dfSDag-Erling Smørgrav 	u_int i;
8134f52dfbbSDag-Erling Smørgrav 	int r;
8144f52dfbbSDag-Erling Smørgrav 	char *ret;
815af12a3e7SDag-Erling Smørgrav 
8164f52dfbbSDag-Erling Smørgrav 	if ((buf = sshbuf_new()) == NULL)
8174f52dfbbSDag-Erling Smørgrav 		fatal("%s: sshbuf_new", __func__);
8184f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_putf(buf,
8194f52dfbbSDag-Erling Smørgrav 	    "The following connections are open:\r\n")) != 0)
8204f52dfbbSDag-Erling Smørgrav 		fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
8214f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8224f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
823af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
824af12a3e7SDag-Erling Smørgrav 			continue;
825af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
826af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
827af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
828af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
829af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
830af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
831af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
832e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
833b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
834a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
835a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
836af12a3e7SDag-Erling Smørgrav 			continue;
837af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
838af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
839af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
840af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
8414f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
8424f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
843af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
844af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
845ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
846ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
8474f52dfbbSDag-Erling Smørgrav 			if ((r = sshbuf_putf(buf, "  #%d %.300s "
8484f52dfbbSDag-Erling Smørgrav 			    "(t%d %s%u i%u/%zu o%u/%zu fd %d/%d cc %d)\r\n",
849af12a3e7SDag-Erling Smørgrav 			    c->self, c->remote_name,
8504f52dfbbSDag-Erling Smørgrav 			    c->type,
8514f52dfbbSDag-Erling Smørgrav 			    c->have_remote_id ? "r" : "nr", c->remote_id,
8524f52dfbbSDag-Erling Smørgrav 			    c->istate, sshbuf_len(c->input),
8534f52dfbbSDag-Erling Smørgrav 			    c->ostate, sshbuf_len(c->output),
8544f52dfbbSDag-Erling Smørgrav 			    c->rfd, c->wfd, c->ctl_chan)) != 0)
8554f52dfbbSDag-Erling Smørgrav 				fatal("%s: sshbuf_putf: %s",
8564f52dfbbSDag-Erling Smørgrav 				    __func__, ssh_err(r));
857af12a3e7SDag-Erling Smørgrav 			continue;
858af12a3e7SDag-Erling Smørgrav 		default:
8594f52dfbbSDag-Erling Smørgrav 			fatal("%s: bad channel type %d", __func__, c->type);
860af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
861af12a3e7SDag-Erling Smørgrav 		}
862af12a3e7SDag-Erling Smørgrav 	}
8634f52dfbbSDag-Erling Smørgrav 	if ((ret = sshbuf_dup_string(buf)) == NULL)
8644f52dfbbSDag-Erling Smørgrav 		fatal("%s: sshbuf_dup_string", __func__);
8654f52dfbbSDag-Erling Smørgrav 	sshbuf_free(buf);
8664f52dfbbSDag-Erling Smørgrav 	return ret;
8674f52dfbbSDag-Erling Smørgrav }
8684f52dfbbSDag-Erling Smørgrav 
8694f52dfbbSDag-Erling Smørgrav static void
8704f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
8714f52dfbbSDag-Erling Smørgrav {
8724f52dfbbSDag-Erling Smørgrav 	int r;
8734f52dfbbSDag-Erling Smørgrav 
8744f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
8754f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
8764f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
8774f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
8784f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
8794f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: open: %s", where, c->self, ssh_err(r));
8804f52dfbbSDag-Erling Smørgrav 	}
881af12a3e7SDag-Erling Smørgrav }
882af12a3e7SDag-Erling Smørgrav 
883af12a3e7SDag-Erling Smørgrav void
8844f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id)
885af12a3e7SDag-Erling Smørgrav {
8864f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
8874f52dfbbSDag-Erling Smørgrav 	int r;
888f388f5efSDag-Erling Smørgrav 
889af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
890221552e4SDag-Erling Smørgrav 		logit("channel_send_open: %d: bad id", id);
891af12a3e7SDag-Erling Smørgrav 		return;
892af12a3e7SDag-Erling Smørgrav 	}
893e73e9afaSDag-Erling Smørgrav 	debug2("channel %d: send open", id);
8944f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, c->ctype);
8954f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
8964f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
897af12a3e7SDag-Erling Smørgrav }
898af12a3e7SDag-Erling Smørgrav 
899af12a3e7SDag-Erling Smørgrav void
9004f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
901af12a3e7SDag-Erling Smørgrav {
9024f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
9034f52dfbbSDag-Erling Smørgrav 	int r;
904f388f5efSDag-Erling Smørgrav 
905af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
9064f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: unknown channel id", __func__, id);
907af12a3e7SDag-Erling Smørgrav 		return;
908af12a3e7SDag-Erling Smørgrav 	}
9094f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
9104f52dfbbSDag-Erling Smørgrav 		fatal(":%s: channel %d: no remote id", __func__, c->self);
9114f52dfbbSDag-Erling Smørgrav 
91221e764dfSDag-Erling Smørgrav 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
9134f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
9144f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
9154f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
9164f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
9174f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
9184f52dfbbSDag-Erling Smørgrav 	}
919af12a3e7SDag-Erling Smørgrav }
920333ee039SDag-Erling Smørgrav 
921af12a3e7SDag-Erling Smørgrav void
9224f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id,
9234f52dfbbSDag-Erling Smørgrav     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
924d4af9e69SDag-Erling Smørgrav {
925d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
926d4af9e69SDag-Erling Smørgrav 	Channel *c;
927d4af9e69SDag-Erling Smørgrav 
9284f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL)
9294f52dfbbSDag-Erling Smørgrav 		fatal("%s: %d: bad id", __func__, id);
930d4af9e69SDag-Erling Smørgrav 
9310a37d4a3SXin LI 	cc = xcalloc(1, sizeof(*cc));
932d4af9e69SDag-Erling Smørgrav 	cc->cb = cb;
933d4af9e69SDag-Erling Smørgrav 	cc->abandon_cb = abandon_cb;
934d4af9e69SDag-Erling Smørgrav 	cc->ctx = ctx;
935d4af9e69SDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
936d4af9e69SDag-Erling Smørgrav }
937d4af9e69SDag-Erling Smørgrav 
938d4af9e69SDag-Erling Smørgrav void
9394f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id,
9404f52dfbbSDag-Erling Smørgrav     channel_open_fn *fn, void *ctx)
941af12a3e7SDag-Erling Smørgrav {
9424f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
943f388f5efSDag-Erling Smørgrav 
944af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
9454f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id", __func__, id);
946af12a3e7SDag-Erling Smørgrav 		return;
947af12a3e7SDag-Erling Smørgrav 	}
948d4af9e69SDag-Erling Smørgrav 	c->open_confirm = fn;
949d4af9e69SDag-Erling Smørgrav 	c->open_confirm_ctx = ctx;
950af12a3e7SDag-Erling Smørgrav }
951333ee039SDag-Erling Smørgrav 
952af12a3e7SDag-Erling Smørgrav void
9534f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id,
9544f52dfbbSDag-Erling Smørgrav     channel_callback_fn *fn, int do_close)
955af12a3e7SDag-Erling Smørgrav {
9564f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
957f388f5efSDag-Erling Smørgrav 
958af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
9594f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id", __func__, id);
960af12a3e7SDag-Erling Smørgrav 		return;
961af12a3e7SDag-Erling Smørgrav 	}
962af12a3e7SDag-Erling Smørgrav 	c->detach_user = fn;
963b74df5b2SDag-Erling Smørgrav 	c->detach_close = do_close;
964af12a3e7SDag-Erling Smørgrav }
965333ee039SDag-Erling Smørgrav 
966af12a3e7SDag-Erling Smørgrav void
9674f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id)
968af12a3e7SDag-Erling Smørgrav {
9694f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
970f388f5efSDag-Erling Smørgrav 
971af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
9724f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id", __func__, id);
973af12a3e7SDag-Erling Smørgrav 		return;
974af12a3e7SDag-Erling Smørgrav 	}
975af12a3e7SDag-Erling Smørgrav 	c->detach_user = NULL;
976b74df5b2SDag-Erling Smørgrav 	c->detach_close = 0;
977af12a3e7SDag-Erling Smørgrav }
978333ee039SDag-Erling Smørgrav 
979af12a3e7SDag-Erling Smørgrav void
9804f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
981d4af9e69SDag-Erling Smørgrav     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
982af12a3e7SDag-Erling Smørgrav {
9834f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
984f388f5efSDag-Erling Smørgrav 
985af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
9864f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: bad id", __func__, id);
987af12a3e7SDag-Erling Smørgrav 		return;
988af12a3e7SDag-Erling Smørgrav 	}
989b74df5b2SDag-Erling Smørgrav 	c->input_filter = ifn;
990b74df5b2SDag-Erling Smørgrav 	c->output_filter = ofn;
991d4af9e69SDag-Erling Smørgrav 	c->filter_ctx = ctx;
992d4af9e69SDag-Erling Smørgrav 	c->filter_cleanup = cfn;
993af12a3e7SDag-Erling Smørgrav }
994af12a3e7SDag-Erling Smørgrav 
995af12a3e7SDag-Erling Smørgrav void
9964f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
997d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty, u_int window_max)
998af12a3e7SDag-Erling Smørgrav {
9994f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
10004f52dfbbSDag-Erling Smørgrav 	int r;
1001f388f5efSDag-Erling Smørgrav 
1002af12a3e7SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1003af12a3e7SDag-Erling Smørgrav 		fatal("channel_activate for non-larval channel %d.", id);
10044f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
10054f52dfbbSDag-Erling Smørgrav 		fatal(":%s: channel %d: no remote id", __func__, c->self);
10064f52dfbbSDag-Erling Smørgrav 
10074f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1008af12a3e7SDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1009af12a3e7SDag-Erling Smørgrav 	c->local_window = c->local_window_max = window_max;
10104f52dfbbSDag-Erling Smørgrav 
10114f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
10124f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
10134f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
10144f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
10154f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
1016511b41d2SMark Murray }
1017511b41d2SMark Murray 
1018af12a3e7SDag-Erling Smørgrav static void
10194f52dfbbSDag-Erling Smørgrav channel_pre_listener(struct ssh *ssh, Channel *c,
10204f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1021511b41d2SMark Murray {
1022a04a10f8SKris Kennaway 	FD_SET(c->sock, readset);
1023a04a10f8SKris Kennaway }
1024a04a10f8SKris Kennaway 
1025af12a3e7SDag-Erling Smørgrav static void
10264f52dfbbSDag-Erling Smørgrav channel_pre_connecting(struct ssh *ssh, Channel *c,
10274f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1028ca3176e7SBrian Feldman {
1029ca3176e7SBrian Feldman 	debug3("channel %d: waiting for connection", c->self);
1030ca3176e7SBrian Feldman 	FD_SET(c->sock, writeset);
1031ca3176e7SBrian Feldman }
1032ca3176e7SBrian Feldman 
1033af12a3e7SDag-Erling Smørgrav static void
10344f52dfbbSDag-Erling Smørgrav channel_pre_open(struct ssh *ssh, Channel *c,
10354f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1036a04a10f8SKris Kennaway {
1037a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
10384f52dfbbSDag-Erling Smørgrav 	    c->remote_window > 0 &&
10394f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->input) < c->remote_window &&
10404f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
1041a04a10f8SKris Kennaway 		FD_SET(c->rfd, readset);
1042a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1043a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
10444f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0) {
1045a04a10f8SKris Kennaway 			FD_SET(c->wfd, writeset);
1046a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
104780628bacSDag-Erling Smørgrav 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
10484f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
10494f52dfbbSDag-Erling Smørgrav 				    "obuf_empty delayed efd %d/(%zu)", c->self,
10504f52dfbbSDag-Erling Smørgrav 				    c->efd, sshbuf_len(c->extended));
105180628bacSDag-Erling Smørgrav 			else
10524f52dfbbSDag-Erling Smørgrav 				chan_obuf_empty(ssh, c);
1053a04a10f8SKris Kennaway 		}
1054a04a10f8SKris Kennaway 	}
1055a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
10564f52dfbbSDag-Erling Smørgrav 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
10574f52dfbbSDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1058a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
10594f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) > 0)
1060a04a10f8SKris Kennaway 			FD_SET(c->efd, writeset);
1061e2f6069cSDag-Erling Smørgrav 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
1062e2f6069cSDag-Erling Smørgrav 		    (c->extended_usage == CHAN_EXTENDED_READ ||
1063e2f6069cSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
10644f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) < c->remote_window)
1065a04a10f8SKris Kennaway 			FD_SET(c->efd, readset);
1066a04a10f8SKris Kennaway 	}
106721e764dfSDag-Erling Smørgrav 	/* XXX: What about efd? races? */
1068a04a10f8SKris Kennaway }
1069a04a10f8SKris Kennaway 
1070a04a10f8SKris Kennaway /*
1071a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
1072a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
1073a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
1074a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
1075a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
1076a04a10f8SKris Kennaway  * XXX All this happens at the client side.
1077af12a3e7SDag-Erling Smørgrav  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1078a04a10f8SKris Kennaway  */
1079af12a3e7SDag-Erling Smørgrav static int
10804f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1081a04a10f8SKris Kennaway {
10824f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
1083ca3176e7SBrian Feldman 	u_char *ucp;
1084ca3176e7SBrian Feldman 	u_int proto_len, data_len;
1085511b41d2SMark Murray 
1086557f75e5SDag-Erling Smørgrav 	/* Is this being called after the refusal deadline? */
10874f52dfbbSDag-Erling Smørgrav 	if (sc->x11_refuse_time != 0 &&
10884f52dfbbSDag-Erling Smørgrav 	    (u_int)monotime() >= sc->x11_refuse_time) {
1089557f75e5SDag-Erling Smørgrav 		verbose("Rejected X11 connection after ForwardX11Timeout "
1090557f75e5SDag-Erling Smørgrav 		    "expired");
1091557f75e5SDag-Erling Smørgrav 		return -1;
1092557f75e5SDag-Erling Smørgrav 	}
1093557f75e5SDag-Erling Smørgrav 
1094511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
10954f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) < 12)
1096a04a10f8SKris Kennaway 		return 0;
1097511b41d2SMark Murray 
1098511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
10994f52dfbbSDag-Erling Smørgrav 	ucp = sshbuf_mutable_ptr(b);
1100511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1101511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
1102511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
1103511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1104511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
1105511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
1106511b41d2SMark Murray 	} else {
1107221552e4SDag-Erling Smørgrav 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1108511b41d2SMark Murray 		    ucp[0]);
1109a04a10f8SKris Kennaway 		return -1;
1110511b41d2SMark Murray 	}
1111511b41d2SMark Murray 
1112511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
11134f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) <
1114511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1115a04a10f8SKris Kennaway 		return 0;
1116511b41d2SMark Murray 
1117511b41d2SMark Murray 	/* Check if authentication protocol matches. */
11184f52dfbbSDag-Erling Smørgrav 	if (proto_len != strlen(sc->x11_saved_proto) ||
11194f52dfbbSDag-Erling Smørgrav 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1120221552e4SDag-Erling Smørgrav 		debug2("X11 connection uses different authentication protocol.");
1121a04a10f8SKris Kennaway 		return -1;
1122511b41d2SMark Murray 	}
1123511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
11244f52dfbbSDag-Erling Smørgrav 	if (data_len != sc->x11_fake_data_len ||
1125e2f6069cSDag-Erling Smørgrav 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
11264f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1127221552e4SDag-Erling Smørgrav 		debug2("X11 auth data does not match fake data.");
1128a04a10f8SKris Kennaway 		return -1;
1129511b41d2SMark Murray 	}
1130511b41d2SMark Murray 	/* Check fake data length */
11314f52dfbbSDag-Erling Smørgrav 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1132511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
11334f52dfbbSDag-Erling Smørgrav 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1134a04a10f8SKris Kennaway 		return -1;
1135511b41d2SMark Murray 	}
1136511b41d2SMark Murray 	/*
1137511b41d2SMark Murray 	 * Received authentication protocol and data match
1138511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
1139511b41d2SMark Murray 	 * data.
1140511b41d2SMark Murray 	 */
1141511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
11424f52dfbbSDag-Erling Smørgrav 	    sc->x11_saved_data, sc->x11_saved_data_len);
1143a04a10f8SKris Kennaway 	return 1;
1144a04a10f8SKris Kennaway }
1145511b41d2SMark Murray 
1146af12a3e7SDag-Erling Smørgrav static void
11474f52dfbbSDag-Erling Smørgrav channel_pre_x11_open(struct ssh *ssh, Channel *c,
11484f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1149a04a10f8SKris Kennaway {
11504f52dfbbSDag-Erling Smørgrav 	int ret = x11_open_helper(ssh, c->output);
1151af12a3e7SDag-Erling Smørgrav 
1152af12a3e7SDag-Erling Smørgrav 	/* c->force_drain = 1; */
1153af12a3e7SDag-Erling Smørgrav 
1154a04a10f8SKris Kennaway 	if (ret == 1) {
1155a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
11564f52dfbbSDag-Erling Smørgrav 		channel_pre_open(ssh, c, readset, writeset);
1157a04a10f8SKris Kennaway 	} else if (ret == -1) {
1158221552e4SDag-Erling Smørgrav 		logit("X11 connection rejected because of wrong authentication.");
11594f52dfbbSDag-Erling Smørgrav 		debug2("X11 rejected %d i%d/o%d",
11604f52dfbbSDag-Erling Smørgrav 		    c->self, c->istate, c->ostate);
11614f52dfbbSDag-Erling Smørgrav 		chan_read_failed(ssh, c);
11624f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
11634f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
11644f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->output);
11654f52dfbbSDag-Erling Smørgrav 		chan_write_failed(ssh, c);
1166221552e4SDag-Erling Smørgrav 		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
1167a04a10f8SKris Kennaway 	}
1168a04a10f8SKris Kennaway }
1169a04a10f8SKris Kennaway 
1170b15c8340SDag-Erling Smørgrav static void
11714f52dfbbSDag-Erling Smørgrav channel_pre_mux_client(struct ssh *ssh,
11724f52dfbbSDag-Erling Smørgrav     Channel *c, fd_set *readset, fd_set *writeset)
1173b15c8340SDag-Erling Smørgrav {
1174e2f6069cSDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
11754f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
1176b15c8340SDag-Erling Smørgrav 		FD_SET(c->rfd, readset);
1177b15c8340SDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1178b15c8340SDag-Erling Smørgrav 		/* clear buffer immediately (discard any partial packet) */
11794f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
11804f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
1181b15c8340SDag-Erling Smørgrav 		/* Start output drain. XXX just kill chan? */
11824f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
1183b15c8340SDag-Erling Smørgrav 	}
1184b15c8340SDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1185b15c8340SDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
11864f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0)
1187b15c8340SDag-Erling Smørgrav 			FD_SET(c->wfd, writeset);
1188b15c8340SDag-Erling Smørgrav 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
11894f52dfbbSDag-Erling Smørgrav 			chan_obuf_empty(ssh, c);
1190b15c8340SDag-Erling Smørgrav 	}
1191b15c8340SDag-Erling Smørgrav }
1192b15c8340SDag-Erling Smørgrav 
1193ca3176e7SBrian Feldman /* try to decode a socks4 header */
1194af12a3e7SDag-Erling Smørgrav static int
11954f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1196ca3176e7SBrian Feldman {
11974f52dfbbSDag-Erling Smørgrav 	const u_char *p;
11984f52dfbbSDag-Erling Smørgrav 	char *host;
1199cce7d346SDag-Erling Smørgrav 	u_int len, have, i, found, need;
1200ca3176e7SBrian Feldman 	char username[256];
1201ca3176e7SBrian Feldman 	struct {
1202ca3176e7SBrian Feldman 		u_int8_t version;
1203ca3176e7SBrian Feldman 		u_int8_t command;
1204ca3176e7SBrian Feldman 		u_int16_t dest_port;
1205ca3176e7SBrian Feldman 		struct in_addr dest_addr;
1206ca3176e7SBrian Feldman 	} s4_req, s4_rsp;
12074f52dfbbSDag-Erling Smørgrav 	int r;
1208ca3176e7SBrian Feldman 
1209ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4", c->self);
1210ca3176e7SBrian Feldman 
12114f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1212ca3176e7SBrian Feldman 	len = sizeof(s4_req);
1213ca3176e7SBrian Feldman 	if (have < len)
1214ca3176e7SBrian Feldman 		return 0;
12154f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1216cce7d346SDag-Erling Smørgrav 
1217cce7d346SDag-Erling Smørgrav 	need = 1;
1218cce7d346SDag-Erling Smørgrav 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1219cce7d346SDag-Erling Smørgrav 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1220cce7d346SDag-Erling Smørgrav 		debug2("channel %d: socks4a request", c->self);
1221cce7d346SDag-Erling Smørgrav 		/* ... and needs an extra string (the hostname) */
1222cce7d346SDag-Erling Smørgrav 		need = 2;
1223cce7d346SDag-Erling Smørgrav 	}
1224cce7d346SDag-Erling Smørgrav 	/* Check for terminating NUL on the string(s) */
1225ca3176e7SBrian Feldman 	for (found = 0, i = len; i < have; i++) {
1226ca3176e7SBrian Feldman 		if (p[i] == '\0') {
1227cce7d346SDag-Erling Smørgrav 			found++;
1228cce7d346SDag-Erling Smørgrav 			if (found == need)
1229ca3176e7SBrian Feldman 				break;
1230ca3176e7SBrian Feldman 		}
1231ca3176e7SBrian Feldman 		if (i > 1024) {
1232ca3176e7SBrian Feldman 			/* the peer is probably sending garbage */
1233ca3176e7SBrian Feldman 			debug("channel %d: decode socks4: too long",
1234ca3176e7SBrian Feldman 			    c->self);
1235ca3176e7SBrian Feldman 			return -1;
1236ca3176e7SBrian Feldman 		}
1237ca3176e7SBrian Feldman 	}
1238cce7d346SDag-Erling Smørgrav 	if (found < need)
1239ca3176e7SBrian Feldman 		return 0;
12404f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
12414f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
12424f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
12434f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
12444f52dfbbSDag-Erling Smørgrav 		debug("channels %d: decode socks4: %s", c->self, ssh_err(r));
12454f52dfbbSDag-Erling Smørgrav 		return -1;
12464f52dfbbSDag-Erling Smørgrav 	}
12474f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
12484f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
12494f52dfbbSDag-Erling Smørgrav 	if (memchr(p, '\0', have) == NULL) {
12504f52dfbbSDag-Erling Smørgrav 		error("channel %d: decode socks4: user not nul terminated",
1251b83788ffSDag-Erling Smørgrav 		    c->self);
12524f52dfbbSDag-Erling Smørgrav 		return -1;
12534f52dfbbSDag-Erling Smørgrav 	}
1254ca3176e7SBrian Feldman 	len = strlen(p);
1255ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1256cce7d346SDag-Erling Smørgrav 	len++; /* trailing '\0' */
1257ca3176e7SBrian Feldman 	strlcpy(username, p, sizeof(username));
12584f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(input, len)) != 0) {
12594f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: consume: %s", __func__,
12604f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
12614f52dfbbSDag-Erling Smørgrav 	}
1262e4a9863fSDag-Erling Smørgrav 	free(c->path);
1263cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1264cce7d346SDag-Erling Smørgrav 	if (need == 1) {			/* SOCKS4: one string */
1265ca3176e7SBrian Feldman 		host = inet_ntoa(s4_req.dest_addr);
1266cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
1267cce7d346SDag-Erling Smørgrav 	} else {				/* SOCKS4A: two strings */
12684f52dfbbSDag-Erling Smørgrav 		have = sshbuf_len(input);
12694f52dfbbSDag-Erling Smørgrav 		p = sshbuf_ptr(input);
12704f52dfbbSDag-Erling Smørgrav 		if (memchr(p, '\0', have) == NULL) {
12714f52dfbbSDag-Erling Smørgrav 			error("channel %d: decode socks4a: host not nul "
12724f52dfbbSDag-Erling Smørgrav 			    "terminated", c->self);
12734f52dfbbSDag-Erling Smørgrav 			return -1;
12744f52dfbbSDag-Erling Smørgrav 		}
1275cce7d346SDag-Erling Smørgrav 		len = strlen(p);
1276cce7d346SDag-Erling Smørgrav 		debug2("channel %d: decode socks4a: host %s/%d",
1277cce7d346SDag-Erling Smørgrav 		    c->self, p, len);
1278cce7d346SDag-Erling Smørgrav 		len++;				/* trailing '\0' */
1279cce7d346SDag-Erling Smørgrav 		if (len > NI_MAXHOST) {
1280cce7d346SDag-Erling Smørgrav 			error("channel %d: hostname \"%.100s\" too long",
1281cce7d346SDag-Erling Smørgrav 			    c->self, p);
1282cce7d346SDag-Erling Smørgrav 			return -1;
1283cce7d346SDag-Erling Smørgrav 		}
1284cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(p);
12854f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_consume(input, len)) != 0) {
12864f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: consume: %s", __func__,
12874f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
12884f52dfbbSDag-Erling Smørgrav 		}
1289cce7d346SDag-Erling Smørgrav 	}
1290ca3176e7SBrian Feldman 	c->host_port = ntohs(s4_req.dest_port);
1291ca3176e7SBrian Feldman 
1292221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1293cce7d346SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s4_req.command);
1294ca3176e7SBrian Feldman 
1295ca3176e7SBrian Feldman 	if (s4_req.command != 1) {
1296cce7d346SDag-Erling Smørgrav 		debug("channel %d: cannot handle: %s cn %d",
1297cce7d346SDag-Erling Smørgrav 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1298ca3176e7SBrian Feldman 		return -1;
1299ca3176e7SBrian Feldman 	}
1300ca3176e7SBrian Feldman 	s4_rsp.version = 0;			/* vn: 0 for reply */
1301ca3176e7SBrian Feldman 	s4_rsp.command = 90;			/* cd: req granted */
1302ca3176e7SBrian Feldman 	s4_rsp.dest_port = 0;			/* ignored */
1303ca3176e7SBrian Feldman 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
13044f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) {
13054f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: append reply: %s", __func__,
13064f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
13074f52dfbbSDag-Erling Smørgrav 	}
1308ca3176e7SBrian Feldman 	return 1;
1309ca3176e7SBrian Feldman }
1310ca3176e7SBrian Feldman 
1311221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */
1312221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE	0x1000
1313221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH	0x00
1314221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4		0x01
1315221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN	0x03
1316221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6		0x04
1317221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT	0x01
1318221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS	0x00
1319221552e4SDag-Erling Smørgrav 
1320221552e4SDag-Erling Smørgrav static int
13214f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1322221552e4SDag-Erling Smørgrav {
13234f52dfbbSDag-Erling Smørgrav 	/* XXX use get/put_u8 instead of trusting struct padding */
1324221552e4SDag-Erling Smørgrav 	struct {
1325221552e4SDag-Erling Smørgrav 		u_int8_t version;
1326221552e4SDag-Erling Smørgrav 		u_int8_t command;
1327221552e4SDag-Erling Smørgrav 		u_int8_t reserved;
1328221552e4SDag-Erling Smørgrav 		u_int8_t atyp;
1329221552e4SDag-Erling Smørgrav 	} s5_req, s5_rsp;
1330221552e4SDag-Erling Smørgrav 	u_int16_t dest_port;
1331e4a9863fSDag-Erling Smørgrav 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
13324f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1333333ee039SDag-Erling Smørgrav 	u_int have, need, i, found, nmethods, addrlen, af;
13344f52dfbbSDag-Erling Smørgrav 	int r;
1335221552e4SDag-Erling Smørgrav 
1336221552e4SDag-Erling Smørgrav 	debug2("channel %d: decode socks5", c->self);
13374f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1338221552e4SDag-Erling Smørgrav 	if (p[0] != 0x05)
1339221552e4SDag-Erling Smørgrav 		return -1;
13404f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1341221552e4SDag-Erling Smørgrav 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1342221552e4SDag-Erling Smørgrav 		/* format: ver | nmethods | methods */
1343221552e4SDag-Erling Smørgrav 		if (have < 2)
1344221552e4SDag-Erling Smørgrav 			return 0;
1345221552e4SDag-Erling Smørgrav 		nmethods = p[1];
1346221552e4SDag-Erling Smørgrav 		if (have < nmethods + 2)
1347221552e4SDag-Erling Smørgrav 			return 0;
1348221552e4SDag-Erling Smørgrav 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1349221552e4SDag-Erling Smørgrav 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1350221552e4SDag-Erling Smørgrav 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1351221552e4SDag-Erling Smørgrav 				found = 1;
1352221552e4SDag-Erling Smørgrav 				break;
1353221552e4SDag-Erling Smørgrav 			}
1354221552e4SDag-Erling Smørgrav 		}
1355221552e4SDag-Erling Smørgrav 		if (!found) {
1356221552e4SDag-Erling Smørgrav 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1357221552e4SDag-Erling Smørgrav 			    c->self);
1358221552e4SDag-Erling Smørgrav 			return -1;
1359221552e4SDag-Erling Smørgrav 		}
13604f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0) {
13614f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: consume: %s", __func__,
13624f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
13634f52dfbbSDag-Erling Smørgrav 		}
13644f52dfbbSDag-Erling Smørgrav 		/* version, method */
13654f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
13664f52dfbbSDag-Erling Smørgrav 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) {
13674f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: append reply: %s", __func__,
13684f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
13694f52dfbbSDag-Erling Smørgrav 		}
1370221552e4SDag-Erling Smørgrav 		c->flags |= SSH_SOCKS5_AUTHDONE;
1371221552e4SDag-Erling Smørgrav 		debug2("channel %d: socks5 auth done", c->self);
1372221552e4SDag-Erling Smørgrav 		return 0;				/* need more */
1373221552e4SDag-Erling Smørgrav 	}
1374221552e4SDag-Erling Smørgrav 	debug2("channel %d: socks5 post auth", c->self);
1375221552e4SDag-Erling Smørgrav 	if (have < sizeof(s5_req)+1)
1376221552e4SDag-Erling Smørgrav 		return 0;			/* need more */
1377333ee039SDag-Erling Smørgrav 	memcpy(&s5_req, p, sizeof(s5_req));
1378221552e4SDag-Erling Smørgrav 	if (s5_req.version != 0x05 ||
1379221552e4SDag-Erling Smørgrav 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1380221552e4SDag-Erling Smørgrav 	    s5_req.reserved != 0x00) {
1381221552e4SDag-Erling Smørgrav 		debug2("channel %d: only socks5 connect supported", c->self);
1382221552e4SDag-Erling Smørgrav 		return -1;
1383221552e4SDag-Erling Smørgrav 	}
1384221552e4SDag-Erling Smørgrav 	switch (s5_req.atyp){
1385221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV4:
1386221552e4SDag-Erling Smørgrav 		addrlen = 4;
1387221552e4SDag-Erling Smørgrav 		af = AF_INET;
1388221552e4SDag-Erling Smørgrav 		break;
1389221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_DOMAIN:
1390221552e4SDag-Erling Smørgrav 		addrlen = p[sizeof(s5_req)];
1391221552e4SDag-Erling Smørgrav 		af = -1;
1392221552e4SDag-Erling Smørgrav 		break;
1393221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV6:
1394221552e4SDag-Erling Smørgrav 		addrlen = 16;
1395221552e4SDag-Erling Smørgrav 		af = AF_INET6;
1396221552e4SDag-Erling Smørgrav 		break;
1397221552e4SDag-Erling Smørgrav 	default:
1398221552e4SDag-Erling Smørgrav 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1399221552e4SDag-Erling Smørgrav 		return -1;
1400221552e4SDag-Erling Smørgrav 	}
1401333ee039SDag-Erling Smørgrav 	need = sizeof(s5_req) + addrlen + 2;
1402333ee039SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1403333ee039SDag-Erling Smørgrav 		need++;
1404333ee039SDag-Erling Smørgrav 	if (have < need)
1405221552e4SDag-Erling Smørgrav 		return 0;
14064f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) {
14074f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: consume: %s", __func__,
14084f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
14094f52dfbbSDag-Erling Smørgrav 	}
14104f52dfbbSDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
14114f52dfbbSDag-Erling Smørgrav 		/* host string length */
14124f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_consume(input, 1)) != 0) {
14134f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: consume: %s", __func__,
14144f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
14154f52dfbbSDag-Erling Smørgrav 		}
14164f52dfbbSDag-Erling Smørgrav 	}
14174f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
14184f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
14194f52dfbbSDag-Erling Smørgrav 		debug("channel %d: parse addr/port: %s", c->self, ssh_err(r));
14204f52dfbbSDag-Erling Smørgrav 		return -1;
14214f52dfbbSDag-Erling Smørgrav 	}
1422221552e4SDag-Erling Smørgrav 	dest_addr[addrlen] = '\0';
1423e4a9863fSDag-Erling Smørgrav 	free(c->path);
1424cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1425cce7d346SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1426cce7d346SDag-Erling Smørgrav 		if (addrlen >= NI_MAXHOST) {
1427cce7d346SDag-Erling Smørgrav 			error("channel %d: dynamic request: socks5 hostname "
1428cce7d346SDag-Erling Smørgrav 			    "\"%.100s\" too long", c->self, dest_addr);
1429221552e4SDag-Erling Smørgrav 			return -1;
1430cce7d346SDag-Erling Smørgrav 		}
1431cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(dest_addr);
1432cce7d346SDag-Erling Smørgrav 	} else {
1433cce7d346SDag-Erling Smørgrav 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1434cce7d346SDag-Erling Smørgrav 			return -1;
1435cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(ntop);
1436cce7d346SDag-Erling Smørgrav 	}
1437221552e4SDag-Erling Smørgrav 	c->host_port = ntohs(dest_port);
1438221552e4SDag-Erling Smørgrav 
1439221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1440221552e4SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s5_req.command);
1441221552e4SDag-Erling Smørgrav 
1442221552e4SDag-Erling Smørgrav 	s5_rsp.version = 0x05;
1443221552e4SDag-Erling Smørgrav 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1444221552e4SDag-Erling Smørgrav 	s5_rsp.reserved = 0;			/* ignored */
1445221552e4SDag-Erling Smørgrav 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1446221552e4SDag-Erling Smørgrav 	dest_port = 0;				/* ignored */
1447221552e4SDag-Erling Smørgrav 
14484f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
14494f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
14504f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
14514f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: append reply: %s", __func__,
14524f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
1453221552e4SDag-Erling Smørgrav 	return 1;
1454221552e4SDag-Erling Smørgrav }
1455221552e4SDag-Erling Smørgrav 
1456b15c8340SDag-Erling Smørgrav Channel *
14574f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh,
14584f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, u_short port_to_connect, int in, int out)
1459b15c8340SDag-Erling Smørgrav {
1460b15c8340SDag-Erling Smørgrav 	Channel *c;
1461b15c8340SDag-Erling Smørgrav 
14624f52dfbbSDag-Erling Smørgrav 	debug("%s %s:%d", __func__, host_to_connect, port_to_connect);
1463b15c8340SDag-Erling Smørgrav 
14644f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
1465b15c8340SDag-Erling Smørgrav 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
1466b15c8340SDag-Erling Smørgrav 	    0, "stdio-forward", /*nonblock*/0);
1467b15c8340SDag-Erling Smørgrav 
1468b15c8340SDag-Erling Smørgrav 	c->path = xstrdup(host_to_connect);
1469b15c8340SDag-Erling Smørgrav 	c->host_port = port_to_connect;
1470b15c8340SDag-Erling Smørgrav 	c->listening_port = 0;
1471b15c8340SDag-Erling Smørgrav 	c->force_drain = 1;
1472b15c8340SDag-Erling Smørgrav 
14734f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
14744f52dfbbSDag-Erling Smørgrav 	port_open_helper(ssh, c, "direct-tcpip");
1475b15c8340SDag-Erling Smørgrav 
1476b15c8340SDag-Erling Smørgrav 	return c;
1477b15c8340SDag-Erling Smørgrav }
1478b15c8340SDag-Erling Smørgrav 
1479ca3176e7SBrian Feldman /* dynamic port forwarding */
1480af12a3e7SDag-Erling Smørgrav static void
14814f52dfbbSDag-Erling Smørgrav channel_pre_dynamic(struct ssh *ssh, Channel *c,
14824f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1483ca3176e7SBrian Feldman {
14844f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1485d4ecd108SDag-Erling Smørgrav 	u_int have;
1486d4ecd108SDag-Erling Smørgrav 	int ret;
1487ca3176e7SBrian Feldman 
14884f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->input);
1489ca3176e7SBrian Feldman 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
14904f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->input, stderr); */
1491ca3176e7SBrian Feldman 	/* check if the fixed size part of the packet is in buffer. */
1492221552e4SDag-Erling Smørgrav 	if (have < 3) {
1493ca3176e7SBrian Feldman 		/* need more */
1494ca3176e7SBrian Feldman 		FD_SET(c->sock, readset);
1495ca3176e7SBrian Feldman 		return;
1496ca3176e7SBrian Feldman 	}
1497ca3176e7SBrian Feldman 	/* try to guess the protocol */
14984f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->input);
14994f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u8? */
1500ca3176e7SBrian Feldman 	switch (p[0]) {
1501ca3176e7SBrian Feldman 	case 0x04:
15024f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->input, c->output);
1503ca3176e7SBrian Feldman 		break;
1504221552e4SDag-Erling Smørgrav 	case 0x05:
15054f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->input, c->output);
1506221552e4SDag-Erling Smørgrav 		break;
1507ca3176e7SBrian Feldman 	default:
1508ca3176e7SBrian Feldman 		ret = -1;
1509ca3176e7SBrian Feldman 		break;
1510ca3176e7SBrian Feldman 	}
1511ca3176e7SBrian Feldman 	if (ret < 0) {
15124f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1513ca3176e7SBrian Feldman 	} else if (ret == 0) {
1514ca3176e7SBrian Feldman 		debug2("channel %d: pre_dynamic: need more", c->self);
1515ca3176e7SBrian Feldman 		/* need more */
1516ca3176e7SBrian Feldman 		FD_SET(c->sock, readset);
15174f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output))
15184f52dfbbSDag-Erling Smørgrav 			FD_SET(c->sock, writeset);
1519ca3176e7SBrian Feldman 	} else {
1520ca3176e7SBrian Feldman 		/* switch to the next state */
1521ca3176e7SBrian Feldman 		c->type = SSH_CHANNEL_OPENING;
15224f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, c, "direct-tcpip");
15234f52dfbbSDag-Erling Smørgrav 	}
15244f52dfbbSDag-Erling Smørgrav }
15254f52dfbbSDag-Erling Smørgrav 
15264f52dfbbSDag-Erling Smørgrav /* simulate read-error */
15274f52dfbbSDag-Erling Smørgrav static void
15284f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c)
15294f52dfbbSDag-Erling Smørgrav {
15304f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
15314f52dfbbSDag-Erling Smørgrav 	chan_read_failed(ssh, c);
15324f52dfbbSDag-Erling Smørgrav 	sshbuf_reset(c->input);
15334f52dfbbSDag-Erling Smørgrav 	chan_ibuf_empty(ssh, c);
15344f52dfbbSDag-Erling Smørgrav 	sshbuf_reset(c->output);
15354f52dfbbSDag-Erling Smørgrav 	chan_write_failed(ssh, c);
15364f52dfbbSDag-Erling Smørgrav }
15374f52dfbbSDag-Erling Smørgrav 
15384f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */
15394f52dfbbSDag-Erling Smørgrav static void
15404f52dfbbSDag-Erling Smørgrav channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c)
15414f52dfbbSDag-Erling Smørgrav {
15424f52dfbbSDag-Erling Smørgrav 	const u_char *p;
15434f52dfbbSDag-Erling Smørgrav 	u_int have, len;
15444f52dfbbSDag-Erling Smørgrav 	int r, ret;
15454f52dfbbSDag-Erling Smørgrav 
15464f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->output);
15474f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
15484f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->output, stderr); */
15494f52dfbbSDag-Erling Smørgrav 	/* EOF received */
15504f52dfbbSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
15514f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_consume(c->output, have)) != 0) {
15524f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: consume: %s",
15534f52dfbbSDag-Erling Smørgrav 			    __func__, c->self, ssh_err(r));
15544f52dfbbSDag-Erling Smørgrav 		}
15554f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
15564f52dfbbSDag-Erling Smørgrav 		return;
15574f52dfbbSDag-Erling Smørgrav 	}
15584f52dfbbSDag-Erling Smørgrav 	/* check if the fixed size part of the packet is in buffer. */
15594f52dfbbSDag-Erling Smørgrav 	if (have < 3)
15604f52dfbbSDag-Erling Smørgrav 		return;
15614f52dfbbSDag-Erling Smørgrav 	/* try to guess the protocol */
15624f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->output);
15634f52dfbbSDag-Erling Smørgrav 	switch (p[0]) {
15644f52dfbbSDag-Erling Smørgrav 	case 0x04:
15654f52dfbbSDag-Erling Smørgrav 		/* switch input/output for reverse forwarding */
15664f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->output, c->input);
15674f52dfbbSDag-Erling Smørgrav 		break;
15684f52dfbbSDag-Erling Smørgrav 	case 0x05:
15694f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->output, c->input);
15704f52dfbbSDag-Erling Smørgrav 		break;
15714f52dfbbSDag-Erling Smørgrav 	default:
15724f52dfbbSDag-Erling Smørgrav 		ret = -1;
15734f52dfbbSDag-Erling Smørgrav 		break;
15744f52dfbbSDag-Erling Smørgrav 	}
15754f52dfbbSDag-Erling Smørgrav 	if (ret < 0) {
15764f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
15774f52dfbbSDag-Erling Smørgrav 	} else if (ret == 0) {
15784f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: pre_rdynamic: need more", c->self);
15794f52dfbbSDag-Erling Smørgrav 		/* send socks request to peer */
15804f52dfbbSDag-Erling Smørgrav 		len = sshbuf_len(c->input);
15814f52dfbbSDag-Erling Smørgrav 		if (len > 0 && len < c->remote_window) {
15824f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
15834f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
15844f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
15854f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0) {
15864f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %i: rdynamic: %s", __func__,
15874f52dfbbSDag-Erling Smørgrav 				    c->self, ssh_err(r));
15884f52dfbbSDag-Erling Smørgrav 			}
15894f52dfbbSDag-Erling Smørgrav 			if ((r = sshbuf_consume(c->input, len)) != 0) {
15904f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %d: consume: %s",
15914f52dfbbSDag-Erling Smørgrav 				    __func__, c->self, ssh_err(r));
15924f52dfbbSDag-Erling Smørgrav 			}
15934f52dfbbSDag-Erling Smørgrav 			c->remote_window -= len;
15944f52dfbbSDag-Erling Smørgrav 		}
15954f52dfbbSDag-Erling Smørgrav 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
15964f52dfbbSDag-Erling Smørgrav 		/* the connect failed */
15974f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
1598ca3176e7SBrian Feldman 	}
1599ca3176e7SBrian Feldman }
1600ca3176e7SBrian Feldman 
1601a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
1602af12a3e7SDag-Erling Smørgrav static void
16034f52dfbbSDag-Erling Smørgrav channel_post_x11_listener(struct ssh *ssh, Channel *c,
16044f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1605511b41d2SMark Murray {
1606af12a3e7SDag-Erling Smørgrav 	Channel *nc;
1607d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
16084f52dfbbSDag-Erling Smørgrav 	int r, newsock, oerrno, remote_port;
1609511b41d2SMark Murray 	socklen_t addrlen;
1610ca3176e7SBrian Feldman 	char buf[16384], *remote_ipaddr;
1611511b41d2SMark Murray 
16124f52dfbbSDag-Erling Smørgrav 	if (!FD_ISSET(c->sock, readset))
16134f52dfbbSDag-Erling Smørgrav 		return;
16144f52dfbbSDag-Erling Smørgrav 
1615511b41d2SMark Murray 	debug("X11 connection requested.");
1616511b41d2SMark Murray 	addrlen = sizeof(addr);
1617d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1618af12a3e7SDag-Erling Smørgrav 	if (c->single_connection) {
1619e4a9863fSDag-Erling Smørgrav 		oerrno = errno;
1620221552e4SDag-Erling Smørgrav 		debug2("single_connection: closing X11 listener.");
16214f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->sock);
16224f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1623e4a9863fSDag-Erling Smørgrav 		errno = oerrno;
1624af12a3e7SDag-Erling Smørgrav 	}
1625511b41d2SMark Murray 	if (newsock < 0) {
1626e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1627e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1628511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1629462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1630e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1631a04a10f8SKris Kennaway 		return;
1632511b41d2SMark Murray 	}
1633af12a3e7SDag-Erling Smørgrav 	set_nodelay(newsock);
1634ca3176e7SBrian Feldman 	remote_ipaddr = get_peer_ipaddr(newsock);
1635a04a10f8SKris Kennaway 	remote_port = get_peer_port(newsock);
1636511b41d2SMark Murray 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1637ca3176e7SBrian Feldman 	    remote_ipaddr, remote_port);
1638a04a10f8SKris Kennaway 
16394f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "accepted x11 socket",
1640a04a10f8SKris Kennaway 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1641221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
16424f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "x11");
164347dd1d1bSDag-Erling Smørgrav 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
164447dd1d1bSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
16454f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: reply %s", __func__,
16464f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
1647511b41d2SMark Murray 	}
16484f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
16494f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r));
1650e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1651a04a10f8SKris Kennaway }
1652511b41d2SMark Murray 
1653af12a3e7SDag-Erling Smørgrav static void
16544f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
1655ca3176e7SBrian Feldman {
1656f7167e0eSDag-Erling Smørgrav 	char *local_ipaddr = get_local_ipaddr(c->sock);
1657076ad2f8SDag-Erling Smørgrav 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1658ca3176e7SBrian Feldman 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1659d4ecd108SDag-Erling Smørgrav 	int remote_port = get_peer_port(c->sock);
16604f52dfbbSDag-Erling Smørgrav 	int r;
1661ca3176e7SBrian Feldman 
1662b15c8340SDag-Erling Smørgrav 	if (remote_port == -1) {
1663b15c8340SDag-Erling Smørgrav 		/* Fake addr/port to appease peers that validate it (Tectia) */
1664e4a9863fSDag-Erling Smørgrav 		free(remote_ipaddr);
1665b15c8340SDag-Erling Smørgrav 		remote_ipaddr = xstrdup("127.0.0.1");
1666b15c8340SDag-Erling Smørgrav 		remote_port = 65535;
1667b15c8340SDag-Erling Smørgrav 	}
1668b15c8340SDag-Erling Smørgrav 
16694f52dfbbSDag-Erling Smørgrav 	free(c->remote_name);
16704f52dfbbSDag-Erling Smørgrav 	xasprintf(&c->remote_name,
1671ca3176e7SBrian Feldman 	    "%s: listening port %d for %.100s port %d, "
1672f7167e0eSDag-Erling Smørgrav 	    "connect from %.200s port %d to %.100s port %d",
1673ca3176e7SBrian Feldman 	    rtype, c->listening_port, c->path, c->host_port,
1674f7167e0eSDag-Erling Smørgrav 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1675ca3176e7SBrian Feldman 
16764f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, rtype);
1677a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "direct-tcpip") == 0) {
1678ca3176e7SBrian Feldman 		/* target host, port */
16794f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
16804f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0) {
16814f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
16824f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
16834f52dfbbSDag-Erling Smørgrav 		}
1684a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
1685a0ee8cc6SDag-Erling Smørgrav 		/* target path */
16864f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) {
16874f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
16884f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
16894f52dfbbSDag-Erling Smørgrav 		}
1690a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1691a0ee8cc6SDag-Erling Smørgrav 		/* listen path */
16924f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) {
16934f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
16944f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
16954f52dfbbSDag-Erling Smørgrav 		}
1696ca3176e7SBrian Feldman 	} else {
1697ca3176e7SBrian Feldman 		/* listen address, port */
16984f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
16994f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, local_port)) != 0) {
17004f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
17014f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
17024f52dfbbSDag-Erling Smørgrav 		}
1703ca3176e7SBrian Feldman 	}
1704a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1705a0ee8cc6SDag-Erling Smørgrav 		/* reserved for future owner/mode info */
17064f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, "")) != 0) {
17074f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
17084f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
17094f52dfbbSDag-Erling Smørgrav 		}
1710a0ee8cc6SDag-Erling Smørgrav 	} else {
1711ca3176e7SBrian Feldman 		/* originator host and port */
17124f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
17134f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) {
17144f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: reply %s", __func__,
17154f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
1716a0ee8cc6SDag-Erling Smørgrav 		}
1717ca3176e7SBrian Feldman 	}
17184f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
17194f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r));
1720e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1721f7167e0eSDag-Erling Smørgrav 	free(local_ipaddr);
1722ca3176e7SBrian Feldman }
1723ca3176e7SBrian Feldman 
1724557f75e5SDag-Erling Smørgrav void
17254f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time)
1726557f75e5SDag-Erling Smørgrav {
17274f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->x11_refuse_time = refuse_time;
1728557f75e5SDag-Erling Smørgrav }
1729557f75e5SDag-Erling Smørgrav 
1730511b41d2SMark Murray /*
1731a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
1732511b41d2SMark Murray  */
1733af12a3e7SDag-Erling Smørgrav static void
17344f52dfbbSDag-Erling Smørgrav channel_post_port_listener(struct ssh *ssh, Channel *c,
17354f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1736a04a10f8SKris Kennaway {
1737ca3176e7SBrian Feldman 	Channel *nc;
1738d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1739af12a3e7SDag-Erling Smørgrav 	int newsock, nextstate;
1740a04a10f8SKris Kennaway 	socklen_t addrlen;
1741ca3176e7SBrian Feldman 	char *rtype;
1742a04a10f8SKris Kennaway 
17434f52dfbbSDag-Erling Smørgrav 	if (!FD_ISSET(c->sock, readset))
17444f52dfbbSDag-Erling Smørgrav 		return;
17454f52dfbbSDag-Erling Smørgrav 
17464f52dfbbSDag-Erling Smørgrav 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1747a04a10f8SKris Kennaway 	    c->listening_port, c->path, c->host_port);
1748ca3176e7SBrian Feldman 
1749af12a3e7SDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1750af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1751af12a3e7SDag-Erling Smørgrav 		rtype = "forwarded-tcpip";
1752a0ee8cc6SDag-Erling Smørgrav 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
1753a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1754a0ee8cc6SDag-Erling Smørgrav 		rtype = "forwarded-streamlocal@openssh.com";
1755a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == PORT_STREAMLOCAL) {
1756a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1757a0ee8cc6SDag-Erling Smørgrav 		rtype = "direct-streamlocal@openssh.com";
1758a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == 0) {
1759af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_DYNAMIC;
1760af12a3e7SDag-Erling Smørgrav 		rtype = "dynamic-tcpip";
1761af12a3e7SDag-Erling Smørgrav 	} else {
1762af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1763af12a3e7SDag-Erling Smørgrav 		rtype = "direct-tcpip";
1764af12a3e7SDag-Erling Smørgrav 	}
1765ca3176e7SBrian Feldman 
1766511b41d2SMark Murray 	addrlen = sizeof(addr);
1767d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1768511b41d2SMark Murray 	if (newsock < 0) {
1769e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1770e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1771511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1772462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1773e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1774a04a10f8SKris Kennaway 		return;
1775511b41d2SMark Murray 	}
1776a0ee8cc6SDag-Erling Smørgrav 	if (c->host_port != PORT_STREAMLOCAL)
1777af12a3e7SDag-Erling Smørgrav 		set_nodelay(newsock);
17784f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
1779221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
1780ca3176e7SBrian Feldman 	nc->listening_port = c->listening_port;
1781ca3176e7SBrian Feldman 	nc->host_port = c->host_port;
1782cce7d346SDag-Erling Smørgrav 	if (c->path != NULL)
1783cce7d346SDag-Erling Smørgrav 		nc->path = xstrdup(c->path);
1784ca3176e7SBrian Feldman 
1785b15c8340SDag-Erling Smørgrav 	if (nextstate != SSH_CHANNEL_DYNAMIC)
17864f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, nc, rtype);
1787a04a10f8SKris Kennaway }
1788511b41d2SMark Murray 
1789511b41d2SMark Murray /*
1790a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
1791a04a10f8SKris Kennaway  * clients.
1792511b41d2SMark Murray  */
1793af12a3e7SDag-Erling Smørgrav static void
17944f52dfbbSDag-Erling Smørgrav channel_post_auth_listener(struct ssh *ssh, Channel *c,
17954f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1796a04a10f8SKris Kennaway {
1797af12a3e7SDag-Erling Smørgrav 	Channel *nc;
17984f52dfbbSDag-Erling Smørgrav 	int r, newsock;
1799d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1800a04a10f8SKris Kennaway 	socklen_t addrlen;
1801a04a10f8SKris Kennaway 
18024f52dfbbSDag-Erling Smørgrav 	if (!FD_ISSET(c->sock, readset))
18034f52dfbbSDag-Erling Smørgrav 		return;
18044f52dfbbSDag-Erling Smørgrav 
1805511b41d2SMark Murray 	addrlen = sizeof(addr);
1806d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1807511b41d2SMark Murray 	if (newsock < 0) {
18084f52dfbbSDag-Erling Smørgrav 		error("accept from auth socket: %.100s", strerror(errno));
1809462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1810e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1811a04a10f8SKris Kennaway 		return;
1812511b41d2SMark Murray 	}
18134f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "accepted auth socket",
1814ca3176e7SBrian Feldman 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1815ca3176e7SBrian Feldman 	    c->local_window_max, c->local_maxpacket,
1816221552e4SDag-Erling Smørgrav 	    0, "accepted auth socket", 1);
18174f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
18184f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
18194f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
1820a04a10f8SKris Kennaway }
1821511b41d2SMark Murray 
1822af12a3e7SDag-Erling Smørgrav static void
18234f52dfbbSDag-Erling Smørgrav channel_post_connecting(struct ssh *ssh, Channel *c,
18244f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1825ca3176e7SBrian Feldman {
18264f52dfbbSDag-Erling Smørgrav 	int err = 0, sock, isopen, r;
1827af12a3e7SDag-Erling Smørgrav 	socklen_t sz = sizeof(err);
1828af12a3e7SDag-Erling Smørgrav 
18294f52dfbbSDag-Erling Smørgrav 	if (!FD_ISSET(c->sock, writeset))
18304f52dfbbSDag-Erling Smørgrav 		return;
18314f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
18324f52dfbbSDag-Erling Smørgrav 		fatal(":%s: channel %d: no remote id", __func__, c->self);
18334f52dfbbSDag-Erling Smørgrav 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
18344f52dfbbSDag-Erling Smørgrav 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
1835af12a3e7SDag-Erling Smørgrav 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
1836af12a3e7SDag-Erling Smørgrav 		err = errno;
1837af12a3e7SDag-Erling Smørgrav 		error("getsockopt SO_ERROR failed");
1838af12a3e7SDag-Erling Smørgrav 	}
1839ca3176e7SBrian Feldman 	if (err == 0) {
1840d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connected to %s port %d",
1841d4af9e69SDag-Erling Smørgrav 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
1842d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
1843af12a3e7SDag-Erling Smørgrav 		c->type = SSH_CHANNEL_OPEN;
18444f52dfbbSDag-Erling Smørgrav 		if (isopen) {
18454f52dfbbSDag-Erling Smørgrav 			/* no message necessary */
1846af12a3e7SDag-Erling Smørgrav 		} else {
18474f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
18484f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
18494f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
18504f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
18514f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
18524f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket))
18534f52dfbbSDag-Erling Smørgrav 			    != 0)
18544f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %i: confirm: %s", __func__,
18554f52dfbbSDag-Erling Smørgrav 				    c->self, ssh_err(r));
18564f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_send(ssh)) != 0)
18574f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %i: %s", __func__, c->self,
18584f52dfbbSDag-Erling Smørgrav 				    ssh_err(r));
1859af12a3e7SDag-Erling Smørgrav 		}
1860ca3176e7SBrian Feldman 	} else {
1861d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connection failed: %s",
1862ca3176e7SBrian Feldman 		    c->self, strerror(err));
1863d4af9e69SDag-Erling Smørgrav 		/* Try next address, if any */
1864d4af9e69SDag-Erling Smørgrav 		if ((sock = connect_next(&c->connect_ctx)) > 0) {
1865d4af9e69SDag-Erling Smørgrav 			close(c->sock);
1866d4af9e69SDag-Erling Smørgrav 			c->sock = c->rfd = c->wfd = sock;
18674f52dfbbSDag-Erling Smørgrav 			channel_find_maxfd(ssh->chanctxt);
1868d4af9e69SDag-Erling Smørgrav 			return;
1869d4af9e69SDag-Erling Smørgrav 		}
1870d4af9e69SDag-Erling Smørgrav 		/* Exhausted all addresses */
1871d4af9e69SDag-Erling Smørgrav 		error("connect_to %.100s port %d: failed.",
1872d4af9e69SDag-Erling Smørgrav 		    c->connect_ctx.host, c->connect_ctx.port);
1873d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
18744f52dfbbSDag-Erling Smørgrav 		if (isopen) {
18754f52dfbbSDag-Erling Smørgrav 			rdynamic_close(ssh, c);
1876af12a3e7SDag-Erling Smørgrav 		} else {
18774f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
18784f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
18794f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
188047dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh,
188147dd1d1bSDag-Erling Smørgrav 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
188247dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
188347dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, "")) != 0) {
18844f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %i: failure: %s", __func__,
18854f52dfbbSDag-Erling Smørgrav 				    c->self, ssh_err(r));
188647dd1d1bSDag-Erling Smørgrav 			}
18874f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_send(ssh)) != 0)
18884f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %i: %s", __func__, c->self,
18894f52dfbbSDag-Erling Smørgrav 				    ssh_err(r));
18904f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
1891ca3176e7SBrian Feldman 		}
1892ca3176e7SBrian Feldman 	}
1893ca3176e7SBrian Feldman }
1894ca3176e7SBrian Feldman 
1895af12a3e7SDag-Erling Smørgrav static int
18964f52dfbbSDag-Erling Smørgrav channel_handle_rfd(struct ssh *ssh, Channel *c,
18974f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
1898a04a10f8SKris Kennaway {
1899aa49c926SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
19004f52dfbbSDag-Erling Smørgrav 	ssize_t len;
19014f52dfbbSDag-Erling Smørgrav 	int r, force;
1902511b41d2SMark Murray 
1903d4af9e69SDag-Erling Smørgrav 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
19044f52dfbbSDag-Erling Smørgrav 
19054f52dfbbSDag-Erling Smørgrav 	if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset)))
19064f52dfbbSDag-Erling Smørgrav 		return 1;
19074f52dfbbSDag-Erling Smørgrav 
1908333ee039SDag-Erling Smørgrav 	errno = 0;
1909a04a10f8SKris Kennaway 	len = read(c->rfd, buf, sizeof(buf));
1910d4af9e69SDag-Erling Smørgrav 	if (len < 0 && (errno == EINTR ||
1911d4af9e69SDag-Erling Smørgrav 	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
1912a04a10f8SKris Kennaway 		return 1;
1913333ee039SDag-Erling Smørgrav #ifndef PTY_ZEROREAD
19140c82706bSBrian Feldman  	if (len <= 0) {
1915333ee039SDag-Erling Smørgrav #else
1916333ee039SDag-Erling Smørgrav 	if ((!c->isatty && len <= 0) ||
1917333ee039SDag-Erling Smørgrav 	    (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
1918333ee039SDag-Erling Smørgrav #endif
19194f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: read<=0 rfd %d len %zd",
1920a04a10f8SKris Kennaway 		    c->self, c->rfd, len);
1921ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
1922221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
19234f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
1924ca3176e7SBrian Feldman 			return -1;
1925a04a10f8SKris Kennaway 		} else {
19264f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
1927a04a10f8SKris Kennaway 		}
1928a04a10f8SKris Kennaway 		return -1;
1929a04a10f8SKris Kennaway 	}
1930b66f2d16SKris Kennaway 	if (c->input_filter != NULL) {
19314f52dfbbSDag-Erling Smørgrav 		if (c->input_filter(ssh, c, buf, len) == -1) {
1932221552e4SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
19334f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
1934b66f2d16SKris Kennaway 		}
1935b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
19364f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
19374f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: put datagram: %s", __func__,
19384f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
19394f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->input, buf, len)) != 0) {
19404f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: put data: %s", __func__,
19414f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
1942b66f2d16SKris Kennaway 	}
1943a04a10f8SKris Kennaway 	return 1;
1944a04a10f8SKris Kennaway }
1945333ee039SDag-Erling Smørgrav 
1946af12a3e7SDag-Erling Smørgrav static int
19474f52dfbbSDag-Erling Smørgrav channel_handle_wfd(struct ssh *ssh, Channel *c,
19484f52dfbbSDag-Erling Smørgrav    fd_set *readset, fd_set *writeset)
1949a04a10f8SKris Kennaway {
1950ca3176e7SBrian Feldman 	struct termios tio;
19514f52dfbbSDag-Erling Smørgrav 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
19524f52dfbbSDag-Erling Smørgrav 	size_t dlen, olen = 0;
19534f52dfbbSDag-Erling Smørgrav 	int r, len;
19544f52dfbbSDag-Erling Smørgrav 
19554f52dfbbSDag-Erling Smørgrav 	if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
19564f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->output) == 0)
19574f52dfbbSDag-Erling Smørgrav 		return 1;
1958a04a10f8SKris Kennaway 
1959a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
19604f52dfbbSDag-Erling Smørgrav 	olen = sshbuf_len(c->output);
1961b74df5b2SDag-Erling Smørgrav 	if (c->output_filter != NULL) {
19624f52dfbbSDag-Erling Smørgrav 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
1963b74df5b2SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
1964b74df5b2SDag-Erling Smørgrav 			if (c->type != SSH_CHANNEL_OPEN)
19654f52dfbbSDag-Erling Smørgrav 				chan_mark_dead(ssh, c);
1966b74df5b2SDag-Erling Smørgrav 			else
19674f52dfbbSDag-Erling Smørgrav 				chan_write_failed(ssh, c);
1968b74df5b2SDag-Erling Smørgrav 			return -1;
1969b74df5b2SDag-Erling Smørgrav 		}
1970b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
19714f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
19724f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: get datagram: %s", __func__,
19734f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
19744f52dfbbSDag-Erling Smørgrav 		buf = data;
1975b74df5b2SDag-Erling Smørgrav 	} else {
19764f52dfbbSDag-Erling Smørgrav 		buf = data = sshbuf_mutable_ptr(c->output);
19774f52dfbbSDag-Erling Smørgrav 		dlen = sshbuf_len(c->output);
1978b74df5b2SDag-Erling Smørgrav 	}
1979b74df5b2SDag-Erling Smørgrav 
1980b74df5b2SDag-Erling Smørgrav 	if (c->datagram) {
1981b74df5b2SDag-Erling Smørgrav 		/* ignore truncated writes, datagrams might get lost */
1982b74df5b2SDag-Erling Smørgrav 		len = write(c->wfd, buf, dlen);
1983e4a9863fSDag-Erling Smørgrav 		free(data);
1984d4af9e69SDag-Erling Smørgrav 		if (len < 0 && (errno == EINTR || errno == EAGAIN ||
1985d4af9e69SDag-Erling Smørgrav 		    errno == EWOULDBLOCK))
1986b74df5b2SDag-Erling Smørgrav 			return 1;
19874f52dfbbSDag-Erling Smørgrav 		if (len <= 0)
19884f52dfbbSDag-Erling Smørgrav 			goto write_fail;
1989e2f6069cSDag-Erling Smørgrav 		goto out;
1990b74df5b2SDag-Erling Smørgrav 	}
19914f52dfbbSDag-Erling Smørgrav 
1992f388f5efSDag-Erling Smørgrav #ifdef _AIX
1993f388f5efSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
19944f52dfbbSDag-Erling Smørgrav 	if (c->wfd_isatty)
1995476cd3b2SDag-Erling Smørgrav 		dlen = MIN(dlen, 8*1024);
1996f388f5efSDag-Erling Smørgrav #endif
1997b74df5b2SDag-Erling Smørgrav 
1998b74df5b2SDag-Erling Smørgrav 	len = write(c->wfd, buf, dlen);
1999d4af9e69SDag-Erling Smørgrav 	if (len < 0 &&
2000d4af9e69SDag-Erling Smørgrav 	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
2001a04a10f8SKris Kennaway 		return 1;
2002511b41d2SMark Murray 	if (len <= 0) {
20034f52dfbbSDag-Erling Smørgrav  write_fail:
2004ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2005221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
20064f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2007ca3176e7SBrian Feldman 			return -1;
2008511b41d2SMark Murray 		} else {
20094f52dfbbSDag-Erling Smørgrav 			chan_write_failed(ssh, c);
2010511b41d2SMark Murray 		}
2011a04a10f8SKris Kennaway 		return -1;
2012511b41d2SMark Murray 	}
20137aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON
20144f52dfbbSDag-Erling Smørgrav 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2015e0fbb1d2SBrian Feldman 		if (tcgetattr(c->wfd, &tio) == 0 &&
2016e0fbb1d2SBrian Feldman 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2017e0fbb1d2SBrian Feldman 			/*
2018e0fbb1d2SBrian Feldman 			 * Simulate echo to reduce the impact of
2019ca3176e7SBrian Feldman 			 * traffic analysis. We need to match the
2020ca3176e7SBrian Feldman 			 * size of a SSH2_MSG_CHANNEL_DATA message
2021b74df5b2SDag-Erling Smørgrav 			 * (4 byte channel id + buf)
2022e0fbb1d2SBrian Feldman 			 */
20234f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
20244f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0)
20254f52dfbbSDag-Erling Smørgrav 				fatal("%s: channel %d: ignore: %s",
20264f52dfbbSDag-Erling Smørgrav 				    __func__, c->self, ssh_err(r));
2027e0fbb1d2SBrian Feldman 		}
2028e0fbb1d2SBrian Feldman 	}
20294f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */
20304f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0) {
20314f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: consume: %s",
20324f52dfbbSDag-Erling Smørgrav 		    __func__, c->self, ssh_err(r));
2033511b41d2SMark Murray 	}
2034e2f6069cSDag-Erling Smørgrav  out:
20354f52dfbbSDag-Erling Smørgrav 	c->local_consumed += olen - sshbuf_len(c->output);
20364f52dfbbSDag-Erling Smørgrav 
2037a04a10f8SKris Kennaway 	return 1;
2038511b41d2SMark Murray }
2039333ee039SDag-Erling Smørgrav 
2040af12a3e7SDag-Erling Smørgrav static int
20414f52dfbbSDag-Erling Smørgrav channel_handle_efd_write(struct ssh *ssh, Channel *c,
20424f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
2043a04a10f8SKris Kennaway {
20444f52dfbbSDag-Erling Smørgrav 	int r;
20454f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2046511b41d2SMark Murray 
20474f52dfbbSDag-Erling Smørgrav 	if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0)
20484f52dfbbSDag-Erling Smørgrav 		return 1;
20494f52dfbbSDag-Erling Smørgrav 
20504f52dfbbSDag-Erling Smørgrav 	len = write(c->efd, sshbuf_ptr(c->extended),
20514f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->extended));
20524f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
2053d4af9e69SDag-Erling Smørgrav 	if (len < 0 && (errno == EINTR || errno == EAGAIN ||
2054d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK))
2055ca3176e7SBrian Feldman 		return 1;
2056ca3176e7SBrian Feldman 	if (len <= 0) {
20574f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
20584f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->efd);
2059ca3176e7SBrian Feldman 	} else {
20604f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_consume(c->extended, len)) != 0) {
20614f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: consume: %s",
20624f52dfbbSDag-Erling Smørgrav 			    __func__, c->self, ssh_err(r));
20634f52dfbbSDag-Erling Smørgrav 		}
2064a04a10f8SKris Kennaway 		c->local_consumed += len;
2065a04a10f8SKris Kennaway 	}
20664f52dfbbSDag-Erling Smørgrav 	return 1;
20674f52dfbbSDag-Erling Smørgrav }
20684f52dfbbSDag-Erling Smørgrav 
20694f52dfbbSDag-Erling Smørgrav static int
20704f52dfbbSDag-Erling Smørgrav channel_handle_efd_read(struct ssh *ssh, Channel *c,
20714f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
20724f52dfbbSDag-Erling Smørgrav {
20734f52dfbbSDag-Erling Smørgrav 	char buf[CHAN_RBUF];
20744f52dfbbSDag-Erling Smørgrav 	int r;
20754f52dfbbSDag-Erling Smørgrav 	ssize_t len;
20764f52dfbbSDag-Erling Smørgrav 
20774f52dfbbSDag-Erling Smørgrav 	if (!c->detach_close && !FD_ISSET(c->efd, readset))
20784f52dfbbSDag-Erling Smørgrav 		return 1;
20794f52dfbbSDag-Erling Smørgrav 
2080a04a10f8SKris Kennaway 	len = read(c->efd, buf, sizeof(buf));
20814f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
2082d4af9e69SDag-Erling Smørgrav 	if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
2083d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK) && !c->detach_close)))
2084ca3176e7SBrian Feldman 		return 1;
2085ca3176e7SBrian Feldman 	if (len <= 0) {
2086ca3176e7SBrian Feldman 		debug2("channel %d: closing read-efd %d",
2087a04a10f8SKris Kennaway 		    c->self, c->efd);
20884f52dfbbSDag-Erling Smørgrav 		channel_close_fd(ssh, &c->efd);
2089ca3176e7SBrian Feldman 	} else {
2090e2f6069cSDag-Erling Smørgrav 		if (c->extended_usage == CHAN_EXTENDED_IGNORE) {
2091e2f6069cSDag-Erling Smørgrav 			debug3("channel %d: discard efd",
2092e2f6069cSDag-Erling Smørgrav 			    c->self);
20934f52dfbbSDag-Erling Smørgrav 		} else if ((r = sshbuf_put(c->extended, buf, len)) != 0) {
20944f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: append: %s",
20954f52dfbbSDag-Erling Smørgrav 			    __func__, c->self, ssh_err(r));
2096a04a10f8SKris Kennaway 		}
2097ca3176e7SBrian Feldman 	}
2098a04a10f8SKris Kennaway 	return 1;
2099a04a10f8SKris Kennaway }
2100333ee039SDag-Erling Smørgrav 
210121e764dfSDag-Erling Smørgrav static int
21024f52dfbbSDag-Erling Smørgrav channel_handle_efd(struct ssh *ssh, Channel *c,
21034f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
2104a04a10f8SKris Kennaway {
21054f52dfbbSDag-Erling Smørgrav 	if (c->efd == -1)
21064f52dfbbSDag-Erling Smørgrav 		return 1;
21074f52dfbbSDag-Erling Smørgrav 
21084f52dfbbSDag-Erling Smørgrav 	/** XXX handle drain efd, too */
21094f52dfbbSDag-Erling Smørgrav 
21104f52dfbbSDag-Erling Smørgrav 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
21114f52dfbbSDag-Erling Smørgrav 		return channel_handle_efd_write(ssh, c, readset, writeset);
21124f52dfbbSDag-Erling Smørgrav 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
21134f52dfbbSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
21144f52dfbbSDag-Erling Smørgrav 		return channel_handle_efd_read(ssh, c, readset, writeset);
21154f52dfbbSDag-Erling Smørgrav 
21164f52dfbbSDag-Erling Smørgrav 	return 1;
21174f52dfbbSDag-Erling Smørgrav }
21184f52dfbbSDag-Erling Smørgrav 
21194f52dfbbSDag-Erling Smørgrav static int
21204f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c)
21214f52dfbbSDag-Erling Smørgrav {
21224f52dfbbSDag-Erling Smørgrav 	int r;
21234f52dfbbSDag-Erling Smørgrav 
2124ca3176e7SBrian Feldman 	if (c->type == SSH_CHANNEL_OPEN &&
2125ca3176e7SBrian Feldman 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2126d4af9e69SDag-Erling Smørgrav 	    ((c->local_window_max - c->local_window >
2127d4af9e69SDag-Erling Smørgrav 	    c->local_maxpacket*3) ||
2128d4af9e69SDag-Erling Smørgrav 	    c->local_window < c->local_window_max/2) &&
2129a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
21304f52dfbbSDag-Erling Smørgrav 		if (!c->have_remote_id)
21314f52dfbbSDag-Erling Smørgrav 			fatal(":%s: channel %d: no remote id",
21324f52dfbbSDag-Erling Smørgrav 			    __func__, c->self);
21334f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh,
21344f52dfbbSDag-Erling Smørgrav 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
21354f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
21364f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
21374f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
21384f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: %s", __func__,
21394f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
21404f52dfbbSDag-Erling Smørgrav 		}
21415b9b2fafSBrian Feldman 		debug2("channel %d: window %d sent adjust %d",
2142a04a10f8SKris Kennaway 		    c->self, c->local_window,
2143a04a10f8SKris Kennaway 		    c->local_consumed);
214460c59fadSDag-Erling Smørgrav 		c->local_window += c->local_consumed;
2145a04a10f8SKris Kennaway 		c->local_consumed = 0;
2146a04a10f8SKris Kennaway 	}
2147a04a10f8SKris Kennaway 	return 1;
2148a04a10f8SKris Kennaway }
2149a04a10f8SKris Kennaway 
2150af12a3e7SDag-Erling Smørgrav static void
21514f52dfbbSDag-Erling Smørgrav channel_post_open(struct ssh *ssh, Channel *c,
21524f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
2153a04a10f8SKris Kennaway {
21544f52dfbbSDag-Erling Smørgrav 	channel_handle_rfd(ssh, c, readset, writeset);
21554f52dfbbSDag-Erling Smørgrav 	channel_handle_wfd(ssh, c, readset, writeset);
21564f52dfbbSDag-Erling Smørgrav 	channel_handle_efd(ssh, c, readset, writeset);
21574f52dfbbSDag-Erling Smørgrav 	channel_check_window(ssh, c);
2158a04a10f8SKris Kennaway }
2159a04a10f8SKris Kennaway 
2160b15c8340SDag-Erling Smørgrav static u_int
21614f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need)
2162b15c8340SDag-Erling Smørgrav {
2163b15c8340SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
21644f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2165b15c8340SDag-Erling Smørgrav 	u_int rlen;
21664f52dfbbSDag-Erling Smørgrav 	int r;
2167b15c8340SDag-Erling Smørgrav 
21684f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(c->input) < need) {
21694f52dfbbSDag-Erling Smørgrav 		rlen = need - sshbuf_len(c->input);
2170ca86bcf2SDag-Erling Smørgrav 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
2171acc1a9efSDag-Erling Smørgrav 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
21724f52dfbbSDag-Erling Smørgrav 			return sshbuf_len(c->input);
2173b15c8340SDag-Erling Smørgrav 		if (len <= 0) {
21744f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
2175b15c8340SDag-Erling Smørgrav 			    c->self, c->rfd, len);
21764f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2177b15c8340SDag-Erling Smørgrav 			return 0;
21784f52dfbbSDag-Erling Smørgrav 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0) {
21794f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: append: %s",
21804f52dfbbSDag-Erling Smørgrav 			    __func__, c->self, ssh_err(r));
2181b15c8340SDag-Erling Smørgrav 		}
21824f52dfbbSDag-Erling Smørgrav 	}
21834f52dfbbSDag-Erling Smørgrav 	return sshbuf_len(c->input);
2184b15c8340SDag-Erling Smørgrav }
2185b15c8340SDag-Erling Smørgrav 
2186b15c8340SDag-Erling Smørgrav static void
21874f52dfbbSDag-Erling Smørgrav channel_post_mux_client_read(struct ssh *ssh, Channel *c,
21884f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
2189b15c8340SDag-Erling Smørgrav {
2190b15c8340SDag-Erling Smørgrav 	u_int need;
2191b15c8340SDag-Erling Smørgrav 
21924f52dfbbSDag-Erling Smørgrav 	if (c->rfd == -1 || !FD_ISSET(c->rfd, readset))
21934f52dfbbSDag-Erling Smørgrav 		return;
21944f52dfbbSDag-Erling Smørgrav 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
21954f52dfbbSDag-Erling Smørgrav 		return;
21964f52dfbbSDag-Erling Smørgrav 	if (c->mux_pause)
21974f52dfbbSDag-Erling Smørgrav 		return;
2198b15c8340SDag-Erling Smørgrav 
2199b15c8340SDag-Erling Smørgrav 	/*
2200b15c8340SDag-Erling Smørgrav 	 * Don't not read past the precise end of packets to
2201b15c8340SDag-Erling Smørgrav 	 * avoid disrupting fd passing.
2202b15c8340SDag-Erling Smørgrav 	 */
22034f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, 4) < 4) /* read header */
2204b15c8340SDag-Erling Smørgrav 		return;
22054f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u32 */
22064f52dfbbSDag-Erling Smørgrav 	need = PEEK_U32(sshbuf_ptr(c->input));
2207b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
2208b15c8340SDag-Erling Smørgrav 	if (need > CHANNEL_MUX_MAX_PACKET) {
2209b15c8340SDag-Erling Smørgrav 		debug2("channel %d: packet too big %u > %u",
2210b15c8340SDag-Erling Smørgrav 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
22114f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
2212b15c8340SDag-Erling Smørgrav 		return;
2213b15c8340SDag-Erling Smørgrav 	}
22144f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
2215b15c8340SDag-Erling Smørgrav 		return;
22164f52dfbbSDag-Erling Smørgrav 	if (c->mux_rcb(ssh, c) != 0) {
2217b15c8340SDag-Erling Smørgrav 		debug("channel %d: mux_rcb failed", c->self);
22184f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
2219b15c8340SDag-Erling Smørgrav 		return;
2220b15c8340SDag-Erling Smørgrav 	}
2221b15c8340SDag-Erling Smørgrav }
2222b15c8340SDag-Erling Smørgrav 
2223b15c8340SDag-Erling Smørgrav static void
22244f52dfbbSDag-Erling Smørgrav channel_post_mux_client_write(struct ssh *ssh, Channel *c,
22254f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
22264f52dfbbSDag-Erling Smørgrav {
22274f52dfbbSDag-Erling Smørgrav 	ssize_t len;
22284f52dfbbSDag-Erling Smørgrav 	int r;
22294f52dfbbSDag-Erling Smørgrav 
22304f52dfbbSDag-Erling Smørgrav 	if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
22314f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->output) == 0)
22324f52dfbbSDag-Erling Smørgrav 		return;
22334f52dfbbSDag-Erling Smørgrav 
22344f52dfbbSDag-Erling Smørgrav 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
22354f52dfbbSDag-Erling Smørgrav 	if (len < 0 && (errno == EINTR || errno == EAGAIN))
22364f52dfbbSDag-Erling Smørgrav 		return;
22374f52dfbbSDag-Erling Smørgrav 	if (len <= 0) {
22384f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
22394f52dfbbSDag-Erling Smørgrav 		return;
22404f52dfbbSDag-Erling Smørgrav 	}
22414f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0)
22424f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: consume: %s", __func__,
22434f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
22444f52dfbbSDag-Erling Smørgrav }
22454f52dfbbSDag-Erling Smørgrav 
22464f52dfbbSDag-Erling Smørgrav static void
22474f52dfbbSDag-Erling Smørgrav channel_post_mux_client(struct ssh *ssh, Channel *c,
22484f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
22494f52dfbbSDag-Erling Smørgrav {
22504f52dfbbSDag-Erling Smørgrav 	channel_post_mux_client_read(ssh, c, readset, writeset);
22514f52dfbbSDag-Erling Smørgrav 	channel_post_mux_client_write(ssh, c, readset, writeset);
22524f52dfbbSDag-Erling Smørgrav }
22534f52dfbbSDag-Erling Smørgrav 
22544f52dfbbSDag-Erling Smørgrav static void
22554f52dfbbSDag-Erling Smørgrav channel_post_mux_listener(struct ssh *ssh, Channel *c,
22564f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset)
2257b15c8340SDag-Erling Smørgrav {
2258b15c8340SDag-Erling Smørgrav 	Channel *nc;
2259b15c8340SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2260b15c8340SDag-Erling Smørgrav 	socklen_t addrlen;
2261b15c8340SDag-Erling Smørgrav 	int newsock;
2262b15c8340SDag-Erling Smørgrav 	uid_t euid;
2263b15c8340SDag-Erling Smørgrav 	gid_t egid;
2264b15c8340SDag-Erling Smørgrav 
2265b15c8340SDag-Erling Smørgrav 	if (!FD_ISSET(c->sock, readset))
2266b15c8340SDag-Erling Smørgrav 		return;
2267b15c8340SDag-Erling Smørgrav 
2268b15c8340SDag-Erling Smørgrav 	debug("multiplexing control connection");
2269b15c8340SDag-Erling Smørgrav 
2270b15c8340SDag-Erling Smørgrav 	/*
2271b15c8340SDag-Erling Smørgrav 	 * Accept connection on control socket
2272b15c8340SDag-Erling Smørgrav 	 */
2273b15c8340SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
2274b15c8340SDag-Erling Smørgrav 	addrlen = sizeof(addr);
2275b15c8340SDag-Erling Smørgrav 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
2276b15c8340SDag-Erling Smørgrav 	    &addrlen)) == -1) {
2277b15c8340SDag-Erling Smørgrav 		error("%s accept: %s", __func__, strerror(errno));
2278462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2279e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2280b15c8340SDag-Erling Smørgrav 		return;
2281b15c8340SDag-Erling Smørgrav 	}
2282b15c8340SDag-Erling Smørgrav 
2283b15c8340SDag-Erling Smørgrav 	if (getpeereid(newsock, &euid, &egid) < 0) {
2284b15c8340SDag-Erling Smørgrav 		error("%s getpeereid failed: %s", __func__,
2285b15c8340SDag-Erling Smørgrav 		    strerror(errno));
2286b15c8340SDag-Erling Smørgrav 		close(newsock);
2287b15c8340SDag-Erling Smørgrav 		return;
2288b15c8340SDag-Erling Smørgrav 	}
2289b15c8340SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
2290b15c8340SDag-Erling Smørgrav 		error("multiplex uid mismatch: peer euid %u != uid %u",
2291b15c8340SDag-Erling Smørgrav 		    (u_int)euid, (u_int)getuid());
2292b15c8340SDag-Erling Smørgrav 		close(newsock);
2293b15c8340SDag-Erling Smørgrav 		return;
2294b15c8340SDag-Erling Smørgrav 	}
22954f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT,
2296b15c8340SDag-Erling Smørgrav 	    newsock, newsock, -1, c->local_window_max,
2297b15c8340SDag-Erling Smørgrav 	    c->local_maxpacket, 0, "mux-control", 1);
2298b15c8340SDag-Erling Smørgrav 	nc->mux_rcb = c->mux_rcb;
22994f52dfbbSDag-Erling Smørgrav 	debug3("%s: new mux channel %d fd %d", __func__, nc->self, nc->sock);
2300b15c8340SDag-Erling Smørgrav 	/* establish state */
23014f52dfbbSDag-Erling Smørgrav 	nc->mux_rcb(ssh, nc);
2302b15c8340SDag-Erling Smørgrav 	/* mux state transitions must not elicit protocol messages */
2303b15c8340SDag-Erling Smørgrav 	nc->flags |= CHAN_LOCAL;
2304b15c8340SDag-Erling Smørgrav }
2305b15c8340SDag-Erling Smørgrav 
2306af12a3e7SDag-Erling Smørgrav static void
23074f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc)
2308a04a10f8SKris Kennaway {
23094f52dfbbSDag-Erling Smørgrav 	chan_fn **pre, **post;
2310f388f5efSDag-Erling Smørgrav 
23114f52dfbbSDag-Erling Smørgrav 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
23124f52dfbbSDag-Erling Smørgrav 	   (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
23134f52dfbbSDag-Erling Smørgrav 		fatal("%s: allocation failed", __func__);
2314511b41d2SMark Murray 
23154f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
23164f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
23174f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
23184f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
23194f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
23204f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
23214f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
23224f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
23234f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
23244f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
23254f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
23264f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
23274f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2328a04a10f8SKris Kennaway 
23294f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
23304f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
23314f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
23324f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
23334f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
23344f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
23354f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
23364f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
23374f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
23384f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
23394f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
23404f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2341a04a10f8SKris Kennaway 
23424f52dfbbSDag-Erling Smørgrav 	sc->channel_pre = pre;
23434f52dfbbSDag-Erling Smørgrav 	sc->channel_post = post;
2344a04a10f8SKris Kennaway }
2345a04a10f8SKris Kennaway 
2346af12a3e7SDag-Erling Smørgrav /* gc dead channels */
2347af12a3e7SDag-Erling Smørgrav static void
23484f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c)
2349af12a3e7SDag-Erling Smørgrav {
2350af12a3e7SDag-Erling Smørgrav 	if (c == NULL)
2351af12a3e7SDag-Erling Smørgrav 		return;
2352af12a3e7SDag-Erling Smørgrav 	if (c->detach_user != NULL) {
23534f52dfbbSDag-Erling Smørgrav 		if (!chan_is_dead(ssh, c, c->detach_close))
2354af12a3e7SDag-Erling Smørgrav 			return;
2355221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: notify user", c->self);
23564f52dfbbSDag-Erling Smørgrav 		c->detach_user(ssh, c->self, NULL);
2357af12a3e7SDag-Erling Smørgrav 		/* if we still have a callback */
2358af12a3e7SDag-Erling Smørgrav 		if (c->detach_user != NULL)
2359af12a3e7SDag-Erling Smørgrav 			return;
2360221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: user detached", c->self);
2361af12a3e7SDag-Erling Smørgrav 	}
23624f52dfbbSDag-Erling Smørgrav 	if (!chan_is_dead(ssh, c, 1))
2363af12a3e7SDag-Erling Smørgrav 		return;
2364221552e4SDag-Erling Smørgrav 	debug2("channel %d: garbage collecting", c->self);
23654f52dfbbSDag-Erling Smørgrav 	channel_free(ssh, c);
2366af12a3e7SDag-Erling Smørgrav }
2367af12a3e7SDag-Erling Smørgrav 
23684f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST };
23694f52dfbbSDag-Erling Smørgrav 
2370af12a3e7SDag-Erling Smørgrav static void
23714f52dfbbSDag-Erling Smørgrav channel_handler(struct ssh *ssh, int table,
23724f52dfbbSDag-Erling Smørgrav     fd_set *readset, fd_set *writeset, time_t *unpause_secs)
2373a04a10f8SKris Kennaway {
23744f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
23754f52dfbbSDag-Erling Smørgrav 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
2376b15c8340SDag-Erling Smørgrav 	u_int i, oalloc;
2377a04a10f8SKris Kennaway 	Channel *c;
2378462c32cbSDag-Erling Smørgrav 	time_t now;
2379a04a10f8SKris Kennaway 
2380e4a9863fSDag-Erling Smørgrav 	now = monotime();
2381462c32cbSDag-Erling Smørgrav 	if (unpause_secs != NULL)
2382462c32cbSDag-Erling Smørgrav 		*unpause_secs = 0;
23834f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
23844f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2385af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2386511b41d2SMark Murray 			continue;
2387b15c8340SDag-Erling Smørgrav 		if (c->delayed) {
23884f52dfbbSDag-Erling Smørgrav 			if (table == CHAN_PRE)
2389b15c8340SDag-Erling Smørgrav 				c->delayed = 0;
2390b15c8340SDag-Erling Smørgrav 			else
2391b15c8340SDag-Erling Smørgrav 				continue;
2392b15c8340SDag-Erling Smørgrav 		}
2393462c32cbSDag-Erling Smørgrav 		if (ftab[c->type] != NULL) {
2394462c32cbSDag-Erling Smørgrav 			/*
2395462c32cbSDag-Erling Smørgrav 			 * Run handlers that are not paused.
2396462c32cbSDag-Erling Smørgrav 			 */
2397462c32cbSDag-Erling Smørgrav 			if (c->notbefore <= now)
23984f52dfbbSDag-Erling Smørgrav 				(*ftab[c->type])(ssh, c, readset, writeset);
2399462c32cbSDag-Erling Smørgrav 			else if (unpause_secs != NULL) {
2400462c32cbSDag-Erling Smørgrav 				/*
2401462c32cbSDag-Erling Smørgrav 				 * Collect the time that the earliest
2402462c32cbSDag-Erling Smørgrav 				 * channel comes off pause.
2403462c32cbSDag-Erling Smørgrav 				 */
2404462c32cbSDag-Erling Smørgrav 				debug3("%s: chan %d: skip for %d more seconds",
2405462c32cbSDag-Erling Smørgrav 				    __func__, c->self,
2406462c32cbSDag-Erling Smørgrav 				    (int)(c->notbefore - now));
2407462c32cbSDag-Erling Smørgrav 				if (*unpause_secs == 0 ||
2408462c32cbSDag-Erling Smørgrav 				    (c->notbefore - now) < *unpause_secs)
2409462c32cbSDag-Erling Smørgrav 					*unpause_secs = c->notbefore - now;
2410462c32cbSDag-Erling Smørgrav 			}
2411462c32cbSDag-Erling Smørgrav 		}
24124f52dfbbSDag-Erling Smørgrav 		channel_garbage_collect(ssh, c);
2413511b41d2SMark Murray 	}
2414462c32cbSDag-Erling Smørgrav 	if (unpause_secs != NULL && *unpause_secs != 0)
2415462c32cbSDag-Erling Smørgrav 		debug3("%s: first channel unpauses in %d seconds",
2416462c32cbSDag-Erling Smørgrav 		    __func__, (int)*unpause_secs);
2417511b41d2SMark Murray }
2418a04a10f8SKris Kennaway 
2419af12a3e7SDag-Erling Smørgrav /*
24204f52dfbbSDag-Erling Smørgrav  * Create sockets before allocating the select bitmasks.
24214f52dfbbSDag-Erling Smørgrav  * This is necessary for things that need to happen after reading
24224f52dfbbSDag-Erling Smørgrav  * the network-input but before channel_prepare_select().
24234f52dfbbSDag-Erling Smørgrav  */
24244f52dfbbSDag-Erling Smørgrav static void
24254f52dfbbSDag-Erling Smørgrav channel_before_prepare_select(struct ssh *ssh)
24264f52dfbbSDag-Erling Smørgrav {
24274f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
24284f52dfbbSDag-Erling Smørgrav 	Channel *c;
24294f52dfbbSDag-Erling Smørgrav 	u_int i, oalloc;
24304f52dfbbSDag-Erling Smørgrav 
24314f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
24324f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
24334f52dfbbSDag-Erling Smørgrav 		if (c == NULL)
24344f52dfbbSDag-Erling Smørgrav 			continue;
24354f52dfbbSDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
24364f52dfbbSDag-Erling Smørgrav 			channel_before_prepare_select_rdynamic(ssh, c);
24374f52dfbbSDag-Erling Smørgrav 	}
24384f52dfbbSDag-Erling Smørgrav }
24394f52dfbbSDag-Erling Smørgrav 
24404f52dfbbSDag-Erling Smørgrav /*
2441af12a3e7SDag-Erling Smørgrav  * Allocate/update select bitmasks and add any bits relevant to channels in
2442af12a3e7SDag-Erling Smørgrav  * select bitmasks.
2443af12a3e7SDag-Erling Smørgrav  */
2444a04a10f8SKris Kennaway void
24454f52dfbbSDag-Erling Smørgrav channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp,
24464f52dfbbSDag-Erling Smørgrav     int *maxfdp, u_int *nallocp, time_t *minwait_secs)
2447a04a10f8SKris Kennaway {
2448333ee039SDag-Erling Smørgrav 	u_int n, sz, nfdset;
2449ca3176e7SBrian Feldman 
24504f52dfbbSDag-Erling Smørgrav 	channel_before_prepare_select(ssh); /* might update channel_max_fd */
24514f52dfbbSDag-Erling Smørgrav 
24524f52dfbbSDag-Erling Smørgrav 	n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd);
2453ca3176e7SBrian Feldman 
2454333ee039SDag-Erling Smørgrav 	nfdset = howmany(n+1, NFDBITS);
2455333ee039SDag-Erling Smørgrav 	/* Explicitly test here, because xrealloc isn't always called */
2456bc5531deSDag-Erling Smørgrav 	if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask))
2457333ee039SDag-Erling Smørgrav 		fatal("channel_prepare_select: max_fd (%d) is too large", n);
2458333ee039SDag-Erling Smørgrav 	sz = nfdset * sizeof(fd_mask);
2459333ee039SDag-Erling Smørgrav 
2460af12a3e7SDag-Erling Smørgrav 	/* perhaps check sz < nalloc/2 and shrink? */
2461af12a3e7SDag-Erling Smørgrav 	if (*readsetp == NULL || sz > *nallocp) {
2462557f75e5SDag-Erling Smørgrav 		*readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask));
2463557f75e5SDag-Erling Smørgrav 		*writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask));
2464af12a3e7SDag-Erling Smørgrav 		*nallocp = sz;
2465ca3176e7SBrian Feldman 	}
2466af12a3e7SDag-Erling Smørgrav 	*maxfdp = n;
2467ca3176e7SBrian Feldman 	memset(*readsetp, 0, sz);
2468ca3176e7SBrian Feldman 	memset(*writesetp, 0, sz);
2469ca3176e7SBrian Feldman 
24704f52dfbbSDag-Erling Smørgrav 	if (!ssh_packet_is_rekeying(ssh))
24714f52dfbbSDag-Erling Smørgrav 		channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp,
2472462c32cbSDag-Erling Smørgrav 		    minwait_secs);
2473a04a10f8SKris Kennaway }
2474a04a10f8SKris Kennaway 
2475af12a3e7SDag-Erling Smørgrav /*
2476af12a3e7SDag-Erling Smørgrav  * After select, perform any appropriate operations for channels which have
2477af12a3e7SDag-Erling Smørgrav  * events pending.
2478af12a3e7SDag-Erling Smørgrav  */
2479a04a10f8SKris Kennaway void
24804f52dfbbSDag-Erling Smørgrav channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset)
2481a04a10f8SKris Kennaway {
24824f52dfbbSDag-Erling Smørgrav 	channel_handler(ssh, CHAN_POST, readset, writeset, NULL);
2483511b41d2SMark Murray }
2484511b41d2SMark Murray 
24854f52dfbbSDag-Erling Smørgrav /*
24864f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open or draining c->input.
24874f52dfbbSDag-Erling Smørgrav  */
24884f52dfbbSDag-Erling Smørgrav static void
24894f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c)
24904f52dfbbSDag-Erling Smørgrav {
24914f52dfbbSDag-Erling Smørgrav 	size_t len, plen;
24924f52dfbbSDag-Erling Smørgrav 	const u_char *pkt;
24934f52dfbbSDag-Erling Smørgrav 	int r;
24944f52dfbbSDag-Erling Smørgrav 
24954f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->input)) == 0) {
24964f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
24974f52dfbbSDag-Erling Smørgrav 			/*
24984f52dfbbSDag-Erling Smørgrav 			 * input-buffer is empty and read-socket shutdown:
24994f52dfbbSDag-Erling Smørgrav 			 * tell peer, that we will not send more data:
25004f52dfbbSDag-Erling Smørgrav 			 * send IEOF.
25014f52dfbbSDag-Erling Smørgrav 			 * hack for extended data: delay EOF if EFD still
25024f52dfbbSDag-Erling Smørgrav 			 * in use.
25034f52dfbbSDag-Erling Smørgrav 			 */
25044f52dfbbSDag-Erling Smørgrav 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
25054f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
25064f52dfbbSDag-Erling Smørgrav 				    "ibuf_empty delayed efd %d/(%zu)",
25074f52dfbbSDag-Erling Smørgrav 				    c->self, c->efd, sshbuf_len(c->extended));
25084f52dfbbSDag-Erling Smørgrav 			else
25094f52dfbbSDag-Erling Smørgrav 				chan_ibuf_empty(ssh, c);
25104f52dfbbSDag-Erling Smørgrav 		}
25114f52dfbbSDag-Erling Smørgrav 		return;
25124f52dfbbSDag-Erling Smørgrav 	}
25134f52dfbbSDag-Erling Smørgrav 
25144f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
25154f52dfbbSDag-Erling Smørgrav 		fatal(":%s: channel %d: no remote id", __func__, c->self);
25164f52dfbbSDag-Erling Smørgrav 
25174f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
25184f52dfbbSDag-Erling Smørgrav 		/* Check datagram will fit; drop if not */
25194f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
25204f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: get datagram: %s", __func__,
25214f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
25224f52dfbbSDag-Erling Smørgrav 		/*
25234f52dfbbSDag-Erling Smørgrav 		 * XXX this does tail-drop on the datagram queue which is
25244f52dfbbSDag-Erling Smørgrav 		 * usually suboptimal compared to head-drop. Better to have
25254f52dfbbSDag-Erling Smørgrav 		 * backpressure at read time? (i.e. read + discard)
25264f52dfbbSDag-Erling Smørgrav 		 */
25274f52dfbbSDag-Erling Smørgrav 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
25284f52dfbbSDag-Erling Smørgrav 			debug("channel %d: datagram too big", c->self);
25294f52dfbbSDag-Erling Smørgrav 			return;
25304f52dfbbSDag-Erling Smørgrav 		}
25314f52dfbbSDag-Erling Smørgrav 		/* Enqueue it */
25324f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
25334f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
25344f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
25354f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
25364f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %i: datagram: %s", __func__,
25374f52dfbbSDag-Erling Smørgrav 			    c->self, ssh_err(r));
25384f52dfbbSDag-Erling Smørgrav 		}
25394f52dfbbSDag-Erling Smørgrav 		c->remote_window -= plen;
25404f52dfbbSDag-Erling Smørgrav 		return;
25414f52dfbbSDag-Erling Smørgrav 	}
25424f52dfbbSDag-Erling Smørgrav 
25434f52dfbbSDag-Erling Smørgrav 	/* Enqueue packet for buffered data. */
25444f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
25454f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
25464f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
25474f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
25484f52dfbbSDag-Erling Smørgrav 	if (len == 0)
25494f52dfbbSDag-Erling Smørgrav 		return;
25504f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
25514f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
25524f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
25534f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0) {
25544f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: data: %s", __func__,
25554f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
25564f52dfbbSDag-Erling Smørgrav 	}
25574f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->input, len)) != 0)
25584f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: consume: %s", __func__,
25594f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
25604f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
25614f52dfbbSDag-Erling Smørgrav }
25624f52dfbbSDag-Erling Smørgrav 
25634f52dfbbSDag-Erling Smørgrav /*
25644f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open c->extended in read mode.
25654f52dfbbSDag-Erling Smørgrav  */
25664f52dfbbSDag-Erling Smørgrav static void
25674f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
25684f52dfbbSDag-Erling Smørgrav {
25694f52dfbbSDag-Erling Smørgrav 	size_t len;
25704f52dfbbSDag-Erling Smørgrav 	int r;
25714f52dfbbSDag-Erling Smørgrav 
25724f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->extended)) == 0)
25734f52dfbbSDag-Erling Smørgrav 		return;
25744f52dfbbSDag-Erling Smørgrav 
25754f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
25764f52dfbbSDag-Erling Smørgrav 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
25774f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
25784f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
25794f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
25804f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
25814f52dfbbSDag-Erling Smørgrav 	if (len == 0)
25824f52dfbbSDag-Erling Smørgrav 		return;
25834f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
25844f52dfbbSDag-Erling Smørgrav 		fatal(":%s: channel %d: no remote id", __func__, c->self);
25854f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
25864f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
25874f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
25884f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
25894f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0) {
25904f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: data: %s", __func__,
25914f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
25924f52dfbbSDag-Erling Smørgrav 	}
25934f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->extended, len)) != 0)
25944f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: consume: %s", __func__,
25954f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
25964f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
25974f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: sent ext data %zu", c->self, len);
25984f52dfbbSDag-Erling Smørgrav }
2599af12a3e7SDag-Erling Smørgrav 
2600ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */
2601511b41d2SMark Murray void
26024f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh)
2603511b41d2SMark Murray {
26044f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
2605a04a10f8SKris Kennaway 	Channel *c;
26064f52dfbbSDag-Erling Smørgrav 	u_int i;
2607511b41d2SMark Murray 
26084f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
26094f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2610af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2611af12a3e7SDag-Erling Smørgrav 			continue;
2612511b41d2SMark Murray 
2613af12a3e7SDag-Erling Smørgrav 		/*
2614af12a3e7SDag-Erling Smørgrav 		 * We are only interested in channels that can have buffered
2615af12a3e7SDag-Erling Smørgrav 		 * incoming data.
2616af12a3e7SDag-Erling Smørgrav 		 */
2617a04a10f8SKris Kennaway 		if (c->type != SSH_CHANNEL_OPEN)
2618511b41d2SMark Murray 			continue;
26194f52dfbbSDag-Erling Smørgrav 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
2620ca3176e7SBrian Feldman 			/* XXX is this true? */
26214f52dfbbSDag-Erling Smørgrav 			debug3("channel %d: will not send data after close",
26224f52dfbbSDag-Erling Smørgrav 			    c->self);
2623511b41d2SMark Murray 			continue;
2624511b41d2SMark Murray 		}
2625511b41d2SMark Murray 
2626511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
26274f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_OPEN ||
26284f52dfbbSDag-Erling Smørgrav 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
26294f52dfbbSDag-Erling Smørgrav 			channel_output_poll_input_open(ssh, c);
2630a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
26314f52dfbbSDag-Erling Smørgrav 		if (!(c->flags & CHAN_EOF_SENT) &&
26324f52dfbbSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_READ)
26334f52dfbbSDag-Erling Smørgrav 			channel_output_poll_extended_read(ssh, c);
2634511b41d2SMark Murray 	}
2635511b41d2SMark Murray }
2636511b41d2SMark Murray 
2637ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support  */
2638ca86bcf2SDag-Erling Smørgrav 
2639ca86bcf2SDag-Erling Smørgrav /*
2640ca86bcf2SDag-Erling Smørgrav  * When multiplexing channel messages for mux clients we have to deal
2641ca86bcf2SDag-Erling Smørgrav  * with downstream messages from the mux client and upstream messages
2642ca86bcf2SDag-Erling Smørgrav  * from the ssh server:
2643ca86bcf2SDag-Erling Smørgrav  * 1) Handling downstream messages is straightforward and happens
2644ca86bcf2SDag-Erling Smørgrav  *    in channel_proxy_downstream():
2645ca86bcf2SDag-Erling Smørgrav  *    - We forward all messages (mostly) unmodified to the server.
2646ca86bcf2SDag-Erling Smørgrav  *    - However, in order to route messages from upstream to the correct
2647ca86bcf2SDag-Erling Smørgrav  *      downstream client, we have to replace the channel IDs used by the
2648ca86bcf2SDag-Erling Smørgrav  *      mux clients with a unique channel ID because the mux clients might
2649ca86bcf2SDag-Erling Smørgrav  *      use conflicting channel IDs.
2650ca86bcf2SDag-Erling Smørgrav  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
2651ca86bcf2SDag-Erling Smørgrav  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
2652ca86bcf2SDag-Erling Smørgrav  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
2653ca86bcf2SDag-Erling Smørgrav  *      with the newly allocated channel ID.
2654ca86bcf2SDag-Erling Smørgrav  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
2655*190cef3dSDag-Erling Smørgrav  *    channels and processed by channel_proxy_upstream(). The local channel ID
2656ca86bcf2SDag-Erling Smørgrav  *    is then translated back to the original mux client ID.
2657ca86bcf2SDag-Erling Smørgrav  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
2658ca86bcf2SDag-Erling Smørgrav  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
2659ca86bcf2SDag-Erling Smørgrav  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
2660ca86bcf2SDag-Erling Smørgrav  *    downstream mux client are removed.
2661ca86bcf2SDag-Erling Smørgrav  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
2662ca86bcf2SDag-Erling Smørgrav  *    requires more work, because they are not addressed to a specific
2663ca86bcf2SDag-Erling Smørgrav  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
2664ca86bcf2SDag-Erling Smørgrav  *    out whether the request is addressed to the local client or a
2665ca86bcf2SDag-Erling Smørgrav  *    specific downstream client based on the listen-address/port.
2666*190cef3dSDag-Erling Smørgrav  * 6) Agent and X11-Forwarding have a similar problem and are currently
2667ca86bcf2SDag-Erling Smørgrav  *    not supported as the matching session/channel cannot be identified
2668ca86bcf2SDag-Erling Smørgrav  *    easily.
2669ca86bcf2SDag-Erling Smørgrav  */
2670ca86bcf2SDag-Erling Smørgrav 
2671ca86bcf2SDag-Erling Smørgrav /*
2672ca86bcf2SDag-Erling Smørgrav  * receive packets from downstream mux clients:
2673ca86bcf2SDag-Erling Smørgrav  * channel callback fired on read from mux client, creates
2674ca86bcf2SDag-Erling Smørgrav  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
2675ca86bcf2SDag-Erling Smørgrav  * on channel creation.
2676ca86bcf2SDag-Erling Smørgrav  */
2677ca86bcf2SDag-Erling Smørgrav int
26784f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
2679ca86bcf2SDag-Erling Smørgrav {
2680ca86bcf2SDag-Erling Smørgrav 	Channel *c = NULL;
2681ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *original = NULL, *modified = NULL;
2682ca86bcf2SDag-Erling Smørgrav 	const u_char *cp;
2683ca86bcf2SDag-Erling Smørgrav 	char *ctype = NULL, *listen_host = NULL;
2684ca86bcf2SDag-Erling Smørgrav 	u_char type;
2685ca86bcf2SDag-Erling Smørgrav 	size_t have;
26864f52dfbbSDag-Erling Smørgrav 	int ret = -1, r;
2687ca86bcf2SDag-Erling Smørgrav 	u_int id, remote_id, listen_port;
2688ca86bcf2SDag-Erling Smørgrav 
26894f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(downstream->input, stderr); */
26904f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
2691ca86bcf2SDag-Erling Smørgrav 	    != 0) {
2692ca86bcf2SDag-Erling Smørgrav 		error("%s: malformed message: %s", __func__, ssh_err(r));
2693ca86bcf2SDag-Erling Smørgrav 		return -1;
2694ca86bcf2SDag-Erling Smørgrav 	}
2695ca86bcf2SDag-Erling Smørgrav 	if (have < 2) {
2696ca86bcf2SDag-Erling Smørgrav 		error("%s: short message", __func__);
2697ca86bcf2SDag-Erling Smørgrav 		return -1;
2698ca86bcf2SDag-Erling Smørgrav 	}
2699ca86bcf2SDag-Erling Smørgrav 	type = cp[1];
2700ca86bcf2SDag-Erling Smørgrav 	/* skip padlen + type */
2701ca86bcf2SDag-Erling Smørgrav 	cp += 2;
2702ca86bcf2SDag-Erling Smørgrav 	have -= 2;
2703ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
2704ca86bcf2SDag-Erling Smørgrav 		debug3("%s: channel %u: down->up: type %u", __func__,
2705ca86bcf2SDag-Erling Smørgrav 		    downstream->self, type);
2706ca86bcf2SDag-Erling Smørgrav 
2707ca86bcf2SDag-Erling Smørgrav 	switch (type) {
2708ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN:
2709ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
2710ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
2711ca86bcf2SDag-Erling Smørgrav 			error("%s: alloc", __func__);
2712ca86bcf2SDag-Erling Smørgrav 			goto out;
2713ca86bcf2SDag-Erling Smørgrav 		}
2714ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
2715ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
2716ca86bcf2SDag-Erling Smørgrav 			error("%s: parse error %s", __func__, ssh_err(r));
2717ca86bcf2SDag-Erling Smørgrav 			goto out;
2718ca86bcf2SDag-Erling Smørgrav 		}
27194f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
2720ca86bcf2SDag-Erling Smørgrav 		   -1, -1, -1, 0, 0, 0, ctype, 1);
2721ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
2722ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;	/* original downstream id */
2723ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
2724ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
2725ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
2726ca86bcf2SDag-Erling Smørgrav 			error("%s: compose error %s", __func__, ssh_err(r));
27274f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
2728ca86bcf2SDag-Erling Smørgrav 			goto out;
2729ca86bcf2SDag-Erling Smørgrav 		}
2730ca86bcf2SDag-Erling Smørgrav 		break;
2731ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
2732ca86bcf2SDag-Erling Smørgrav 		/*
2733ca86bcf2SDag-Erling Smørgrav 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
2734ca86bcf2SDag-Erling Smørgrav 		 * need to parse 'remote_id' instead of 'ctype'.
2735ca86bcf2SDag-Erling Smørgrav 		 */
2736ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
2737ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
2738ca86bcf2SDag-Erling Smørgrav 			error("%s: alloc", __func__);
2739ca86bcf2SDag-Erling Smørgrav 			goto out;
2740ca86bcf2SDag-Erling Smørgrav 		}
2741ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
2742ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
2743ca86bcf2SDag-Erling Smørgrav 			error("%s: parse error %s", __func__, ssh_err(r));
2744ca86bcf2SDag-Erling Smørgrav 			goto out;
2745ca86bcf2SDag-Erling Smørgrav 		}
27464f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
2747ca86bcf2SDag-Erling Smørgrav 		   -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
2748ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
2749ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;
2750ca86bcf2SDag-Erling Smørgrav 		c->remote_id = remote_id;
27514f52dfbbSDag-Erling Smørgrav 		c->have_remote_id = 1;
2752ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
2753ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
2754ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
2755ca86bcf2SDag-Erling Smørgrav 			error("%s: compose error %s", __func__, ssh_err(r));
27564f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
2757ca86bcf2SDag-Erling Smørgrav 			goto out;
2758ca86bcf2SDag-Erling Smørgrav 		}
2759ca86bcf2SDag-Erling Smørgrav 		break;
2760ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_GLOBAL_REQUEST:
2761ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL) {
2762ca86bcf2SDag-Erling Smørgrav 			error("%s: alloc", __func__);
2763ca86bcf2SDag-Erling Smørgrav 			goto out;
2764ca86bcf2SDag-Erling Smørgrav 		}
2765ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
2766ca86bcf2SDag-Erling Smørgrav 			error("%s: parse error %s", __func__, ssh_err(r));
2767ca86bcf2SDag-Erling Smørgrav 			goto out;
2768ca86bcf2SDag-Erling Smørgrav 		}
2769ca86bcf2SDag-Erling Smørgrav 		if (strcmp(ctype, "tcpip-forward") != 0) {
2770ca86bcf2SDag-Erling Smørgrav 			error("%s: unsupported request %s", __func__, ctype);
2771ca86bcf2SDag-Erling Smørgrav 			goto out;
2772ca86bcf2SDag-Erling Smørgrav 		}
2773ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
2774ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
2775ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
2776ca86bcf2SDag-Erling Smørgrav 			error("%s: parse error %s", __func__, ssh_err(r));
2777ca86bcf2SDag-Erling Smørgrav 			goto out;
2778ca86bcf2SDag-Erling Smørgrav 		}
2779ca86bcf2SDag-Erling Smørgrav 		if (listen_port > 65535) {
2780ca86bcf2SDag-Erling Smørgrav 			error("%s: tcpip-forward for %s: bad port %u",
2781ca86bcf2SDag-Erling Smørgrav 			    __func__, listen_host, listen_port);
2782ca86bcf2SDag-Erling Smørgrav 			goto out;
2783ca86bcf2SDag-Erling Smørgrav 		}
2784ca86bcf2SDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
2785*190cef3dSDag-Erling Smørgrav 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1,
27864f52dfbbSDag-Erling Smørgrav 		    listen_host, NULL, (int)listen_port, downstream);
2787ca86bcf2SDag-Erling Smørgrav 		listen_host = NULL;
2788ca86bcf2SDag-Erling Smørgrav 		break;
2789ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
2790ca86bcf2SDag-Erling Smørgrav 		if (have < 4)
2791ca86bcf2SDag-Erling Smørgrav 			break;
2792ca86bcf2SDag-Erling Smørgrav 		remote_id = PEEK_U32(cp);
27934f52dfbbSDag-Erling Smørgrav 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
2794ca86bcf2SDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD)
27954f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
2796ca86bcf2SDag-Erling Smørgrav 			else
2797ca86bcf2SDag-Erling Smørgrav 				c->flags |= CHAN_CLOSE_SENT;
2798ca86bcf2SDag-Erling Smørgrav 		}
2799ca86bcf2SDag-Erling Smørgrav 		break;
2800ca86bcf2SDag-Erling Smørgrav 	}
2801ca86bcf2SDag-Erling Smørgrav 	if (modified) {
2802ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
2803ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
2804ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
2805ca86bcf2SDag-Erling Smørgrav 			error("%s: send %s", __func__, ssh_err(r));
2806ca86bcf2SDag-Erling Smørgrav 			goto out;
2807ca86bcf2SDag-Erling Smørgrav 		}
2808ca86bcf2SDag-Erling Smørgrav 	} else {
2809ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
2810ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
2811ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
2812ca86bcf2SDag-Erling Smørgrav 			error("%s: send %s", __func__, ssh_err(r));
2813ca86bcf2SDag-Erling Smørgrav 			goto out;
2814ca86bcf2SDag-Erling Smørgrav 		}
2815ca86bcf2SDag-Erling Smørgrav 	}
2816ca86bcf2SDag-Erling Smørgrav 	ret = 0;
2817ca86bcf2SDag-Erling Smørgrav  out:
2818ca86bcf2SDag-Erling Smørgrav 	free(ctype);
2819ca86bcf2SDag-Erling Smørgrav 	free(listen_host);
2820ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(original);
2821ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(modified);
2822ca86bcf2SDag-Erling Smørgrav 	return ret;
2823ca86bcf2SDag-Erling Smørgrav }
2824ca86bcf2SDag-Erling Smørgrav 
2825ca86bcf2SDag-Erling Smørgrav /*
2826ca86bcf2SDag-Erling Smørgrav  * receive packets from upstream server and de-multiplex packets
2827ca86bcf2SDag-Erling Smørgrav  * to correct downstream:
2828ca86bcf2SDag-Erling Smørgrav  * implemented as a helper for channel input handlers,
2829ca86bcf2SDag-Erling Smørgrav  * replaces local (proxy) channel ID with downstream channel ID.
2830ca86bcf2SDag-Erling Smørgrav  */
2831ca86bcf2SDag-Erling Smørgrav int
28324f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
2833ca86bcf2SDag-Erling Smørgrav {
2834ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *b = NULL;
2835ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;
2836ca86bcf2SDag-Erling Smørgrav 	const u_char *cp = NULL;
2837ca86bcf2SDag-Erling Smørgrav 	size_t len;
2838ca86bcf2SDag-Erling Smørgrav 	int r;
2839ca86bcf2SDag-Erling Smørgrav 
2840ca86bcf2SDag-Erling Smørgrav 	/*
2841ca86bcf2SDag-Erling Smørgrav 	 * When receiving packets from the peer we need to check whether we
2842ca86bcf2SDag-Erling Smørgrav 	 * need to forward the packets to the mux client. In this case we
2843*190cef3dSDag-Erling Smørgrav 	 * restore the original channel id and keep track of CLOSE messages,
2844ca86bcf2SDag-Erling Smørgrav 	 * so we can cleanup the channel.
2845ca86bcf2SDag-Erling Smørgrav 	 */
2846ca86bcf2SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
2847ca86bcf2SDag-Erling Smørgrav 		return 0;
2848ca86bcf2SDag-Erling Smørgrav 	if ((downstream = c->mux_ctx) == NULL)
2849ca86bcf2SDag-Erling Smørgrav 		return 0;
2850ca86bcf2SDag-Erling Smørgrav 	switch (type) {
2851ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
2852ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_DATA:
2853ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EOF:
2854ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
2855ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
2856ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
2857ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
2858ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_SUCCESS:
2859ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_FAILURE:
2860ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_REQUEST:
2861ca86bcf2SDag-Erling Smørgrav 		break;
2862ca86bcf2SDag-Erling Smørgrav 	default:
2863ca86bcf2SDag-Erling Smørgrav 		debug2("%s: channel %u: unsupported type %u", __func__,
2864ca86bcf2SDag-Erling Smørgrav 		    c->self, type);
2865ca86bcf2SDag-Erling Smørgrav 		return 0;
2866ca86bcf2SDag-Erling Smørgrav 	}
2867ca86bcf2SDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
2868ca86bcf2SDag-Erling Smørgrav 		error("%s: alloc reply", __func__);
2869ca86bcf2SDag-Erling Smørgrav 		goto out;
2870ca86bcf2SDag-Erling Smørgrav 	}
2871ca86bcf2SDag-Erling Smørgrav 	/* get remaining payload (after id) */
2872ca86bcf2SDag-Erling Smørgrav 	cp = sshpkt_ptr(ssh, &len);
2873ca86bcf2SDag-Erling Smørgrav 	if (cp == NULL) {
2874ca86bcf2SDag-Erling Smørgrav 		error("%s: no packet", __func__);
2875ca86bcf2SDag-Erling Smørgrav 		goto out;
2876ca86bcf2SDag-Erling Smørgrav 	}
2877ca86bcf2SDag-Erling Smørgrav 	/* translate id and send to muxclient */
2878ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
2879ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u8(b, type)) != 0 ||
2880ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
2881ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put(b, cp, len)) != 0 ||
28824f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
2883ca86bcf2SDag-Erling Smørgrav 		error("%s: compose for muxclient %s", __func__, ssh_err(r));
2884ca86bcf2SDag-Erling Smørgrav 		goto out;
2885ca86bcf2SDag-Erling Smørgrav 	}
2886ca86bcf2SDag-Erling Smørgrav 	/* sshbuf_dump(b, stderr); */
2887ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
2888ca86bcf2SDag-Erling Smørgrav 		debug3("%s: channel %u: up->down: type %u", __func__, c->self,
2889ca86bcf2SDag-Erling Smørgrav 		    type);
2890ca86bcf2SDag-Erling Smørgrav  out:
2891ca86bcf2SDag-Erling Smørgrav 	/* update state */
2892ca86bcf2SDag-Erling Smørgrav 	switch (type) {
2893ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
2894ca86bcf2SDag-Erling Smørgrav 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
28954f52dfbbSDag-Erling Smørgrav 		if (cp && len > 4) {
2896ca86bcf2SDag-Erling Smørgrav 			c->remote_id = PEEK_U32(cp);
28974f52dfbbSDag-Erling Smørgrav 			c->have_remote_id = 1;
28984f52dfbbSDag-Erling Smørgrav 		}
2899ca86bcf2SDag-Erling Smørgrav 		break;
2900ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
2901ca86bcf2SDag-Erling Smørgrav 		if (c->flags & CHAN_CLOSE_SENT)
29024f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
2903ca86bcf2SDag-Erling Smørgrav 		else
2904ca86bcf2SDag-Erling Smørgrav 			c->flags |= CHAN_CLOSE_RCVD;
2905ca86bcf2SDag-Erling Smørgrav 		break;
2906ca86bcf2SDag-Erling Smørgrav 	}
2907ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(b);
2908ca86bcf2SDag-Erling Smørgrav 	return 1;
2909ca86bcf2SDag-Erling Smørgrav }
2910af12a3e7SDag-Erling Smørgrav 
2911af12a3e7SDag-Erling Smørgrav /* -- protocol input */
2912511b41d2SMark Murray 
29134f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */
29144f52dfbbSDag-Erling Smørgrav static int
29154f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what)
2916511b41d2SMark Murray {
29174f52dfbbSDag-Erling Smørgrav 	u_int32_t id;
29184f52dfbbSDag-Erling Smørgrav 	int r;
29194f52dfbbSDag-Erling Smørgrav 
29204f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
29214f52dfbbSDag-Erling Smørgrav 		error("%s: parse id: %s", where, ssh_err(r));
29224f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
29234f52dfbbSDag-Erling Smørgrav 	}
29244f52dfbbSDag-Erling Smørgrav 	if (id > INT_MAX) {
29254f52dfbbSDag-Erling Smørgrav 		error("%s: bad channel id %u: %s", where, id, ssh_err(r));
29264f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
29274f52dfbbSDag-Erling Smørgrav 	}
29284f52dfbbSDag-Erling Smørgrav 	return (int)id;
29294f52dfbbSDag-Erling Smørgrav }
29304f52dfbbSDag-Erling Smørgrav 
29314f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */
29324f52dfbbSDag-Erling Smørgrav static Channel *
29334f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
29344f52dfbbSDag-Erling Smørgrav {
29354f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, where, what);
2936a04a10f8SKris Kennaway 	Channel *c;
2937511b41d2SMark Murray 
29384f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
29394f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh,
29404f52dfbbSDag-Erling Smørgrav 		    "%s packet referred to nonexistent channel %d", what, id);
29414f52dfbbSDag-Erling Smørgrav 	}
29424f52dfbbSDag-Erling Smørgrav 	return c;
29434f52dfbbSDag-Erling Smørgrav }
29444f52dfbbSDag-Erling Smørgrav 
29454f52dfbbSDag-Erling Smørgrav int
29464f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
29474f52dfbbSDag-Erling Smørgrav {
29484f52dfbbSDag-Erling Smørgrav 	const u_char *data;
29494f52dfbbSDag-Erling Smørgrav 	size_t data_len, win_len;
29504f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
29514f52dfbbSDag-Erling Smørgrav 	int r;
29524f52dfbbSDag-Erling Smørgrav 
29534f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
2954ca86bcf2SDag-Erling Smørgrav 		return 0;
2955511b41d2SMark Murray 
2956511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
2957a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
29584f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
29594f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
2960a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
2961bc5531deSDag-Erling Smørgrav 		return 0;
2962511b41d2SMark Murray 
2963511b41d2SMark Murray 	/* Get the data. */
29644f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0)
29654f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: get data: %s", __func__,
29664f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
29674f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
29684f52dfbbSDag-Erling Smørgrav 
2969e2f6069cSDag-Erling Smørgrav 	win_len = data_len;
2970e2f6069cSDag-Erling Smørgrav 	if (c->datagram)
2971e2f6069cSDag-Erling Smørgrav 		win_len += 4;  /* string length header */
2972a04a10f8SKris Kennaway 
2973476cd3b2SDag-Erling Smørgrav 	/*
29744f52dfbbSDag-Erling Smørgrav 	 * The sending side reduces its window as it sends data, so we
29754f52dfbbSDag-Erling Smørgrav 	 * must 'fake' consumption of the data in order to ensure that window
29764f52dfbbSDag-Erling Smørgrav 	 * updates are sent back. Otherwise the connection might deadlock.
2977476cd3b2SDag-Erling Smørgrav 	 */
29784f52dfbbSDag-Erling Smørgrav 	if (c->ostate != CHAN_OUTPUT_OPEN) {
2979e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
2980e2f6069cSDag-Erling Smørgrav 		c->local_consumed += win_len;
2981bc5531deSDag-Erling Smørgrav 		return 0;
2982476cd3b2SDag-Erling Smørgrav 	}
2983476cd3b2SDag-Erling Smørgrav 
2984e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_maxpacket) {
29854f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd big packet %zu, maxpack %u",
2986e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_maxpacket);
29874f52dfbbSDag-Erling Smørgrav 		return 0;
2988a04a10f8SKris Kennaway 	}
2989e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_window) {
29904f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much data %zu, win %u",
2991e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_window);
2992bc5531deSDag-Erling Smørgrav 		return 0;
2993a04a10f8SKris Kennaway 	}
2994e2f6069cSDag-Erling Smørgrav 	c->local_window -= win_len;
29954f52dfbbSDag-Erling Smørgrav 
29964f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
29974f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
29984f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %d: append datagram: %s",
29994f52dfbbSDag-Erling Smørgrav 			    __func__, c->self, ssh_err(r));
30004f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
30014f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %d: append data: %s",
30024f52dfbbSDag-Erling Smørgrav 		    __func__, c->self, ssh_err(r));
30034f52dfbbSDag-Erling Smørgrav 
3004bc5531deSDag-Erling Smørgrav 	return 0;
3005511b41d2SMark Murray }
3006af12a3e7SDag-Erling Smørgrav 
3007bc5531deSDag-Erling Smørgrav int
30084f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3009a04a10f8SKris Kennaway {
30104f52dfbbSDag-Erling Smørgrav 	const u_char *data;
30114f52dfbbSDag-Erling Smørgrav 	size_t data_len;
30124f52dfbbSDag-Erling Smørgrav 	u_int32_t tcode;
30134f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
30144f52dfbbSDag-Erling Smørgrav 	int r;
3015a04a10f8SKris Kennaway 
30164f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3017ca86bcf2SDag-Erling Smørgrav 		return 0;
3018a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
30194f52dfbbSDag-Erling Smørgrav 		logit("channel %d: ext data for non open", c->self);
3020bc5531deSDag-Erling Smørgrav 		return 0;
3021a04a10f8SKris Kennaway 	}
302280628bacSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
302380628bacSDag-Erling Smørgrav 		if (datafellows & SSH_BUG_EXTEOF)
30244f52dfbbSDag-Erling Smørgrav 			debug("channel %d: accepting ext data after eof",
30254f52dfbbSDag-Erling Smørgrav 			    c->self);
302680628bacSDag-Erling Smørgrav 		else
30274f52dfbbSDag-Erling Smørgrav 			ssh_packet_disconnect(ssh, "Received extended_data "
30284f52dfbbSDag-Erling Smørgrav 			    "after EOF on channel %d.", c->self);
302980628bacSDag-Erling Smørgrav 	}
30304f52dfbbSDag-Erling Smørgrav 
30314f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
30324f52dfbbSDag-Erling Smørgrav 		error("%s: parse tcode: %s", __func__, ssh_err(r));
30334f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
30344f52dfbbSDag-Erling Smørgrav 	}
3035a04a10f8SKris Kennaway 	if (c->efd == -1 ||
3036a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3037a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3038221552e4SDag-Erling Smørgrav 		logit("channel %d: bad ext data", c->self);
3039bc5531deSDag-Erling Smørgrav 		return 0;
3040a04a10f8SKris Kennaway 	}
30414f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) {
30424f52dfbbSDag-Erling Smørgrav 		error("%s: parse data: %s", __func__, ssh_err(r));
30434f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
30444f52dfbbSDag-Erling Smørgrav 	}
30454f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
30464f52dfbbSDag-Erling Smørgrav 
3047a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
30484f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3049a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
3050bc5531deSDag-Erling Smørgrav 		return 0;
3051a04a10f8SKris Kennaway 	}
30524f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
30534f52dfbbSDag-Erling Smørgrav 	/* XXX sshpkt_getb? */
30544f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
30554f52dfbbSDag-Erling Smørgrav 		error("%s: append: %s", __func__, ssh_err(r));
3056a04a10f8SKris Kennaway 	c->local_window -= data_len;
3057bc5531deSDag-Erling Smørgrav 	return 0;
3058a04a10f8SKris Kennaway }
3059a04a10f8SKris Kennaway 
3060bc5531deSDag-Erling Smørgrav int
30614f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3062a04a10f8SKris Kennaway {
30634f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
3064a04a10f8SKris Kennaway 
30654f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
30664f52dfbbSDag-Erling Smørgrav 
30674f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3068ca86bcf2SDag-Erling Smørgrav 		return 0;
30694f52dfbbSDag-Erling Smørgrav 	chan_rcvd_ieof(ssh, c);
3070af12a3e7SDag-Erling Smørgrav 
3071af12a3e7SDag-Erling Smørgrav 	/* XXX force input close */
3072af12a3e7SDag-Erling Smørgrav 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3073af12a3e7SDag-Erling Smørgrav 		debug("channel %d: FORCE input drain", c->self);
3074af12a3e7SDag-Erling Smørgrav 		c->istate = CHAN_INPUT_WAIT_DRAIN;
30754f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->input) == 0)
30764f52dfbbSDag-Erling Smørgrav 			chan_ibuf_empty(ssh, c);
3077af12a3e7SDag-Erling Smørgrav 	}
3078bc5531deSDag-Erling Smørgrav 	return 0;
3079a04a10f8SKris Kennaway }
3080511b41d2SMark Murray 
3081bc5531deSDag-Erling Smørgrav int
30824f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3083511b41d2SMark Murray {
30844f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
3085511b41d2SMark Murray 
30864f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3087ca86bcf2SDag-Erling Smørgrav 		return 0;
30884f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
30894f52dfbbSDag-Erling Smørgrav 	chan_rcvd_oclose(ssh, c);
3090bc5531deSDag-Erling Smørgrav 	return 0;
3091511b41d2SMark Murray }
3092511b41d2SMark Murray 
3093bc5531deSDag-Erling Smørgrav int
30944f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3095a04a10f8SKris Kennaway {
30964f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
30974f52dfbbSDag-Erling Smørgrav 	u_int32_t remote_window, remote_maxpacket;
30984f52dfbbSDag-Erling Smørgrav 	int r;
3099af12a3e7SDag-Erling Smørgrav 
31004f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3101ca86bcf2SDag-Erling Smørgrav 		return 0;
3102ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
3103a04a10f8SKris Kennaway 		packet_disconnect("Received open confirmation for "
31044f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
31054f52dfbbSDag-Erling Smørgrav 	/*
31064f52dfbbSDag-Erling Smørgrav 	 * Record the remote channel number and mark that the channel
31074f52dfbbSDag-Erling Smørgrav 	 * is now open.
31084f52dfbbSDag-Erling Smørgrav 	 */
31094f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
31104f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
31114f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0) {
31124f52dfbbSDag-Erling Smørgrav 		error("%s: window/maxpacket: %s", __func__, ssh_err(r));
31134f52dfbbSDag-Erling Smørgrav 		packet_disconnect("Invalid open confirmation message");
31144f52dfbbSDag-Erling Smørgrav 	}
31154f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
3116a04a10f8SKris Kennaway 
31174f52dfbbSDag-Erling Smørgrav 	c->have_remote_id = 1;
31184f52dfbbSDag-Erling Smørgrav 	c->remote_window = remote_window;
31194f52dfbbSDag-Erling Smørgrav 	c->remote_maxpacket = remote_maxpacket;
31204f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
3121d4af9e69SDag-Erling Smørgrav 	if (c->open_confirm) {
31224f52dfbbSDag-Erling Smørgrav 		debug2("%s: channel %d: callback start", __func__, c->self);
31234f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
31244f52dfbbSDag-Erling Smørgrav 		debug2("%s: channel %d: callback done", __func__, c->self);
3125a04a10f8SKris Kennaway 	}
3126221552e4SDag-Erling Smørgrav 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3127a04a10f8SKris Kennaway 	    c->remote_window, c->remote_maxpacket);
3128bc5531deSDag-Erling Smørgrav 	return 0;
3129af12a3e7SDag-Erling Smørgrav }
3130af12a3e7SDag-Erling Smørgrav 
3131af12a3e7SDag-Erling Smørgrav static char *
3132af12a3e7SDag-Erling Smørgrav reason2txt(int reason)
3133af12a3e7SDag-Erling Smørgrav {
3134af12a3e7SDag-Erling Smørgrav 	switch (reason) {
3135af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3136af12a3e7SDag-Erling Smørgrav 		return "administratively prohibited";
3137af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_CONNECT_FAILED:
3138af12a3e7SDag-Erling Smørgrav 		return "connect failed";
3139af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3140af12a3e7SDag-Erling Smørgrav 		return "unknown channel type";
3141af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3142af12a3e7SDag-Erling Smørgrav 		return "resource shortage";
3143af12a3e7SDag-Erling Smørgrav 	}
3144af12a3e7SDag-Erling Smørgrav 	return "unknown reason";
3145a04a10f8SKris Kennaway }
3146a04a10f8SKris Kennaway 
3147bc5531deSDag-Erling Smørgrav int
31484f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3149a04a10f8SKris Kennaway {
31504f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
31514f52dfbbSDag-Erling Smørgrav 	u_int32_t reason;
31524f52dfbbSDag-Erling Smørgrav 	char *msg = NULL;
31534f52dfbbSDag-Erling Smørgrav 	int r;
3154a04a10f8SKris Kennaway 
31554f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3156ca86bcf2SDag-Erling Smørgrav 		return 0;
3157ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
3158a04a10f8SKris Kennaway 		packet_disconnect("Received open failure for "
31594f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
31604f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
31614f52dfbbSDag-Erling Smørgrav 		error("%s: reason: %s", __func__, ssh_err(r));
31624f52dfbbSDag-Erling Smørgrav 		packet_disconnect("Invalid open failure message");
3163ca3176e7SBrian Feldman 	}
31644f52dfbbSDag-Erling Smørgrav 	/* skip language */
31654f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
31664f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0) {
31674f52dfbbSDag-Erling Smørgrav 		error("%s: message/lang: %s", __func__, ssh_err(r));
31684f52dfbbSDag-Erling Smørgrav 		packet_disconnect("Invalid open failure message");
31694f52dfbbSDag-Erling Smørgrav 	}
31704f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
31714f52dfbbSDag-Erling Smørgrav 	logit("channel %d: open failed: %s%s%s", c->self,
3172af12a3e7SDag-Erling Smørgrav 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
3173e4a9863fSDag-Erling Smørgrav 	free(msg);
3174e2f6069cSDag-Erling Smørgrav 	if (c->open_confirm) {
31754f52dfbbSDag-Erling Smørgrav 		debug2("%s: channel %d: callback start", __func__, c->self);
31764f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
31774f52dfbbSDag-Erling Smørgrav 		debug2("%s: channel %d: callback done", __func__, c->self);
3178e2f6069cSDag-Erling Smørgrav 	}
3179cce7d346SDag-Erling Smørgrav 	/* Schedule the channel for cleanup/deletion. */
31804f52dfbbSDag-Erling Smørgrav 	chan_mark_dead(ssh, c);
3181bc5531deSDag-Erling Smørgrav 	return 0;
3182a04a10f8SKris Kennaway }
3183a04a10f8SKris Kennaway 
3184bc5531deSDag-Erling Smørgrav int
31854f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3186a04a10f8SKris Kennaway {
31874f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "window adjust");
3188a04a10f8SKris Kennaway 	Channel *c;
31894f52dfbbSDag-Erling Smørgrav 	u_int32_t adjust;
31904f52dfbbSDag-Erling Smørgrav 	u_int new_rwin;
31914f52dfbbSDag-Erling Smørgrav 	int r;
3192a04a10f8SKris Kennaway 
31934f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
3194b74df5b2SDag-Erling Smørgrav 		logit("Received window adjust for non-open channel %d.", id);
3195bc5531deSDag-Erling Smørgrav 		return 0;
3196511b41d2SMark Murray 	}
31974f52dfbbSDag-Erling Smørgrav 
31984f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3199ca86bcf2SDag-Erling Smørgrav 		return 0;
32004f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0) {
32014f52dfbbSDag-Erling Smørgrav 		error("%s: adjust: %s", __func__, ssh_err(r));
32024f52dfbbSDag-Erling Smørgrav 		packet_disconnect("Invalid window adjust message");
32034f52dfbbSDag-Erling Smørgrav 	}
32044f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
32054f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
32064f52dfbbSDag-Erling Smørgrav 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
3207557f75e5SDag-Erling Smørgrav 		fatal("channel %d: adjust %u overflows remote window %u",
32084f52dfbbSDag-Erling Smørgrav 		    c->self, adjust, c->remote_window);
32094f52dfbbSDag-Erling Smørgrav 	}
32104f52dfbbSDag-Erling Smørgrav 	c->remote_window = new_rwin;
3211bc5531deSDag-Erling Smørgrav 	return 0;
3212511b41d2SMark Murray }
3213511b41d2SMark Murray 
3214bc5531deSDag-Erling Smørgrav int
32154f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3216af12a3e7SDag-Erling Smørgrav {
32174f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "status confirm");
3218d4af9e69SDag-Erling Smørgrav 	Channel *c;
3219d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
3220d4af9e69SDag-Erling Smørgrav 
3221d4af9e69SDag-Erling Smørgrav 	/* Reset keepalive timeout */
32227aee6ffeSDag-Erling Smørgrav 	packet_set_alive_timeouts(0);
3223d4af9e69SDag-Erling Smørgrav 
32244f52dfbbSDag-Erling Smørgrav 	debug2("%s: type %d id %d", __func__, type, id);
3225d4af9e69SDag-Erling Smørgrav 
32264f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
32274f52dfbbSDag-Erling Smørgrav 		logit("%s: %d: unknown", __func__, id);
3228bc5531deSDag-Erling Smørgrav 		return 0;
3229d4af9e69SDag-Erling Smørgrav 	}
32304f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3231ca86bcf2SDag-Erling Smørgrav 		return 0;
32324f52dfbbSDag-Erling Smørgrav 	ssh_packet_check_eom(ssh);
3233d4af9e69SDag-Erling Smørgrav 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3234bc5531deSDag-Erling Smørgrav 		return 0;
32354f52dfbbSDag-Erling Smørgrav 	cc->cb(ssh, type, c, cc->ctx);
3236d4af9e69SDag-Erling Smørgrav 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
3237b83788ffSDag-Erling Smørgrav 	explicit_bzero(cc, sizeof(*cc));
3238e4a9863fSDag-Erling Smørgrav 	free(cc);
3239bc5531deSDag-Erling Smørgrav 	return 0;
3240d4af9e69SDag-Erling Smørgrav }
3241af12a3e7SDag-Erling Smørgrav 
3242af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
3243511b41d2SMark Murray 
3244511b41d2SMark Murray void
32454f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af)
3246511b41d2SMark Murray {
32474f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->IPv4or6 = af;
3248511b41d2SMark Murray }
3249511b41d2SMark Murray 
325089986192SBrooks Davis 
3251462c32cbSDag-Erling Smørgrav /*
3252462c32cbSDag-Erling Smørgrav  * Determine whether or not a port forward listens to loopback, the
3253462c32cbSDag-Erling Smørgrav  * specified address or wildcard. On the client, a specified bind
3254462c32cbSDag-Erling Smørgrav  * address will always override gateway_ports. On the server, a
3255462c32cbSDag-Erling Smørgrav  * gateway_ports of 1 (``yes'') will override the client's specification
3256462c32cbSDag-Erling Smørgrav  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3257462c32cbSDag-Erling Smørgrav  * will bind to whatever address the client asked for.
3258462c32cbSDag-Erling Smørgrav  *
3259462c32cbSDag-Erling Smørgrav  * Special-case listen_addrs are:
3260462c32cbSDag-Erling Smørgrav  *
3261462c32cbSDag-Erling Smørgrav  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3262462c32cbSDag-Erling Smørgrav  * "" (empty string), "*"  -> wildcard v4/v6
3263462c32cbSDag-Erling Smørgrav  * "localhost"             -> loopback v4/v6
3264a0ee8cc6SDag-Erling Smørgrav  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3265462c32cbSDag-Erling Smørgrav  */
3266462c32cbSDag-Erling Smørgrav static const char *
3267462c32cbSDag-Erling Smørgrav channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
3268a0ee8cc6SDag-Erling Smørgrav     int is_client, struct ForwardOptions *fwd_opts)
3269462c32cbSDag-Erling Smørgrav {
3270462c32cbSDag-Erling Smørgrav 	const char *addr = NULL;
3271462c32cbSDag-Erling Smørgrav 	int wildcard = 0;
3272462c32cbSDag-Erling Smørgrav 
3273462c32cbSDag-Erling Smørgrav 	if (listen_addr == NULL) {
3274462c32cbSDag-Erling Smørgrav 		/* No address specified: default to gateway_ports setting */
3275a0ee8cc6SDag-Erling Smørgrav 		if (fwd_opts->gateway_ports)
3276462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3277a0ee8cc6SDag-Erling Smørgrav 	} else if (fwd_opts->gateway_ports || is_client) {
3278462c32cbSDag-Erling Smørgrav 		if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
3279462c32cbSDag-Erling Smørgrav 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3280462c32cbSDag-Erling Smørgrav 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
3281a0ee8cc6SDag-Erling Smørgrav 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3282462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3283f7167e0eSDag-Erling Smørgrav 			/*
3284f7167e0eSDag-Erling Smørgrav 			 * Notify client if they requested a specific listen
3285f7167e0eSDag-Erling Smørgrav 			 * address and it was overridden.
3286f7167e0eSDag-Erling Smørgrav 			 */
3287f7167e0eSDag-Erling Smørgrav 			if (*listen_addr != '\0' &&
3288f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
3289f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "*") != 0) {
3290f7167e0eSDag-Erling Smørgrav 				packet_send_debug("Forwarding listen address "
3291f7167e0eSDag-Erling Smørgrav 				    "\"%s\" overridden by server "
3292f7167e0eSDag-Erling Smørgrav 				    "GatewayPorts", listen_addr);
3293f7167e0eSDag-Erling Smørgrav 			}
3294a0ee8cc6SDag-Erling Smørgrav 		} else if (strcmp(listen_addr, "localhost") != 0 ||
3295a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
3296a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "::1") == 0) {
3297a0ee8cc6SDag-Erling Smørgrav 			/* Accept localhost address when GatewayPorts=yes */
3298a0ee8cc6SDag-Erling Smørgrav 			addr = listen_addr;
3299f7167e0eSDag-Erling Smørgrav 		}
3300a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
3301a0ee8cc6SDag-Erling Smørgrav 	    strcmp(listen_addr, "::1") == 0) {
3302a0ee8cc6SDag-Erling Smørgrav 		/*
3303a0ee8cc6SDag-Erling Smørgrav 		 * If a specific IPv4/IPv6 localhost address has been
3304a0ee8cc6SDag-Erling Smørgrav 		 * requested then accept it even if gateway_ports is in
3305a0ee8cc6SDag-Erling Smørgrav 		 * effect. This allows the client to prefer IPv4 or IPv6.
3306a0ee8cc6SDag-Erling Smørgrav 		 */
3307462c32cbSDag-Erling Smørgrav 		addr = listen_addr;
3308462c32cbSDag-Erling Smørgrav 	}
3309462c32cbSDag-Erling Smørgrav 	if (wildcardp != NULL)
3310462c32cbSDag-Erling Smørgrav 		*wildcardp = wildcard;
3311462c32cbSDag-Erling Smørgrav 	return addr;
3312462c32cbSDag-Erling Smørgrav }
3313462c32cbSDag-Erling Smørgrav 
3314af12a3e7SDag-Erling Smørgrav static int
33154f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
33164f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int *allocated_listen_port,
33174f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3318511b41d2SMark Murray {
3319af12a3e7SDag-Erling Smørgrav 	Channel *c;
3320b74df5b2SDag-Erling Smørgrav 	int sock, r, success = 0, wildcard = 0, is_client;
3321511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
3322aa49c926SDag-Erling Smørgrav 	const char *host, *addr;
3323af12a3e7SDag-Erling Smørgrav 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3324cce7d346SDag-Erling Smørgrav 	in_port_t *lport_p;
3325511b41d2SMark Murray 
3326aa49c926SDag-Erling Smørgrav 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3327511b41d2SMark Murray 
3328557f75e5SDag-Erling Smørgrav 	if (is_client && fwd->connect_path != NULL) {
3329557f75e5SDag-Erling Smørgrav 		host = fwd->connect_path;
3330557f75e5SDag-Erling Smørgrav 	} else {
3331557f75e5SDag-Erling Smørgrav 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
3332557f75e5SDag-Erling Smørgrav 		    fwd->listen_host : fwd->connect_host;
3333af12a3e7SDag-Erling Smørgrav 		if (host == NULL) {
3334af12a3e7SDag-Erling Smørgrav 			error("No forward host name.");
3335d4ecd108SDag-Erling Smørgrav 			return 0;
3336ca3176e7SBrian Feldman 		}
3337cce7d346SDag-Erling Smørgrav 		if (strlen(host) >= NI_MAXHOST) {
3338ca3176e7SBrian Feldman 			error("Forward host name too long.");
3339d4ecd108SDag-Erling Smørgrav 			return 0;
3340ca3176e7SBrian Feldman 		}
3341557f75e5SDag-Erling Smørgrav 	}
3342ca3176e7SBrian Feldman 
3343462c32cbSDag-Erling Smørgrav 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
3344a0ee8cc6SDag-Erling Smørgrav 	addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard,
3345a0ee8cc6SDag-Erling Smørgrav 	    is_client, fwd_opts);
3346a0ee8cc6SDag-Erling Smørgrav 	debug3("%s: type %d wildcard %d addr %s", __func__,
3347aa49c926SDag-Erling Smørgrav 	    type, wildcard, (addr == NULL) ? "NULL" : addr);
3348aa49c926SDag-Erling Smørgrav 
3349aa49c926SDag-Erling Smørgrav 	/*
3350511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
3351511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3352511b41d2SMark Murray 	 */
3353511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
33544f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
3355aa49c926SDag-Erling Smørgrav 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3356511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
3357a0ee8cc6SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3358aa49c926SDag-Erling Smørgrav 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3359aa49c926SDag-Erling Smørgrav 		if (addr == NULL) {
3360aa49c926SDag-Erling Smørgrav 			/* This really shouldn't happen */
3361aa49c926SDag-Erling Smørgrav 			packet_disconnect("getaddrinfo: fatal error: %s",
3362d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3363aa49c926SDag-Erling Smørgrav 		} else {
3364a0ee8cc6SDag-Erling Smørgrav 			error("%s: getaddrinfo(%.64s): %s", __func__, addr,
3365d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3366aa49c926SDag-Erling Smørgrav 		}
3367d4ecd108SDag-Erling Smørgrav 		return 0;
3368aa49c926SDag-Erling Smørgrav 	}
3369cce7d346SDag-Erling Smørgrav 	if (allocated_listen_port != NULL)
3370cce7d346SDag-Erling Smørgrav 		*allocated_listen_port = 0;
3371511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
3372cce7d346SDag-Erling Smørgrav 		switch (ai->ai_family) {
3373cce7d346SDag-Erling Smørgrav 		case AF_INET:
3374cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3375cce7d346SDag-Erling Smørgrav 			    sin_port;
3376cce7d346SDag-Erling Smørgrav 			break;
3377cce7d346SDag-Erling Smørgrav 		case AF_INET6:
3378cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3379cce7d346SDag-Erling Smørgrav 			    sin6_port;
3380cce7d346SDag-Erling Smørgrav 			break;
3381cce7d346SDag-Erling Smørgrav 		default:
3382511b41d2SMark Murray 			continue;
3383cce7d346SDag-Erling Smørgrav 		}
3384cce7d346SDag-Erling Smørgrav 		/*
3385cce7d346SDag-Erling Smørgrav 		 * If allocating a port for -R forwards, then use the
3386cce7d346SDag-Erling Smørgrav 		 * same port for all address families.
3387cce7d346SDag-Erling Smørgrav 		 */
33884f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
33894f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
33904f52dfbbSDag-Erling Smørgrav 		    *allocated_listen_port > 0)
3391cce7d346SDag-Erling Smørgrav 			*lport_p = htons(*allocated_listen_port);
3392cce7d346SDag-Erling Smørgrav 
3393511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
33944f52dfbbSDag-Erling Smørgrav 		    strport, sizeof(strport),
33954f52dfbbSDag-Erling Smørgrav 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
3396a0ee8cc6SDag-Erling Smørgrav 			error("%s: getnameinfo failed", __func__);
3397511b41d2SMark Murray 			continue;
3398511b41d2SMark Murray 		}
3399511b41d2SMark Murray 		/* Create a port to listen for the host. */
3400221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
3401511b41d2SMark Murray 		if (sock < 0) {
3402511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
340347dd1d1bSDag-Erling Smørgrav 			verbose("socket [%s]:%s: %.100s", ntop, strport,
340447dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3405511b41d2SMark Murray 			continue;
3406511b41d2SMark Murray 		}
3407b74df5b2SDag-Erling Smørgrav 
340847dd1d1bSDag-Erling Smørgrav 		set_reuseaddr(sock);
3409b15c8340SDag-Erling Smørgrav 		if (ai->ai_family == AF_INET6)
3410b15c8340SDag-Erling Smørgrav 			sock_set_v6only(sock);
3411f388f5efSDag-Erling Smørgrav 
3412cce7d346SDag-Erling Smørgrav 		debug("Local forwarding listening on %s port %s.",
3413cce7d346SDag-Erling Smørgrav 		    ntop, strport);
3414511b41d2SMark Murray 
3415511b41d2SMark Murray 		/* Bind the socket to the address. */
3416511b41d2SMark Murray 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
34174f52dfbbSDag-Erling Smørgrav 			/*
34184f52dfbbSDag-Erling Smørgrav 			 * address can be in if use ipv6 address is
34194f52dfbbSDag-Erling Smørgrav 			 * already bound
34204f52dfbbSDag-Erling Smørgrav 			 */
3421989dd127SDag-Erling Smørgrav 			if (!ai->ai_next)
342247dd1d1bSDag-Erling Smørgrav 				error("bind [%s]:%s: %.100s",
342347dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3424989dd127SDag-Erling Smørgrav 			else
342547dd1d1bSDag-Erling Smørgrav 				verbose("bind [%s]:%s: %.100s",
342647dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3427989dd127SDag-Erling Smørgrav 
3428511b41d2SMark Murray 			close(sock);
3429511b41d2SMark Murray 			continue;
3430511b41d2SMark Murray 		}
3431511b41d2SMark Murray 		/* Start listening for connections on the socket. */
3432476cd3b2SDag-Erling Smørgrav 		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
3433511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
343447dd1d1bSDag-Erling Smørgrav 			error("listen [%s]:%s: %.100s", ntop, strport,
343547dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3436511b41d2SMark Murray 			close(sock);
3437511b41d2SMark Murray 			continue;
3438511b41d2SMark Murray 		}
3439cce7d346SDag-Erling Smørgrav 
3440cce7d346SDag-Erling Smørgrav 		/*
3441a0ee8cc6SDag-Erling Smørgrav 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3442cce7d346SDag-Erling Smørgrav 		 * record what we got.
3443cce7d346SDag-Erling Smørgrav 		 */
34444f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
34454f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 &&
3446cce7d346SDag-Erling Smørgrav 		    allocated_listen_port != NULL &&
3447cce7d346SDag-Erling Smørgrav 		    *allocated_listen_port == 0) {
3448076ad2f8SDag-Erling Smørgrav 			*allocated_listen_port = get_local_port(sock);
3449cce7d346SDag-Erling Smørgrav 			debug("Allocated listen port %d",
3450cce7d346SDag-Erling Smørgrav 			    *allocated_listen_port);
3451cce7d346SDag-Erling Smørgrav 		}
3452cce7d346SDag-Erling Smørgrav 
345360c59fadSDag-Erling Smørgrav 		/* Allocate a channel number for the socket. */
34544f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "port listener", type, sock, sock, -1,
3455a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3456221552e4SDag-Erling Smørgrav 		    0, "port listener", 1);
3457cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
3458a0ee8cc6SDag-Erling Smørgrav 		c->host_port = fwd->connect_port;
3459462c32cbSDag-Erling Smørgrav 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
3460a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
3461462c32cbSDag-Erling Smørgrav 		    !(datafellows & SSH_BUG_DYNAMIC_RPORT))
3462462c32cbSDag-Erling Smørgrav 			c->listening_port = *allocated_listen_port;
3463462c32cbSDag-Erling Smørgrav 		else
3464a0ee8cc6SDag-Erling Smørgrav 			c->listening_port = fwd->listen_port;
3465511b41d2SMark Murray 		success = 1;
3466511b41d2SMark Murray 	}
3467511b41d2SMark Murray 	if (success == 0)
3468a0ee8cc6SDag-Erling Smørgrav 		error("%s: cannot listen to port: %d", __func__,
3469a0ee8cc6SDag-Erling Smørgrav 		    fwd->listen_port);
3470511b41d2SMark Murray 	freeaddrinfo(aitop);
3471ca3176e7SBrian Feldman 	return success;
3472511b41d2SMark Murray }
3473511b41d2SMark Murray 
3474a0ee8cc6SDag-Erling Smørgrav static int
34754f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
34764f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
3477a0ee8cc6SDag-Erling Smørgrav {
3478a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
3479a0ee8cc6SDag-Erling Smørgrav 	const char *path;
3480a0ee8cc6SDag-Erling Smørgrav 	Channel *c;
3481a0ee8cc6SDag-Erling Smørgrav 	int port, sock;
3482a0ee8cc6SDag-Erling Smørgrav 	mode_t omask;
3483a0ee8cc6SDag-Erling Smørgrav 
3484a0ee8cc6SDag-Erling Smørgrav 	switch (type) {
3485a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_UNIX_LISTENER:
3486a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
3487a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
3488a0ee8cc6SDag-Erling Smørgrav 				error("Local connecting path too long: %s",
3489a0ee8cc6SDag-Erling Smørgrav 				    fwd->connect_path);
3490a0ee8cc6SDag-Erling Smørgrav 				return 0;
3491a0ee8cc6SDag-Erling Smørgrav 			}
3492a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_path;
3493a0ee8cc6SDag-Erling Smørgrav 			port = PORT_STREAMLOCAL;
3494a0ee8cc6SDag-Erling Smørgrav 		} else {
3495a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_host == NULL) {
3496a0ee8cc6SDag-Erling Smørgrav 				error("No forward host name.");
3497a0ee8cc6SDag-Erling Smørgrav 				return 0;
3498a0ee8cc6SDag-Erling Smørgrav 			}
3499a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
3500a0ee8cc6SDag-Erling Smørgrav 				error("Forward host name too long.");
3501a0ee8cc6SDag-Erling Smørgrav 				return 0;
3502a0ee8cc6SDag-Erling Smørgrav 			}
3503a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_host;
3504a0ee8cc6SDag-Erling Smørgrav 			port = fwd->connect_port;
3505a0ee8cc6SDag-Erling Smørgrav 		}
3506a0ee8cc6SDag-Erling Smørgrav 		break;
3507a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_RUNIX_LISTENER:
3508a0ee8cc6SDag-Erling Smørgrav 		path = fwd->listen_path;
3509a0ee8cc6SDag-Erling Smørgrav 		port = PORT_STREAMLOCAL;
3510a0ee8cc6SDag-Erling Smørgrav 		break;
3511a0ee8cc6SDag-Erling Smørgrav 	default:
3512a0ee8cc6SDag-Erling Smørgrav 		error("%s: unexpected channel type %d", __func__, type);
3513a0ee8cc6SDag-Erling Smørgrav 		return 0;
3514a0ee8cc6SDag-Erling Smørgrav 	}
3515a0ee8cc6SDag-Erling Smørgrav 
3516a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path == NULL) {
3517a0ee8cc6SDag-Erling Smørgrav 		error("No forward path name.");
3518a0ee8cc6SDag-Erling Smørgrav 		return 0;
3519a0ee8cc6SDag-Erling Smørgrav 	}
3520a0ee8cc6SDag-Erling Smørgrav 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
3521a0ee8cc6SDag-Erling Smørgrav 		error("Local listening path too long: %s", fwd->listen_path);
3522a0ee8cc6SDag-Erling Smørgrav 		return 0;
3523a0ee8cc6SDag-Erling Smørgrav 	}
3524a0ee8cc6SDag-Erling Smørgrav 
3525a0ee8cc6SDag-Erling Smørgrav 	debug3("%s: type %d path %s", __func__, type, fwd->listen_path);
3526a0ee8cc6SDag-Erling Smørgrav 
3527a0ee8cc6SDag-Erling Smørgrav 	/* Start a Unix domain listener. */
3528a0ee8cc6SDag-Erling Smørgrav 	omask = umask(fwd_opts->streamlocal_bind_mask);
3529a0ee8cc6SDag-Erling Smørgrav 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
3530a0ee8cc6SDag-Erling Smørgrav 	    fwd_opts->streamlocal_bind_unlink);
3531a0ee8cc6SDag-Erling Smørgrav 	umask(omask);
3532a0ee8cc6SDag-Erling Smørgrav 	if (sock < 0)
3533a0ee8cc6SDag-Erling Smørgrav 		return 0;
3534a0ee8cc6SDag-Erling Smørgrav 
3535a0ee8cc6SDag-Erling Smørgrav 	debug("Local forwarding listening on path %s.", fwd->listen_path);
3536a0ee8cc6SDag-Erling Smørgrav 
3537a0ee8cc6SDag-Erling Smørgrav 	/* Allocate a channel number for the socket. */
35384f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "unix listener", type, sock, sock, -1,
3539a0ee8cc6SDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3540a0ee8cc6SDag-Erling Smørgrav 	    0, "unix listener", 1);
3541a0ee8cc6SDag-Erling Smørgrav 	c->path = xstrdup(path);
3542a0ee8cc6SDag-Erling Smørgrav 	c->host_port = port;
3543a0ee8cc6SDag-Erling Smørgrav 	c->listening_port = PORT_STREAMLOCAL;
3544a0ee8cc6SDag-Erling Smørgrav 	c->listening_addr = xstrdup(fwd->listen_path);
3545a0ee8cc6SDag-Erling Smørgrav 	return 1;
3546a0ee8cc6SDag-Erling Smørgrav }
3547a0ee8cc6SDag-Erling Smørgrav 
3548a0ee8cc6SDag-Erling Smørgrav static int
35494f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh,
35504f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
355121e764dfSDag-Erling Smørgrav {
355221e764dfSDag-Erling Smørgrav 	u_int i;
355321e764dfSDag-Erling Smørgrav 	int found = 0;
355421e764dfSDag-Erling Smørgrav 
35554f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
35564f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3557462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
3558462c32cbSDag-Erling Smørgrav 			continue;
3559462c32cbSDag-Erling Smørgrav 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
3560462c32cbSDag-Erling Smørgrav 			debug2("%s: close channel %d", __func__, i);
35614f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3562462c32cbSDag-Erling Smørgrav 			found = 1;
3563462c32cbSDag-Erling Smørgrav 		}
3564462c32cbSDag-Erling Smørgrav 	}
356521e764dfSDag-Erling Smørgrav 
35664f52dfbbSDag-Erling Smørgrav 	return found;
3567462c32cbSDag-Erling Smørgrav }
3568462c32cbSDag-Erling Smørgrav 
3569a0ee8cc6SDag-Erling Smørgrav static int
35704f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
3571462c32cbSDag-Erling Smørgrav {
3572462c32cbSDag-Erling Smørgrav 	u_int i;
3573462c32cbSDag-Erling Smørgrav 	int found = 0;
3574a0ee8cc6SDag-Erling Smørgrav 
35754f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
35764f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3577a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
3578a0ee8cc6SDag-Erling Smørgrav 			continue;
3579a0ee8cc6SDag-Erling Smørgrav 		if (c->path == NULL)
3580a0ee8cc6SDag-Erling Smørgrav 			continue;
3581a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->path, path) == 0) {
3582a0ee8cc6SDag-Erling Smørgrav 			debug2("%s: close channel %d", __func__, i);
35834f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3584a0ee8cc6SDag-Erling Smørgrav 			found = 1;
3585a0ee8cc6SDag-Erling Smørgrav 		}
3586a0ee8cc6SDag-Erling Smørgrav 	}
3587a0ee8cc6SDag-Erling Smørgrav 
35884f52dfbbSDag-Erling Smørgrav 	return found;
3589a0ee8cc6SDag-Erling Smørgrav }
3590a0ee8cc6SDag-Erling Smørgrav 
3591a0ee8cc6SDag-Erling Smørgrav int
35924f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
3593a0ee8cc6SDag-Erling Smørgrav {
35944f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
35954f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_streamlocal(ssh,
35964f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
35974f52dfbbSDag-Erling Smørgrav 	} else {
35984f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_tcpip(ssh,
35994f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port);
36004f52dfbbSDag-Erling Smørgrav 	}
3601a0ee8cc6SDag-Erling Smørgrav }
3602a0ee8cc6SDag-Erling Smørgrav 
3603a0ee8cc6SDag-Erling Smørgrav static int
36044f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh,
36054f52dfbbSDag-Erling Smørgrav     const char *lhost, u_short lport, int cport,
36064f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3607a0ee8cc6SDag-Erling Smørgrav {
3608a0ee8cc6SDag-Erling Smørgrav 	u_int i;
3609a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
3610a0ee8cc6SDag-Erling Smørgrav 	const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts);
3611462c32cbSDag-Erling Smørgrav 
36124f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
36134f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3614462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
3615462c32cbSDag-Erling Smørgrav 			continue;
3616462c32cbSDag-Erling Smørgrav 		if (c->listening_port != lport)
3617462c32cbSDag-Erling Smørgrav 			continue;
3618462c32cbSDag-Erling Smørgrav 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
3619462c32cbSDag-Erling Smørgrav 			/* skip dynamic forwardings */
3620462c32cbSDag-Erling Smørgrav 			if (c->host_port == 0)
3621462c32cbSDag-Erling Smørgrav 				continue;
3622462c32cbSDag-Erling Smørgrav 		} else {
3623462c32cbSDag-Erling Smørgrav 			if (c->host_port != cport)
3624462c32cbSDag-Erling Smørgrav 				continue;
3625462c32cbSDag-Erling Smørgrav 		}
3626462c32cbSDag-Erling Smørgrav 		if ((c->listening_addr == NULL && addr != NULL) ||
3627462c32cbSDag-Erling Smørgrav 		    (c->listening_addr != NULL && addr == NULL))
3628462c32cbSDag-Erling Smørgrav 			continue;
3629462c32cbSDag-Erling Smørgrav 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
3630aa49c926SDag-Erling Smørgrav 			debug2("%s: close channel %d", __func__, i);
36314f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
363221e764dfSDag-Erling Smørgrav 			found = 1;
363321e764dfSDag-Erling Smørgrav 		}
363421e764dfSDag-Erling Smørgrav 	}
363521e764dfSDag-Erling Smørgrav 
36364f52dfbbSDag-Erling Smørgrav 	return found;
363721e764dfSDag-Erling Smørgrav }
363821e764dfSDag-Erling Smørgrav 
3639a0ee8cc6SDag-Erling Smørgrav static int
36404f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
3641a0ee8cc6SDag-Erling Smørgrav {
3642a0ee8cc6SDag-Erling Smørgrav 	u_int i;
3643a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
3644a0ee8cc6SDag-Erling Smørgrav 
3645a0ee8cc6SDag-Erling Smørgrav 	if (path == NULL) {
3646a0ee8cc6SDag-Erling Smørgrav 		error("%s: no path specified.", __func__);
3647a0ee8cc6SDag-Erling Smørgrav 		return 0;
3648a0ee8cc6SDag-Erling Smørgrav 	}
3649a0ee8cc6SDag-Erling Smørgrav 
36504f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
36514f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3652a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
3653a0ee8cc6SDag-Erling Smørgrav 			continue;
3654a0ee8cc6SDag-Erling Smørgrav 		if (c->listening_addr == NULL)
3655a0ee8cc6SDag-Erling Smørgrav 			continue;
3656a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->listening_addr, path) == 0) {
3657a0ee8cc6SDag-Erling Smørgrav 			debug2("%s: close channel %d", __func__, i);
36584f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3659a0ee8cc6SDag-Erling Smørgrav 			found = 1;
3660a0ee8cc6SDag-Erling Smørgrav 		}
3661a0ee8cc6SDag-Erling Smørgrav 	}
3662a0ee8cc6SDag-Erling Smørgrav 
36634f52dfbbSDag-Erling Smørgrav 	return found;
3664a0ee8cc6SDag-Erling Smørgrav }
3665a0ee8cc6SDag-Erling Smørgrav 
3666a0ee8cc6SDag-Erling Smørgrav int
36674f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh,
36684f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
3669af12a3e7SDag-Erling Smørgrav {
3670a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
36714f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_streamlocal(ssh,
36724f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
36734f52dfbbSDag-Erling Smørgrav 	} else {
36744f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_tcpip(ssh,
36754f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
36764f52dfbbSDag-Erling Smørgrav 	}
36774f52dfbbSDag-Erling Smørgrav }
36784f52dfbbSDag-Erling Smørgrav 
36794f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */
36804f52dfbbSDag-Erling Smørgrav int
36814f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh,
36824f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
36834f52dfbbSDag-Erling Smørgrav {
36844f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
36854f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
3686a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
3687a0ee8cc6SDag-Erling Smørgrav 	} else {
36884f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
36894f52dfbbSDag-Erling Smørgrav 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
3690a0ee8cc6SDag-Erling Smørgrav 	}
3691af12a3e7SDag-Erling Smørgrav }
3692af12a3e7SDag-Erling Smørgrav 
3693*190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */
3694*190cef3dSDag-Erling Smørgrav static int
3695*190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd)
3696*190cef3dSDag-Erling Smørgrav {
3697*190cef3dSDag-Erling Smørgrav 	int ret;
3698*190cef3dSDag-Erling Smørgrav 	char *lhost;
3699*190cef3dSDag-Erling Smørgrav 
3700*190cef3dSDag-Erling Smørgrav 	/* XXX add ACLs for streamlocal */
3701*190cef3dSDag-Erling Smørgrav 	if (fwd->listen_path != NULL)
3702*190cef3dSDag-Erling Smørgrav 		return 1;
3703*190cef3dSDag-Erling Smørgrav 
3704*190cef3dSDag-Erling Smørgrav 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
3705*190cef3dSDag-Erling Smørgrav 		return 0;
3706*190cef3dSDag-Erling Smørgrav 
3707*190cef3dSDag-Erling Smørgrav 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
3708*190cef3dSDag-Erling Smørgrav 	    allowed_open->listen_port != fwd->listen_port)
3709*190cef3dSDag-Erling Smørgrav 		return 0;
3710*190cef3dSDag-Erling Smørgrav 
3711*190cef3dSDag-Erling Smørgrav 	/* Match hostnames case-insensitively */
3712*190cef3dSDag-Erling Smørgrav 	lhost = xstrdup(fwd->listen_host);
3713*190cef3dSDag-Erling Smørgrav 	lowercase(lhost);
3714*190cef3dSDag-Erling Smørgrav 	ret = match_pattern(lhost, allowed_open->listen_host);
3715*190cef3dSDag-Erling Smørgrav 	free(lhost);
3716*190cef3dSDag-Erling Smørgrav 
3717*190cef3dSDag-Erling Smørgrav 	return ret;
3718*190cef3dSDag-Erling Smørgrav }
3719*190cef3dSDag-Erling Smørgrav 
3720*190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */
3721*190cef3dSDag-Erling Smørgrav static int
3722*190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
3723*190cef3dSDag-Erling Smørgrav {
3724*190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3725*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->remote_perms;
3726*190cef3dSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
3727*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
3728*190cef3dSDag-Erling Smørgrav 
3729*190cef3dSDag-Erling Smørgrav 	/* XXX apply GatewayPorts override before checking? */
3730*190cef3dSDag-Erling Smørgrav 
3731*190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
3732*190cef3dSDag-Erling Smørgrav 	if (!permit) {
3733*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
3734*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
3735*190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
3736*190cef3dSDag-Erling Smørgrav 				permit = 1;
3737*190cef3dSDag-Erling Smørgrav 				break;
3738*190cef3dSDag-Erling Smørgrav 			}
3739*190cef3dSDag-Erling Smørgrav 		}
3740*190cef3dSDag-Erling Smørgrav 	}
3741*190cef3dSDag-Erling Smørgrav 
3742*190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
3743*190cef3dSDag-Erling Smørgrav 		permit_adm = 0;
3744*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
3745*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
3746*190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
3747*190cef3dSDag-Erling Smørgrav 				permit_adm = 1;
3748*190cef3dSDag-Erling Smørgrav 				break;
3749*190cef3dSDag-Erling Smørgrav 			}
3750*190cef3dSDag-Erling Smørgrav 		}
3751*190cef3dSDag-Erling Smørgrav 	}
3752*190cef3dSDag-Erling Smørgrav 
3753*190cef3dSDag-Erling Smørgrav 	return permit && permit_adm;
3754*190cef3dSDag-Erling Smørgrav }
3755*190cef3dSDag-Erling Smørgrav 
3756af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */
3757af12a3e7SDag-Erling Smørgrav int
37584f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
3759a0ee8cc6SDag-Erling Smørgrav     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
3760af12a3e7SDag-Erling Smørgrav {
3761*190cef3dSDag-Erling Smørgrav 	if (!check_rfwd_permission(ssh, fwd)) {
3762*190cef3dSDag-Erling Smørgrav 		packet_send_debug("port forwarding refused");
3763*190cef3dSDag-Erling Smørgrav 		return 0;
3764*190cef3dSDag-Erling Smørgrav 	}
3765a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
37664f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
3767a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
3768a0ee8cc6SDag-Erling Smørgrav 	} else {
37694f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
3770a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
3771a0ee8cc6SDag-Erling Smørgrav 		    fwd_opts);
3772a0ee8cc6SDag-Erling Smørgrav 	}
3773af12a3e7SDag-Erling Smørgrav }
3774af12a3e7SDag-Erling Smørgrav 
3775511b41d2SMark Murray /*
3776462c32cbSDag-Erling Smørgrav  * Translate the requested rfwd listen host to something usable for
3777462c32cbSDag-Erling Smørgrav  * this server.
3778462c32cbSDag-Erling Smørgrav  */
3779462c32cbSDag-Erling Smørgrav static const char *
3780462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host)
3781462c32cbSDag-Erling Smørgrav {
3782462c32cbSDag-Erling Smørgrav 	if (listen_host == NULL) {
3783462c32cbSDag-Erling Smørgrav 		return "localhost";
3784462c32cbSDag-Erling Smørgrav 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
3785462c32cbSDag-Erling Smørgrav 		return "";
3786462c32cbSDag-Erling Smørgrav 	} else
3787462c32cbSDag-Erling Smørgrav 		return listen_host;
3788462c32cbSDag-Erling Smørgrav }
3789462c32cbSDag-Erling Smørgrav 
3790462c32cbSDag-Erling Smørgrav /*
3791511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
3792511b41d2SMark Murray  * the secure channel to host:port from local side.
3793462c32cbSDag-Erling Smørgrav  * Returns handle (index) for updating the dynamic listen port with
3794*190cef3dSDag-Erling Smørgrav  * channel_update_permission().
3795511b41d2SMark Murray  */
3796333ee039SDag-Erling Smørgrav int
37974f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
3798511b41d2SMark Murray {
37994f52dfbbSDag-Erling Smørgrav 	int r, success = 0, idx = -1;
38004f52dfbbSDag-Erling Smørgrav 	char *host_to_connect, *listen_host, *listen_path;
38014f52dfbbSDag-Erling Smørgrav 	int port_to_connect, listen_port;
3802ca3176e7SBrian Feldman 
3803511b41d2SMark Murray 	/* Send the forward request to the remote side. */
3804a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
38054f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
38064f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
38074f52dfbbSDag-Erling Smørgrav 		    "streamlocal-forward@openssh.com")) != 0 ||
38084f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
38094f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
38104f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
38114f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
38124f52dfbbSDag-Erling Smørgrav 			fatal("%s: request streamlocal: %s",
38134f52dfbbSDag-Erling Smørgrav 			    __func__, ssh_err(r));
3814a0ee8cc6SDag-Erling Smørgrav 	} else {
38154f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
38164f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
38174f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
38184f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
38194f52dfbbSDag-Erling Smørgrav 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
38204f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
38214f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
38224f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
38234f52dfbbSDag-Erling Smørgrav 			fatal("%s: request tcpip-forward: %s",
38244f52dfbbSDag-Erling Smørgrav 			    __func__, ssh_err(r));
3825a0ee8cc6SDag-Erling Smørgrav 	}
3826ca3176e7SBrian Feldman 	/* Assume that server accepts the request */
3827ca3176e7SBrian Feldman 	success = 1;
3828ca3176e7SBrian Feldman 	if (success) {
3829e2f6069cSDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
38304f52dfbbSDag-Erling Smørgrav 		host_to_connect = listen_host = listen_path = NULL;
38314f52dfbbSDag-Erling Smørgrav 		port_to_connect = listen_port = 0;
3832a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
38334f52dfbbSDag-Erling Smørgrav 			host_to_connect = xstrdup(fwd->connect_path);
38344f52dfbbSDag-Erling Smørgrav 			port_to_connect = PORT_STREAMLOCAL;
3835a0ee8cc6SDag-Erling Smørgrav 		} else {
38364f52dfbbSDag-Erling Smørgrav 			host_to_connect = xstrdup(fwd->connect_host);
38374f52dfbbSDag-Erling Smørgrav 			port_to_connect = fwd->connect_port;
3838a0ee8cc6SDag-Erling Smørgrav 		}
3839a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_path != NULL) {
38404f52dfbbSDag-Erling Smørgrav 			listen_path = xstrdup(fwd->listen_path);
38414f52dfbbSDag-Erling Smørgrav 			listen_port = PORT_STREAMLOCAL;
3842a0ee8cc6SDag-Erling Smørgrav 		} else {
38434f52dfbbSDag-Erling Smørgrav 			if (fwd->listen_host != NULL)
38444f52dfbbSDag-Erling Smørgrav 				listen_host = xstrdup(fwd->listen_host);
38454f52dfbbSDag-Erling Smørgrav 			listen_port = fwd->listen_port;
3846a0ee8cc6SDag-Erling Smørgrav 		}
3847*190cef3dSDag-Erling Smørgrav 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
38484f52dfbbSDag-Erling Smørgrav 		    host_to_connect, port_to_connect,
38494f52dfbbSDag-Erling Smørgrav 		    listen_host, listen_path, listen_port, NULL);
3850511b41d2SMark Murray 	}
38514f52dfbbSDag-Erling Smørgrav 	return idx;
3852a04a10f8SKris Kennaway }
3853511b41d2SMark Murray 
3854a0ee8cc6SDag-Erling Smørgrav static int
3855*190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost,
3856a0ee8cc6SDag-Erling Smørgrav     int requestedport)
3857a0ee8cc6SDag-Erling Smørgrav {
3858a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
3859a0ee8cc6SDag-Erling Smørgrav 		return 0;
3860a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
3861a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->port_to_connect != requestedport)
3862a0ee8cc6SDag-Erling Smørgrav 		return 0;
3863076ad2f8SDag-Erling Smørgrav 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
3864076ad2f8SDag-Erling Smørgrav 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
3865a0ee8cc6SDag-Erling Smørgrav 		return 0;
3866a0ee8cc6SDag-Erling Smørgrav 	return 1;
3867a0ee8cc6SDag-Erling Smørgrav }
3868a0ee8cc6SDag-Erling Smørgrav 
3869a0ee8cc6SDag-Erling Smørgrav /*
3870a0ee8cc6SDag-Erling Smørgrav  * Note that in the listen host/port case
3871a0ee8cc6SDag-Erling Smørgrav  * we don't support FWD_PERMIT_ANY_PORT and
3872a0ee8cc6SDag-Erling Smørgrav  * need to translate between the configured-host (listen_host)
3873a0ee8cc6SDag-Erling Smørgrav  * and what we've sent to the remote server (channel_rfwd_bind_host)
3874a0ee8cc6SDag-Erling Smørgrav  */
3875a0ee8cc6SDag-Erling Smørgrav static int
3876*190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open,
3877a0ee8cc6SDag-Erling Smørgrav     const char *requestedhost, u_short requestedport, int translate)
3878a0ee8cc6SDag-Erling Smørgrav {
3879a0ee8cc6SDag-Erling Smørgrav 	const char *allowed_host;
3880a0ee8cc6SDag-Erling Smørgrav 
3881a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
3882a0ee8cc6SDag-Erling Smørgrav 		return 0;
3883a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != requestedport)
3884a0ee8cc6SDag-Erling Smørgrav 		return 0;
3885a0ee8cc6SDag-Erling Smørgrav 	if (!translate && allowed_open->listen_host == NULL &&
3886a0ee8cc6SDag-Erling Smørgrav 	    requestedhost == NULL)
3887a0ee8cc6SDag-Erling Smørgrav 		return 1;
3888a0ee8cc6SDag-Erling Smørgrav 	allowed_host = translate ?
3889a0ee8cc6SDag-Erling Smørgrav 	    channel_rfwd_bind_host(allowed_open->listen_host) :
3890a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->listen_host;
3891*190cef3dSDag-Erling Smørgrav 	if (allowed_host == NULL || requestedhost == NULL ||
3892a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_host, requestedhost) != 0)
3893a0ee8cc6SDag-Erling Smørgrav 		return 0;
3894a0ee8cc6SDag-Erling Smørgrav 	return 1;
3895a0ee8cc6SDag-Erling Smørgrav }
3896a0ee8cc6SDag-Erling Smørgrav 
3897a0ee8cc6SDag-Erling Smørgrav static int
3898*190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open,
3899a0ee8cc6SDag-Erling Smørgrav     const char *requestedpath)
3900a0ee8cc6SDag-Erling Smørgrav {
3901a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
3902a0ee8cc6SDag-Erling Smørgrav 		return 0;
3903a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
3904a0ee8cc6SDag-Erling Smørgrav 		return 0;
3905a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_path == NULL ||
3906a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
3907a0ee8cc6SDag-Erling Smørgrav 		return 0;
3908a0ee8cc6SDag-Erling Smørgrav 	return 1;
3909a0ee8cc6SDag-Erling Smørgrav }
3910a0ee8cc6SDag-Erling Smørgrav 
3911511b41d2SMark Murray /*
391221e764dfSDag-Erling Smørgrav  * Request cancellation of remote forwarding of connection host:port from
391321e764dfSDag-Erling Smørgrav  * local side.
391421e764dfSDag-Erling Smørgrav  */
3915a0ee8cc6SDag-Erling Smørgrav static int
39164f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh,
39174f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
391821e764dfSDag-Erling Smørgrav {
39194f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3920*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
39214f52dfbbSDag-Erling Smørgrav 	int r;
39224f52dfbbSDag-Erling Smørgrav 	u_int i;
3923*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
392421e764dfSDag-Erling Smørgrav 
3925*190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
3926*190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
3927*190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm, host, port, 0))
392821e764dfSDag-Erling Smørgrav 			break;
3929*190cef3dSDag-Erling Smørgrav 		perm = NULL;
393021e764dfSDag-Erling Smørgrav 	}
3931*190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
393221e764dfSDag-Erling Smørgrav 		debug("%s: requested forward not found", __func__);
3933462c32cbSDag-Erling Smørgrav 		return -1;
393421e764dfSDag-Erling Smørgrav 	}
39354f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
39364f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
39374f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
39384f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
39394f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
39404f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
39414f52dfbbSDag-Erling Smørgrav 		fatal("%s: send cancel: %s", __func__, ssh_err(r));
394221e764dfSDag-Erling Smørgrav 
3943*190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
3944462c32cbSDag-Erling Smørgrav 
3945462c32cbSDag-Erling Smørgrav 	return 0;
394621e764dfSDag-Erling Smørgrav }
394721e764dfSDag-Erling Smørgrav 
394821e764dfSDag-Erling Smørgrav /*
3949a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of Unix domain socket
3950a0ee8cc6SDag-Erling Smørgrav  * path from local side.
3951a0ee8cc6SDag-Erling Smørgrav  */
3952a0ee8cc6SDag-Erling Smørgrav static int
39534f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
3954a0ee8cc6SDag-Erling Smørgrav {
39554f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3956*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
39574f52dfbbSDag-Erling Smørgrav 	int r;
39584f52dfbbSDag-Erling Smørgrav 	u_int i;
3959*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
3960a0ee8cc6SDag-Erling Smørgrav 
3961*190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
3962*190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
3963*190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path))
3964a0ee8cc6SDag-Erling Smørgrav 			break;
3965*190cef3dSDag-Erling Smørgrav 		perm = NULL;
3966a0ee8cc6SDag-Erling Smørgrav 	}
3967*190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
3968a0ee8cc6SDag-Erling Smørgrav 		debug("%s: requested forward not found", __func__);
3969a0ee8cc6SDag-Erling Smørgrav 		return -1;
3970a0ee8cc6SDag-Erling Smørgrav 	}
39714f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
39724f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh,
39734f52dfbbSDag-Erling Smørgrav 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
39744f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
39754f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
39764f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
39774f52dfbbSDag-Erling Smørgrav 		fatal("%s: send cancel: %s", __func__, ssh_err(r));
3978a0ee8cc6SDag-Erling Smørgrav 
3979*190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
3980a0ee8cc6SDag-Erling Smørgrav 
3981a0ee8cc6SDag-Erling Smørgrav 	return 0;
3982a0ee8cc6SDag-Erling Smørgrav }
3983a0ee8cc6SDag-Erling Smørgrav 
3984a0ee8cc6SDag-Erling Smørgrav /*
3985a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of a connection from local side.
3986a0ee8cc6SDag-Erling Smørgrav  */
3987a0ee8cc6SDag-Erling Smørgrav int
39884f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
3989a0ee8cc6SDag-Erling Smørgrav {
3990a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
39914f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_streamlocal(ssh,
39924f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
3993a0ee8cc6SDag-Erling Smørgrav 	} else {
39944f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_tcpip(ssh,
39954f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host,
39964f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
3997a0ee8cc6SDag-Erling Smørgrav 	}
3998a0ee8cc6SDag-Erling Smørgrav }
3999a0ee8cc6SDag-Erling Smørgrav 
4000a0ee8cc6SDag-Erling Smørgrav /*
4001*190cef3dSDag-Erling Smørgrav  * Permits opening to any host/port if permitted_user[] is empty.  This is
4002ca3176e7SBrian Feldman  * usually called by the server, because the user could connect to any port
4003ca3176e7SBrian Feldman  * anyway, and the server has no way to know but to trust the client anyway.
4004ca3176e7SBrian Feldman  */
4005ca3176e7SBrian Feldman void
4006*190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where)
4007ca3176e7SBrian Feldman {
4008*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
4009*190cef3dSDag-Erling Smørgrav 
4010*190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_user == 0)
4011*190cef3dSDag-Erling Smørgrav 		pset->all_permitted = 1;
4012ca3176e7SBrian Feldman }
4013ca3176e7SBrian Feldman 
4014*190cef3dSDag-Erling Smørgrav /*
4015*190cef3dSDag-Erling Smørgrav  * Permit the specified host/port for forwarding.
4016*190cef3dSDag-Erling Smørgrav  */
4017ca3176e7SBrian Feldman void
4018*190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where,
4019*190cef3dSDag-Erling Smørgrav     char *host, int port)
4020ca3176e7SBrian Feldman {
4021*190cef3dSDag-Erling Smørgrav 	int local = where == FORWARD_LOCAL;
4022*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
40234f52dfbbSDag-Erling Smørgrav 
4024*190cef3dSDag-Erling Smørgrav 	debug("allow %s forwarding to host %s port %d",
4025*190cef3dSDag-Erling Smørgrav 	    fwd_ident(who, where), host, port);
4026*190cef3dSDag-Erling Smørgrav 	/*
4027*190cef3dSDag-Erling Smørgrav 	 * Remote forwards set listen_host/port, local forwards set
4028*190cef3dSDag-Erling Smørgrav 	 * host/port_to_connect.
4029*190cef3dSDag-Erling Smørgrav 	 */
4030*190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, who, where,
4031*190cef3dSDag-Erling Smørgrav 	    local ? host : 0, local ? port : 0,
4032*190cef3dSDag-Erling Smørgrav 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
4033*190cef3dSDag-Erling Smørgrav 	pset->all_permitted = 0;
4034*190cef3dSDag-Erling Smørgrav }
4035*190cef3dSDag-Erling Smørgrav 
4036*190cef3dSDag-Erling Smørgrav /*
4037*190cef3dSDag-Erling Smørgrav  * Administratively disable forwarding.
4038*190cef3dSDag-Erling Smørgrav  */
4039*190cef3dSDag-Erling Smørgrav void
4040*190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where)
4041*190cef3dSDag-Erling Smørgrav {
4042*190cef3dSDag-Erling Smørgrav 	channel_clear_permission(ssh, FORWARD_ADM, where);
4043*190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, FORWARD_ADM, where,
4044*190cef3dSDag-Erling Smørgrav 	    NULL, 0, NULL, NULL, 0, NULL);
4045*190cef3dSDag-Erling Smørgrav }
4046*190cef3dSDag-Erling Smørgrav 
4047*190cef3dSDag-Erling Smørgrav /*
4048*190cef3dSDag-Erling Smørgrav  * Clear a list of permitted opens.
4049*190cef3dSDag-Erling Smørgrav  */
4050*190cef3dSDag-Erling Smørgrav void
4051*190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where)
4052*190cef3dSDag-Erling Smørgrav {
4053*190cef3dSDag-Erling Smørgrav 	struct permission **permp;
4054*190cef3dSDag-Erling Smørgrav 	u_int *npermp;
4055*190cef3dSDag-Erling Smørgrav 
4056*190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
4057*190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
4058*190cef3dSDag-Erling Smørgrav 	*npermp = 0;
4059ca3176e7SBrian Feldman }
4060ca3176e7SBrian Feldman 
4061462c32cbSDag-Erling Smørgrav /*
4062462c32cbSDag-Erling Smørgrav  * Update the listen port for a dynamic remote forward, after
4063462c32cbSDag-Erling Smørgrav  * the actual 'newport' has been allocated. If 'newport' < 0 is
4064462c32cbSDag-Erling Smørgrav  * passed then they entry will be invalidated.
4065462c32cbSDag-Erling Smørgrav  */
4066462c32cbSDag-Erling Smørgrav void
4067*190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport)
4068462c32cbSDag-Erling Smørgrav {
4069*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &ssh->chanctxt->local_perms;
40704f52dfbbSDag-Erling Smørgrav 
4071*190cef3dSDag-Erling Smørgrav 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
4072*190cef3dSDag-Erling Smørgrav 		debug("%s: index out of range: %d num_permitted_user %d",
4073*190cef3dSDag-Erling Smørgrav 		    __func__, idx, pset->num_permitted_user);
4074462c32cbSDag-Erling Smørgrav 		return;
4075462c32cbSDag-Erling Smørgrav 	}
4076462c32cbSDag-Erling Smørgrav 	debug("%s allowed port %d for forwarding to host %s port %d",
4077462c32cbSDag-Erling Smørgrav 	    newport > 0 ? "Updating" : "Removing",
4078462c32cbSDag-Erling Smørgrav 	    newport,
4079*190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].host_to_connect,
4080*190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].port_to_connect);
40814f52dfbbSDag-Erling Smørgrav 	if (newport <= 0)
4082*190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(&pset->permitted_user[idx]);
40834f52dfbbSDag-Erling Smørgrav 	else {
4084*190cef3dSDag-Erling Smørgrav 		pset->permitted_user[idx].listen_port =
4085462c32cbSDag-Erling Smørgrav 		    (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4086462c32cbSDag-Erling Smørgrav 	}
4087462c32cbSDag-Erling Smørgrav }
4088462c32cbSDag-Erling Smørgrav 
4089462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
4090462c32cbSDag-Erling Smørgrav int
4091462c32cbSDag-Erling Smørgrav permitopen_port(const char *p)
4092462c32cbSDag-Erling Smørgrav {
4093462c32cbSDag-Erling Smørgrav 	int port;
4094462c32cbSDag-Erling Smørgrav 
4095462c32cbSDag-Erling Smørgrav 	if (strcmp(p, "*") == 0)
4096462c32cbSDag-Erling Smørgrav 		return FWD_PERMIT_ANY_PORT;
4097462c32cbSDag-Erling Smørgrav 	if ((port = a2port(p)) > 0)
4098462c32cbSDag-Erling Smørgrav 		return port;
4099462c32cbSDag-Erling Smørgrav 	return -1;
4100462c32cbSDag-Erling Smørgrav }
4101462c32cbSDag-Erling Smørgrav 
4102d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */
4103d4af9e69SDag-Erling Smørgrav static int
4104d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx)
4105d4af9e69SDag-Erling Smørgrav {
4106d4af9e69SDag-Erling Smørgrav 	int sock, saved_errno;
4107a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un *sunaddr;
41084f52dfbbSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
41094f52dfbbSDag-Erling Smørgrav 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4110d4af9e69SDag-Erling Smørgrav 
4111d4af9e69SDag-Erling Smørgrav 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
4112a0ee8cc6SDag-Erling Smørgrav 		switch (cctx->ai->ai_family) {
4113a0ee8cc6SDag-Erling Smørgrav 		case AF_UNIX:
4114a0ee8cc6SDag-Erling Smørgrav 			/* unix:pathname instead of host:port */
4115a0ee8cc6SDag-Erling Smørgrav 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
4116a0ee8cc6SDag-Erling Smørgrav 			strlcpy(ntop, "unix", sizeof(ntop));
4117a0ee8cc6SDag-Erling Smørgrav 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
4118a0ee8cc6SDag-Erling Smørgrav 			break;
4119a0ee8cc6SDag-Erling Smørgrav 		case AF_INET:
4120a0ee8cc6SDag-Erling Smørgrav 		case AF_INET6:
4121d4af9e69SDag-Erling Smørgrav 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4122d4af9e69SDag-Erling Smørgrav 			    ntop, sizeof(ntop), strport, sizeof(strport),
4123d4af9e69SDag-Erling Smørgrav 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
4124d4af9e69SDag-Erling Smørgrav 				error("connect_next: getnameinfo failed");
4125511b41d2SMark Murray 				continue;
4126511b41d2SMark Murray 			}
4127a0ee8cc6SDag-Erling Smørgrav 			break;
4128a0ee8cc6SDag-Erling Smørgrav 		default:
4129a0ee8cc6SDag-Erling Smørgrav 			continue;
4130a0ee8cc6SDag-Erling Smørgrav 		}
4131d4af9e69SDag-Erling Smørgrav 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4132d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_protocol)) == -1) {
4133d4af9e69SDag-Erling Smørgrav 			if (cctx->ai->ai_next == NULL)
4134511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
4135e73e9afaSDag-Erling Smørgrav 			else
4136e73e9afaSDag-Erling Smørgrav 				verbose("socket: %.100s", strerror(errno));
4137511b41d2SMark Murray 			continue;
4138511b41d2SMark Murray 		}
413921e764dfSDag-Erling Smørgrav 		if (set_nonblock(sock) == -1)
414021e764dfSDag-Erling Smørgrav 			fatal("%s: set_nonblock(%d)", __func__, sock);
4141d4af9e69SDag-Erling Smørgrav 		if (connect(sock, cctx->ai->ai_addr,
4142d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
4143d4af9e69SDag-Erling Smørgrav 			debug("connect_next: host %.100s ([%.100s]:%s): "
4144d4af9e69SDag-Erling Smørgrav 			    "%.100s", cctx->host, ntop, strport,
4145511b41d2SMark Murray 			    strerror(errno));
4146d4af9e69SDag-Erling Smørgrav 			saved_errno = errno;
4147511b41d2SMark Murray 			close(sock);
4148d4af9e69SDag-Erling Smørgrav 			errno = saved_errno;
4149511b41d2SMark Murray 			continue;	/* fail -- try next */
4150511b41d2SMark Murray 		}
4151a0ee8cc6SDag-Erling Smørgrav 		if (cctx->ai->ai_family != AF_UNIX)
4152a0ee8cc6SDag-Erling Smørgrav 			set_nodelay(sock);
4153d4af9e69SDag-Erling Smørgrav 		debug("connect_next: host %.100s ([%.100s]:%s) "
4154d4af9e69SDag-Erling Smørgrav 		    "in progress, fd=%d", cctx->host, ntop, strport, sock);
4155d4af9e69SDag-Erling Smørgrav 		cctx->ai = cctx->ai->ai_next;
4156a04a10f8SKris Kennaway 		return sock;
4157a04a10f8SKris Kennaway 	}
4158ca3176e7SBrian Feldman 	return -1;
4159ca3176e7SBrian Feldman }
4160ca3176e7SBrian Feldman 
4161d4af9e69SDag-Erling Smørgrav static void
4162d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx)
4163d4af9e69SDag-Erling Smørgrav {
4164e4a9863fSDag-Erling Smørgrav 	free(cctx->host);
4165a0ee8cc6SDag-Erling Smørgrav 	if (cctx->aitop) {
4166a0ee8cc6SDag-Erling Smørgrav 		if (cctx->aitop->ai_family == AF_UNIX)
4167a0ee8cc6SDag-Erling Smørgrav 			free(cctx->aitop);
4168a0ee8cc6SDag-Erling Smørgrav 		else
4169d4af9e69SDag-Erling Smørgrav 			freeaddrinfo(cctx->aitop);
4170a0ee8cc6SDag-Erling Smørgrav 	}
4171b83788ffSDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
4172d4af9e69SDag-Erling Smørgrav }
4173d4af9e69SDag-Erling Smørgrav 
4174d93a896eSDag-Erling Smørgrav /*
41754f52dfbbSDag-Erling Smørgrav  * Return connecting socket to remote host:port or local socket path,
4176d93a896eSDag-Erling Smørgrav  * passing back the failure reason if appropriate.
4177d93a896eSDag-Erling Smørgrav  */
41784f52dfbbSDag-Erling Smørgrav static int
41794f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
41804f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, struct channel_connect *cctx,
4181d93a896eSDag-Erling Smørgrav     int *reason, const char **errmsg)
4182d4af9e69SDag-Erling Smørgrav {
4183d4af9e69SDag-Erling Smørgrav 	struct addrinfo hints;
4184d4af9e69SDag-Erling Smørgrav 	int gaierr;
4185d4af9e69SDag-Erling Smørgrav 	int sock = -1;
4186d4af9e69SDag-Erling Smørgrav 	char strport[NI_MAXSERV];
4187a0ee8cc6SDag-Erling Smørgrav 
4188a0ee8cc6SDag-Erling Smørgrav 	if (port == PORT_STREAMLOCAL) {
4189a0ee8cc6SDag-Erling Smørgrav 		struct sockaddr_un *sunaddr;
4190a0ee8cc6SDag-Erling Smørgrav 		struct addrinfo *ai;
4191a0ee8cc6SDag-Erling Smørgrav 
4192a0ee8cc6SDag-Erling Smørgrav 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
4193a0ee8cc6SDag-Erling Smørgrav 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
41944f52dfbbSDag-Erling Smørgrav 			return -1;
4195a0ee8cc6SDag-Erling Smørgrav 		}
4196a0ee8cc6SDag-Erling Smørgrav 
4197a0ee8cc6SDag-Erling Smørgrav 		/*
4198a0ee8cc6SDag-Erling Smørgrav 		 * Fake up a struct addrinfo for AF_UNIX connections.
4199a0ee8cc6SDag-Erling Smørgrav 		 * channel_connect_ctx_free() must check ai_family
4200a0ee8cc6SDag-Erling Smørgrav 		 * and use free() not freeaddirinfo() for AF_UNIX.
4201a0ee8cc6SDag-Erling Smørgrav 		 */
4202a0ee8cc6SDag-Erling Smørgrav 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
4203a0ee8cc6SDag-Erling Smørgrav 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
4204a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addr = (struct sockaddr *)(ai + 1);
4205a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addrlen = sizeof(*sunaddr);
4206a0ee8cc6SDag-Erling Smørgrav 		ai->ai_family = AF_UNIX;
42074f52dfbbSDag-Erling Smørgrav 		ai->ai_socktype = socktype;
4208a0ee8cc6SDag-Erling Smørgrav 		ai->ai_protocol = PF_UNSPEC;
4209a0ee8cc6SDag-Erling Smørgrav 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
4210a0ee8cc6SDag-Erling Smørgrav 		sunaddr->sun_family = AF_UNIX;
4211a0ee8cc6SDag-Erling Smørgrav 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
42124f52dfbbSDag-Erling Smørgrav 		cctx->aitop = ai;
4213a0ee8cc6SDag-Erling Smørgrav 	} else {
4214d4af9e69SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
42154f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
42164f52dfbbSDag-Erling Smørgrav 		hints.ai_socktype = socktype;
4217d4af9e69SDag-Erling Smørgrav 		snprintf(strport, sizeof strport, "%d", port);
42184f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
4219d93a896eSDag-Erling Smørgrav 		    != 0) {
4220d93a896eSDag-Erling Smørgrav 			if (errmsg != NULL)
4221d93a896eSDag-Erling Smørgrav 				*errmsg = ssh_gai_strerror(gaierr);
4222d93a896eSDag-Erling Smørgrav 			if (reason != NULL)
4223d93a896eSDag-Erling Smørgrav 				*reason = SSH2_OPEN_CONNECT_FAILED;
4224a0ee8cc6SDag-Erling Smørgrav 			error("connect_to %.100s: unknown host (%s)", name,
4225d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
42264f52dfbbSDag-Erling Smørgrav 			return -1;
4227d4af9e69SDag-Erling Smørgrav 		}
4228a0ee8cc6SDag-Erling Smørgrav 	}
4229d4af9e69SDag-Erling Smørgrav 
42304f52dfbbSDag-Erling Smørgrav 	cctx->host = xstrdup(name);
42314f52dfbbSDag-Erling Smørgrav 	cctx->port = port;
42324f52dfbbSDag-Erling Smørgrav 	cctx->ai = cctx->aitop;
4233d4af9e69SDag-Erling Smørgrav 
42344f52dfbbSDag-Erling Smørgrav 	if ((sock = connect_next(cctx)) == -1) {
4235d4af9e69SDag-Erling Smørgrav 		error("connect to %.100s port %d failed: %s",
4236a0ee8cc6SDag-Erling Smørgrav 		    name, port, strerror(errno));
42374f52dfbbSDag-Erling Smørgrav 		return -1;
4238d4af9e69SDag-Erling Smørgrav 	}
42394f52dfbbSDag-Erling Smørgrav 
42404f52dfbbSDag-Erling Smørgrav 	return sock;
4241d4af9e69SDag-Erling Smørgrav }
4242d4af9e69SDag-Erling Smørgrav 
4243d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */
4244d93a896eSDag-Erling Smørgrav static Channel *
42454f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port,
42464f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4247d93a896eSDag-Erling Smørgrav {
42484f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
42494f52dfbbSDag-Erling Smørgrav 	Channel *c;
42504f52dfbbSDag-Erling Smørgrav 	int sock;
42514f52dfbbSDag-Erling Smørgrav 
42524f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
42534f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
42544f52dfbbSDag-Erling Smørgrav 	    &cctx, NULL, NULL);
42554f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
42564f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
42574f52dfbbSDag-Erling Smørgrav 		return NULL;
42584f52dfbbSDag-Erling Smørgrav 	}
42594f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
42604f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
42614f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
42624f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
42634f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
42644f52dfbbSDag-Erling Smørgrav 
42654f52dfbbSDag-Erling Smørgrav 	return c;
4266d93a896eSDag-Erling Smørgrav }
4267d93a896eSDag-Erling Smørgrav 
4268ca86bcf2SDag-Erling Smørgrav /*
4269ca86bcf2SDag-Erling Smørgrav  * returns either the newly connected channel or the downstream channel
4270ca86bcf2SDag-Erling Smørgrav  * that needs to deal with this connection.
4271ca86bcf2SDag-Erling Smørgrav  */
4272d4af9e69SDag-Erling Smørgrav Channel *
42734f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
4274a0ee8cc6SDag-Erling Smørgrav     u_short listen_port, char *ctype, char *rname)
4275d4af9e69SDag-Erling Smørgrav {
42764f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4277*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
42784f52dfbbSDag-Erling Smørgrav 	u_int i;
4279*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4280d4af9e69SDag-Erling Smørgrav 
4281*190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4282*190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4283*190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm,
4284*190cef3dSDag-Erling Smørgrav 		    listen_host, listen_port, 1)) {
4285*190cef3dSDag-Erling Smørgrav 			if (perm->downstream)
4286*190cef3dSDag-Erling Smørgrav 				return perm->downstream;
4287*190cef3dSDag-Erling Smørgrav 			if (perm->port_to_connect == 0)
42884f52dfbbSDag-Erling Smørgrav 				return rdynamic_connect_prepare(ssh,
42894f52dfbbSDag-Erling Smørgrav 				    ctype, rname);
42904f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4291*190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
42924f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4293d4af9e69SDag-Erling Smørgrav 		}
4294d4af9e69SDag-Erling Smørgrav 	}
4295d4af9e69SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4296d4af9e69SDag-Erling Smørgrav 	    listen_port);
4297d4af9e69SDag-Erling Smørgrav 	return NULL;
4298d4af9e69SDag-Erling Smørgrav }
4299d4af9e69SDag-Erling Smørgrav 
4300a0ee8cc6SDag-Erling Smørgrav Channel *
43014f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path,
43024f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4303a0ee8cc6SDag-Erling Smørgrav {
43044f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4305*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
43064f52dfbbSDag-Erling Smørgrav 	u_int i;
4307*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4308a0ee8cc6SDag-Erling Smørgrav 
4309*190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4310*190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4311*190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path)) {
43124f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4313*190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
43144f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4315a0ee8cc6SDag-Erling Smørgrav 		}
4316a0ee8cc6SDag-Erling Smørgrav 	}
4317a0ee8cc6SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown path %.100s",
4318a0ee8cc6SDag-Erling Smørgrav 	    path);
4319a0ee8cc6SDag-Erling Smørgrav 	return NULL;
4320a0ee8cc6SDag-Erling Smørgrav }
4321a0ee8cc6SDag-Erling Smørgrav 
4322ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */
4323d4af9e69SDag-Erling Smørgrav Channel *
43244f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
43254f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, int *reason, const char **errmsg)
4326ca3176e7SBrian Feldman {
43274f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4328*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
43294f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
43304f52dfbbSDag-Erling Smørgrav 	Channel *c;
43314f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
43324f52dfbbSDag-Erling Smørgrav 	int sock;
4333*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4334ca3176e7SBrian Feldman 
4335*190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4336ca3176e7SBrian Feldman 	if (!permit) {
4337*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4338*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4339*190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4340ca3176e7SBrian Feldman 				permit = 1;
4341a0ee8cc6SDag-Erling Smørgrav 				break;
4342a0ee8cc6SDag-Erling Smørgrav 			}
4343ca3176e7SBrian Feldman 		}
43444f52dfbbSDag-Erling Smørgrav 	}
4345333ee039SDag-Erling Smørgrav 
4346*190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4347333ee039SDag-Erling Smørgrav 		permit_adm = 0;
4348*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4349*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4350*190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4351333ee039SDag-Erling Smørgrav 				permit_adm = 1;
4352a0ee8cc6SDag-Erling Smørgrav 				break;
4353a0ee8cc6SDag-Erling Smørgrav 			}
4354333ee039SDag-Erling Smørgrav 		}
43554f52dfbbSDag-Erling Smørgrav 	}
4356333ee039SDag-Erling Smørgrav 
4357333ee039SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4358221552e4SDag-Erling Smørgrav 		logit("Received request to connect to host %.100s port %d, "
4359ca3176e7SBrian Feldman 		    "but the request was denied.", host, port);
4360d93a896eSDag-Erling Smørgrav 		if (reason != NULL)
4361d93a896eSDag-Erling Smørgrav 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4362d4af9e69SDag-Erling Smørgrav 		return NULL;
4363ca3176e7SBrian Feldman 	}
43644f52dfbbSDag-Erling Smørgrav 
43654f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
43664f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
43674f52dfbbSDag-Erling Smørgrav 	    &cctx, reason, errmsg);
43684f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
43694f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
43704f52dfbbSDag-Erling Smørgrav 		return NULL;
43714f52dfbbSDag-Erling Smørgrav 	}
43724f52dfbbSDag-Erling Smørgrav 
43734f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
43744f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
43754f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
43764f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
43774f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
43784f52dfbbSDag-Erling Smørgrav 
43794f52dfbbSDag-Erling Smørgrav 	return c;
4380ca3176e7SBrian Feldman }
4381ca3176e7SBrian Feldman 
4382a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */
4383a0ee8cc6SDag-Erling Smørgrav Channel *
43844f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path,
43854f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4386a0ee8cc6SDag-Erling Smørgrav {
43874f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4388*190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
43894f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4390*190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4391a0ee8cc6SDag-Erling Smørgrav 
4392*190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4393a0ee8cc6SDag-Erling Smørgrav 	if (!permit) {
4394*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4395*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4396*190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4397a0ee8cc6SDag-Erling Smørgrav 				permit = 1;
4398a0ee8cc6SDag-Erling Smørgrav 				break;
4399a0ee8cc6SDag-Erling Smørgrav 			}
4400a0ee8cc6SDag-Erling Smørgrav 		}
44014f52dfbbSDag-Erling Smørgrav 	}
4402a0ee8cc6SDag-Erling Smørgrav 
4403*190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4404a0ee8cc6SDag-Erling Smørgrav 		permit_adm = 0;
4405*190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4406*190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4407*190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4408a0ee8cc6SDag-Erling Smørgrav 				permit_adm = 1;
4409a0ee8cc6SDag-Erling Smørgrav 				break;
4410a0ee8cc6SDag-Erling Smørgrav 			}
4411a0ee8cc6SDag-Erling Smørgrav 		}
44124f52dfbbSDag-Erling Smørgrav 	}
4413a0ee8cc6SDag-Erling Smørgrav 
4414a0ee8cc6SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4415a0ee8cc6SDag-Erling Smørgrav 		logit("Received request to connect to path %.100s, "
4416a0ee8cc6SDag-Erling Smørgrav 		    "but the request was denied.", path);
4417a0ee8cc6SDag-Erling Smørgrav 		return NULL;
4418a0ee8cc6SDag-Erling Smørgrav 	}
44194f52dfbbSDag-Erling Smørgrav 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
4420a0ee8cc6SDag-Erling Smørgrav }
4421a0ee8cc6SDag-Erling Smørgrav 
442221e764dfSDag-Erling Smørgrav void
44234f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh)
442421e764dfSDag-Erling Smørgrav {
44254f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
442621e764dfSDag-Erling Smørgrav 	struct winsize ws;
44274f52dfbbSDag-Erling Smørgrav 	int r;
44284f52dfbbSDag-Erling Smørgrav 	u_int i;
442921e764dfSDag-Erling Smørgrav 
44304f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
44314f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
44324f52dfbbSDag-Erling Smørgrav 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
443321e764dfSDag-Erling Smørgrav 			continue;
44344f52dfbbSDag-Erling Smørgrav 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
443521e764dfSDag-Erling Smørgrav 			continue;
44364f52dfbbSDag-Erling Smørgrav 		channel_request_start(ssh, i, "window-change", 0);
44374f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
44384f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
44394f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
44404f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
44414f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0)
44424f52dfbbSDag-Erling Smørgrav 			fatal("%s: channel %u: send window-change: %s",
44434f52dfbbSDag-Erling Smørgrav 			    __func__, i, ssh_err(r));
444421e764dfSDag-Erling Smørgrav 	}
444521e764dfSDag-Erling Smørgrav }
444621e764dfSDag-Erling Smørgrav 
44474f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
44484f52dfbbSDag-Erling Smørgrav static Channel *
44494f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
44504f52dfbbSDag-Erling Smørgrav {
44514f52dfbbSDag-Erling Smørgrav 	Channel *c;
44524f52dfbbSDag-Erling Smørgrav 	int r;
44534f52dfbbSDag-Erling Smørgrav 
44544f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
44554f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
44564f52dfbbSDag-Erling Smørgrav 	c->host_port = 0;
44574f52dfbbSDag-Erling Smørgrav 	c->path = NULL;
44584f52dfbbSDag-Erling Smørgrav 
44594f52dfbbSDag-Erling Smørgrav 	/*
44604f52dfbbSDag-Erling Smørgrav 	 * We need to open the channel before we have a FD,
44614f52dfbbSDag-Erling Smørgrav 	 * so that we can get SOCKS header from peer.
44624f52dfbbSDag-Erling Smørgrav 	 */
44634f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
44644f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
44654f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
44664f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
44674f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
44684f52dfbbSDag-Erling Smørgrav 		fatal("%s: channel %i: confirm: %s", __func__,
44694f52dfbbSDag-Erling Smørgrav 		    c->self, ssh_err(r));
44704f52dfbbSDag-Erling Smørgrav 	}
44714f52dfbbSDag-Erling Smørgrav 	return c;
44724f52dfbbSDag-Erling Smørgrav }
44734f52dfbbSDag-Erling Smørgrav 
44744f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */
44754f52dfbbSDag-Erling Smørgrav static int
44764f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c)
44774f52dfbbSDag-Erling Smørgrav {
44784f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
44794f52dfbbSDag-Erling Smørgrav 	int sock;
44804f52dfbbSDag-Erling Smørgrav 
44814f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
44824f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
44834f52dfbbSDag-Erling Smørgrav 	    NULL, &cctx, NULL, NULL);
44844f52dfbbSDag-Erling Smørgrav 	if (sock == -1)
44854f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
44864f52dfbbSDag-Erling Smørgrav 	else {
44874f52dfbbSDag-Erling Smørgrav 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
44884f52dfbbSDag-Erling Smørgrav 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
44894f52dfbbSDag-Erling Smørgrav 		c->connect_ctx = cctx;
44904f52dfbbSDag-Erling Smørgrav 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
44914f52dfbbSDag-Erling Smørgrav 	}
44924f52dfbbSDag-Erling Smørgrav 	return sock;
44934f52dfbbSDag-Erling Smørgrav }
44944f52dfbbSDag-Erling Smørgrav 
4495af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */
4496511b41d2SMark Murray 
4497511b41d2SMark Murray /*
4498511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
4499a82e551fSDag-Erling Smørgrav  * Returns 0 and a suitable display number for the DISPLAY variable
4500a82e551fSDag-Erling Smørgrav  * stored in display_numberp , or -1 if an error occurs.
4501511b41d2SMark Murray  */
4502af12a3e7SDag-Erling Smørgrav int
45034f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
45044f52dfbbSDag-Erling Smørgrav     int x11_use_localhost, int single_connection,
45054f52dfbbSDag-Erling Smørgrav     u_int *display_numberp, int **chanids)
4506511b41d2SMark Murray {
4507af12a3e7SDag-Erling Smørgrav 	Channel *nc = NULL;
4508511b41d2SMark Murray 	int display_number, sock;
4509511b41d2SMark Murray 	u_short port;
4510511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
4511511b41d2SMark Murray 	char strport[NI_MAXSERV];
4512511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
4513511b41d2SMark Murray 
4514b74df5b2SDag-Erling Smørgrav 	if (chanids == NULL)
4515b74df5b2SDag-Erling Smørgrav 		return -1;
4516b74df5b2SDag-Erling Smørgrav 
4517511b41d2SMark Murray 	for (display_number = x11_display_offset;
4518511b41d2SMark Murray 	    display_number < MAX_DISPLAYS;
4519511b41d2SMark Murray 	    display_number++) {
4520511b41d2SMark Murray 		port = 6000 + display_number;
4521511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
45224f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
4523af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
4524511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
4525511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
45264f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(NULL, strport,
45274f52dfbbSDag-Erling Smørgrav 		    &hints, &aitop)) != 0) {
4528d4af9e69SDag-Erling Smørgrav 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
4529af12a3e7SDag-Erling Smørgrav 			return -1;
4530511b41d2SMark Murray 		}
4531511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
45324f52dfbbSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
45334f52dfbbSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
4534511b41d2SMark Murray 				continue;
4535221552e4SDag-Erling Smørgrav 			sock = socket(ai->ai_family, ai->ai_socktype,
4536221552e4SDag-Erling Smørgrav 			    ai->ai_protocol);
4537511b41d2SMark Murray 			if (sock < 0) {
45388ad9b54aSDag-Erling Smørgrav 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
45398ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT
45408ad9b54aSDag-Erling Smørgrav 				    && (errno != EPFNOSUPPORT)
45418ad9b54aSDag-Erling Smørgrav #endif
45428ad9b54aSDag-Erling Smørgrav 				    ) {
4543511b41d2SMark Murray 					error("socket: %.100s", strerror(errno));
454421e764dfSDag-Erling Smørgrav 					freeaddrinfo(aitop);
4545af12a3e7SDag-Erling Smørgrav 					return -1;
4546989dd127SDag-Erling Smørgrav 				} else {
4547989dd127SDag-Erling Smørgrav 					debug("x11_create_display_inet: Socket family %d not supported",
4548989dd127SDag-Erling Smørgrav 						 ai->ai_family);
4549989dd127SDag-Erling Smørgrav 					continue;
4550511b41d2SMark Murray 				}
4551989dd127SDag-Erling Smørgrav 			}
4552b15c8340SDag-Erling Smørgrav 			if (ai->ai_family == AF_INET6)
4553b15c8340SDag-Erling Smørgrav 				sock_set_v6only(sock);
4554d4af9e69SDag-Erling Smørgrav 			if (x11_use_localhost)
455547dd1d1bSDag-Erling Smørgrav 				set_reuseaddr(sock);
4556511b41d2SMark Murray 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
45574f52dfbbSDag-Erling Smørgrav 				debug2("%s: bind port %d: %.100s", __func__,
45584f52dfbbSDag-Erling Smørgrav 				    port, strerror(errno));
4559511b41d2SMark Murray 				close(sock);
45604f52dfbbSDag-Erling Smørgrav 				for (n = 0; n < num_socks; n++)
4561511b41d2SMark Murray 					close(socks[n]);
4562511b41d2SMark Murray 				num_socks = 0;
4563511b41d2SMark Murray 				break;
4564511b41d2SMark Murray 			}
4565511b41d2SMark Murray 			socks[num_socks++] = sock;
4566511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
4567511b41d2SMark Murray 				break;
4568511b41d2SMark Murray 		}
4569ca3176e7SBrian Feldman 		freeaddrinfo(aitop);
4570511b41d2SMark Murray 		if (num_socks > 0)
4571511b41d2SMark Murray 			break;
4572511b41d2SMark Murray 	}
4573511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
4574511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
4575af12a3e7SDag-Erling Smørgrav 		return -1;
4576511b41d2SMark Murray 	}
4577511b41d2SMark Murray 	/* Start listening for connections on the socket. */
4578511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
4579511b41d2SMark Murray 		sock = socks[n];
4580476cd3b2SDag-Erling Smørgrav 		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
4581511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
4582511b41d2SMark Murray 			close(sock);
4583af12a3e7SDag-Erling Smørgrav 			return -1;
4584511b41d2SMark Murray 		}
4585511b41d2SMark Murray 	}
4586511b41d2SMark Murray 
4587511b41d2SMark Murray 	/* Allocate a channel for each socket. */
4588333ee039SDag-Erling Smørgrav 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
4589511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
4590511b41d2SMark Murray 		sock = socks[n];
45914f52dfbbSDag-Erling Smørgrav 		nc = channel_new(ssh, "x11 listener",
4592a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
4593a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
4594221552e4SDag-Erling Smørgrav 		    0, "X11 inet listener", 1);
4595af12a3e7SDag-Erling Smørgrav 		nc->single_connection = single_connection;
4596d4ecd108SDag-Erling Smørgrav 		(*chanids)[n] = nc->self;
4597511b41d2SMark Murray 	}
4598d4ecd108SDag-Erling Smørgrav 	(*chanids)[n] = -1;
4599511b41d2SMark Murray 
4600af12a3e7SDag-Erling Smørgrav 	/* Return the display number for the DISPLAY environment variable. */
4601a82e551fSDag-Erling Smørgrav 	*display_numberp = display_number;
46024f52dfbbSDag-Erling Smørgrav 	return 0;
4603511b41d2SMark Murray }
4604511b41d2SMark Murray 
4605af12a3e7SDag-Erling Smørgrav static int
4606cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname)
4607511b41d2SMark Murray {
4608511b41d2SMark Murray 	int sock;
4609511b41d2SMark Murray 	struct sockaddr_un addr;
4610511b41d2SMark Murray 
4611511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
4612511b41d2SMark Murray 	if (sock < 0)
4613511b41d2SMark Murray 		error("socket: %.100s", strerror(errno));
4614511b41d2SMark Murray 	memset(&addr, 0, sizeof(addr));
4615511b41d2SMark Murray 	addr.sun_family = AF_UNIX;
4616cce7d346SDag-Erling Smørgrav 	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
4617511b41d2SMark Murray 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
4618511b41d2SMark Murray 		return sock;
4619511b41d2SMark Murray 	close(sock);
4620511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
4621511b41d2SMark Murray 	return -1;
4622511b41d2SMark Murray }
4623511b41d2SMark Murray 
4624cce7d346SDag-Erling Smørgrav static int
4625cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr)
4626cce7d346SDag-Erling Smørgrav {
4627cce7d346SDag-Erling Smørgrav 	char buf[1024];
4628cce7d346SDag-Erling Smørgrav 	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
4629cce7d346SDag-Erling Smørgrav 	return connect_local_xsocket_path(buf);
4630cce7d346SDag-Erling Smørgrav }
4631cce7d346SDag-Erling Smørgrav 
4632d93a896eSDag-Erling Smørgrav #ifdef __APPLE__
4633d93a896eSDag-Erling Smørgrav static int
4634d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen)
4635d93a896eSDag-Erling Smørgrav {
4636d93a896eSDag-Erling Smørgrav 	struct stat sbuf;
4637d93a896eSDag-Erling Smørgrav 
4638d93a896eSDag-Erling Smørgrav 	if (strlcpy(path, display, pathlen) >= pathlen) {
4639d93a896eSDag-Erling Smørgrav 		error("%s: display path too long", __func__);
4640d93a896eSDag-Erling Smørgrav 		return 0;
4641d93a896eSDag-Erling Smørgrav 	}
4642d93a896eSDag-Erling Smørgrav 	if (display[0] != '/')
4643d93a896eSDag-Erling Smørgrav 		return 0;
4644d93a896eSDag-Erling Smørgrav 	if (stat(path, &sbuf) == 0) {
4645d93a896eSDag-Erling Smørgrav 		return 1;
4646d93a896eSDag-Erling Smørgrav 	} else {
4647d93a896eSDag-Erling Smørgrav 		char *dot = strrchr(path, '.');
4648d93a896eSDag-Erling Smørgrav 		if (dot != NULL) {
4649d93a896eSDag-Erling Smørgrav 			*dot = '\0';
4650d93a896eSDag-Erling Smørgrav 			if (stat(path, &sbuf) == 0) {
4651d93a896eSDag-Erling Smørgrav 				return 1;
4652d93a896eSDag-Erling Smørgrav 			}
4653d93a896eSDag-Erling Smørgrav 		}
4654d93a896eSDag-Erling Smørgrav 	}
4655d93a896eSDag-Erling Smørgrav 	return 0;
4656d93a896eSDag-Erling Smørgrav }
4657d93a896eSDag-Erling Smørgrav #endif
4658d93a896eSDag-Erling Smørgrav 
4659a04a10f8SKris Kennaway int
46604f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh)
4661511b41d2SMark Murray {
4662333ee039SDag-Erling Smørgrav 	u_int display_number;
4663511b41d2SMark Murray 	const char *display;
4664a04a10f8SKris Kennaway 	char buf[1024], *cp;
4665511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
4666511b41d2SMark Murray 	char strport[NI_MAXSERV];
4667333ee039SDag-Erling Smørgrav 	int gaierr, sock = 0;
4668511b41d2SMark Murray 
4669511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
4670511b41d2SMark Murray 	display = getenv("DISPLAY");
4671511b41d2SMark Murray 	if (!display) {
4672511b41d2SMark Murray 		error("DISPLAY not set.");
4673a04a10f8SKris Kennaway 		return -1;
4674511b41d2SMark Murray 	}
4675511b41d2SMark Murray 	/*
4676511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
4677511b41d2SMark Murray 	 * connection to the real X server.
4678511b41d2SMark Murray 	 */
4679511b41d2SMark Murray 
4680cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
4681d93a896eSDag-Erling Smørgrav 	/* Check if display is a path to a socket (as set by launchd). */
4682d93a896eSDag-Erling Smørgrav 	{
4683d93a896eSDag-Erling Smørgrav 		char path[PATH_MAX];
4684d93a896eSDag-Erling Smørgrav 
4685d93a896eSDag-Erling Smørgrav 		if (is_path_to_xsocket(display, path, sizeof(path))) {
4686d93a896eSDag-Erling Smørgrav 			debug("x11_connect_display: $DISPLAY is launchd");
4687d93a896eSDag-Erling Smørgrav 
4688d93a896eSDag-Erling Smørgrav 			/* Create a socket. */
4689d93a896eSDag-Erling Smørgrav 			sock = connect_local_xsocket_path(path);
4690cce7d346SDag-Erling Smørgrav 			if (sock < 0)
4691cce7d346SDag-Erling Smørgrav 				return -1;
4692cce7d346SDag-Erling Smørgrav 
4693cce7d346SDag-Erling Smørgrav 			/* OK, we now have a connection to the display. */
4694cce7d346SDag-Erling Smørgrav 			return sock;
4695cce7d346SDag-Erling Smørgrav 		}
4696d93a896eSDag-Erling Smørgrav 	}
4697cce7d346SDag-Erling Smørgrav #endif
4698511b41d2SMark Murray 	/*
4699511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
4700511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
4701511b41d2SMark Murray 	 */
4702511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
4703511b41d2SMark Murray 	    display[0] == ':') {
4704511b41d2SMark Murray 		/* Connect to the unix domain socket. */
47054f52dfbbSDag-Erling Smørgrav 		if (sscanf(strrchr(display, ':') + 1, "%u",
47064f52dfbbSDag-Erling Smørgrav 		    &display_number) != 1) {
47074f52dfbbSDag-Erling Smørgrav 			error("Could not parse display number from DISPLAY: "
47084f52dfbbSDag-Erling Smørgrav 			    "%.100s", display);
4709a04a10f8SKris Kennaway 			return -1;
4710511b41d2SMark Murray 		}
4711511b41d2SMark Murray 		/* Create a socket. */
4712511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
4713511b41d2SMark Murray 		if (sock < 0)
4714a04a10f8SKris Kennaway 			return -1;
4715511b41d2SMark Murray 
4716511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
4717a04a10f8SKris Kennaway 		return sock;
4718511b41d2SMark Murray 	}
4719511b41d2SMark Murray 	/*
4720511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
4721511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
4722511b41d2SMark Murray 	 */
4723af12a3e7SDag-Erling Smørgrav 	strlcpy(buf, display, sizeof(buf));
4724511b41d2SMark Murray 	cp = strchr(buf, ':');
4725511b41d2SMark Murray 	if (!cp) {
4726511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
4727a04a10f8SKris Kennaway 		return -1;
4728511b41d2SMark Murray 	}
4729511b41d2SMark Murray 	*cp = 0;
47304f52dfbbSDag-Erling Smørgrav 	/*
47314f52dfbbSDag-Erling Smørgrav 	 * buf now contains the host name.  But first we parse the
47324f52dfbbSDag-Erling Smørgrav 	 * display number.
47334f52dfbbSDag-Erling Smørgrav 	 */
4734333ee039SDag-Erling Smørgrav 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
4735511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
4736511b41d2SMark Murray 		    display);
4737a04a10f8SKris Kennaway 		return -1;
4738511b41d2SMark Murray 	}
4739511b41d2SMark Murray 
4740511b41d2SMark Murray 	/* Look up the host address */
4741511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
47424f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
4743511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
4744333ee039SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
4745511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
4746d4af9e69SDag-Erling Smørgrav 		error("%.100s: unknown host. (%s)", buf,
4747d4af9e69SDag-Erling Smørgrav 		ssh_gai_strerror(gaierr));
4748a04a10f8SKris Kennaway 		return -1;
4749511b41d2SMark Murray 	}
4750511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
4751511b41d2SMark Murray 		/* Create a socket. */
4752221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
4753511b41d2SMark Murray 		if (sock < 0) {
4754221552e4SDag-Erling Smørgrav 			debug2("socket: %.100s", strerror(errno));
4755511b41d2SMark Murray 			continue;
4756511b41d2SMark Murray 		}
4757511b41d2SMark Murray 		/* Connect it to the display. */
4758511b41d2SMark Murray 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
4759333ee039SDag-Erling Smørgrav 			debug2("connect %.100s port %u: %.100s", buf,
4760a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
4761511b41d2SMark Murray 			close(sock);
4762511b41d2SMark Murray 			continue;
4763511b41d2SMark Murray 		}
4764511b41d2SMark Murray 		/* Success */
4765511b41d2SMark Murray 		break;
4766a04a10f8SKris Kennaway 	}
4767511b41d2SMark Murray 	freeaddrinfo(aitop);
4768511b41d2SMark Murray 	if (!ai) {
47694f52dfbbSDag-Erling Smørgrav 		error("connect %.100s port %u: %.100s", buf,
47704f52dfbbSDag-Erling Smørgrav 		    6000 + display_number, strerror(errno));
4771a04a10f8SKris Kennaway 		return -1;
4772511b41d2SMark Murray 	}
4773af12a3e7SDag-Erling Smørgrav 	set_nodelay(sock);
4774a04a10f8SKris Kennaway 	return sock;
4775a04a10f8SKris Kennaway }
4776511b41d2SMark Murray 
4777a04a10f8SKris Kennaway /*
4778511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
4779511b41d2SMark Murray  * data, and enables authentication spoofing.
4780af12a3e7SDag-Erling Smørgrav  * This should be called in the client only.
4781511b41d2SMark Murray  */
4782511b41d2SMark Murray void
47834f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
47844f52dfbbSDag-Erling Smørgrav     const char *disp, const char *proto, const char *data, int want_reply)
4785511b41d2SMark Murray {
47864f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4787ca3176e7SBrian Feldman 	u_int data_len = (u_int) strlen(data) / 2;
4788d4ecd108SDag-Erling Smørgrav 	u_int i, value;
4789511b41d2SMark Murray 	const char *cp;
47904f52dfbbSDag-Erling Smørgrav 	char *new_data;
47914f52dfbbSDag-Erling Smørgrav 	int r, screen_number;
4792511b41d2SMark Murray 
47934f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_display == NULL)
47944f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_display = xstrdup(disp);
47954f52dfbbSDag-Erling Smørgrav 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
4796d4ecd108SDag-Erling Smørgrav 		error("x11_request_forwarding_with_spoofing: different "
4797d4ecd108SDag-Erling Smørgrav 		    "$DISPLAY already forwarded");
4798d4ecd108SDag-Erling Smørgrav 		return;
4799d4ecd108SDag-Erling Smørgrav 	}
4800d4ecd108SDag-Erling Smørgrav 
4801d4ecd108SDag-Erling Smørgrav 	cp = strchr(disp, ':');
4802511b41d2SMark Murray 	if (cp)
4803511b41d2SMark Murray 		cp = strchr(cp, '.');
4804511b41d2SMark Murray 	if (cp)
4805333ee039SDag-Erling Smørgrav 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
4806511b41d2SMark Murray 	else
4807511b41d2SMark Murray 		screen_number = 0;
4808511b41d2SMark Murray 
48094f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_proto == NULL) {
4810511b41d2SMark Murray 		/* Save protocol name. */
48114f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_proto = xstrdup(proto);
4812ca86bcf2SDag-Erling Smørgrav 
4813ca86bcf2SDag-Erling Smørgrav 		/* Extract real authentication data. */
48144f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data = xmalloc(data_len);
4815511b41d2SMark Murray 		for (i = 0; i < data_len; i++) {
4816511b41d2SMark Murray 			if (sscanf(data + 2 * i, "%2x", &value) != 1)
4817d4ecd108SDag-Erling Smørgrav 				fatal("x11_request_forwarding: bad "
4818d4ecd108SDag-Erling Smørgrav 				    "authentication data: %.100s", data);
48194f52dfbbSDag-Erling Smørgrav 			sc->x11_saved_data[i] = value;
4820511b41d2SMark Murray 		}
48214f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data_len = data_len;
4822ca86bcf2SDag-Erling Smørgrav 
4823ca86bcf2SDag-Erling Smørgrav 		/* Generate fake data of the same length. */
48244f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data = xmalloc(data_len);
48254f52dfbbSDag-Erling Smørgrav 		arc4random_buf(sc->x11_fake_data, data_len);
48264f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data_len = data_len;
4827d4ecd108SDag-Erling Smørgrav 	}
4828511b41d2SMark Murray 
4829511b41d2SMark Murray 	/* Convert the fake data into hex. */
48304f52dfbbSDag-Erling Smørgrav 	new_data = tohex(sc->x11_fake_data, data_len);
4831511b41d2SMark Murray 
4832511b41d2SMark Murray 	/* Send the request packet. */
48334f52dfbbSDag-Erling Smørgrav 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
48344f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
48354f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
48364f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
48374f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
48384f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0 ||
48394f52dfbbSDag-Erling Smørgrav 	    (r = ssh_packet_write_wait(ssh)) != 0)
48404f52dfbbSDag-Erling Smørgrav 		fatal("%s: send x11-req: %s", __func__, ssh_err(r));
4841e4a9863fSDag-Erling Smørgrav 	free(new_data);
4842511b41d2SMark Murray }
4843