xref: /freebsd/crypto/openssh/channels.c (revision f374ba41f55c1a127303d92d830dd58eef2f5243)
1*f374ba41SEd Maste /* $OpenBSD: channels.c,v 1.427 2023/01/18 02:00:10 djm 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>
601323ec57SEd Maste #ifdef HAVE_POLL_H
611323ec57SEd Maste #include <poll.h>
621323ec57SEd Maste #endif
634f52dfbbSDag-Erling Smørgrav #include <stdarg.h>
64bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H
65bc5531deSDag-Erling Smørgrav # include <stdint.h>
66bc5531deSDag-Erling Smørgrav #endif
67333ee039SDag-Erling Smørgrav #include <stdio.h>
68333ee039SDag-Erling Smørgrav #include <stdlib.h>
69333ee039SDag-Erling Smørgrav #include <string.h>
70333ee039SDag-Erling Smørgrav #include <termios.h>
71333ee039SDag-Erling Smørgrav #include <unistd.h>
72333ee039SDag-Erling Smørgrav 
73d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
74333ee039SDag-Erling Smørgrav #include "xmalloc.h"
75ca3176e7SBrian Feldman #include "ssh.h"
76ca3176e7SBrian Feldman #include "ssh2.h"
77ca86bcf2SDag-Erling Smørgrav #include "ssherr.h"
784f52dfbbSDag-Erling Smørgrav #include "sshbuf.h"
79ca3176e7SBrian Feldman #include "packet.h"
80ca3176e7SBrian Feldman #include "log.h"
81ca3176e7SBrian Feldman #include "misc.h"
82ca3176e7SBrian Feldman #include "channels.h"
83ca3176e7SBrian Feldman #include "compat.h"
84ca3176e7SBrian Feldman #include "canohost.h"
85190cef3dSDag-Erling Smørgrav #include "sshkey.h"
86b66f2d16SKris Kennaway #include "authfd.h"
87af12a3e7SDag-Erling Smørgrav #include "pathnames.h"
88190cef3dSDag-Erling Smørgrav #include "match.h"
89511b41d2SMark Murray 
901323ec57SEd Maste /* XXX remove once we're satisfied there's no lurking bugs */
911323ec57SEd Maste /* #define DEBUG_CHANNEL_POLL 1 */
921323ec57SEd Maste 
934f52dfbbSDag-Erling Smørgrav /* -- agent forwarding */
944f52dfbbSDag-Erling Smørgrav #define	NUM_SOCKS	10
95511b41d2SMark Murray 
96af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
974f52dfbbSDag-Erling Smørgrav /* special-case port number meaning allow any port */
984f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT	0
994f52dfbbSDag-Erling Smørgrav 
1004f52dfbbSDag-Erling Smørgrav /* special-case wildcard meaning allow any host */
1014f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST	"*"
1024f52dfbbSDag-Erling Smørgrav 
1034f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */
1044f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */
1054f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS  1000
106511b41d2SMark Murray 
1071323ec57SEd Maste /* Per-channel callback for pre/post IO actions */
1081323ec57SEd Maste typedef void chan_fn(struct ssh *, Channel *c);
109190cef3dSDag-Erling Smørgrav 
110511b41d2SMark Murray /*
111511b41d2SMark Murray  * Data structure for storing which hosts are permitted for forward requests.
112511b41d2SMark Murray  * The local sides of any remote forwards are stored in this array to prevent
113511b41d2SMark Murray  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
114511b41d2SMark Murray  * network (which might be behind a firewall).
115511b41d2SMark Murray  */
116a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */
117a0ee8cc6SDag-Erling Smørgrav /*      Overload host_to_connect; we could just make this match Forward */
118a0ee8cc6SDag-Erling Smørgrav /*	XXX - can we use listen_host instead of listen_path? */
119190cef3dSDag-Erling Smørgrav struct permission {
120a04a10f8SKris Kennaway 	char *host_to_connect;		/* Connect to 'host'. */
121a0ee8cc6SDag-Erling Smørgrav 	int port_to_connect;		/* Connect to 'port'. */
122a0ee8cc6SDag-Erling Smørgrav 	char *listen_host;		/* Remote side should listen address. */
123a0ee8cc6SDag-Erling Smørgrav 	char *listen_path;		/* Remote side should listen path. */
124a0ee8cc6SDag-Erling Smørgrav 	int listen_port;		/* Remote side should listen port. */
125ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;		/* Downstream mux*/
126190cef3dSDag-Erling Smørgrav };
127511b41d2SMark Murray 
128190cef3dSDag-Erling Smørgrav /*
129190cef3dSDag-Erling Smørgrav  * Stores the forwarding permission state for a single direction (local or
130190cef3dSDag-Erling Smørgrav  * remote).
131190cef3dSDag-Erling Smørgrav  */
132190cef3dSDag-Erling Smørgrav struct permission_set {
133190cef3dSDag-Erling Smørgrav 	/*
134190cef3dSDag-Erling Smørgrav 	 * List of all local permitted host/port pairs to allow for the
135190cef3dSDag-Erling Smørgrav 	 * user.
136190cef3dSDag-Erling Smørgrav 	 */
137190cef3dSDag-Erling Smørgrav 	u_int num_permitted_user;
138190cef3dSDag-Erling Smørgrav 	struct permission *permitted_user;
139190cef3dSDag-Erling Smørgrav 
140190cef3dSDag-Erling Smørgrav 	/*
141190cef3dSDag-Erling Smørgrav 	 * List of all permitted host/port pairs to allow for the admin.
142190cef3dSDag-Erling Smørgrav 	 */
143190cef3dSDag-Erling Smørgrav 	u_int num_permitted_admin;
144190cef3dSDag-Erling Smørgrav 	struct permission *permitted_admin;
145190cef3dSDag-Erling Smørgrav 
146190cef3dSDag-Erling Smørgrav 	/*
147190cef3dSDag-Erling Smørgrav 	 * If this is true, all opens/listens are permitted.  This is the
148190cef3dSDag-Erling Smørgrav 	 * case on the server on which we have to trust the client anyway,
149190cef3dSDag-Erling Smørgrav 	 * and the user could do anything after logging in.
150190cef3dSDag-Erling Smørgrav 	 */
151190cef3dSDag-Erling Smørgrav 	int all_permitted;
152190cef3dSDag-Erling Smørgrav };
153af12a3e7SDag-Erling Smørgrav 
154*f374ba41SEd Maste /* Used to record timeouts per channel type */
155*f374ba41SEd Maste struct ssh_channel_timeout {
156*f374ba41SEd Maste 	char *type_pattern;
157*f374ba41SEd Maste 	u_int timeout_secs;
158*f374ba41SEd Maste };
159*f374ba41SEd Maste 
1604f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */
1614f52dfbbSDag-Erling Smørgrav struct ssh_channels {
1624f52dfbbSDag-Erling Smørgrav 	/*
1634f52dfbbSDag-Erling Smørgrav 	 * Pointer to an array containing all allocated channels.  The array
1644f52dfbbSDag-Erling Smørgrav 	 * is dynamically extended as needed.
1654f52dfbbSDag-Erling Smørgrav 	 */
1664f52dfbbSDag-Erling Smørgrav 	Channel **channels;
167076ad2f8SDag-Erling Smørgrav 
168511b41d2SMark Murray 	/*
1694f52dfbbSDag-Erling Smørgrav 	 * Size of the channel array.  All slots of the array must always be
1704f52dfbbSDag-Erling Smørgrav 	 * initialized (at least the type field); unused slots set to NULL
171511b41d2SMark Murray 	 */
1724f52dfbbSDag-Erling Smørgrav 	u_int channels_alloc;
173511b41d2SMark Murray 
1744f52dfbbSDag-Erling Smørgrav 	/*
1751323ec57SEd Maste 	 * 'channel_pre*' are called just before IO to add any bits
1761323ec57SEd Maste 	 * relevant to channels in the c->io_want bitmasks.
1774f52dfbbSDag-Erling Smørgrav 	 *
1784f52dfbbSDag-Erling Smørgrav 	 * 'channel_post*': perform any appropriate operations for
1791323ec57SEd Maste 	 * channels which have c->io_ready events pending.
1804f52dfbbSDag-Erling Smørgrav 	 */
1814f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_pre;
1824f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_post;
1834f52dfbbSDag-Erling Smørgrav 
1844f52dfbbSDag-Erling Smørgrav 	/* -- tcp forwarding */
185190cef3dSDag-Erling Smørgrav 	struct permission_set local_perms;
186190cef3dSDag-Erling Smørgrav 	struct permission_set remote_perms;
187af12a3e7SDag-Erling Smørgrav 
188af12a3e7SDag-Erling Smørgrav 	/* -- X11 forwarding */
189af12a3e7SDag-Erling Smørgrav 
190d4ecd108SDag-Erling Smørgrav 	/* Saved X11 local (client) display. */
1914f52dfbbSDag-Erling Smørgrav 	char *x11_saved_display;
192d4ecd108SDag-Erling Smørgrav 
193af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication protocol name. */
1944f52dfbbSDag-Erling Smørgrav 	char *x11_saved_proto;
195af12a3e7SDag-Erling Smørgrav 
196af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication data.  This is the real data. */
1974f52dfbbSDag-Erling Smørgrav 	char *x11_saved_data;
1984f52dfbbSDag-Erling Smørgrav 	u_int x11_saved_data_len;
199af12a3e7SDag-Erling Smørgrav 
200557f75e5SDag-Erling Smørgrav 	/* Deadline after which all X11 connections are refused */
2014f52dfbbSDag-Erling Smørgrav 	u_int x11_refuse_time;
202557f75e5SDag-Erling Smørgrav 
203af12a3e7SDag-Erling Smørgrav 	/*
2044f52dfbbSDag-Erling Smørgrav 	 * Fake X11 authentication data.  This is what the server will be
2054f52dfbbSDag-Erling Smørgrav 	 * sending us; we should replace any occurrences of this by the
2064f52dfbbSDag-Erling Smørgrav 	 * real data.
207af12a3e7SDag-Erling Smørgrav 	 */
2084f52dfbbSDag-Erling Smørgrav 	u_char *x11_fake_data;
2094f52dfbbSDag-Erling Smørgrav 	u_int x11_fake_data_len;
210af12a3e7SDag-Erling Smørgrav 
211ca3176e7SBrian Feldman 	/* AF_UNSPEC or AF_INET or AF_INET6 */
2124f52dfbbSDag-Erling Smørgrav 	int IPv4or6;
213*f374ba41SEd Maste 
214*f374ba41SEd Maste 	/* Channel timeouts by type */
215*f374ba41SEd Maste 	struct ssh_channel_timeout *timeouts;
216*f374ba41SEd Maste 	size_t ntimeouts;
2174f52dfbbSDag-Erling Smørgrav };
218ca3176e7SBrian Feldman 
219af12a3e7SDag-Erling Smørgrav /* helper */
2204f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
221ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host);
222ca3176e7SBrian Feldman 
223d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */
224d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *);
225d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *);
2264f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
2274f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *);
2284f52dfbbSDag-Erling Smørgrav 
2294f52dfbbSDag-Erling Smørgrav /* Setup helper */
2304f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc);
231d4af9e69SDag-Erling Smørgrav 
232af12a3e7SDag-Erling Smørgrav /* -- channel core */
233a04a10f8SKris Kennaway 
2344f52dfbbSDag-Erling Smørgrav void
2354f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh)
2364f52dfbbSDag-Erling Smørgrav {
2374f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc;
2384f52dfbbSDag-Erling Smørgrav 
23919261079SEd Maste 	if ((sc = calloc(1, sizeof(*sc))) == NULL)
24019261079SEd Maste 		fatal_f("allocation failed");
2414f52dfbbSDag-Erling Smørgrav 	sc->channels_alloc = 10;
2424f52dfbbSDag-Erling Smørgrav 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2434f52dfbbSDag-Erling Smørgrav 	sc->IPv4or6 = AF_UNSPEC;
2444f52dfbbSDag-Erling Smørgrav 	channel_handler_init(sc);
2454f52dfbbSDag-Erling Smørgrav 
2464f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt = sc;
2474f52dfbbSDag-Erling Smørgrav }
2484f52dfbbSDag-Erling Smørgrav 
249a04a10f8SKris Kennaway Channel *
2504f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id)
251a04a10f8SKris Kennaway {
252a04a10f8SKris Kennaway 	Channel *c;
253af12a3e7SDag-Erling Smørgrav 
2544f52dfbbSDag-Erling Smørgrav 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
25519261079SEd Maste 		logit_f("%d: bad id", id);
256a04a10f8SKris Kennaway 		return NULL;
257a04a10f8SKris Kennaway 	}
2584f52dfbbSDag-Erling Smørgrav 	c = ssh->chanctxt->channels[id];
259af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
26019261079SEd Maste 		logit_f("%d: bad id: channel free", id);
261a04a10f8SKris Kennaway 		return NULL;
262a04a10f8SKris Kennaway 	}
263a04a10f8SKris Kennaway 	return c;
264a04a10f8SKris Kennaway }
265a04a10f8SKris Kennaway 
266ca86bcf2SDag-Erling Smørgrav Channel *
2674f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id)
268ca86bcf2SDag-Erling Smørgrav {
269ca86bcf2SDag-Erling Smørgrav 	Channel *c;
270ca86bcf2SDag-Erling Smørgrav 	u_int i;
271ca86bcf2SDag-Erling Smørgrav 
2724f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2734f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
2744f52dfbbSDag-Erling Smørgrav 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
275ca86bcf2SDag-Erling Smørgrav 			return c;
276ca86bcf2SDag-Erling Smørgrav 	}
277ca86bcf2SDag-Erling Smørgrav 	return NULL;
278ca86bcf2SDag-Erling Smørgrav }
279ca86bcf2SDag-Erling Smørgrav 
280a04a10f8SKris Kennaway /*
281b74df5b2SDag-Erling Smørgrav  * Returns the channel if it is allowed to receive protocol messages.
282b74df5b2SDag-Erling Smørgrav  * Private channels, like listening sockets, may not receive messages.
283b74df5b2SDag-Erling Smørgrav  */
284b74df5b2SDag-Erling Smørgrav Channel *
2854f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id)
286b74df5b2SDag-Erling Smørgrav {
287b74df5b2SDag-Erling Smørgrav 	Channel *c;
288b74df5b2SDag-Erling Smørgrav 
2894f52dfbbSDag-Erling Smørgrav 	if ((c = channel_by_id(ssh, id)) == NULL)
2904f52dfbbSDag-Erling Smørgrav 		return NULL;
291b74df5b2SDag-Erling Smørgrav 
292b74df5b2SDag-Erling Smørgrav 	switch (c->type) {
293b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_X11_OPEN:
294b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_LARVAL:
295b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_CONNECTING:
296b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_DYNAMIC:
2974f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_OPEN:
2984f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_FINISH:
299b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPENING:
300b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPEN:
301e4a9863fSDag-Erling Smørgrav 	case SSH_CHANNEL_ABANDONED:
302ca86bcf2SDag-Erling Smørgrav 	case SSH_CHANNEL_MUX_PROXY:
3034f52dfbbSDag-Erling Smørgrav 		return c;
304b74df5b2SDag-Erling Smørgrav 	}
305b74df5b2SDag-Erling Smørgrav 	logit("Non-public channel %d, type %d.", id, c->type);
3064f52dfbbSDag-Erling Smørgrav 	return NULL;
307b74df5b2SDag-Erling Smørgrav }
308b74df5b2SDag-Erling Smørgrav 
309b74df5b2SDag-Erling Smørgrav /*
310*f374ba41SEd Maste  * Add a timeout for open channels whose c->ctype (or c->xctype if it is set)
311*f374ba41SEd Maste  * match type_pattern.
312*f374ba41SEd Maste  */
313*f374ba41SEd Maste void
314*f374ba41SEd Maste channel_add_timeout(struct ssh *ssh, const char *type_pattern,
315*f374ba41SEd Maste     u_int timeout_secs)
316*f374ba41SEd Maste {
317*f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
318*f374ba41SEd Maste 
319*f374ba41SEd Maste 	debug2_f("channel type \"%s\" timeout %u seconds",
320*f374ba41SEd Maste 	    type_pattern, timeout_secs);
321*f374ba41SEd Maste 	sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts,
322*f374ba41SEd Maste 	    sc->ntimeouts + 1, sizeof(*sc->timeouts));
323*f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern);
324*f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs;
325*f374ba41SEd Maste 	sc->ntimeouts++;
326*f374ba41SEd Maste }
327*f374ba41SEd Maste 
328*f374ba41SEd Maste /* Clears all previously-added channel timeouts */
329*f374ba41SEd Maste void
330*f374ba41SEd Maste channel_clear_timeouts(struct ssh *ssh)
331*f374ba41SEd Maste {
332*f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
333*f374ba41SEd Maste 	size_t i;
334*f374ba41SEd Maste 
335*f374ba41SEd Maste 	debug3_f("clearing");
336*f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++)
337*f374ba41SEd Maste 		free(sc->timeouts[i].type_pattern);
338*f374ba41SEd Maste 	free(sc->timeouts);
339*f374ba41SEd Maste 	sc->timeouts = NULL;
340*f374ba41SEd Maste 	sc->ntimeouts = 0;
341*f374ba41SEd Maste }
342*f374ba41SEd Maste 
343*f374ba41SEd Maste static u_int
344*f374ba41SEd Maste lookup_timeout(struct ssh *ssh, const char *type)
345*f374ba41SEd Maste {
346*f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
347*f374ba41SEd Maste 	size_t i;
348*f374ba41SEd Maste 
349*f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++) {
350*f374ba41SEd Maste 		if (match_pattern(type, sc->timeouts[i].type_pattern))
351*f374ba41SEd Maste 			return sc->timeouts[i].timeout_secs;
352*f374ba41SEd Maste 	}
353*f374ba41SEd Maste 
354*f374ba41SEd Maste 	return 0;
355*f374ba41SEd Maste }
356*f374ba41SEd Maste 
357*f374ba41SEd Maste /*
358*f374ba41SEd Maste  * Sets "extended type" of a channel; used by session layer to add additional
359*f374ba41SEd Maste  * information about channel types (e.g. shell, login, subsystem) that can then
360*f374ba41SEd Maste  * be used to select timeouts.
361*f374ba41SEd Maste  * Will reset c->inactive_deadline as a side-effect.
362*f374ba41SEd Maste  */
363*f374ba41SEd Maste void
364*f374ba41SEd Maste channel_set_xtype(struct ssh *ssh, int id, const char *xctype)
365*f374ba41SEd Maste {
366*f374ba41SEd Maste 	Channel *c;
367*f374ba41SEd Maste 
368*f374ba41SEd Maste 	if ((c = channel_by_id(ssh, id)) == NULL)
369*f374ba41SEd Maste 		fatal_f("missing channel %d", id);
370*f374ba41SEd Maste 	if (c->xctype != NULL)
371*f374ba41SEd Maste 		free(c->xctype);
372*f374ba41SEd Maste 	c->xctype = xstrdup(xctype);
373*f374ba41SEd Maste 	/* Type has changed, so look up inactivity deadline again */
374*f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->xctype);
375*f374ba41SEd Maste 	debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype,
376*f374ba41SEd Maste 	    c->inactive_deadline);
377*f374ba41SEd Maste }
378*f374ba41SEd Maste 
379*f374ba41SEd Maste /*
380a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
381a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
382a04a10f8SKris Kennaway  */
383af12a3e7SDag-Erling Smørgrav static void
3844f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
385d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty)
386a04a10f8SKris Kennaway {
38738a52bd3SEd Maste 	int val;
38838a52bd3SEd Maste 
389b15c8340SDag-Erling Smørgrav 	if (rfd != -1)
390b15c8340SDag-Erling Smørgrav 		fcntl(rfd, F_SETFD, FD_CLOEXEC);
391b15c8340SDag-Erling Smørgrav 	if (wfd != -1 && wfd != rfd)
392b15c8340SDag-Erling Smørgrav 		fcntl(wfd, F_SETFD, FD_CLOEXEC);
393b15c8340SDag-Erling Smørgrav 	if (efd != -1 && efd != rfd && efd != wfd)
394b15c8340SDag-Erling Smørgrav 		fcntl(efd, F_SETFD, FD_CLOEXEC);
395a04a10f8SKris Kennaway 
396a04a10f8SKris Kennaway 	c->rfd = rfd;
397a04a10f8SKris Kennaway 	c->wfd = wfd;
398a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
399a04a10f8SKris Kennaway 	c->efd = efd;
400a04a10f8SKris Kennaway 	c->extended_usage = extusage;
4015b9b2fafSBrian Feldman 
402d4af9e69SDag-Erling Smørgrav 	if ((c->isatty = is_tty) != 0)
403221552e4SDag-Erling Smørgrav 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
404e4a9863fSDag-Erling Smørgrav #ifdef _AIX
405e4a9863fSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
406d4af9e69SDag-Erling Smørgrav 	c->wfd_isatty = is_tty || isatty(c->wfd);
407e4a9863fSDag-Erling Smørgrav #endif
408e0fbb1d2SBrian Feldman 
4095b9b2fafSBrian Feldman 	/* enable nonblocking mode */
41019261079SEd Maste 	c->restore_block = 0;
41119261079SEd Maste 	if (nonblock == CHANNEL_NONBLOCK_STDIO) {
41219261079SEd Maste 		/*
41319261079SEd Maste 		 * Special handling for stdio file descriptors: do not set
41419261079SEd Maste 		 * non-blocking mode if they are TTYs. Otherwise prepare to
41519261079SEd Maste 		 * restore their blocking state on exit to avoid interfering
41619261079SEd Maste 		 * with other programs that follow.
41719261079SEd Maste 		 */
41838a52bd3SEd Maste 		if (rfd != -1 && !isatty(rfd) &&
41938a52bd3SEd Maste 		    (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
420*f374ba41SEd Maste 			c->restore_flags[0] = val;
42119261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_RFD;
42219261079SEd Maste 			set_nonblock(rfd);
42319261079SEd Maste 		}
42438a52bd3SEd Maste 		if (wfd != -1 && !isatty(wfd) &&
42538a52bd3SEd Maste 		    (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
426*f374ba41SEd Maste 			c->restore_flags[1] = val;
42719261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_WFD;
42819261079SEd Maste 			set_nonblock(wfd);
42919261079SEd Maste 		}
43038a52bd3SEd Maste 		if (efd != -1 && !isatty(efd) &&
43138a52bd3SEd Maste 		    (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
432*f374ba41SEd Maste 			c->restore_flags[2] = val;
43319261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_EFD;
43419261079SEd Maste 			set_nonblock(efd);
43519261079SEd Maste 		}
43619261079SEd Maste 	} else if (nonblock) {
437a04a10f8SKris Kennaway 		if (rfd != -1)
438a04a10f8SKris Kennaway 			set_nonblock(rfd);
439a04a10f8SKris Kennaway 		if (wfd != -1)
440a04a10f8SKris Kennaway 			set_nonblock(wfd);
441a04a10f8SKris Kennaway 		if (efd != -1)
442a04a10f8SKris Kennaway 			set_nonblock(efd);
443a04a10f8SKris Kennaway 	}
4445b9b2fafSBrian Feldman }
445a04a10f8SKris Kennaway 
446511b41d2SMark Murray /*
44738a52bd3SEd Maste  * Allocate a new channel object and set its type and socket.
448511b41d2SMark Murray  */
449af12a3e7SDag-Erling Smørgrav Channel *
4504f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
45138a52bd3SEd Maste     u_int window, u_int maxpack, int extusage, const char *remote_name,
45238a52bd3SEd Maste     int nonblock)
453511b41d2SMark Murray {
4544f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
45538a52bd3SEd Maste 	u_int i, found = 0;
456511b41d2SMark Murray 	Channel *c;
45719261079SEd Maste 	int r;
458511b41d2SMark Murray 
459511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
4604f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
4614f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL) {
462511b41d2SMark Murray 			/* Found a free slot. */
4634f52dfbbSDag-Erling Smørgrav 			found = i;
464511b41d2SMark Murray 			break;
465511b41d2SMark Murray 		}
4664f52dfbbSDag-Erling Smørgrav 	}
4674f52dfbbSDag-Erling Smørgrav 	if (i >= sc->channels_alloc) {
4684f52dfbbSDag-Erling Smørgrav 		/*
4694f52dfbbSDag-Erling Smørgrav 		 * There are no free slots. Take last+1 slot and expand
4704f52dfbbSDag-Erling Smørgrav 		 * the array.
4714f52dfbbSDag-Erling Smørgrav 		 */
4724f52dfbbSDag-Erling Smørgrav 		found = sc->channels_alloc;
4734f52dfbbSDag-Erling Smørgrav 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
47419261079SEd Maste 			fatal_f("internal error: channels_alloc %d too big",
47519261079SEd Maste 			    sc->channels_alloc);
4764f52dfbbSDag-Erling Smørgrav 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
4774f52dfbbSDag-Erling Smørgrav 		    sc->channels_alloc + 10, sizeof(*sc->channels));
4784f52dfbbSDag-Erling Smørgrav 		sc->channels_alloc += 10;
4794f52dfbbSDag-Erling Smørgrav 		debug2("channel: expanding %d", sc->channels_alloc);
480511b41d2SMark Murray 	}
481af12a3e7SDag-Erling Smørgrav 	/* Initialize and return new channel. */
4824f52dfbbSDag-Erling Smørgrav 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
4834f52dfbbSDag-Erling Smørgrav 	if ((c->input = sshbuf_new()) == NULL ||
4844f52dfbbSDag-Erling Smørgrav 	    (c->output = sshbuf_new()) == NULL ||
4854f52dfbbSDag-Erling Smørgrav 	    (c->extended = sshbuf_new()) == NULL)
48619261079SEd Maste 		fatal_f("sshbuf_new failed");
48719261079SEd Maste 	if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
48819261079SEd Maste 		fatal_fr(r, "sshbuf_set_max_size");
489af12a3e7SDag-Erling Smørgrav 	c->ostate = CHAN_OUTPUT_OPEN;
490af12a3e7SDag-Erling Smørgrav 	c->istate = CHAN_INPUT_OPEN;
4914f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
492511b41d2SMark Murray 	c->self = found;
493511b41d2SMark Murray 	c->type = type;
494a04a10f8SKris Kennaway 	c->ctype = ctype;
495a04a10f8SKris Kennaway 	c->local_window = window;
496a04a10f8SKris Kennaway 	c->local_window_max = window;
497a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
498221552e4SDag-Erling Smørgrav 	c->remote_name = xstrdup(remote_name);
499b15c8340SDag-Erling Smørgrav 	c->ctl_chan = -1;
500b15c8340SDag-Erling Smørgrav 	c->delayed = 1;		/* prevent call to channel_post handler */
501*f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->ctype);
502d4af9e69SDag-Erling Smørgrav 	TAILQ_INIT(&c->status_confirms);
503*f374ba41SEd Maste 	debug("channel %d: new %s [%s] (inactive timeout: %u)",
504*f374ba41SEd Maste 	    found, c->ctype, remote_name, c->inactive_deadline);
505af12a3e7SDag-Erling Smørgrav 	return c;
506a04a10f8SKris Kennaway }
507511b41d2SMark Murray 
508af12a3e7SDag-Erling Smørgrav int
50919261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp)
510af12a3e7SDag-Erling Smørgrav {
51119261079SEd Maste 	int ret, fd = *fdp;
512af12a3e7SDag-Erling Smørgrav 
51319261079SEd Maste 	if (fd == -1)
51419261079SEd Maste 		return 0;
51519261079SEd Maste 
516*f374ba41SEd Maste 	/* restore blocking */
517*f374ba41SEd Maste 	if (*fdp == c->rfd &&
518*f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_RFD) != 0)
519*f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[0]);
520*f374ba41SEd Maste 	else if (*fdp == c->wfd &&
521*f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_WFD) != 0)
522*f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[1]);
523*f374ba41SEd Maste 	else if (*fdp == c->efd &&
524*f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_EFD) != 0)
525*f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[2]);
52619261079SEd Maste 
5271323ec57SEd Maste 	if (*fdp == c->rfd) {
5281323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_RFD;
5291323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_RFD;
5301323ec57SEd Maste 		c->rfd = -1;
53187c1498dSEd Maste 		c->pfds[0] = -1;
5321323ec57SEd Maste 	}
5331323ec57SEd Maste 	if (*fdp == c->wfd) {
5341323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_WFD;
5351323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_WFD;
5361323ec57SEd Maste 		c->wfd = -1;
53787c1498dSEd Maste 		c->pfds[1] = -1;
5381323ec57SEd Maste 	}
5391323ec57SEd Maste 	if (*fdp == c->efd) {
5401323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_EFD;
5411323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_EFD;
5421323ec57SEd Maste 		c->efd = -1;
54387c1498dSEd Maste 		c->pfds[2] = -1;
5441323ec57SEd Maste 	}
5451323ec57SEd Maste 	if (*fdp == c->sock) {
5461323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_SOCK;
5471323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_SOCK;
5481323ec57SEd Maste 		c->sock = -1;
54987c1498dSEd Maste 		c->pfds[3] = -1;
5501323ec57SEd Maste 	}
5511323ec57SEd Maste 
552af12a3e7SDag-Erling Smørgrav 	ret = close(fd);
5531323ec57SEd Maste 	*fdp = -1; /* probably redundant */
554af12a3e7SDag-Erling Smørgrav 	return ret;
555af12a3e7SDag-Erling Smørgrav }
556a04a10f8SKris Kennaway 
557a04a10f8SKris Kennaway /* Close all channel fd/socket. */
558af12a3e7SDag-Erling Smørgrav static void
5594f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c)
560511b41d2SMark Murray {
56147dd1d1bSDag-Erling Smørgrav 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
56247dd1d1bSDag-Erling Smørgrav 
56319261079SEd Maste 	channel_close_fd(ssh, c, &c->sock);
56447dd1d1bSDag-Erling Smørgrav 	if (rfd != sock)
56519261079SEd Maste 		channel_close_fd(ssh, c, &c->rfd);
56647dd1d1bSDag-Erling Smørgrav 	if (wfd != sock && wfd != rfd)
56719261079SEd Maste 		channel_close_fd(ssh, c, &c->wfd);
56847dd1d1bSDag-Erling Smørgrav 	if (efd != sock && efd != rfd && efd != wfd)
56919261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
5704f52dfbbSDag-Erling Smørgrav }
5714f52dfbbSDag-Erling Smørgrav 
5724f52dfbbSDag-Erling Smørgrav static void
573190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm)
5744f52dfbbSDag-Erling Smørgrav {
575190cef3dSDag-Erling Smørgrav 	free(perm->host_to_connect);
576190cef3dSDag-Erling Smørgrav 	free(perm->listen_host);
577190cef3dSDag-Erling Smørgrav 	free(perm->listen_path);
57819261079SEd Maste 	memset(perm, 0, sizeof(*perm));
5794f52dfbbSDag-Erling Smørgrav }
5804f52dfbbSDag-Erling Smørgrav 
581190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */
582190cef3dSDag-Erling Smørgrav static const char *
583190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where)
584190cef3dSDag-Erling Smørgrav {
585190cef3dSDag-Erling Smørgrav 	if (who == FORWARD_ADM) {
586190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
587190cef3dSDag-Erling Smørgrav 			return "admin local";
588190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
589190cef3dSDag-Erling Smørgrav 			return "admin remote";
590190cef3dSDag-Erling Smørgrav 	} else if (who == FORWARD_USER) {
591190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
592190cef3dSDag-Erling Smørgrav 			return "user local";
593190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
594190cef3dSDag-Erling Smørgrav 			return "user remote";
595190cef3dSDag-Erling Smørgrav 	}
596190cef3dSDag-Erling Smørgrav 	fatal("Unknown forward permission list %d/%d", who, where);
597190cef3dSDag-Erling Smørgrav }
5984f52dfbbSDag-Erling Smørgrav 
599190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */
600190cef3dSDag-Erling Smørgrav static struct permission_set *
601190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where)
602190cef3dSDag-Erling Smørgrav {
603190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
604190cef3dSDag-Erling Smørgrav 
605190cef3dSDag-Erling Smørgrav 	switch (where) {
606190cef3dSDag-Erling Smørgrav 	case FORWARD_LOCAL:
607190cef3dSDag-Erling Smørgrav 		return &sc->local_perms;
608190cef3dSDag-Erling Smørgrav 		break;
609190cef3dSDag-Erling Smørgrav 	case FORWARD_REMOTE:
610190cef3dSDag-Erling Smørgrav 		return &sc->remote_perms;
611190cef3dSDag-Erling Smørgrav 		break;
612190cef3dSDag-Erling Smørgrav 	default:
61319261079SEd Maste 		fatal_f("invalid forwarding direction %d", where);
614190cef3dSDag-Erling Smørgrav 	}
615190cef3dSDag-Erling Smørgrav }
616190cef3dSDag-Erling Smørgrav 
61719261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */
618190cef3dSDag-Erling Smørgrav static void
619190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where,
620190cef3dSDag-Erling Smørgrav     struct permission ***permpp, u_int **npermpp)
621190cef3dSDag-Erling Smørgrav {
622190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
623190cef3dSDag-Erling Smørgrav 
624190cef3dSDag-Erling Smørgrav 	switch (who) {
625190cef3dSDag-Erling Smørgrav 	case FORWARD_USER:
626190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_user;
627190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_user;
628190cef3dSDag-Erling Smørgrav 		break;
629190cef3dSDag-Erling Smørgrav 	case FORWARD_ADM:
630190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_admin;
631190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_admin;
632190cef3dSDag-Erling Smørgrav 		break;
633190cef3dSDag-Erling Smørgrav 	default:
63419261079SEd Maste 		fatal_f("invalid forwarding client %d", who);
635190cef3dSDag-Erling Smørgrav 	}
636190cef3dSDag-Erling Smørgrav }
637190cef3dSDag-Erling Smørgrav 
6381323ec57SEd Maste /* Adds an entry to the specified forwarding list */
6394f52dfbbSDag-Erling Smørgrav static int
640190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where,
6414f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, int port_to_connect,
6424f52dfbbSDag-Erling Smørgrav     const char *listen_host, const char *listen_path, int listen_port,
6434f52dfbbSDag-Erling Smørgrav     Channel *downstream)
6444f52dfbbSDag-Erling Smørgrav {
645190cef3dSDag-Erling Smørgrav 	struct permission **permp;
646190cef3dSDag-Erling Smørgrav 	u_int n, *npermp;
6474f52dfbbSDag-Erling Smørgrav 
648190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
6494f52dfbbSDag-Erling Smørgrav 
650190cef3dSDag-Erling Smørgrav 	if (*npermp >= INT_MAX)
65119261079SEd Maste 		fatal_f("%s overflow", fwd_ident(who, where));
6524f52dfbbSDag-Erling Smørgrav 
653190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
654190cef3dSDag-Erling Smørgrav 	n = (*npermp)++;
6554f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
656190cef3dSDag-Erling Smørgrav 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
657190cef3dSDag-Erling Smørgrav 	(*permp)[n].port_to_connect = port_to_connect;
658190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
659190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
660190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_port = listen_port;
661190cef3dSDag-Erling Smørgrav 	(*permp)[n].downstream = downstream;
6624f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP
6634f52dfbbSDag-Erling Smørgrav 	return (int)n;
6644f52dfbbSDag-Erling Smørgrav }
6654f52dfbbSDag-Erling Smørgrav 
6664f52dfbbSDag-Erling Smørgrav static void
6674f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
6684f52dfbbSDag-Erling Smørgrav {
6694f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
670190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
671190cef3dSDag-Erling Smørgrav 	struct permission *perm;
6724f52dfbbSDag-Erling Smørgrav 	int r;
6734f52dfbbSDag-Erling Smørgrav 	u_int i;
6744f52dfbbSDag-Erling Smørgrav 
675190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
676190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
677190cef3dSDag-Erling Smørgrav 		if (perm->downstream != c)
6784f52dfbbSDag-Erling Smørgrav 			continue;
6794f52dfbbSDag-Erling Smørgrav 
6804f52dfbbSDag-Erling Smørgrav 		/* cancel on the server, since mux client is gone */
6814f52dfbbSDag-Erling Smørgrav 		debug("channel %d: cleanup remote forward for %s:%u",
682190cef3dSDag-Erling Smørgrav 		    c->self, perm->listen_host, perm->listen_port);
6834f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
6844f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
6854f52dfbbSDag-Erling Smørgrav 		    "cancel-tcpip-forward")) != 0 ||
6864f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
6874f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
688190cef3dSDag-Erling Smørgrav 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
689190cef3dSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
6904f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
69119261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
6924f52dfbbSDag-Erling Smørgrav 		}
693190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(perm); /* unregister */
6944f52dfbbSDag-Erling Smørgrav 	}
695a04a10f8SKris Kennaway }
696511b41d2SMark Murray 
697a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
698a04a10f8SKris Kennaway void
6994f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c)
700a04a10f8SKris Kennaway {
7014f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
702af12a3e7SDag-Erling Smørgrav 	char *s;
70321e764dfSDag-Erling Smørgrav 	u_int i, n;
704ca86bcf2SDag-Erling Smørgrav 	Channel *other;
705d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
706ca3176e7SBrian Feldman 
7074f52dfbbSDag-Erling Smørgrav 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
7084f52dfbbSDag-Erling Smørgrav 		if ((other = sc->channels[i]) == NULL)
7094f52dfbbSDag-Erling Smørgrav 			continue;
710af12a3e7SDag-Erling Smørgrav 		n++;
711ca86bcf2SDag-Erling Smørgrav 		/* detach from mux client and prepare for closing */
712ca86bcf2SDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
713ca86bcf2SDag-Erling Smørgrav 		    other->type == SSH_CHANNEL_MUX_PROXY &&
714ca86bcf2SDag-Erling Smørgrav 		    other->mux_ctx == c) {
715ca86bcf2SDag-Erling Smørgrav 			other->mux_ctx = NULL;
716ca86bcf2SDag-Erling Smørgrav 			other->type = SSH_CHANNEL_OPEN;
717ca86bcf2SDag-Erling Smørgrav 			other->istate = CHAN_INPUT_CLOSED;
718ca86bcf2SDag-Erling Smørgrav 			other->ostate = CHAN_OUTPUT_CLOSED;
719ca86bcf2SDag-Erling Smørgrav 		}
720ca86bcf2SDag-Erling Smørgrav 	}
72121e764dfSDag-Erling Smørgrav 	debug("channel %d: free: %s, nchannels %u", c->self,
722af12a3e7SDag-Erling Smørgrav 	    c->remote_name ? c->remote_name : "???", n);
723af12a3e7SDag-Erling Smørgrav 
724e9e8876aSEd Maste 	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
7254f52dfbbSDag-Erling Smørgrav 		mux_remove_remote_forwardings(ssh, c);
726e9e8876aSEd Maste 		free(c->mux_ctx);
727e9e8876aSEd Maste 		c->mux_ctx = NULL;
728e9e8876aSEd Maste 	} else if (c->type == SSH_CHANNEL_MUX_LISTENER) {
72919261079SEd Maste 		free(c->mux_ctx);
73019261079SEd Maste 		c->mux_ctx = NULL;
73119261079SEd Maste 	}
732ca86bcf2SDag-Erling Smørgrav 
733190cef3dSDag-Erling Smørgrav 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
7344f52dfbbSDag-Erling Smørgrav 		s = channel_open_message(ssh);
735221552e4SDag-Erling Smørgrav 		debug3("channel %d: status: %s", c->self, s);
736e4a9863fSDag-Erling Smørgrav 		free(s);
737190cef3dSDag-Erling Smørgrav 	}
738ca3176e7SBrian Feldman 
7394f52dfbbSDag-Erling Smørgrav 	channel_close_fds(ssh, c);
7404f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->input);
7414f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->output);
7424f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->extended);
7434f52dfbbSDag-Erling Smørgrav 	c->input = c->output = c->extended = NULL;
744e4a9863fSDag-Erling Smørgrav 	free(c->remote_name);
745a04a10f8SKris Kennaway 	c->remote_name = NULL;
746e4a9863fSDag-Erling Smørgrav 	free(c->path);
747cce7d346SDag-Erling Smørgrav 	c->path = NULL;
748e4a9863fSDag-Erling Smørgrav 	free(c->listening_addr);
749462c32cbSDag-Erling Smørgrav 	c->listening_addr = NULL;
750*f374ba41SEd Maste 	free(c->xctype);
751*f374ba41SEd Maste 	c->xctype = NULL;
752d4af9e69SDag-Erling Smørgrav 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
753d4af9e69SDag-Erling Smørgrav 		if (cc->abandon_cb != NULL)
7544f52dfbbSDag-Erling Smørgrav 			cc->abandon_cb(ssh, c, cc->ctx);
755d4af9e69SDag-Erling Smørgrav 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
75619261079SEd Maste 		freezero(cc, sizeof(*cc));
757d4af9e69SDag-Erling Smørgrav 	}
758d4af9e69SDag-Erling Smørgrav 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
7594f52dfbbSDag-Erling Smørgrav 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
7604f52dfbbSDag-Erling Smørgrav 	sc->channels[c->self] = NULL;
76119261079SEd Maste 	freezero(c, sizeof(*c));
762af12a3e7SDag-Erling Smørgrav }
763af12a3e7SDag-Erling Smørgrav 
764af12a3e7SDag-Erling Smørgrav void
7654f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh)
766af12a3e7SDag-Erling Smørgrav {
76721e764dfSDag-Erling Smørgrav 	u_int i;
76819261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
769af12a3e7SDag-Erling Smørgrav 
77019261079SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
77119261079SEd Maste 		if (sc->channels[i] != NULL)
77219261079SEd Maste 			channel_free(ssh, sc->channels[i]);
77319261079SEd Maste 
77419261079SEd Maste 	free(sc->channels);
77519261079SEd Maste 	sc->channels = NULL;
77619261079SEd Maste 	sc->channels_alloc = 0;
77719261079SEd Maste 
77819261079SEd Maste 	free(sc->x11_saved_display);
77919261079SEd Maste 	sc->x11_saved_display = NULL;
78019261079SEd Maste 
78119261079SEd Maste 	free(sc->x11_saved_proto);
78219261079SEd Maste 	sc->x11_saved_proto = NULL;
78319261079SEd Maste 
78419261079SEd Maste 	free(sc->x11_saved_data);
78519261079SEd Maste 	sc->x11_saved_data = NULL;
78619261079SEd Maste 	sc->x11_saved_data_len = 0;
78719261079SEd Maste 
78819261079SEd Maste 	free(sc->x11_fake_data);
78919261079SEd Maste 	sc->x11_fake_data = NULL;
79019261079SEd Maste 	sc->x11_fake_data_len = 0;
791af12a3e7SDag-Erling Smørgrav }
792af12a3e7SDag-Erling Smørgrav 
793af12a3e7SDag-Erling Smørgrav /*
794af12a3e7SDag-Erling Smørgrav  * Closes the sockets/fds of all channels.  This is used to close extra file
795af12a3e7SDag-Erling Smørgrav  * descriptors after a fork.
796af12a3e7SDag-Erling Smørgrav  */
797af12a3e7SDag-Erling Smørgrav void
7984f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh)
799af12a3e7SDag-Erling Smørgrav {
80021e764dfSDag-Erling Smørgrav 	u_int i;
801af12a3e7SDag-Erling Smørgrav 
8024f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
8034f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
8044f52dfbbSDag-Erling Smørgrav 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
805af12a3e7SDag-Erling Smørgrav }
806af12a3e7SDag-Erling Smørgrav 
807af12a3e7SDag-Erling Smørgrav /*
808af12a3e7SDag-Erling Smørgrav  * Stop listening to channels.
809af12a3e7SDag-Erling Smørgrav  */
810af12a3e7SDag-Erling Smørgrav void
8114f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh)
812af12a3e7SDag-Erling Smørgrav {
81321e764dfSDag-Erling Smørgrav 	u_int i;
814af12a3e7SDag-Erling Smørgrav 	Channel *c;
815af12a3e7SDag-Erling Smørgrav 
8164f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8174f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
818af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
819af12a3e7SDag-Erling Smørgrav 			switch (c->type) {
820af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_AUTH_SOCKET:
821af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_PORT_LISTENER:
822af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_RPORT_LISTENER:
823af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_X11_LISTENER:
824a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_UNIX_LISTENER:
825a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_RUNIX_LISTENER:
82619261079SEd Maste 				channel_close_fd(ssh, c, &c->sock);
8274f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
828af12a3e7SDag-Erling Smørgrav 				break;
829af12a3e7SDag-Erling Smørgrav 			}
830af12a3e7SDag-Erling Smørgrav 		}
831af12a3e7SDag-Erling Smørgrav 	}
832af12a3e7SDag-Erling Smørgrav }
833af12a3e7SDag-Erling Smørgrav 
834af12a3e7SDag-Erling Smørgrav /*
835af12a3e7SDag-Erling Smørgrav  * Returns true if no channel has too much buffered data, and false if one or
836af12a3e7SDag-Erling Smørgrav  * more channel is overfull.
837af12a3e7SDag-Erling Smørgrav  */
838af12a3e7SDag-Erling Smørgrav int
8394f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh)
840af12a3e7SDag-Erling Smørgrav {
841af12a3e7SDag-Erling Smørgrav 	u_int i;
8424f52dfbbSDag-Erling Smørgrav 	u_int maxsize = ssh_packet_get_maxsize(ssh);
843af12a3e7SDag-Erling Smørgrav 	Channel *c;
844af12a3e7SDag-Erling Smørgrav 
8454f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8464f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
8474f52dfbbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
8484f52dfbbSDag-Erling Smørgrav 			continue;
8494f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > maxsize) {
8504f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: big output buffer %zu > %u",
8514f52dfbbSDag-Erling Smørgrav 			    c->self, sshbuf_len(c->output), maxsize);
852af12a3e7SDag-Erling Smørgrav 			return 0;
853af12a3e7SDag-Erling Smørgrav 		}
854af12a3e7SDag-Erling Smørgrav 	}
855af12a3e7SDag-Erling Smørgrav 	return 1;
856af12a3e7SDag-Erling Smørgrav }
857af12a3e7SDag-Erling Smørgrav 
858af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */
859af12a3e7SDag-Erling Smørgrav int
8604f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh)
861af12a3e7SDag-Erling Smørgrav {
86221e764dfSDag-Erling Smørgrav 	u_int i;
863af12a3e7SDag-Erling Smørgrav 	Channel *c;
864af12a3e7SDag-Erling Smørgrav 
8654f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8664f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
867af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
868af12a3e7SDag-Erling Smørgrav 			continue;
869af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
870af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
871af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
872af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
873b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
874af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
875af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
876af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
8774f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
878af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
879af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
880e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
881a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
882a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
883af12a3e7SDag-Erling Smørgrav 			continue;
884af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
885af12a3e7SDag-Erling Smørgrav 			continue;
886af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
887af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
8884f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
889af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
890b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
891ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
892af12a3e7SDag-Erling Smørgrav 			return 1;
893af12a3e7SDag-Erling Smørgrav 		default:
89419261079SEd Maste 			fatal_f("bad channel type %d", c->type);
895af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
896af12a3e7SDag-Erling Smørgrav 		}
897af12a3e7SDag-Erling Smørgrav 	}
898af12a3e7SDag-Erling Smørgrav 	return 0;
899af12a3e7SDag-Erling Smørgrav }
900af12a3e7SDag-Erling Smørgrav 
901af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */
902af12a3e7SDag-Erling Smørgrav int
9034f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh)
904af12a3e7SDag-Erling Smørgrav {
90521e764dfSDag-Erling Smørgrav 	u_int i;
906af12a3e7SDag-Erling Smørgrav 	Channel *c;
907af12a3e7SDag-Erling Smørgrav 
9084f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9094f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
9104f52dfbbSDag-Erling Smørgrav 		if (c == NULL || !c->have_remote_id)
911af12a3e7SDag-Erling Smørgrav 			continue;
912af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
913af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
914af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9154f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
9164f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
917af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
918af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
919af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
920b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
921b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
922ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
923af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
924af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
925af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
926e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
927a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
928a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
929af12a3e7SDag-Erling Smørgrav 			continue;
930af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
931af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
932af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
933af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
934af12a3e7SDag-Erling Smørgrav 			return i;
935af12a3e7SDag-Erling Smørgrav 		default:
93619261079SEd Maste 			fatal_f("bad channel type %d", c->type);
937af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
938af12a3e7SDag-Erling Smørgrav 		}
939af12a3e7SDag-Erling Smørgrav 	}
940af12a3e7SDag-Erling Smørgrav 	return -1;
941af12a3e7SDag-Erling Smørgrav }
942af12a3e7SDag-Erling Smørgrav 
9432f513db7SEd Maste /* Returns the state of the channel's extended usage flag */
9442f513db7SEd Maste const char *
9452f513db7SEd Maste channel_format_extended_usage(const Channel *c)
9462f513db7SEd Maste {
9472f513db7SEd Maste 	if (c->efd == -1)
9482f513db7SEd Maste 		return "closed";
9492f513db7SEd Maste 
9502f513db7SEd Maste 	switch (c->extended_usage) {
9512f513db7SEd Maste 	case CHAN_EXTENDED_WRITE:
9522f513db7SEd Maste 		return "write";
9532f513db7SEd Maste 	case CHAN_EXTENDED_READ:
9542f513db7SEd Maste 		return "read";
9552f513db7SEd Maste 	case CHAN_EXTENDED_IGNORE:
9562f513db7SEd Maste 		return "ignore";
9572f513db7SEd Maste 	default:
9582f513db7SEd Maste 		return "UNKNOWN";
9592f513db7SEd Maste 	}
9602f513db7SEd Maste }
9612f513db7SEd Maste 
9622f513db7SEd Maste static char *
9632f513db7SEd Maste channel_format_status(const Channel *c)
9642f513db7SEd Maste {
9652f513db7SEd Maste 	char *ret = NULL;
9662f513db7SEd Maste 
967*f374ba41SEd Maste 	xasprintf(&ret, "t%d [%s] %s%u i%u/%zu o%u/%zu e[%s]/%zu "
9681323ec57SEd Maste 	    "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x",
969*f374ba41SEd Maste 	    c->type, c->xctype != NULL ? c->xctype : c->ctype,
9702f513db7SEd Maste 	    c->have_remote_id ? "r" : "nr", c->remote_id,
9712f513db7SEd Maste 	    c->istate, sshbuf_len(c->input),
9722f513db7SEd Maste 	    c->ostate, sshbuf_len(c->output),
9732f513db7SEd Maste 	    channel_format_extended_usage(c), sshbuf_len(c->extended),
9741323ec57SEd Maste 	    c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan,
9751323ec57SEd Maste 	    c->io_want, c->io_ready);
9762f513db7SEd Maste 	return ret;
9772f513db7SEd Maste }
9782f513db7SEd Maste 
979af12a3e7SDag-Erling Smørgrav /*
980af12a3e7SDag-Erling Smørgrav  * Returns a message describing the currently open forwarded connections,
981af12a3e7SDag-Erling Smørgrav  * suitable for sending to the client.  The message contains crlf pairs for
982af12a3e7SDag-Erling Smørgrav  * newlines.
983af12a3e7SDag-Erling Smørgrav  */
984af12a3e7SDag-Erling Smørgrav char *
9854f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh)
986af12a3e7SDag-Erling Smørgrav {
9874f52dfbbSDag-Erling Smørgrav 	struct sshbuf *buf;
988af12a3e7SDag-Erling Smørgrav 	Channel *c;
98921e764dfSDag-Erling Smørgrav 	u_int i;
9904f52dfbbSDag-Erling Smørgrav 	int r;
9912f513db7SEd Maste 	char *cp, *ret;
992af12a3e7SDag-Erling Smørgrav 
9934f52dfbbSDag-Erling Smørgrav 	if ((buf = sshbuf_new()) == NULL)
99419261079SEd Maste 		fatal_f("sshbuf_new");
9954f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_putf(buf,
9964f52dfbbSDag-Erling Smørgrav 	    "The following connections are open:\r\n")) != 0)
99719261079SEd Maste 		fatal_fr(r, "sshbuf_putf");
9984f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9994f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
1000af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
1001af12a3e7SDag-Erling Smørgrav 			continue;
1002af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
1003af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
1004af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
1005af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
1006af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
1007af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
1008af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
1009e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
1010b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
1011a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
1012a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
1013af12a3e7SDag-Erling Smørgrav 			continue;
1014af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
1015af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
1016af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
1017af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
10184f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
10194f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
1020af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
1021af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
1022ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
1023ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
10242f513db7SEd Maste 			cp = channel_format_status(c);
10252f513db7SEd Maste 			if ((r = sshbuf_putf(buf, "  #%d %.300s (%s)\r\n",
10262f513db7SEd Maste 			    c->self, c->remote_name, cp)) != 0) {
10272f513db7SEd Maste 				free(cp);
102819261079SEd Maste 				fatal_fr(r, "sshbuf_putf");
10292f513db7SEd Maste 			}
10302f513db7SEd Maste 			free(cp);
1031af12a3e7SDag-Erling Smørgrav 			continue;
1032af12a3e7SDag-Erling Smørgrav 		default:
103319261079SEd Maste 			fatal_f("bad channel type %d", c->type);
1034af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
1035af12a3e7SDag-Erling Smørgrav 		}
1036af12a3e7SDag-Erling Smørgrav 	}
10374f52dfbbSDag-Erling Smørgrav 	if ((ret = sshbuf_dup_string(buf)) == NULL)
103819261079SEd Maste 		fatal_f("sshbuf_dup_string");
10394f52dfbbSDag-Erling Smørgrav 	sshbuf_free(buf);
10404f52dfbbSDag-Erling Smørgrav 	return ret;
10414f52dfbbSDag-Erling Smørgrav }
10424f52dfbbSDag-Erling Smørgrav 
10434f52dfbbSDag-Erling Smørgrav static void
10444f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
10454f52dfbbSDag-Erling Smørgrav {
10464f52dfbbSDag-Erling Smørgrav 	int r;
10474f52dfbbSDag-Erling Smørgrav 
10484f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
10494f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
10504f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
10514f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
10524f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
105319261079SEd Maste 		fatal_r(r, "%s: channel %i: open", where, c->self);
10544f52dfbbSDag-Erling Smørgrav 	}
1055af12a3e7SDag-Erling Smørgrav }
1056af12a3e7SDag-Erling Smørgrav 
1057af12a3e7SDag-Erling Smørgrav void
10584f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id)
1059af12a3e7SDag-Erling Smørgrav {
10604f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
10614f52dfbbSDag-Erling Smørgrav 	int r;
1062f388f5efSDag-Erling Smørgrav 
1063af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
1064221552e4SDag-Erling Smørgrav 		logit("channel_send_open: %d: bad id", id);
1065af12a3e7SDag-Erling Smørgrav 		return;
1066af12a3e7SDag-Erling Smørgrav 	}
1067e73e9afaSDag-Erling Smørgrav 	debug2("channel %d: send open", id);
10684f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, c->ctype);
10694f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
107019261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1071af12a3e7SDag-Erling Smørgrav }
1072af12a3e7SDag-Erling Smørgrav 
1073af12a3e7SDag-Erling Smørgrav void
10744f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
1075af12a3e7SDag-Erling Smørgrav {
10764f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
10774f52dfbbSDag-Erling Smørgrav 	int r;
1078f388f5efSDag-Erling Smørgrav 
1079af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
108019261079SEd Maste 		logit_f("%d: unknown channel id", id);
1081af12a3e7SDag-Erling Smørgrav 		return;
1082af12a3e7SDag-Erling Smørgrav 	}
10834f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
108419261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
10854f52dfbbSDag-Erling Smørgrav 
108621e764dfSDag-Erling Smørgrav 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
10874f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
10884f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
10894f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
10904f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
109119261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
10924f52dfbbSDag-Erling Smørgrav 	}
1093af12a3e7SDag-Erling Smørgrav }
1094333ee039SDag-Erling Smørgrav 
1095af12a3e7SDag-Erling Smørgrav void
10964f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id,
10974f52dfbbSDag-Erling Smørgrav     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
1098d4af9e69SDag-Erling Smørgrav {
1099d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
1100d4af9e69SDag-Erling Smørgrav 	Channel *c;
1101d4af9e69SDag-Erling Smørgrav 
11024f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL)
110319261079SEd Maste 		fatal_f("%d: bad id", id);
1104d4af9e69SDag-Erling Smørgrav 
11050a37d4a3SXin LI 	cc = xcalloc(1, sizeof(*cc));
1106d4af9e69SDag-Erling Smørgrav 	cc->cb = cb;
1107d4af9e69SDag-Erling Smørgrav 	cc->abandon_cb = abandon_cb;
1108d4af9e69SDag-Erling Smørgrav 	cc->ctx = ctx;
1109d4af9e69SDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
1110d4af9e69SDag-Erling Smørgrav }
1111d4af9e69SDag-Erling Smørgrav 
1112d4af9e69SDag-Erling Smørgrav void
11134f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id,
11144f52dfbbSDag-Erling Smørgrav     channel_open_fn *fn, void *ctx)
1115af12a3e7SDag-Erling Smørgrav {
11164f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1117f388f5efSDag-Erling Smørgrav 
1118af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
111919261079SEd Maste 		logit_f("%d: bad id", id);
1120af12a3e7SDag-Erling Smørgrav 		return;
1121af12a3e7SDag-Erling Smørgrav 	}
1122d4af9e69SDag-Erling Smørgrav 	c->open_confirm = fn;
1123d4af9e69SDag-Erling Smørgrav 	c->open_confirm_ctx = ctx;
1124af12a3e7SDag-Erling Smørgrav }
1125333ee039SDag-Erling Smørgrav 
1126af12a3e7SDag-Erling Smørgrav void
11274f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id,
11284f52dfbbSDag-Erling Smørgrav     channel_callback_fn *fn, int do_close)
1129af12a3e7SDag-Erling Smørgrav {
11304f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1131f388f5efSDag-Erling Smørgrav 
1132af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
113319261079SEd Maste 		logit_f("%d: bad id", id);
1134af12a3e7SDag-Erling Smørgrav 		return;
1135af12a3e7SDag-Erling Smørgrav 	}
1136af12a3e7SDag-Erling Smørgrav 	c->detach_user = fn;
1137b74df5b2SDag-Erling Smørgrav 	c->detach_close = do_close;
1138af12a3e7SDag-Erling Smørgrav }
1139333ee039SDag-Erling Smørgrav 
1140af12a3e7SDag-Erling Smørgrav void
11414f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id)
1142af12a3e7SDag-Erling Smørgrav {
11434f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1144f388f5efSDag-Erling Smørgrav 
1145af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
114619261079SEd Maste 		logit_f("%d: bad id", id);
1147af12a3e7SDag-Erling Smørgrav 		return;
1148af12a3e7SDag-Erling Smørgrav 	}
1149af12a3e7SDag-Erling Smørgrav 	c->detach_user = NULL;
1150b74df5b2SDag-Erling Smørgrav 	c->detach_close = 0;
1151af12a3e7SDag-Erling Smørgrav }
1152333ee039SDag-Erling Smørgrav 
1153af12a3e7SDag-Erling Smørgrav void
11544f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
1155d4af9e69SDag-Erling Smørgrav     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
1156af12a3e7SDag-Erling Smørgrav {
11574f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1158f388f5efSDag-Erling Smørgrav 
1159af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
116019261079SEd Maste 		logit_f("%d: bad id", id);
1161af12a3e7SDag-Erling Smørgrav 		return;
1162af12a3e7SDag-Erling Smørgrav 	}
1163b74df5b2SDag-Erling Smørgrav 	c->input_filter = ifn;
1164b74df5b2SDag-Erling Smørgrav 	c->output_filter = ofn;
1165d4af9e69SDag-Erling Smørgrav 	c->filter_ctx = ctx;
1166d4af9e69SDag-Erling Smørgrav 	c->filter_cleanup = cfn;
1167af12a3e7SDag-Erling Smørgrav }
1168af12a3e7SDag-Erling Smørgrav 
1169af12a3e7SDag-Erling Smørgrav void
11704f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
1171d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty, u_int window_max)
1172af12a3e7SDag-Erling Smørgrav {
11734f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
11744f52dfbbSDag-Erling Smørgrav 	int r;
1175f388f5efSDag-Erling Smørgrav 
1176af12a3e7SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1177af12a3e7SDag-Erling Smørgrav 		fatal("channel_activate for non-larval channel %d.", id);
11784f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
117919261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
11804f52dfbbSDag-Erling Smørgrav 
11814f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1182af12a3e7SDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1183*f374ba41SEd Maste 	c->lastused = monotime();
1184af12a3e7SDag-Erling Smørgrav 	c->local_window = c->local_window_max = window_max;
11854f52dfbbSDag-Erling Smørgrav 
11864f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
11874f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
11884f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
11894f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
119019261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1191511b41d2SMark Murray }
1192511b41d2SMark Murray 
1193af12a3e7SDag-Erling Smørgrav static void
11941323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c)
1195511b41d2SMark Murray {
11961323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_R;
1197a04a10f8SKris Kennaway }
1198a04a10f8SKris Kennaway 
1199af12a3e7SDag-Erling Smørgrav static void
12001323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c)
1201ca3176e7SBrian Feldman {
1202ca3176e7SBrian Feldman 	debug3("channel %d: waiting for connection", c->self);
12031323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_W;
1204ca3176e7SBrian Feldman }
1205ca3176e7SBrian Feldman 
1206af12a3e7SDag-Erling Smørgrav static void
12071323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c)
1208a04a10f8SKris Kennaway {
12091323ec57SEd Maste 	c->io_want = 0;
1210a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
12114f52dfbbSDag-Erling Smørgrav 	    c->remote_window > 0 &&
12124f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->input) < c->remote_window &&
12134f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
12141323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1215a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1216a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
12174f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0) {
12181323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1219a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
122080628bacSDag-Erling Smørgrav 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
12214f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
12224f52dfbbSDag-Erling Smørgrav 				    "obuf_empty delayed efd %d/(%zu)", c->self,
12234f52dfbbSDag-Erling Smørgrav 				    c->efd, sshbuf_len(c->extended));
122480628bacSDag-Erling Smørgrav 			else
12254f52dfbbSDag-Erling Smørgrav 				chan_obuf_empty(ssh, c);
1226a04a10f8SKris Kennaway 		}
1227a04a10f8SKris Kennaway 	}
1228a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
12294f52dfbbSDag-Erling Smørgrav 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
12304f52dfbbSDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1231a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
12324f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) > 0)
12331323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_W;
1234e2f6069cSDag-Erling Smørgrav 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
1235e2f6069cSDag-Erling Smørgrav 		    (c->extended_usage == CHAN_EXTENDED_READ ||
1236e2f6069cSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
12374f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) < c->remote_window)
12381323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_R;
1239a04a10f8SKris Kennaway 	}
124021e764dfSDag-Erling Smørgrav 	/* XXX: What about efd? races? */
1241a04a10f8SKris Kennaway }
1242a04a10f8SKris Kennaway 
1243a04a10f8SKris Kennaway /*
1244a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
1245a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
1246a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
1247a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
1248a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
1249a04a10f8SKris Kennaway  * XXX All this happens at the client side.
1250af12a3e7SDag-Erling Smørgrav  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1251a04a10f8SKris Kennaway  */
1252af12a3e7SDag-Erling Smørgrav static int
12534f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1254a04a10f8SKris Kennaway {
12554f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
1256ca3176e7SBrian Feldman 	u_char *ucp;
1257ca3176e7SBrian Feldman 	u_int proto_len, data_len;
1258511b41d2SMark Murray 
1259557f75e5SDag-Erling Smørgrav 	/* Is this being called after the refusal deadline? */
12604f52dfbbSDag-Erling Smørgrav 	if (sc->x11_refuse_time != 0 &&
12614f52dfbbSDag-Erling Smørgrav 	    (u_int)monotime() >= sc->x11_refuse_time) {
1262557f75e5SDag-Erling Smørgrav 		verbose("Rejected X11 connection after ForwardX11Timeout "
1263557f75e5SDag-Erling Smørgrav 		    "expired");
1264557f75e5SDag-Erling Smørgrav 		return -1;
1265557f75e5SDag-Erling Smørgrav 	}
1266557f75e5SDag-Erling Smørgrav 
1267511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
12684f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) < 12)
1269a04a10f8SKris Kennaway 		return 0;
1270511b41d2SMark Murray 
1271511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
12724f52dfbbSDag-Erling Smørgrav 	ucp = sshbuf_mutable_ptr(b);
1273511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1274511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
1275511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
1276511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1277511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
1278511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
1279511b41d2SMark Murray 	} else {
1280221552e4SDag-Erling Smørgrav 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1281511b41d2SMark Murray 		    ucp[0]);
1282a04a10f8SKris Kennaway 		return -1;
1283511b41d2SMark Murray 	}
1284511b41d2SMark Murray 
1285511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
12864f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) <
1287511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1288a04a10f8SKris Kennaway 		return 0;
1289511b41d2SMark Murray 
1290511b41d2SMark Murray 	/* Check if authentication protocol matches. */
12914f52dfbbSDag-Erling Smørgrav 	if (proto_len != strlen(sc->x11_saved_proto) ||
12924f52dfbbSDag-Erling Smørgrav 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1293221552e4SDag-Erling Smørgrav 		debug2("X11 connection uses different authentication protocol.");
1294a04a10f8SKris Kennaway 		return -1;
1295511b41d2SMark Murray 	}
1296511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
12974f52dfbbSDag-Erling Smørgrav 	if (data_len != sc->x11_fake_data_len ||
1298e2f6069cSDag-Erling Smørgrav 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
12994f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1300221552e4SDag-Erling Smørgrav 		debug2("X11 auth data does not match fake data.");
1301a04a10f8SKris Kennaway 		return -1;
1302511b41d2SMark Murray 	}
1303511b41d2SMark Murray 	/* Check fake data length */
13044f52dfbbSDag-Erling Smørgrav 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1305511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
13064f52dfbbSDag-Erling Smørgrav 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1307a04a10f8SKris Kennaway 		return -1;
1308511b41d2SMark Murray 	}
1309511b41d2SMark Murray 	/*
1310511b41d2SMark Murray 	 * Received authentication protocol and data match
1311511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
1312511b41d2SMark Murray 	 * data.
1313511b41d2SMark Murray 	 */
1314511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
13154f52dfbbSDag-Erling Smørgrav 	    sc->x11_saved_data, sc->x11_saved_data_len);
1316a04a10f8SKris Kennaway 	return 1;
1317a04a10f8SKris Kennaway }
1318511b41d2SMark Murray 
1319*f374ba41SEd Maste void
1320*f374ba41SEd Maste channel_force_close(struct ssh *ssh, Channel *c, int abandon)
1321*f374ba41SEd Maste {
1322*f374ba41SEd Maste 	debug3_f("channel %d: forcibly closing", c->self);
1323*f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_OPEN)
1324*f374ba41SEd Maste 		chan_read_failed(ssh, c);
1325*f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1326*f374ba41SEd Maste 		sshbuf_reset(c->input);
1327*f374ba41SEd Maste 		chan_ibuf_empty(ssh, c);
1328*f374ba41SEd Maste 	}
1329*f374ba41SEd Maste 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1330*f374ba41SEd Maste 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1331*f374ba41SEd Maste 		sshbuf_reset(c->output);
1332*f374ba41SEd Maste 		chan_write_failed(ssh, c);
1333*f374ba41SEd Maste 	}
1334*f374ba41SEd Maste 	if (c->detach_user)
1335*f374ba41SEd Maste 		c->detach_user(ssh, c->self, 1, NULL);
1336*f374ba41SEd Maste 	if (c->efd != -1)
1337*f374ba41SEd Maste 		channel_close_fd(ssh, c, &c->efd);
1338*f374ba41SEd Maste 	if (abandon)
1339*f374ba41SEd Maste 		c->type = SSH_CHANNEL_ABANDONED;
1340*f374ba41SEd Maste 	/* exempt from inactivity timeouts */
1341*f374ba41SEd Maste 	c->inactive_deadline = 0;
1342*f374ba41SEd Maste 	c->lastused = 0;
1343*f374ba41SEd Maste }
1344*f374ba41SEd Maste 
1345af12a3e7SDag-Erling Smørgrav static void
13461323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c)
1347a04a10f8SKris Kennaway {
13484f52dfbbSDag-Erling Smørgrav 	int ret = x11_open_helper(ssh, c->output);
1349af12a3e7SDag-Erling Smørgrav 
1350af12a3e7SDag-Erling Smørgrav 	/* c->force_drain = 1; */
1351af12a3e7SDag-Erling Smørgrav 
1352a04a10f8SKris Kennaway 	if (ret == 1) {
1353a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
1354*f374ba41SEd Maste 		c->lastused = monotime();
13551323ec57SEd Maste 		channel_pre_open(ssh, c);
1356a04a10f8SKris Kennaway 	} else if (ret == -1) {
1357*f374ba41SEd Maste 		logit("X11 connection rejected because of wrong "
1358*f374ba41SEd Maste 		    "authentication.");
13594f52dfbbSDag-Erling Smørgrav 		debug2("X11 rejected %d i%d/o%d",
13604f52dfbbSDag-Erling Smørgrav 		    c->self, c->istate, c->ostate);
1361*f374ba41SEd Maste 		channel_force_close(ssh, c, 0);
1362a04a10f8SKris Kennaway 	}
1363a04a10f8SKris Kennaway }
1364a04a10f8SKris Kennaway 
1365b15c8340SDag-Erling Smørgrav static void
13661323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c)
1367b15c8340SDag-Erling Smørgrav {
13681323ec57SEd Maste 	c->io_want = 0;
1369e2f6069cSDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
13704f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
13711323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1372b15c8340SDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1373b15c8340SDag-Erling Smørgrav 		/* clear buffer immediately (discard any partial packet) */
13744f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
13754f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
1376b15c8340SDag-Erling Smørgrav 		/* Start output drain. XXX just kill chan? */
13774f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
1378b15c8340SDag-Erling Smørgrav 	}
1379b15c8340SDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1380b15c8340SDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
13814f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0)
13821323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1383b15c8340SDag-Erling Smørgrav 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
13844f52dfbbSDag-Erling Smørgrav 			chan_obuf_empty(ssh, c);
1385b15c8340SDag-Erling Smørgrav 	}
1386b15c8340SDag-Erling Smørgrav }
1387b15c8340SDag-Erling Smørgrav 
1388ca3176e7SBrian Feldman /* try to decode a socks4 header */
1389af12a3e7SDag-Erling Smørgrav static int
13904f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1391ca3176e7SBrian Feldman {
13924f52dfbbSDag-Erling Smørgrav 	const u_char *p;
13934f52dfbbSDag-Erling Smørgrav 	char *host;
1394cce7d346SDag-Erling Smørgrav 	u_int len, have, i, found, need;
1395ca3176e7SBrian Feldman 	char username[256];
1396ca3176e7SBrian Feldman 	struct {
1397ca3176e7SBrian Feldman 		u_int8_t version;
1398ca3176e7SBrian Feldman 		u_int8_t command;
1399ca3176e7SBrian Feldman 		u_int16_t dest_port;
1400ca3176e7SBrian Feldman 		struct in_addr dest_addr;
1401ca3176e7SBrian Feldman 	} s4_req, s4_rsp;
14024f52dfbbSDag-Erling Smørgrav 	int r;
1403ca3176e7SBrian Feldman 
1404ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4", c->self);
1405ca3176e7SBrian Feldman 
14064f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1407ca3176e7SBrian Feldman 	len = sizeof(s4_req);
1408ca3176e7SBrian Feldman 	if (have < len)
1409ca3176e7SBrian Feldman 		return 0;
14104f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1411cce7d346SDag-Erling Smørgrav 
1412cce7d346SDag-Erling Smørgrav 	need = 1;
1413cce7d346SDag-Erling Smørgrav 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1414cce7d346SDag-Erling Smørgrav 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1415cce7d346SDag-Erling Smørgrav 		debug2("channel %d: socks4a request", c->self);
1416cce7d346SDag-Erling Smørgrav 		/* ... and needs an extra string (the hostname) */
1417cce7d346SDag-Erling Smørgrav 		need = 2;
1418cce7d346SDag-Erling Smørgrav 	}
1419cce7d346SDag-Erling Smørgrav 	/* Check for terminating NUL on the string(s) */
1420ca3176e7SBrian Feldman 	for (found = 0, i = len; i < have; i++) {
1421ca3176e7SBrian Feldman 		if (p[i] == '\0') {
1422cce7d346SDag-Erling Smørgrav 			found++;
1423cce7d346SDag-Erling Smørgrav 			if (found == need)
1424ca3176e7SBrian Feldman 				break;
1425ca3176e7SBrian Feldman 		}
1426ca3176e7SBrian Feldman 		if (i > 1024) {
1427ca3176e7SBrian Feldman 			/* the peer is probably sending garbage */
1428ca3176e7SBrian Feldman 			debug("channel %d: decode socks4: too long",
1429ca3176e7SBrian Feldman 			    c->self);
1430ca3176e7SBrian Feldman 			return -1;
1431ca3176e7SBrian Feldman 		}
1432ca3176e7SBrian Feldman 	}
1433cce7d346SDag-Erling Smørgrav 	if (found < need)
1434ca3176e7SBrian Feldman 		return 0;
14354f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
14364f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
14374f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
14384f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
143919261079SEd Maste 		debug_r(r, "channels %d: decode socks4", c->self);
14404f52dfbbSDag-Erling Smørgrav 		return -1;
14414f52dfbbSDag-Erling Smørgrav 	}
14424f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
14434f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
14444f52dfbbSDag-Erling Smørgrav 	if (memchr(p, '\0', have) == NULL) {
144519261079SEd Maste 		error("channel %d: decode socks4: unterminated user", c->self);
14464f52dfbbSDag-Erling Smørgrav 		return -1;
14474f52dfbbSDag-Erling Smørgrav 	}
1448ca3176e7SBrian Feldman 	len = strlen(p);
1449ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1450cce7d346SDag-Erling Smørgrav 	len++; /* trailing '\0' */
1451ca3176e7SBrian Feldman 	strlcpy(username, p, sizeof(username));
145219261079SEd Maste 	if ((r = sshbuf_consume(input, len)) != 0)
145319261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
1454e4a9863fSDag-Erling Smørgrav 	free(c->path);
1455cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1456cce7d346SDag-Erling Smørgrav 	if (need == 1) {			/* SOCKS4: one string */
1457ca3176e7SBrian Feldman 		host = inet_ntoa(s4_req.dest_addr);
1458cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
1459cce7d346SDag-Erling Smørgrav 	} else {				/* SOCKS4A: two strings */
14604f52dfbbSDag-Erling Smørgrav 		have = sshbuf_len(input);
14614f52dfbbSDag-Erling Smørgrav 		p = sshbuf_ptr(input);
14624f52dfbbSDag-Erling Smørgrav 		if (memchr(p, '\0', have) == NULL) {
14634f52dfbbSDag-Erling Smørgrav 			error("channel %d: decode socks4a: host not nul "
14644f52dfbbSDag-Erling Smørgrav 			    "terminated", c->self);
14654f52dfbbSDag-Erling Smørgrav 			return -1;
14664f52dfbbSDag-Erling Smørgrav 		}
1467cce7d346SDag-Erling Smørgrav 		len = strlen(p);
1468cce7d346SDag-Erling Smørgrav 		debug2("channel %d: decode socks4a: host %s/%d",
1469cce7d346SDag-Erling Smørgrav 		    c->self, p, len);
1470cce7d346SDag-Erling Smørgrav 		len++;				/* trailing '\0' */
1471cce7d346SDag-Erling Smørgrav 		if (len > NI_MAXHOST) {
1472cce7d346SDag-Erling Smørgrav 			error("channel %d: hostname \"%.100s\" too long",
1473cce7d346SDag-Erling Smørgrav 			    c->self, p);
1474cce7d346SDag-Erling Smørgrav 			return -1;
1475cce7d346SDag-Erling Smørgrav 		}
1476cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(p);
147719261079SEd Maste 		if ((r = sshbuf_consume(input, len)) != 0)
147819261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
1479cce7d346SDag-Erling Smørgrav 	}
1480ca3176e7SBrian Feldman 	c->host_port = ntohs(s4_req.dest_port);
1481ca3176e7SBrian Feldman 
1482221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1483cce7d346SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s4_req.command);
1484ca3176e7SBrian Feldman 
1485ca3176e7SBrian Feldman 	if (s4_req.command != 1) {
1486cce7d346SDag-Erling Smørgrav 		debug("channel %d: cannot handle: %s cn %d",
1487cce7d346SDag-Erling Smørgrav 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1488ca3176e7SBrian Feldman 		return -1;
1489ca3176e7SBrian Feldman 	}
1490ca3176e7SBrian Feldman 	s4_rsp.version = 0;			/* vn: 0 for reply */
1491ca3176e7SBrian Feldman 	s4_rsp.command = 90;			/* cd: req granted */
1492ca3176e7SBrian Feldman 	s4_rsp.dest_port = 0;			/* ignored */
1493ca3176e7SBrian Feldman 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
149419261079SEd Maste 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0)
149519261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1496ca3176e7SBrian Feldman 	return 1;
1497ca3176e7SBrian Feldman }
1498ca3176e7SBrian Feldman 
1499221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */
1500221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE	0x1000
1501221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH	0x00
1502221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4		0x01
1503221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN	0x03
1504221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6		0x04
1505221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT	0x01
1506221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS	0x00
1507221552e4SDag-Erling Smørgrav 
1508221552e4SDag-Erling Smørgrav static int
15094f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1510221552e4SDag-Erling Smørgrav {
15114f52dfbbSDag-Erling Smørgrav 	/* XXX use get/put_u8 instead of trusting struct padding */
1512221552e4SDag-Erling Smørgrav 	struct {
1513221552e4SDag-Erling Smørgrav 		u_int8_t version;
1514221552e4SDag-Erling Smørgrav 		u_int8_t command;
1515221552e4SDag-Erling Smørgrav 		u_int8_t reserved;
1516221552e4SDag-Erling Smørgrav 		u_int8_t atyp;
1517221552e4SDag-Erling Smørgrav 	} s5_req, s5_rsp;
1518221552e4SDag-Erling Smørgrav 	u_int16_t dest_port;
1519e4a9863fSDag-Erling Smørgrav 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
15204f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1521333ee039SDag-Erling Smørgrav 	u_int have, need, i, found, nmethods, addrlen, af;
15224f52dfbbSDag-Erling Smørgrav 	int r;
1523221552e4SDag-Erling Smørgrav 
1524221552e4SDag-Erling Smørgrav 	debug2("channel %d: decode socks5", c->self);
15254f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1526221552e4SDag-Erling Smørgrav 	if (p[0] != 0x05)
1527221552e4SDag-Erling Smørgrav 		return -1;
15284f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1529221552e4SDag-Erling Smørgrav 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1530221552e4SDag-Erling Smørgrav 		/* format: ver | nmethods | methods */
1531221552e4SDag-Erling Smørgrav 		if (have < 2)
1532221552e4SDag-Erling Smørgrav 			return 0;
1533221552e4SDag-Erling Smørgrav 		nmethods = p[1];
1534221552e4SDag-Erling Smørgrav 		if (have < nmethods + 2)
1535221552e4SDag-Erling Smørgrav 			return 0;
1536221552e4SDag-Erling Smørgrav 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1537221552e4SDag-Erling Smørgrav 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1538221552e4SDag-Erling Smørgrav 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1539221552e4SDag-Erling Smørgrav 				found = 1;
1540221552e4SDag-Erling Smørgrav 				break;
1541221552e4SDag-Erling Smørgrav 			}
1542221552e4SDag-Erling Smørgrav 		}
1543221552e4SDag-Erling Smørgrav 		if (!found) {
1544221552e4SDag-Erling Smørgrav 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1545221552e4SDag-Erling Smørgrav 			    c->self);
1546221552e4SDag-Erling Smørgrav 			return -1;
1547221552e4SDag-Erling Smørgrav 		}
154819261079SEd Maste 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0)
154919261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
15504f52dfbbSDag-Erling Smørgrav 		/* version, method */
15514f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
155219261079SEd Maste 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0)
155319261079SEd Maste 			fatal_fr(r, "channel %d: append reply", c->self);
1554221552e4SDag-Erling Smørgrav 		c->flags |= SSH_SOCKS5_AUTHDONE;
1555221552e4SDag-Erling Smørgrav 		debug2("channel %d: socks5 auth done", c->self);
1556221552e4SDag-Erling Smørgrav 		return 0;				/* need more */
1557221552e4SDag-Erling Smørgrav 	}
1558221552e4SDag-Erling Smørgrav 	debug2("channel %d: socks5 post auth", c->self);
1559221552e4SDag-Erling Smørgrav 	if (have < sizeof(s5_req)+1)
1560221552e4SDag-Erling Smørgrav 		return 0;			/* need more */
1561333ee039SDag-Erling Smørgrav 	memcpy(&s5_req, p, sizeof(s5_req));
1562221552e4SDag-Erling Smørgrav 	if (s5_req.version != 0x05 ||
1563221552e4SDag-Erling Smørgrav 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1564221552e4SDag-Erling Smørgrav 	    s5_req.reserved != 0x00) {
1565221552e4SDag-Erling Smørgrav 		debug2("channel %d: only socks5 connect supported", c->self);
1566221552e4SDag-Erling Smørgrav 		return -1;
1567221552e4SDag-Erling Smørgrav 	}
1568221552e4SDag-Erling Smørgrav 	switch (s5_req.atyp){
1569221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV4:
1570221552e4SDag-Erling Smørgrav 		addrlen = 4;
1571221552e4SDag-Erling Smørgrav 		af = AF_INET;
1572221552e4SDag-Erling Smørgrav 		break;
1573221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_DOMAIN:
1574221552e4SDag-Erling Smørgrav 		addrlen = p[sizeof(s5_req)];
1575221552e4SDag-Erling Smørgrav 		af = -1;
1576221552e4SDag-Erling Smørgrav 		break;
1577221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV6:
1578221552e4SDag-Erling Smørgrav 		addrlen = 16;
1579221552e4SDag-Erling Smørgrav 		af = AF_INET6;
1580221552e4SDag-Erling Smørgrav 		break;
1581221552e4SDag-Erling Smørgrav 	default:
1582221552e4SDag-Erling Smørgrav 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1583221552e4SDag-Erling Smørgrav 		return -1;
1584221552e4SDag-Erling Smørgrav 	}
1585333ee039SDag-Erling Smørgrav 	need = sizeof(s5_req) + addrlen + 2;
1586333ee039SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1587333ee039SDag-Erling Smørgrav 		need++;
1588333ee039SDag-Erling Smørgrav 	if (have < need)
1589221552e4SDag-Erling Smørgrav 		return 0;
159019261079SEd Maste 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0)
159119261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
15924f52dfbbSDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
15934f52dfbbSDag-Erling Smørgrav 		/* host string length */
159419261079SEd Maste 		if ((r = sshbuf_consume(input, 1)) != 0)
159519261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
15964f52dfbbSDag-Erling Smørgrav 	}
15974f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
15984f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
159919261079SEd Maste 		debug_r(r, "channel %d: parse addr/port", c->self);
16004f52dfbbSDag-Erling Smørgrav 		return -1;
16014f52dfbbSDag-Erling Smørgrav 	}
1602221552e4SDag-Erling Smørgrav 	dest_addr[addrlen] = '\0';
1603e4a9863fSDag-Erling Smørgrav 	free(c->path);
1604cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1605cce7d346SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1606cce7d346SDag-Erling Smørgrav 		if (addrlen >= NI_MAXHOST) {
1607cce7d346SDag-Erling Smørgrav 			error("channel %d: dynamic request: socks5 hostname "
1608cce7d346SDag-Erling Smørgrav 			    "\"%.100s\" too long", c->self, dest_addr);
1609221552e4SDag-Erling Smørgrav 			return -1;
1610cce7d346SDag-Erling Smørgrav 		}
1611cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(dest_addr);
1612cce7d346SDag-Erling Smørgrav 	} else {
1613cce7d346SDag-Erling Smørgrav 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1614cce7d346SDag-Erling Smørgrav 			return -1;
1615cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(ntop);
1616cce7d346SDag-Erling Smørgrav 	}
1617221552e4SDag-Erling Smørgrav 	c->host_port = ntohs(dest_port);
1618221552e4SDag-Erling Smørgrav 
1619221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1620221552e4SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s5_req.command);
1621221552e4SDag-Erling Smørgrav 
1622221552e4SDag-Erling Smørgrav 	s5_rsp.version = 0x05;
1623221552e4SDag-Erling Smørgrav 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1624221552e4SDag-Erling Smørgrav 	s5_rsp.reserved = 0;			/* ignored */
1625221552e4SDag-Erling Smørgrav 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1626221552e4SDag-Erling Smørgrav 	dest_port = 0;				/* ignored */
1627221552e4SDag-Erling Smørgrav 
16284f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
16294f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
16304f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
163119261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1632221552e4SDag-Erling Smørgrav 	return 1;
1633221552e4SDag-Erling Smørgrav }
1634221552e4SDag-Erling Smørgrav 
1635b15c8340SDag-Erling Smørgrav Channel *
16364f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh,
163719261079SEd Maste     const char *host_to_connect, u_short port_to_connect,
163819261079SEd Maste     int in, int out, int nonblock)
1639b15c8340SDag-Erling Smørgrav {
1640b15c8340SDag-Erling Smørgrav 	Channel *c;
1641b15c8340SDag-Erling Smørgrav 
164219261079SEd Maste 	debug_f("%s:%d", host_to_connect, port_to_connect);
1643b15c8340SDag-Erling Smørgrav 
16444f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
1645b15c8340SDag-Erling Smørgrav 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
164619261079SEd Maste 	    0, "stdio-forward", nonblock);
1647b15c8340SDag-Erling Smørgrav 
1648b15c8340SDag-Erling Smørgrav 	c->path = xstrdup(host_to_connect);
1649b15c8340SDag-Erling Smørgrav 	c->host_port = port_to_connect;
1650b15c8340SDag-Erling Smørgrav 	c->listening_port = 0;
1651b15c8340SDag-Erling Smørgrav 	c->force_drain = 1;
1652b15c8340SDag-Erling Smørgrav 
16534f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
16544f52dfbbSDag-Erling Smørgrav 	port_open_helper(ssh, c, "direct-tcpip");
1655b15c8340SDag-Erling Smørgrav 
1656b15c8340SDag-Erling Smørgrav 	return c;
1657b15c8340SDag-Erling Smørgrav }
1658b15c8340SDag-Erling Smørgrav 
1659ca3176e7SBrian Feldman /* dynamic port forwarding */
1660af12a3e7SDag-Erling Smørgrav static void
16611323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c)
1662ca3176e7SBrian Feldman {
16634f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1664d4ecd108SDag-Erling Smørgrav 	u_int have;
1665d4ecd108SDag-Erling Smørgrav 	int ret;
1666ca3176e7SBrian Feldman 
16671323ec57SEd Maste 	c->io_want = 0;
16684f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->input);
1669ca3176e7SBrian Feldman 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
16704f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->input, stderr); */
1671ca3176e7SBrian Feldman 	/* check if the fixed size part of the packet is in buffer. */
1672221552e4SDag-Erling Smørgrav 	if (have < 3) {
1673ca3176e7SBrian Feldman 		/* need more */
16741323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1675ca3176e7SBrian Feldman 		return;
1676ca3176e7SBrian Feldman 	}
1677ca3176e7SBrian Feldman 	/* try to guess the protocol */
16784f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->input);
16794f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u8? */
1680ca3176e7SBrian Feldman 	switch (p[0]) {
1681ca3176e7SBrian Feldman 	case 0x04:
16824f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->input, c->output);
1683ca3176e7SBrian Feldman 		break;
1684221552e4SDag-Erling Smørgrav 	case 0x05:
16854f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->input, c->output);
1686221552e4SDag-Erling Smørgrav 		break;
1687ca3176e7SBrian Feldman 	default:
1688ca3176e7SBrian Feldman 		ret = -1;
1689ca3176e7SBrian Feldman 		break;
1690ca3176e7SBrian Feldman 	}
1691ca3176e7SBrian Feldman 	if (ret < 0) {
16924f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1693ca3176e7SBrian Feldman 	} else if (ret == 0) {
1694ca3176e7SBrian Feldman 		debug2("channel %d: pre_dynamic: need more", c->self);
1695ca3176e7SBrian Feldman 		/* need more */
16961323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
16974f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output))
16981323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1699ca3176e7SBrian Feldman 	} else {
1700ca3176e7SBrian Feldman 		/* switch to the next state */
1701ca3176e7SBrian Feldman 		c->type = SSH_CHANNEL_OPENING;
17024f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, c, "direct-tcpip");
17034f52dfbbSDag-Erling Smørgrav 	}
17044f52dfbbSDag-Erling Smørgrav }
17054f52dfbbSDag-Erling Smørgrav 
17064f52dfbbSDag-Erling Smørgrav /* simulate read-error */
17074f52dfbbSDag-Erling Smørgrav static void
17084f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c)
17094f52dfbbSDag-Erling Smørgrav {
17104f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1711*f374ba41SEd Maste 	channel_force_close(ssh, c, 0);
17124f52dfbbSDag-Erling Smørgrav }
17134f52dfbbSDag-Erling Smørgrav 
17144f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */
17154f52dfbbSDag-Erling Smørgrav static void
17161323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c)
17174f52dfbbSDag-Erling Smørgrav {
17184f52dfbbSDag-Erling Smørgrav 	const u_char *p;
17194f52dfbbSDag-Erling Smørgrav 	u_int have, len;
17204f52dfbbSDag-Erling Smørgrav 	int r, ret;
17214f52dfbbSDag-Erling Smørgrav 
17224f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->output);
17234f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
17244f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->output, stderr); */
17254f52dfbbSDag-Erling Smørgrav 	/* EOF received */
17264f52dfbbSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
172719261079SEd Maste 		if ((r = sshbuf_consume(c->output, have)) != 0)
172819261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
17294f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
17304f52dfbbSDag-Erling Smørgrav 		return;
17314f52dfbbSDag-Erling Smørgrav 	}
17324f52dfbbSDag-Erling Smørgrav 	/* check if the fixed size part of the packet is in buffer. */
17334f52dfbbSDag-Erling Smørgrav 	if (have < 3)
17344f52dfbbSDag-Erling Smørgrav 		return;
17354f52dfbbSDag-Erling Smørgrav 	/* try to guess the protocol */
17364f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->output);
17374f52dfbbSDag-Erling Smørgrav 	switch (p[0]) {
17384f52dfbbSDag-Erling Smørgrav 	case 0x04:
17394f52dfbbSDag-Erling Smørgrav 		/* switch input/output for reverse forwarding */
17404f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->output, c->input);
17414f52dfbbSDag-Erling Smørgrav 		break;
17424f52dfbbSDag-Erling Smørgrav 	case 0x05:
17434f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->output, c->input);
17444f52dfbbSDag-Erling Smørgrav 		break;
17454f52dfbbSDag-Erling Smørgrav 	default:
17464f52dfbbSDag-Erling Smørgrav 		ret = -1;
17474f52dfbbSDag-Erling Smørgrav 		break;
17484f52dfbbSDag-Erling Smørgrav 	}
17494f52dfbbSDag-Erling Smørgrav 	if (ret < 0) {
17504f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
17514f52dfbbSDag-Erling Smørgrav 	} else if (ret == 0) {
17524f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: pre_rdynamic: need more", c->self);
17534f52dfbbSDag-Erling Smørgrav 		/* send socks request to peer */
17544f52dfbbSDag-Erling Smørgrav 		len = sshbuf_len(c->input);
17554f52dfbbSDag-Erling Smørgrav 		if (len > 0 && len < c->remote_window) {
17564f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
17574f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
17584f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
17594f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0) {
176019261079SEd Maste 				fatal_fr(r, "channel %i: rdynamic", c->self);
17614f52dfbbSDag-Erling Smørgrav 			}
176219261079SEd Maste 			if ((r = sshbuf_consume(c->input, len)) != 0)
176319261079SEd Maste 				fatal_fr(r, "channel %d: consume", c->self);
17644f52dfbbSDag-Erling Smørgrav 			c->remote_window -= len;
17654f52dfbbSDag-Erling Smørgrav 		}
17664f52dfbbSDag-Erling Smørgrav 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
17674f52dfbbSDag-Erling Smørgrav 		/* the connect failed */
17684f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
1769ca3176e7SBrian Feldman 	}
1770ca3176e7SBrian Feldman }
1771ca3176e7SBrian Feldman 
1772a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
1773af12a3e7SDag-Erling Smørgrav static void
17741323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c)
1775511b41d2SMark Murray {
1776af12a3e7SDag-Erling Smørgrav 	Channel *nc;
1777d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
17784f52dfbbSDag-Erling Smørgrav 	int r, newsock, oerrno, remote_port;
1779511b41d2SMark Murray 	socklen_t addrlen;
1780ca3176e7SBrian Feldman 	char buf[16384], *remote_ipaddr;
1781511b41d2SMark Murray 
17821323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
17834f52dfbbSDag-Erling Smørgrav 		return;
17844f52dfbbSDag-Erling Smørgrav 
1785511b41d2SMark Murray 	debug("X11 connection requested.");
1786511b41d2SMark Murray 	addrlen = sizeof(addr);
1787d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1788af12a3e7SDag-Erling Smørgrav 	if (c->single_connection) {
1789e4a9863fSDag-Erling Smørgrav 		oerrno = errno;
1790221552e4SDag-Erling Smørgrav 		debug2("single_connection: closing X11 listener.");
179119261079SEd Maste 		channel_close_fd(ssh, c, &c->sock);
17924f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1793e4a9863fSDag-Erling Smørgrav 		errno = oerrno;
1794af12a3e7SDag-Erling Smørgrav 	}
179519261079SEd Maste 	if (newsock == -1) {
1796e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1797e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1798511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1799462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1800e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1801a04a10f8SKris Kennaway 		return;
1802511b41d2SMark Murray 	}
1803af12a3e7SDag-Erling Smørgrav 	set_nodelay(newsock);
1804ca3176e7SBrian Feldman 	remote_ipaddr = get_peer_ipaddr(newsock);
1805a04a10f8SKris Kennaway 	remote_port = get_peer_port(newsock);
1806511b41d2SMark Murray 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1807ca3176e7SBrian Feldman 	    remote_ipaddr, remote_port);
1808a04a10f8SKris Kennaway 
1809*f374ba41SEd Maste 	nc = channel_new(ssh, "x11-connection",
1810a04a10f8SKris Kennaway 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1811221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
18124f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "x11");
181347dd1d1bSDag-Erling Smørgrav 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
181447dd1d1bSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
181519261079SEd Maste 		fatal_fr(r, "channel %i: reply", c->self);
1816511b41d2SMark Murray 	}
18174f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
181819261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1819e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1820a04a10f8SKris Kennaway }
1821511b41d2SMark Murray 
1822af12a3e7SDag-Erling Smørgrav static void
18234f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
1824ca3176e7SBrian Feldman {
1825f7167e0eSDag-Erling Smørgrav 	char *local_ipaddr = get_local_ipaddr(c->sock);
1826076ad2f8SDag-Erling Smørgrav 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1827ca3176e7SBrian Feldman 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1828d4ecd108SDag-Erling Smørgrav 	int remote_port = get_peer_port(c->sock);
18294f52dfbbSDag-Erling Smørgrav 	int r;
1830ca3176e7SBrian Feldman 
1831b15c8340SDag-Erling Smørgrav 	if (remote_port == -1) {
1832b15c8340SDag-Erling Smørgrav 		/* Fake addr/port to appease peers that validate it (Tectia) */
1833e4a9863fSDag-Erling Smørgrav 		free(remote_ipaddr);
1834b15c8340SDag-Erling Smørgrav 		remote_ipaddr = xstrdup("127.0.0.1");
1835b15c8340SDag-Erling Smørgrav 		remote_port = 65535;
1836b15c8340SDag-Erling Smørgrav 	}
1837b15c8340SDag-Erling Smørgrav 
18384f52dfbbSDag-Erling Smørgrav 	free(c->remote_name);
18394f52dfbbSDag-Erling Smørgrav 	xasprintf(&c->remote_name,
1840ca3176e7SBrian Feldman 	    "%s: listening port %d for %.100s port %d, "
1841f7167e0eSDag-Erling Smørgrav 	    "connect from %.200s port %d to %.100s port %d",
1842ca3176e7SBrian Feldman 	    rtype, c->listening_port, c->path, c->host_port,
1843f7167e0eSDag-Erling Smørgrav 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1844ca3176e7SBrian Feldman 
18454f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, rtype);
1846a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "direct-tcpip") == 0) {
1847ca3176e7SBrian Feldman 		/* target host, port */
18484f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
184919261079SEd Maste 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0)
185019261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1851a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
1852a0ee8cc6SDag-Erling Smørgrav 		/* target path */
185319261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
185419261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1855a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1856a0ee8cc6SDag-Erling Smørgrav 		/* listen path */
185719261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
185819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1859ca3176e7SBrian Feldman 	} else {
1860ca3176e7SBrian Feldman 		/* listen address, port */
18614f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
186219261079SEd Maste 		    (r = sshpkt_put_u32(ssh, local_port)) != 0)
186319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1864ca3176e7SBrian Feldman 	}
1865a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1866a0ee8cc6SDag-Erling Smørgrav 		/* reserved for future owner/mode info */
186719261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, "")) != 0)
186819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1869a0ee8cc6SDag-Erling Smørgrav 	} else {
1870ca3176e7SBrian Feldman 		/* originator host and port */
18714f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
187219261079SEd Maste 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0)
187319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1874ca3176e7SBrian Feldman 	}
18754f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
187619261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1877e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1878f7167e0eSDag-Erling Smørgrav 	free(local_ipaddr);
1879ca3176e7SBrian Feldman }
1880ca3176e7SBrian Feldman 
1881557f75e5SDag-Erling Smørgrav void
18824f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time)
1883557f75e5SDag-Erling Smørgrav {
18844f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->x11_refuse_time = refuse_time;
1885557f75e5SDag-Erling Smørgrav }
1886557f75e5SDag-Erling Smørgrav 
1887511b41d2SMark Murray /*
1888a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
1889511b41d2SMark Murray  */
1890af12a3e7SDag-Erling Smørgrav static void
18911323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c)
1892a04a10f8SKris Kennaway {
1893ca3176e7SBrian Feldman 	Channel *nc;
1894d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1895af12a3e7SDag-Erling Smørgrav 	int newsock, nextstate;
1896a04a10f8SKris Kennaway 	socklen_t addrlen;
1897ca3176e7SBrian Feldman 	char *rtype;
1898a04a10f8SKris Kennaway 
18991323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
19004f52dfbbSDag-Erling Smørgrav 		return;
19014f52dfbbSDag-Erling Smørgrav 
19024f52dfbbSDag-Erling Smørgrav 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1903a04a10f8SKris Kennaway 	    c->listening_port, c->path, c->host_port);
1904ca3176e7SBrian Feldman 
1905af12a3e7SDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1906af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1907af12a3e7SDag-Erling Smørgrav 		rtype = "forwarded-tcpip";
1908a0ee8cc6SDag-Erling Smørgrav 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
1909a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1910a0ee8cc6SDag-Erling Smørgrav 		rtype = "forwarded-streamlocal@openssh.com";
1911a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == PORT_STREAMLOCAL) {
1912a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1913a0ee8cc6SDag-Erling Smørgrav 		rtype = "direct-streamlocal@openssh.com";
1914a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == 0) {
1915af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_DYNAMIC;
1916af12a3e7SDag-Erling Smørgrav 		rtype = "dynamic-tcpip";
1917af12a3e7SDag-Erling Smørgrav 	} else {
1918af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1919af12a3e7SDag-Erling Smørgrav 		rtype = "direct-tcpip";
1920af12a3e7SDag-Erling Smørgrav 	}
1921ca3176e7SBrian Feldman 
1922511b41d2SMark Murray 	addrlen = sizeof(addr);
1923d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
192419261079SEd Maste 	if (newsock == -1) {
1925e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1926e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1927511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1928462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1929e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1930a04a10f8SKris Kennaway 		return;
1931511b41d2SMark Murray 	}
1932a0ee8cc6SDag-Erling Smørgrav 	if (c->host_port != PORT_STREAMLOCAL)
1933af12a3e7SDag-Erling Smørgrav 		set_nodelay(newsock);
19344f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
1935221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
1936ca3176e7SBrian Feldman 	nc->listening_port = c->listening_port;
1937ca3176e7SBrian Feldman 	nc->host_port = c->host_port;
1938cce7d346SDag-Erling Smørgrav 	if (c->path != NULL)
1939cce7d346SDag-Erling Smørgrav 		nc->path = xstrdup(c->path);
1940ca3176e7SBrian Feldman 
1941b15c8340SDag-Erling Smørgrav 	if (nextstate != SSH_CHANNEL_DYNAMIC)
19424f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, nc, rtype);
1943a04a10f8SKris Kennaway }
1944511b41d2SMark Murray 
1945511b41d2SMark Murray /*
1946a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
1947a04a10f8SKris Kennaway  * clients.
1948511b41d2SMark Murray  */
1949af12a3e7SDag-Erling Smørgrav static void
19501323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c)
1951a04a10f8SKris Kennaway {
1952af12a3e7SDag-Erling Smørgrav 	Channel *nc;
19534f52dfbbSDag-Erling Smørgrav 	int r, newsock;
1954d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1955a04a10f8SKris Kennaway 	socklen_t addrlen;
1956a04a10f8SKris Kennaway 
19571323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
19584f52dfbbSDag-Erling Smørgrav 		return;
19594f52dfbbSDag-Erling Smørgrav 
1960511b41d2SMark Murray 	addrlen = sizeof(addr);
1961d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
196219261079SEd Maste 	if (newsock == -1) {
19634f52dfbbSDag-Erling Smørgrav 		error("accept from auth socket: %.100s", strerror(errno));
1964462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1965e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1966a04a10f8SKris Kennaway 		return;
1967511b41d2SMark Murray 	}
1968*f374ba41SEd Maste 	nc = channel_new(ssh, "agent-connection",
1969ca3176e7SBrian Feldman 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1970ca3176e7SBrian Feldman 	    c->local_window_max, c->local_maxpacket,
1971221552e4SDag-Erling Smørgrav 	    0, "accepted auth socket", 1);
19724f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
19734f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
197419261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1975a04a10f8SKris Kennaway }
1976511b41d2SMark Murray 
1977af12a3e7SDag-Erling Smørgrav static void
19781323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c)
1979ca3176e7SBrian Feldman {
19804f52dfbbSDag-Erling Smørgrav 	int err = 0, sock, isopen, r;
1981af12a3e7SDag-Erling Smørgrav 	socklen_t sz = sizeof(err);
1982af12a3e7SDag-Erling Smørgrav 
19831323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
19844f52dfbbSDag-Erling Smørgrav 		return;
19854f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
198619261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
19874f52dfbbSDag-Erling Smørgrav 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
19884f52dfbbSDag-Erling Smørgrav 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
198919261079SEd Maste 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) {
1990af12a3e7SDag-Erling Smørgrav 		err = errno;
1991af12a3e7SDag-Erling Smørgrav 		error("getsockopt SO_ERROR failed");
1992af12a3e7SDag-Erling Smørgrav 	}
1993ca3176e7SBrian Feldman 	if (err == 0) {
1994d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connected to %s port %d",
1995d4af9e69SDag-Erling Smørgrav 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
1996d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
1997af12a3e7SDag-Erling Smørgrav 		c->type = SSH_CHANNEL_OPEN;
1998*f374ba41SEd Maste 		c->lastused = monotime();
19994f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20004f52dfbbSDag-Erling Smørgrav 			/* no message necessary */
2001af12a3e7SDag-Erling Smørgrav 		} else {
20024f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20034f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
20044f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
20054f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
20064f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
200719261079SEd Maste 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
200819261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
200919261079SEd Maste 				fatal_fr(r, "channel %i open confirm", c->self);
2010af12a3e7SDag-Erling Smørgrav 		}
2011ca3176e7SBrian Feldman 	} else {
2012d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connection failed: %s",
2013ca3176e7SBrian Feldman 		    c->self, strerror(err));
2014d4af9e69SDag-Erling Smørgrav 		/* Try next address, if any */
2015d4af9e69SDag-Erling Smørgrav 		if ((sock = connect_next(&c->connect_ctx)) > 0) {
2016d4af9e69SDag-Erling Smørgrav 			close(c->sock);
2017d4af9e69SDag-Erling Smørgrav 			c->sock = c->rfd = c->wfd = sock;
2018d4af9e69SDag-Erling Smørgrav 			return;
2019d4af9e69SDag-Erling Smørgrav 		}
2020d4af9e69SDag-Erling Smørgrav 		/* Exhausted all addresses */
2021d4af9e69SDag-Erling Smørgrav 		error("connect_to %.100s port %d: failed.",
2022d4af9e69SDag-Erling Smørgrav 		    c->connect_ctx.host, c->connect_ctx.port);
2023d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
20244f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20254f52dfbbSDag-Erling Smørgrav 			rdynamic_close(ssh, c);
2026af12a3e7SDag-Erling Smørgrav 		} else {
20274f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20284f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
20294f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
203047dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh,
203147dd1d1bSDag-Erling Smørgrav 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
203247dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
203319261079SEd Maste 			    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
203419261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
203519261079SEd Maste 				fatal_fr(r, "channel %i: failure", c->self);
20364f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2037ca3176e7SBrian Feldman 		}
2038ca3176e7SBrian Feldman 	}
2039ca3176e7SBrian Feldman }
2040ca3176e7SBrian Feldman 
2041af12a3e7SDag-Erling Smørgrav static int
20421323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c)
2043a04a10f8SKris Kennaway {
2044aa49c926SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
20454f52dfbbSDag-Erling Smørgrav 	ssize_t len;
20464f52dfbbSDag-Erling Smørgrav 	int r, force;
2047*f374ba41SEd Maste 	size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ;
20481323ec57SEd Maste 	int pty_zeroread = 0;
20491323ec57SEd Maste 
20501323ec57SEd Maste #ifdef PTY_ZEROREAD
20511323ec57SEd Maste 	/* Bug on AIX: read(1) can return 0 for a non-closed fd */
20521323ec57SEd Maste 	pty_zeroread = c->isatty;
20531323ec57SEd Maste #endif
2054511b41d2SMark Murray 
2055d4af9e69SDag-Erling Smørgrav 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
20564f52dfbbSDag-Erling Smørgrav 
20571323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0)
20584f52dfbbSDag-Erling Smørgrav 		return 1;
20591323ec57SEd Maste 	if ((avail = sshbuf_avail(c->input)) == 0)
20601323ec57SEd Maste 		return 1; /* Shouldn't happen */
20611323ec57SEd Maste 
20621323ec57SEd Maste 	/*
20631323ec57SEd Maste 	 * For "simple" channels (i.e. not datagram or filtered), we can
20641323ec57SEd Maste 	 * read directly to the channel buffer.
20651323ec57SEd Maste 	 */
20661323ec57SEd Maste 	if (!pty_zeroread && c->input_filter == NULL && !c->datagram) {
20671323ec57SEd Maste 		/* Only OPEN channels have valid rwin */
20681323ec57SEd Maste 		if (c->type == SSH_CHANNEL_OPEN) {
20691323ec57SEd Maste 			if ((have = sshbuf_len(c->input)) >= c->remote_window)
20701323ec57SEd Maste 				return 1; /* shouldn't happen */
20711323ec57SEd Maste 			if (maxlen > c->remote_window - have)
20721323ec57SEd Maste 				maxlen = c->remote_window - have;
20731323ec57SEd Maste 		}
20741323ec57SEd Maste 		if (maxlen > avail)
20751323ec57SEd Maste 			maxlen = avail;
2076*f374ba41SEd Maste 		if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) {
20771323ec57SEd Maste 			if (errno == EINTR || (!force &&
20781323ec57SEd Maste 			    (errno == EAGAIN || errno == EWOULDBLOCK)))
20791323ec57SEd Maste 				return 1;
20801323ec57SEd Maste 			debug2("channel %d: read failed rfd %d maxlen %zu: %s",
20811323ec57SEd Maste 			    c->self, c->rfd, maxlen, ssh_err(r));
20821323ec57SEd Maste 			goto rfail;
20831323ec57SEd Maste 		}
2084*f374ba41SEd Maste 		if (nr != 0)
2085*f374ba41SEd Maste 			c->lastused = monotime();
20861323ec57SEd Maste 		return 1;
20871323ec57SEd Maste 	}
20884f52dfbbSDag-Erling Smørgrav 
2089333ee039SDag-Erling Smørgrav 	errno = 0;
2090a04a10f8SKris Kennaway 	len = read(c->rfd, buf, sizeof(buf));
20911323ec57SEd Maste 	/* fixup AIX zero-length read with errno set to look more like errors */
20921323ec57SEd Maste 	if (pty_zeroread && len == 0 && errno != 0)
20931323ec57SEd Maste 		len = -1;
209419261079SEd Maste 	if (len == -1 && (errno == EINTR ||
2095d4af9e69SDag-Erling Smørgrav 	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
2096a04a10f8SKris Kennaway 		return 1;
20971323ec57SEd Maste 	if (len < 0 || (!pty_zeroread && len == 0)) {
20981323ec57SEd Maste 		debug2("channel %d: read<=0 rfd %d len %zd: %s",
20991323ec57SEd Maste 		    c->self, c->rfd, len,
21001323ec57SEd Maste 		    len == 0 ? "closed" : strerror(errno));
21011323ec57SEd Maste  rfail:
2102ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2103221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
21044f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2105ca3176e7SBrian Feldman 			return -1;
2106a04a10f8SKris Kennaway 		} else {
21074f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2108a04a10f8SKris Kennaway 		}
2109a04a10f8SKris Kennaway 		return -1;
2110a04a10f8SKris Kennaway 	}
2111*f374ba41SEd Maste 	c->lastused = monotime();
2112b66f2d16SKris Kennaway 	if (c->input_filter != NULL) {
21134f52dfbbSDag-Erling Smørgrav 		if (c->input_filter(ssh, c, buf, len) == -1) {
2114221552e4SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
21154f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2116b66f2d16SKris Kennaway 		}
2117b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
21184f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
211919261079SEd Maste 			fatal_fr(r, "channel %i: put datagram", c->self);
212019261079SEd Maste 	} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
212119261079SEd Maste 		fatal_fr(r, "channel %i: put data", c->self);
21221323ec57SEd Maste 
2123a04a10f8SKris Kennaway 	return 1;
2124a04a10f8SKris Kennaway }
2125333ee039SDag-Erling Smørgrav 
2126af12a3e7SDag-Erling Smørgrav static int
21271323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c)
2128a04a10f8SKris Kennaway {
2129ca3176e7SBrian Feldman 	struct termios tio;
21304f52dfbbSDag-Erling Smørgrav 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
21314f52dfbbSDag-Erling Smørgrav 	size_t dlen, olen = 0;
21324f52dfbbSDag-Erling Smørgrav 	int r, len;
21334f52dfbbSDag-Erling Smørgrav 
21341323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
21351323ec57SEd Maste 		return 1;
21361323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
21374f52dfbbSDag-Erling Smørgrav 		return 1;
2138a04a10f8SKris Kennaway 
2139a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
21404f52dfbbSDag-Erling Smørgrav 	olen = sshbuf_len(c->output);
2141b74df5b2SDag-Erling Smørgrav 	if (c->output_filter != NULL) {
21424f52dfbbSDag-Erling Smørgrav 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
2143b74df5b2SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
2144b74df5b2SDag-Erling Smørgrav 			if (c->type != SSH_CHANNEL_OPEN)
21454f52dfbbSDag-Erling Smørgrav 				chan_mark_dead(ssh, c);
2146b74df5b2SDag-Erling Smørgrav 			else
21474f52dfbbSDag-Erling Smørgrav 				chan_write_failed(ssh, c);
2148b74df5b2SDag-Erling Smørgrav 			return -1;
2149b74df5b2SDag-Erling Smørgrav 		}
2150b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
21514f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
215219261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
21534f52dfbbSDag-Erling Smørgrav 		buf = data;
2154b74df5b2SDag-Erling Smørgrav 	} else {
21554f52dfbbSDag-Erling Smørgrav 		buf = data = sshbuf_mutable_ptr(c->output);
21564f52dfbbSDag-Erling Smørgrav 		dlen = sshbuf_len(c->output);
2157b74df5b2SDag-Erling Smørgrav 	}
2158b74df5b2SDag-Erling Smørgrav 
2159b74df5b2SDag-Erling Smørgrav 	if (c->datagram) {
2160b74df5b2SDag-Erling Smørgrav 		/* ignore truncated writes, datagrams might get lost */
2161b74df5b2SDag-Erling Smørgrav 		len = write(c->wfd, buf, dlen);
2162e4a9863fSDag-Erling Smørgrav 		free(data);
216319261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2164d4af9e69SDag-Erling Smørgrav 		    errno == EWOULDBLOCK))
2165b74df5b2SDag-Erling Smørgrav 			return 1;
21664f52dfbbSDag-Erling Smørgrav 		if (len <= 0)
21674f52dfbbSDag-Erling Smørgrav 			goto write_fail;
2168e2f6069cSDag-Erling Smørgrav 		goto out;
2169b74df5b2SDag-Erling Smørgrav 	}
21704f52dfbbSDag-Erling Smørgrav 
2171f388f5efSDag-Erling Smørgrav #ifdef _AIX
2172f388f5efSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
21734f52dfbbSDag-Erling Smørgrav 	if (c->wfd_isatty)
21741323ec57SEd Maste 		dlen = MINIMUM(dlen, 8*1024);
2175f388f5efSDag-Erling Smørgrav #endif
2176b74df5b2SDag-Erling Smørgrav 
2177b74df5b2SDag-Erling Smørgrav 	len = write(c->wfd, buf, dlen);
217819261079SEd Maste 	if (len == -1 &&
2179d4af9e69SDag-Erling Smørgrav 	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
2180a04a10f8SKris Kennaway 		return 1;
2181511b41d2SMark Murray 	if (len <= 0) {
21824f52dfbbSDag-Erling Smørgrav  write_fail:
2183ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2184221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
21854f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2186ca3176e7SBrian Feldman 			return -1;
2187511b41d2SMark Murray 		} else {
21884f52dfbbSDag-Erling Smørgrav 			chan_write_failed(ssh, c);
2189511b41d2SMark Murray 		}
2190a04a10f8SKris Kennaway 		return -1;
2191511b41d2SMark Murray 	}
2192*f374ba41SEd Maste 	c->lastused = monotime();
21937aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON
21944f52dfbbSDag-Erling Smørgrav 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2195e0fbb1d2SBrian Feldman 		if (tcgetattr(c->wfd, &tio) == 0 &&
2196e0fbb1d2SBrian Feldman 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2197e0fbb1d2SBrian Feldman 			/*
2198e0fbb1d2SBrian Feldman 			 * Simulate echo to reduce the impact of
2199ca3176e7SBrian Feldman 			 * traffic analysis. We need to match the
2200ca3176e7SBrian Feldman 			 * size of a SSH2_MSG_CHANNEL_DATA message
2201b74df5b2SDag-Erling Smørgrav 			 * (4 byte channel id + buf)
2202e0fbb1d2SBrian Feldman 			 */
22034f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
22044f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0)
220519261079SEd Maste 				fatal_fr(r, "channel %i: ignore", c->self);
2206e0fbb1d2SBrian Feldman 		}
2207e0fbb1d2SBrian Feldman 	}
22084f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */
220919261079SEd Maste 	if ((r = sshbuf_consume(c->output, len)) != 0)
221019261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
2211e2f6069cSDag-Erling Smørgrav  out:
22124f52dfbbSDag-Erling Smørgrav 	c->local_consumed += olen - sshbuf_len(c->output);
22134f52dfbbSDag-Erling Smørgrav 
2214a04a10f8SKris Kennaway 	return 1;
2215511b41d2SMark Murray }
2216333ee039SDag-Erling Smørgrav 
2217af12a3e7SDag-Erling Smørgrav static int
22181323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c)
2219a04a10f8SKris Kennaway {
22204f52dfbbSDag-Erling Smørgrav 	int r;
22214f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2222511b41d2SMark Murray 
22231323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0)
22241323ec57SEd Maste 		return 1;
22251323ec57SEd Maste 	if (sshbuf_len(c->extended) == 0)
22264f52dfbbSDag-Erling Smørgrav 		return 1;
22274f52dfbbSDag-Erling Smørgrav 
22284f52dfbbSDag-Erling Smørgrav 	len = write(c->efd, sshbuf_ptr(c->extended),
22294f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->extended));
22304f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
223119261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2232d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK))
2233ca3176e7SBrian Feldman 		return 1;
2234ca3176e7SBrian Feldman 	if (len <= 0) {
22354f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
223619261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2237ca3176e7SBrian Feldman 	} else {
223819261079SEd Maste 		if ((r = sshbuf_consume(c->extended, len)) != 0)
223919261079SEd Maste 			fatal_fr(r, "channel %i: consume", c->self);
2240a04a10f8SKris Kennaway 		c->local_consumed += len;
2241*f374ba41SEd Maste 		c->lastused = monotime();
2242a04a10f8SKris Kennaway 	}
22434f52dfbbSDag-Erling Smørgrav 	return 1;
22444f52dfbbSDag-Erling Smørgrav }
22454f52dfbbSDag-Erling Smørgrav 
22464f52dfbbSDag-Erling Smørgrav static int
22471323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c)
22484f52dfbbSDag-Erling Smørgrav {
22494f52dfbbSDag-Erling Smørgrav 	char buf[CHAN_RBUF];
22504f52dfbbSDag-Erling Smørgrav 	ssize_t len;
225119261079SEd Maste 	int r, force;
22524f52dfbbSDag-Erling Smørgrav 
225319261079SEd Maste 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
225419261079SEd Maste 
22551323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
22564f52dfbbSDag-Erling Smørgrav 		return 1;
22574f52dfbbSDag-Erling Smørgrav 
2258a04a10f8SKris Kennaway 	len = read(c->efd, buf, sizeof(buf));
22594f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
226019261079SEd Maste 	if (len == -1 && (errno == EINTR || ((errno == EAGAIN ||
226119261079SEd Maste 	    errno == EWOULDBLOCK) && !force)))
2262ca3176e7SBrian Feldman 		return 1;
2263ca3176e7SBrian Feldman 	if (len <= 0) {
226419261079SEd Maste 		debug2("channel %d: closing read-efd %d", c->self, c->efd);
226519261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2266*f374ba41SEd Maste 		return 1;
2267*f374ba41SEd Maste 	}
2268*f374ba41SEd Maste 	c->lastused = monotime();
2269*f374ba41SEd Maste 	if (c->extended_usage == CHAN_EXTENDED_IGNORE)
227019261079SEd Maste 		debug3("channel %d: discard efd", c->self);
227119261079SEd Maste 	else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
227219261079SEd Maste 		fatal_fr(r, "channel %i: append", c->self);
2273a04a10f8SKris Kennaway 	return 1;
2274a04a10f8SKris Kennaway }
2275333ee039SDag-Erling Smørgrav 
227621e764dfSDag-Erling Smørgrav static int
22771323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c)
2278a04a10f8SKris Kennaway {
22794f52dfbbSDag-Erling Smørgrav 	if (c->efd == -1)
22804f52dfbbSDag-Erling Smørgrav 		return 1;
22814f52dfbbSDag-Erling Smørgrav 
22824f52dfbbSDag-Erling Smørgrav 	/** XXX handle drain efd, too */
22834f52dfbbSDag-Erling Smørgrav 
22844f52dfbbSDag-Erling Smørgrav 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
22851323ec57SEd Maste 		return channel_handle_efd_write(ssh, c);
22864f52dfbbSDag-Erling Smørgrav 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
22874f52dfbbSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
22881323ec57SEd Maste 		return channel_handle_efd_read(ssh, c);
22894f52dfbbSDag-Erling Smørgrav 
22904f52dfbbSDag-Erling Smørgrav 	return 1;
22914f52dfbbSDag-Erling Smørgrav }
22924f52dfbbSDag-Erling Smørgrav 
22934f52dfbbSDag-Erling Smørgrav static int
22944f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c)
22954f52dfbbSDag-Erling Smørgrav {
22964f52dfbbSDag-Erling Smørgrav 	int r;
22974f52dfbbSDag-Erling Smørgrav 
2298ca3176e7SBrian Feldman 	if (c->type == SSH_CHANNEL_OPEN &&
2299ca3176e7SBrian Feldman 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2300d4af9e69SDag-Erling Smørgrav 	    ((c->local_window_max - c->local_window >
2301d4af9e69SDag-Erling Smørgrav 	    c->local_maxpacket*3) ||
2302d4af9e69SDag-Erling Smørgrav 	    c->local_window < c->local_window_max/2) &&
2303a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
23044f52dfbbSDag-Erling Smørgrav 		if (!c->have_remote_id)
230519261079SEd Maste 			fatal_f("channel %d: no remote id", c->self);
23064f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh,
23074f52dfbbSDag-Erling Smørgrav 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
23084f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
23094f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
23104f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
231119261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
23124f52dfbbSDag-Erling Smørgrav 		}
231319261079SEd Maste 		debug2("channel %d: window %d sent adjust %d", c->self,
231419261079SEd Maste 		    c->local_window, c->local_consumed);
231560c59fadSDag-Erling Smørgrav 		c->local_window += c->local_consumed;
2316a04a10f8SKris Kennaway 		c->local_consumed = 0;
2317a04a10f8SKris Kennaway 	}
2318a04a10f8SKris Kennaway 	return 1;
2319a04a10f8SKris Kennaway }
2320a04a10f8SKris Kennaway 
2321af12a3e7SDag-Erling Smørgrav static void
23221323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c)
2323a04a10f8SKris Kennaway {
23241323ec57SEd Maste 	channel_handle_rfd(ssh, c);
23251323ec57SEd Maste 	channel_handle_wfd(ssh, c);
23261323ec57SEd Maste 	channel_handle_efd(ssh, c);
23274f52dfbbSDag-Erling Smørgrav 	channel_check_window(ssh, c);
2328a04a10f8SKris Kennaway }
2329a04a10f8SKris Kennaway 
2330b15c8340SDag-Erling Smørgrav static u_int
23314f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need)
2332b15c8340SDag-Erling Smørgrav {
2333b15c8340SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
23344f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2335b15c8340SDag-Erling Smørgrav 	u_int rlen;
23364f52dfbbSDag-Erling Smørgrav 	int r;
2337b15c8340SDag-Erling Smørgrav 
23384f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(c->input) < need) {
23394f52dfbbSDag-Erling Smørgrav 		rlen = need - sshbuf_len(c->input);
2340ca86bcf2SDag-Erling Smørgrav 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
234119261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
23424f52dfbbSDag-Erling Smørgrav 			return sshbuf_len(c->input);
2343b15c8340SDag-Erling Smørgrav 		if (len <= 0) {
23444f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
2345b15c8340SDag-Erling Smørgrav 			    c->self, c->rfd, len);
23464f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2347b15c8340SDag-Erling Smørgrav 			return 0;
234819261079SEd Maste 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
234919261079SEd Maste 			fatal_fr(r, "channel %i: append", c->self);
23504f52dfbbSDag-Erling Smørgrav 	}
23514f52dfbbSDag-Erling Smørgrav 	return sshbuf_len(c->input);
2352b15c8340SDag-Erling Smørgrav }
2353b15c8340SDag-Erling Smørgrav 
2354b15c8340SDag-Erling Smørgrav static void
23551323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c)
2356b15c8340SDag-Erling Smørgrav {
2357b15c8340SDag-Erling Smørgrav 	u_int need;
2358b15c8340SDag-Erling Smørgrav 
23591323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
23604f52dfbbSDag-Erling Smørgrav 		return;
23614f52dfbbSDag-Erling Smørgrav 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
23624f52dfbbSDag-Erling Smørgrav 		return;
23634f52dfbbSDag-Erling Smørgrav 	if (c->mux_pause)
23644f52dfbbSDag-Erling Smørgrav 		return;
2365b15c8340SDag-Erling Smørgrav 
2366b15c8340SDag-Erling Smørgrav 	/*
2367b15c8340SDag-Erling Smørgrav 	 * Don't not read past the precise end of packets to
2368b15c8340SDag-Erling Smørgrav 	 * avoid disrupting fd passing.
2369b15c8340SDag-Erling Smørgrav 	 */
23704f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, 4) < 4) /* read header */
2371b15c8340SDag-Erling Smørgrav 		return;
23724f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u32 */
23734f52dfbbSDag-Erling Smørgrav 	need = PEEK_U32(sshbuf_ptr(c->input));
2374b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
2375b15c8340SDag-Erling Smørgrav 	if (need > CHANNEL_MUX_MAX_PACKET) {
2376b15c8340SDag-Erling Smørgrav 		debug2("channel %d: packet too big %u > %u",
2377b15c8340SDag-Erling Smørgrav 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
23784f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
2379b15c8340SDag-Erling Smørgrav 		return;
2380b15c8340SDag-Erling Smørgrav 	}
23814f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
2382b15c8340SDag-Erling Smørgrav 		return;
23834f52dfbbSDag-Erling Smørgrav 	if (c->mux_rcb(ssh, c) != 0) {
2384b15c8340SDag-Erling Smørgrav 		debug("channel %d: mux_rcb failed", c->self);
23854f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
2386b15c8340SDag-Erling Smørgrav 		return;
2387b15c8340SDag-Erling Smørgrav 	}
2388b15c8340SDag-Erling Smørgrav }
2389b15c8340SDag-Erling Smørgrav 
2390b15c8340SDag-Erling Smørgrav static void
23911323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c)
23924f52dfbbSDag-Erling Smørgrav {
23934f52dfbbSDag-Erling Smørgrav 	ssize_t len;
23944f52dfbbSDag-Erling Smørgrav 	int r;
23954f52dfbbSDag-Erling Smørgrav 
23961323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
23971323ec57SEd Maste 		return;
23981323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
23994f52dfbbSDag-Erling Smørgrav 		return;
24004f52dfbbSDag-Erling Smørgrav 
24014f52dfbbSDag-Erling Smørgrav 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
240219261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
24034f52dfbbSDag-Erling Smørgrav 		return;
24044f52dfbbSDag-Erling Smørgrav 	if (len <= 0) {
24054f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
24064f52dfbbSDag-Erling Smørgrav 		return;
24074f52dfbbSDag-Erling Smørgrav 	}
24084f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0)
240919261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
24104f52dfbbSDag-Erling Smørgrav }
24114f52dfbbSDag-Erling Smørgrav 
24124f52dfbbSDag-Erling Smørgrav static void
24131323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c)
24144f52dfbbSDag-Erling Smørgrav {
24151323ec57SEd Maste 	channel_post_mux_client_read(ssh, c);
24161323ec57SEd Maste 	channel_post_mux_client_write(ssh, c);
24174f52dfbbSDag-Erling Smørgrav }
24184f52dfbbSDag-Erling Smørgrav 
24194f52dfbbSDag-Erling Smørgrav static void
24201323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c)
2421b15c8340SDag-Erling Smørgrav {
2422b15c8340SDag-Erling Smørgrav 	Channel *nc;
2423b15c8340SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2424b15c8340SDag-Erling Smørgrav 	socklen_t addrlen;
2425b15c8340SDag-Erling Smørgrav 	int newsock;
2426b15c8340SDag-Erling Smørgrav 	uid_t euid;
2427b15c8340SDag-Erling Smørgrav 	gid_t egid;
2428b15c8340SDag-Erling Smørgrav 
24291323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
2430b15c8340SDag-Erling Smørgrav 		return;
2431b15c8340SDag-Erling Smørgrav 
2432b15c8340SDag-Erling Smørgrav 	debug("multiplexing control connection");
2433b15c8340SDag-Erling Smørgrav 
2434b15c8340SDag-Erling Smørgrav 	/*
2435b15c8340SDag-Erling Smørgrav 	 * Accept connection on control socket
2436b15c8340SDag-Erling Smørgrav 	 */
2437b15c8340SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
2438b15c8340SDag-Erling Smørgrav 	addrlen = sizeof(addr);
2439b15c8340SDag-Erling Smørgrav 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
2440b15c8340SDag-Erling Smørgrav 	    &addrlen)) == -1) {
244119261079SEd Maste 		error_f("accept: %s", strerror(errno));
2442462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2443e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2444b15c8340SDag-Erling Smørgrav 		return;
2445b15c8340SDag-Erling Smørgrav 	}
2446b15c8340SDag-Erling Smørgrav 
244719261079SEd Maste 	if (getpeereid(newsock, &euid, &egid) == -1) {
244819261079SEd Maste 		error_f("getpeereid failed: %s", strerror(errno));
2449b15c8340SDag-Erling Smørgrav 		close(newsock);
2450b15c8340SDag-Erling Smørgrav 		return;
2451b15c8340SDag-Erling Smørgrav 	}
2452b15c8340SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
2453b15c8340SDag-Erling Smørgrav 		error("multiplex uid mismatch: peer euid %u != uid %u",
2454b15c8340SDag-Erling Smørgrav 		    (u_int)euid, (u_int)getuid());
2455b15c8340SDag-Erling Smørgrav 		close(newsock);
2456b15c8340SDag-Erling Smørgrav 		return;
2457b15c8340SDag-Erling Smørgrav 	}
2458*f374ba41SEd Maste 	nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT,
2459b15c8340SDag-Erling Smørgrav 	    newsock, newsock, -1, c->local_window_max,
2460b15c8340SDag-Erling Smørgrav 	    c->local_maxpacket, 0, "mux-control", 1);
2461b15c8340SDag-Erling Smørgrav 	nc->mux_rcb = c->mux_rcb;
246219261079SEd Maste 	debug3_f("new mux channel %d fd %d", nc->self, nc->sock);
2463b15c8340SDag-Erling Smørgrav 	/* establish state */
24644f52dfbbSDag-Erling Smørgrav 	nc->mux_rcb(ssh, nc);
2465b15c8340SDag-Erling Smørgrav 	/* mux state transitions must not elicit protocol messages */
2466b15c8340SDag-Erling Smørgrav 	nc->flags |= CHAN_LOCAL;
2467b15c8340SDag-Erling Smørgrav }
2468b15c8340SDag-Erling Smørgrav 
2469af12a3e7SDag-Erling Smørgrav static void
24704f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc)
2471a04a10f8SKris Kennaway {
24724f52dfbbSDag-Erling Smørgrav 	chan_fn **pre, **post;
2473f388f5efSDag-Erling Smørgrav 
24744f52dfbbSDag-Erling Smørgrav 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
24754f52dfbbSDag-Erling Smørgrav 	    (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
247619261079SEd Maste 		fatal_f("allocation failed");
2477511b41d2SMark Murray 
24784f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
24794f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
24804f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
24814f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
24824f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
24834f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
24844f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
24854f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
24864f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
24874f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
24884f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
24894f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
24904f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2491a04a10f8SKris Kennaway 
24924f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
24934f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
24944f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
24954f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
24964f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
24974f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
24984f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
24994f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
25004f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
25014f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
25024f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
25034f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2504a04a10f8SKris Kennaway 
25054f52dfbbSDag-Erling Smørgrav 	sc->channel_pre = pre;
25064f52dfbbSDag-Erling Smørgrav 	sc->channel_post = post;
2507a04a10f8SKris Kennaway }
2508a04a10f8SKris Kennaway 
2509af12a3e7SDag-Erling Smørgrav /* gc dead channels */
2510af12a3e7SDag-Erling Smørgrav static void
25114f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c)
2512af12a3e7SDag-Erling Smørgrav {
2513af12a3e7SDag-Erling Smørgrav 	if (c == NULL)
2514af12a3e7SDag-Erling Smørgrav 		return;
2515af12a3e7SDag-Erling Smørgrav 	if (c->detach_user != NULL) {
25164f52dfbbSDag-Erling Smørgrav 		if (!chan_is_dead(ssh, c, c->detach_close))
2517af12a3e7SDag-Erling Smørgrav 			return;
25182f513db7SEd Maste 
2519221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: notify user", c->self);
2520*f374ba41SEd Maste 		c->detach_user(ssh, c->self, 0, NULL);
2521af12a3e7SDag-Erling Smørgrav 		/* if we still have a callback */
2522af12a3e7SDag-Erling Smørgrav 		if (c->detach_user != NULL)
2523af12a3e7SDag-Erling Smørgrav 			return;
2524221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: user detached", c->self);
2525af12a3e7SDag-Erling Smørgrav 	}
25264f52dfbbSDag-Erling Smørgrav 	if (!chan_is_dead(ssh, c, 1))
2527af12a3e7SDag-Erling Smørgrav 		return;
2528221552e4SDag-Erling Smørgrav 	debug2("channel %d: garbage collecting", c->self);
25294f52dfbbSDag-Erling Smørgrav 	channel_free(ssh, c);
2530af12a3e7SDag-Erling Smørgrav }
2531af12a3e7SDag-Erling Smørgrav 
25324f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST };
25334f52dfbbSDag-Erling Smørgrav 
2534af12a3e7SDag-Erling Smørgrav static void
2535*f374ba41SEd Maste channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
2536a04a10f8SKris Kennaway {
25374f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
25384f52dfbbSDag-Erling Smørgrav 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
2539b15c8340SDag-Erling Smørgrav 	u_int i, oalloc;
2540a04a10f8SKris Kennaway 	Channel *c;
2541462c32cbSDag-Erling Smørgrav 	time_t now;
2542a04a10f8SKris Kennaway 
2543e4a9863fSDag-Erling Smørgrav 	now = monotime();
25444f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
25454f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2546af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2547511b41d2SMark Murray 			continue;
254838a52bd3SEd Maste 		/* Try to keep IO going while rekeying */
254938a52bd3SEd Maste 		if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN)
255038a52bd3SEd Maste 			continue;
2551b15c8340SDag-Erling Smørgrav 		if (c->delayed) {
25524f52dfbbSDag-Erling Smørgrav 			if (table == CHAN_PRE)
2553b15c8340SDag-Erling Smørgrav 				c->delayed = 0;
2554b15c8340SDag-Erling Smørgrav 			else
2555b15c8340SDag-Erling Smørgrav 				continue;
2556b15c8340SDag-Erling Smørgrav 		}
2557462c32cbSDag-Erling Smørgrav 		if (ftab[c->type] != NULL) {
2558*f374ba41SEd Maste 			if (table == CHAN_PRE &&
2559*f374ba41SEd Maste 			    c->type == SSH_CHANNEL_OPEN &&
2560*f374ba41SEd Maste 			    c->inactive_deadline != 0 && c->lastused != 0 &&
2561*f374ba41SEd Maste 			    now >= c->lastused + c->inactive_deadline) {
2562*f374ba41SEd Maste 				/* channel closed for inactivity */
2563*f374ba41SEd Maste 				verbose("channel %d: closing after %u seconds "
2564*f374ba41SEd Maste 				    "of inactivity", c->self,
2565*f374ba41SEd Maste 				    c->inactive_deadline);
2566*f374ba41SEd Maste 				channel_force_close(ssh, c, 1);
2567*f374ba41SEd Maste 			} else if (c->notbefore <= now) {
2568*f374ba41SEd Maste 				/* Run handlers that are not paused. */
25691323ec57SEd Maste 				(*ftab[c->type])(ssh, c);
2570*f374ba41SEd Maste 				/* inactivity timeouts must interrupt poll() */
2571*f374ba41SEd Maste 				if (timeout != NULL &&
2572*f374ba41SEd Maste 				    c->type == SSH_CHANNEL_OPEN &&
2573*f374ba41SEd Maste 				    c->lastused != 0 &&
2574*f374ba41SEd Maste 				    c->inactive_deadline != 0) {
2575*f374ba41SEd Maste 					ptimeout_deadline_monotime(timeout,
2576*f374ba41SEd Maste 					    c->lastused + c->inactive_deadline);
2577*f374ba41SEd Maste 				}
2578*f374ba41SEd Maste 			} else if (timeout != NULL) {
2579462c32cbSDag-Erling Smørgrav 				/*
2580*f374ba41SEd Maste 				 * Arrange for poll() wakeup when channel pause
2581*f374ba41SEd Maste 				 * timer expires.
2582462c32cbSDag-Erling Smørgrav 				 */
2583*f374ba41SEd Maste 				ptimeout_deadline_monotime(timeout,
2584*f374ba41SEd Maste 				    c->notbefore);
2585462c32cbSDag-Erling Smørgrav 			}
2586462c32cbSDag-Erling Smørgrav 		}
25874f52dfbbSDag-Erling Smørgrav 		channel_garbage_collect(ssh, c);
2588511b41d2SMark Murray 	}
2589511b41d2SMark Murray }
2590a04a10f8SKris Kennaway 
2591af12a3e7SDag-Erling Smørgrav /*
25921323ec57SEd Maste  * Create sockets before preparing IO.
25934f52dfbbSDag-Erling Smørgrav  * This is necessary for things that need to happen after reading
25941323ec57SEd Maste  * the network-input but need to be completed before IO event setup, e.g.
25951323ec57SEd Maste  * because they may create new channels.
25964f52dfbbSDag-Erling Smørgrav  */
25974f52dfbbSDag-Erling Smørgrav static void
25981323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh)
25994f52dfbbSDag-Erling Smørgrav {
26004f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
26014f52dfbbSDag-Erling Smørgrav 	Channel *c;
26024f52dfbbSDag-Erling Smørgrav 	u_int i, oalloc;
26034f52dfbbSDag-Erling Smørgrav 
26044f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26054f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
26064f52dfbbSDag-Erling Smørgrav 		if (c == NULL)
26074f52dfbbSDag-Erling Smørgrav 			continue;
26084f52dfbbSDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
26091323ec57SEd Maste 			channel_before_prepare_io_rdynamic(ssh, c);
26104f52dfbbSDag-Erling Smørgrav 	}
26114f52dfbbSDag-Erling Smørgrav }
26124f52dfbbSDag-Erling Smørgrav 
26131323ec57SEd Maste static void
26141323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c,
26151323ec57SEd Maste     u_int pollfd_offset, struct pollfd *pfd)
2616a04a10f8SKris Kennaway {
26171323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
261887c1498dSEd Maste 	debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] "
261987c1498dSEd Maste 	    "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d "
262087c1498dSEd Maste 	    "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what,
262187c1498dSEd Maste 	    c->rfd, c->wfd, c->efd, c->sock,
262287c1498dSEd Maste 	    c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3],
262387c1498dSEd Maste 	    c->io_want, c->io_ready,
262487c1498dSEd Maste 	    pollfd_offset, pfd->fd, pfd->events, pfd->revents);
26251323ec57SEd Maste #endif
2626ca3176e7SBrian Feldman }
2627ca3176e7SBrian Feldman 
26281323ec57SEd Maste /* Prepare pollfd entries for a single channel */
26291323ec57SEd Maste static void
26301323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd,
26311323ec57SEd Maste     struct pollfd *pfd, u_int npfd)
26321323ec57SEd Maste {
263387c1498dSEd Maste 	u_int ev, p = *next_pollfd;
26341323ec57SEd Maste 
26351323ec57SEd Maste 	if (c == NULL)
26361323ec57SEd Maste 		return;
26371323ec57SEd Maste 	if (p + 4 > npfd) {
26381323ec57SEd Maste 		/* Shouldn't happen */
26391323ec57SEd Maste 		fatal_f("channel %d: bad pfd offset %u (max %u)",
26401323ec57SEd Maste 		    c->self, p, npfd);
26411323ec57SEd Maste 	}
264287c1498dSEd Maste 	c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1;
26431323ec57SEd Maste 	/*
26441323ec57SEd Maste 	 * prepare c->rfd
26451323ec57SEd Maste 	 *
26461323ec57SEd Maste 	 * This is a special case, since c->rfd might be the same as
26471323ec57SEd Maste 	 * c->wfd, c->efd and/or c->sock. Handle those here if they want
26481323ec57SEd Maste 	 * IO too.
26491323ec57SEd Maste 	 */
26501323ec57SEd Maste 	if (c->rfd != -1) {
265187c1498dSEd Maste 		ev = 0;
26521323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_RFD) != 0)
265387c1498dSEd Maste 			ev |= POLLIN;
26541323ec57SEd Maste 		/* rfd == wfd */
265587c1498dSEd Maste 		if (c->wfd == c->rfd) {
265687c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_WFD) != 0)
265787c1498dSEd Maste 				ev |= POLLOUT;
265887c1498dSEd Maste 		}
26591323ec57SEd Maste 		/* rfd == efd */
266087c1498dSEd Maste 		if (c->efd == c->rfd) {
266187c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
266287c1498dSEd Maste 				ev |= POLLIN;
266387c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
266487c1498dSEd Maste 				ev |= POLLOUT;
266587c1498dSEd Maste 		}
26661323ec57SEd Maste 		/* rfd == sock */
266787c1498dSEd Maste 		if (c->sock == c->rfd) {
266887c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
266987c1498dSEd Maste 				ev |= POLLIN;
267087c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
267187c1498dSEd Maste 				ev |= POLLOUT;
267287c1498dSEd Maste 		}
267387c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
267487c1498dSEd Maste 		if (ev != 0) {
267587c1498dSEd Maste 			c->pfds[0] = p;
267687c1498dSEd Maste 			pfd[p].fd = c->rfd;
267787c1498dSEd Maste 			pfd[p].events = ev;
26781323ec57SEd Maste 			dump_channel_poll(__func__, "rfd", c, p, &pfd[p]);
26791323ec57SEd Maste 			p++;
26801323ec57SEd Maste 		}
268187c1498dSEd Maste 	}
268287c1498dSEd Maste 	/* prepare c->wfd if wanting IO and not already handled above */
26831323ec57SEd Maste 	if (c->wfd != -1 && c->rfd != c->wfd) {
268487c1498dSEd Maste 		ev = 0;
268587c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_WFD))
268687c1498dSEd Maste 			ev |= POLLOUT;
268787c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
268887c1498dSEd Maste 		if (ev != 0) {
268987c1498dSEd Maste 			c->pfds[1] = p;
26901323ec57SEd Maste 			pfd[p].fd = c->wfd;
269187c1498dSEd Maste 			pfd[p].events = ev;
26921323ec57SEd Maste 			dump_channel_poll(__func__, "wfd", c, p, &pfd[p]);
26931323ec57SEd Maste 			p++;
26941323ec57SEd Maste 		}
269587c1498dSEd Maste 	}
269687c1498dSEd Maste 	/* prepare c->efd if wanting IO and not already handled above */
26971323ec57SEd Maste 	if (c->efd != -1 && c->rfd != c->efd) {
269887c1498dSEd Maste 		ev = 0;
26991323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
270087c1498dSEd Maste 			ev |= POLLIN;
27011323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
270287c1498dSEd Maste 			ev |= POLLOUT;
270387c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
270487c1498dSEd Maste 		if (ev != 0) {
270587c1498dSEd Maste 			c->pfds[2] = p;
270687c1498dSEd Maste 			pfd[p].fd = c->efd;
270787c1498dSEd Maste 			pfd[p].events = ev;
27081323ec57SEd Maste 			dump_channel_poll(__func__, "efd", c, p, &pfd[p]);
27091323ec57SEd Maste 			p++;
27101323ec57SEd Maste 		}
271187c1498dSEd Maste 	}
271287c1498dSEd Maste 	/* prepare c->sock if wanting IO and not already handled above */
27131323ec57SEd Maste 	if (c->sock != -1 && c->rfd != c->sock) {
271487c1498dSEd Maste 		ev = 0;
271587c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
271687c1498dSEd Maste 			ev |= POLLIN;
271787c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
271887c1498dSEd Maste 			ev |= POLLOUT;
271987c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
272087c1498dSEd Maste 		if (ev != 0) {
272187c1498dSEd Maste 			c->pfds[3] = p;
27221323ec57SEd Maste 			pfd[p].fd = c->sock;
27231323ec57SEd Maste 			pfd[p].events = 0;
27241323ec57SEd Maste 			dump_channel_poll(__func__, "sock", c, p, &pfd[p]);
27251323ec57SEd Maste 			p++;
27261323ec57SEd Maste 		}
272787c1498dSEd Maste 	}
27281323ec57SEd Maste 	*next_pollfd = p;
27291323ec57SEd Maste }
27301323ec57SEd Maste 
27311323ec57SEd Maste /* * Allocate/prepare poll structure */
27321323ec57SEd Maste void
27331323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
2734*f374ba41SEd Maste     u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout)
27351323ec57SEd Maste {
27361323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
27371323ec57SEd Maste 	u_int i, oalloc, p, npfd = npfd_reserved;
27381323ec57SEd Maste 
27391323ec57SEd Maste 	channel_before_prepare_io(ssh); /* might create a new channel */
27409fce8d41SEd Maste 	/* clear out I/O flags from last poll */
27419fce8d41SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
27429fce8d41SEd Maste 		if (sc->channels[i] == NULL)
27439fce8d41SEd Maste 			continue;
27449fce8d41SEd Maste 		sc->channels[i]->io_want = sc->channels[i]->io_ready = 0;
27459fce8d41SEd Maste 	}
27461323ec57SEd Maste 	/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */
27471323ec57SEd Maste 	if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved)
27481323ec57SEd Maste 		fatal_f("too many channels"); /* shouldn't happen */
27491323ec57SEd Maste 	npfd += sc->channels_alloc * 4;
27501323ec57SEd Maste 	if (npfd > *npfd_allocp) {
27511323ec57SEd Maste 		*pfdp = xrecallocarray(*pfdp, *npfd_allocp,
27521323ec57SEd Maste 		    npfd, sizeof(**pfdp));
27531323ec57SEd Maste 		*npfd_allocp = npfd;
27541323ec57SEd Maste 	}
27551323ec57SEd Maste 	*npfd_activep = npfd_reserved;
27561323ec57SEd Maste 	oalloc = sc->channels_alloc;
27571323ec57SEd Maste 
2758*f374ba41SEd Maste 	channel_handler(ssh, CHAN_PRE, timeout);
27591323ec57SEd Maste 
27601323ec57SEd Maste 	if (oalloc != sc->channels_alloc) {
27611323ec57SEd Maste 		/* shouldn't happen */
27621323ec57SEd Maste 		fatal_f("channels_alloc changed during CHAN_PRE "
27631323ec57SEd Maste 		    "(was %u, now %u)", oalloc, sc->channels_alloc);
27641323ec57SEd Maste 	}
27651323ec57SEd Maste 
27661323ec57SEd Maste 	/* Prepare pollfd */
27671323ec57SEd Maste 	p = npfd_reserved;
27681323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
27691323ec57SEd Maste 		channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
27701323ec57SEd Maste 	*npfd_activep = p;
27711323ec57SEd Maste }
27721323ec57SEd Maste 
27731323ec57SEd Maste static void
277487c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd,
27751323ec57SEd Maste     const char *what, u_int revents_mask, u_int ready)
27761323ec57SEd Maste {
27771323ec57SEd Maste 	struct pollfd *pfd = &pfds[p];
27781323ec57SEd Maste 
27791323ec57SEd Maste 	if (fd == -1)
27801323ec57SEd Maste 		return;
278187c1498dSEd Maste 	if (p == -1 || (u_int)p >= npfd)
278287c1498dSEd Maste 		fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd);
27831323ec57SEd Maste 	dump_channel_poll(__func__, what, c, p, pfd);
27841323ec57SEd Maste 	if (pfd->fd != fd) {
27851323ec57SEd Maste 		fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
27861323ec57SEd Maste 		    "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
27871323ec57SEd Maste 		    c->rfd, c->wfd, c->efd, c->sock);
27881323ec57SEd Maste 	}
27891323ec57SEd Maste 	if ((pfd->revents & POLLNVAL) != 0) {
27901323ec57SEd Maste 		fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d",
27911323ec57SEd Maste 		    c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock);
27921323ec57SEd Maste 	}
27931323ec57SEd Maste 	if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0)
27941323ec57SEd Maste 		c->io_ready |= ready & c->io_want;
2795a04a10f8SKris Kennaway }
2796a04a10f8SKris Kennaway 
2797af12a3e7SDag-Erling Smørgrav /*
27981323ec57SEd Maste  * After poll, perform any appropriate operations for channels which have
2799af12a3e7SDag-Erling Smørgrav  * events pending.
2800af12a3e7SDag-Erling Smørgrav  */
2801a04a10f8SKris Kennaway void
28021323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
2803a04a10f8SKris Kennaway {
28041323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
280587c1498dSEd Maste 	u_int i;
280687c1498dSEd Maste 	int p;
28071323ec57SEd Maste 	Channel *c;
28081323ec57SEd Maste 
28091323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
281087c1498dSEd Maste 	for (p = 0; p < (int)npfd; p++) {
28111323ec57SEd Maste 		if (pfd[p].revents == 0)
28121323ec57SEd Maste 			continue;
28131323ec57SEd Maste 		debug_f("pfd[%u].fd %d rev 0x%04x",
28141323ec57SEd Maste 		    p, pfd[p].fd, pfd[p].revents);
28151323ec57SEd Maste 	}
28161323ec57SEd Maste #endif
28171323ec57SEd Maste 
28181323ec57SEd Maste 	/* Convert pollfd into c->io_ready */
28191323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
28201323ec57SEd Maste 		c = sc->channels[i];
282187c1498dSEd Maste 		if (c == NULL)
28221323ec57SEd Maste 			continue;
28231323ec57SEd Maste 		/* if rfd is shared with efd/sock then wfd should be too */
28241323ec57SEd Maste 		if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd &&
28251323ec57SEd Maste 		    (c->rfd == c->efd || c->rfd == c->sock)) {
28261323ec57SEd Maste 			/* Shouldn't happen */
28271323ec57SEd Maste 			fatal_f("channel %d: unexpected fds r%d w%d e%d s%d",
28281323ec57SEd Maste 			    c->self, c->rfd, c->wfd, c->efd, c->sock);
28291323ec57SEd Maste 		}
28301323ec57SEd Maste 		c->io_ready = 0;
28311323ec57SEd Maste 		/* rfd, potentially shared with wfd, efd and sock */
283287c1498dSEd Maste 		if (c->rfd != -1 && (p = c->pfds[0]) != -1) {
283387c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->rfd,
283487c1498dSEd Maste 			    "rfd", POLLIN, SSH_CHAN_IO_RFD);
28351323ec57SEd Maste 			if (c->rfd == c->wfd) {
283687c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->wfd,
283787c1498dSEd Maste 				    "wfd/r", POLLOUT, SSH_CHAN_IO_WFD);
28381323ec57SEd Maste 			}
28391323ec57SEd Maste 			if (c->rfd == c->efd) {
284087c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
284187c1498dSEd Maste 				    "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R);
284287c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
284387c1498dSEd Maste 				    "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W);
28441323ec57SEd Maste 			}
28451323ec57SEd Maste 			if (c->rfd == c->sock) {
284687c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
284787c1498dSEd Maste 				    "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R);
284887c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
284987c1498dSEd Maste 				    "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W);
28501323ec57SEd Maste 			}
285187c1498dSEd Maste 			dump_channel_poll(__func__, "rfd", c, p, pfd);
28521323ec57SEd Maste 		}
28531323ec57SEd Maste 		/* wfd */
285487c1498dSEd Maste 		if (c->wfd != -1 && c->wfd != c->rfd &&
285587c1498dSEd Maste 		    (p = c->pfds[1]) != -1) {
285687c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->wfd,
285787c1498dSEd Maste 			    "wfd", POLLOUT, SSH_CHAN_IO_WFD);
285887c1498dSEd Maste 			dump_channel_poll(__func__, "wfd", c, p, pfd);
28591323ec57SEd Maste 		}
28601323ec57SEd Maste 		/* efd */
286187c1498dSEd Maste 		if (c->efd != -1 && c->efd != c->rfd &&
286287c1498dSEd Maste 		    (p = c->pfds[2]) != -1) {
286387c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
286487c1498dSEd Maste 			    "efdr", POLLIN, SSH_CHAN_IO_EFD_R);
286587c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
286687c1498dSEd Maste 			    "efdw", POLLOUT, SSH_CHAN_IO_EFD_W);
286787c1498dSEd Maste 			dump_channel_poll(__func__, "efd", c, p, pfd);
28681323ec57SEd Maste 		}
28691323ec57SEd Maste 		/* sock */
287087c1498dSEd Maste 		if (c->sock != -1 && c->sock != c->rfd &&
287187c1498dSEd Maste 		    (p = c->pfds[3]) != -1) {
287287c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
287387c1498dSEd Maste 			    "sockr", POLLIN, SSH_CHAN_IO_SOCK_R);
287487c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
287587c1498dSEd Maste 			    "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W);
287687c1498dSEd Maste 			dump_channel_poll(__func__, "sock", c, p, pfd);
28771323ec57SEd Maste 		}
28781323ec57SEd Maste 	}
28791323ec57SEd Maste 	channel_handler(ssh, CHAN_POST, NULL);
2880511b41d2SMark Murray }
2881511b41d2SMark Murray 
28824f52dfbbSDag-Erling Smørgrav /*
28834f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open or draining c->input.
28844f52dfbbSDag-Erling Smørgrav  */
28854f52dfbbSDag-Erling Smørgrav static void
28864f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c)
28874f52dfbbSDag-Erling Smørgrav {
28884f52dfbbSDag-Erling Smørgrav 	size_t len, plen;
28894f52dfbbSDag-Erling Smørgrav 	const u_char *pkt;
28904f52dfbbSDag-Erling Smørgrav 	int r;
28914f52dfbbSDag-Erling Smørgrav 
28924f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->input)) == 0) {
28934f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
28944f52dfbbSDag-Erling Smørgrav 			/*
28954f52dfbbSDag-Erling Smørgrav 			 * input-buffer is empty and read-socket shutdown:
28964f52dfbbSDag-Erling Smørgrav 			 * tell peer, that we will not send more data:
28974f52dfbbSDag-Erling Smørgrav 			 * send IEOF.
28984f52dfbbSDag-Erling Smørgrav 			 * hack for extended data: delay EOF if EFD still
28994f52dfbbSDag-Erling Smørgrav 			 * in use.
29004f52dfbbSDag-Erling Smørgrav 			 */
29014f52dfbbSDag-Erling Smørgrav 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
29024f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
29034f52dfbbSDag-Erling Smørgrav 				    "ibuf_empty delayed efd %d/(%zu)",
29044f52dfbbSDag-Erling Smørgrav 				    c->self, c->efd, sshbuf_len(c->extended));
29054f52dfbbSDag-Erling Smørgrav 			else
29064f52dfbbSDag-Erling Smørgrav 				chan_ibuf_empty(ssh, c);
29074f52dfbbSDag-Erling Smørgrav 		}
29084f52dfbbSDag-Erling Smørgrav 		return;
29094f52dfbbSDag-Erling Smørgrav 	}
29104f52dfbbSDag-Erling Smørgrav 
29114f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
291219261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
29134f52dfbbSDag-Erling Smørgrav 
29144f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
29154f52dfbbSDag-Erling Smørgrav 		/* Check datagram will fit; drop if not */
29164f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
291719261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
29184f52dfbbSDag-Erling Smørgrav 		/*
29194f52dfbbSDag-Erling Smørgrav 		 * XXX this does tail-drop on the datagram queue which is
29204f52dfbbSDag-Erling Smørgrav 		 * usually suboptimal compared to head-drop. Better to have
29214f52dfbbSDag-Erling Smørgrav 		 * backpressure at read time? (i.e. read + discard)
29224f52dfbbSDag-Erling Smørgrav 		 */
29234f52dfbbSDag-Erling Smørgrav 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
29244f52dfbbSDag-Erling Smørgrav 			debug("channel %d: datagram too big", c->self);
29254f52dfbbSDag-Erling Smørgrav 			return;
29264f52dfbbSDag-Erling Smørgrav 		}
29274f52dfbbSDag-Erling Smørgrav 		/* Enqueue it */
29284f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
29294f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29304f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
293119261079SEd Maste 		    (r = sshpkt_send(ssh)) != 0)
293219261079SEd Maste 			fatal_fr(r, "channel %i: send datagram", c->self);
29334f52dfbbSDag-Erling Smørgrav 		c->remote_window -= plen;
29344f52dfbbSDag-Erling Smørgrav 		return;
29354f52dfbbSDag-Erling Smørgrav 	}
29364f52dfbbSDag-Erling Smørgrav 
29374f52dfbbSDag-Erling Smørgrav 	/* Enqueue packet for buffered data. */
29384f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
29394f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
29404f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
29414f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
29424f52dfbbSDag-Erling Smørgrav 	if (len == 0)
29434f52dfbbSDag-Erling Smørgrav 		return;
29444f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
29454f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29464f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
294719261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
294819261079SEd Maste 		fatal_fr(r, "channel %i: send data", c->self);
29494f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->input, len)) != 0)
295019261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
29514f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
29524f52dfbbSDag-Erling Smørgrav }
29534f52dfbbSDag-Erling Smørgrav 
29544f52dfbbSDag-Erling Smørgrav /*
29554f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open c->extended in read mode.
29564f52dfbbSDag-Erling Smørgrav  */
29574f52dfbbSDag-Erling Smørgrav static void
29584f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
29594f52dfbbSDag-Erling Smørgrav {
29604f52dfbbSDag-Erling Smørgrav 	size_t len;
29614f52dfbbSDag-Erling Smørgrav 	int r;
29624f52dfbbSDag-Erling Smørgrav 
29634f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->extended)) == 0)
29644f52dfbbSDag-Erling Smørgrav 		return;
29654f52dfbbSDag-Erling Smørgrav 
29664f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
29674f52dfbbSDag-Erling Smørgrav 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
29684f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
29694f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
29704f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
29714f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
29724f52dfbbSDag-Erling Smørgrav 	if (len == 0)
29734f52dfbbSDag-Erling Smørgrav 		return;
29744f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
297519261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
29764f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
29774f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29784f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
29794f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
298019261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
298119261079SEd Maste 		fatal_fr(r, "channel %i: data", c->self);
29824f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->extended, len)) != 0)
298319261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
29844f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
29854f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: sent ext data %zu", c->self, len);
29864f52dfbbSDag-Erling Smørgrav }
2987af12a3e7SDag-Erling Smørgrav 
2988ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */
2989511b41d2SMark Murray void
29904f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh)
2991511b41d2SMark Murray {
29924f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
2993a04a10f8SKris Kennaway 	Channel *c;
29944f52dfbbSDag-Erling Smørgrav 	u_int i;
2995511b41d2SMark Murray 
29964f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
29974f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2998af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2999af12a3e7SDag-Erling Smørgrav 			continue;
3000511b41d2SMark Murray 
3001af12a3e7SDag-Erling Smørgrav 		/*
3002af12a3e7SDag-Erling Smørgrav 		 * We are only interested in channels that can have buffered
3003af12a3e7SDag-Erling Smørgrav 		 * incoming data.
3004af12a3e7SDag-Erling Smørgrav 		 */
3005a04a10f8SKris Kennaway 		if (c->type != SSH_CHANNEL_OPEN)
3006511b41d2SMark Murray 			continue;
30074f52dfbbSDag-Erling Smørgrav 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
3008ca3176e7SBrian Feldman 			/* XXX is this true? */
30094f52dfbbSDag-Erling Smørgrav 			debug3("channel %d: will not send data after close",
30104f52dfbbSDag-Erling Smørgrav 			    c->self);
3011511b41d2SMark Murray 			continue;
3012511b41d2SMark Murray 		}
3013511b41d2SMark Murray 
3014511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
30154f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_OPEN ||
30164f52dfbbSDag-Erling Smørgrav 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
30174f52dfbbSDag-Erling Smørgrav 			channel_output_poll_input_open(ssh, c);
3018a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
30194f52dfbbSDag-Erling Smørgrav 		if (!(c->flags & CHAN_EOF_SENT) &&
30204f52dfbbSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_READ)
30214f52dfbbSDag-Erling Smørgrav 			channel_output_poll_extended_read(ssh, c);
3022511b41d2SMark Murray 	}
3023511b41d2SMark Murray }
3024511b41d2SMark Murray 
3025ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support  */
3026ca86bcf2SDag-Erling Smørgrav 
3027ca86bcf2SDag-Erling Smørgrav /*
3028ca86bcf2SDag-Erling Smørgrav  * When multiplexing channel messages for mux clients we have to deal
3029ca86bcf2SDag-Erling Smørgrav  * with downstream messages from the mux client and upstream messages
3030ca86bcf2SDag-Erling Smørgrav  * from the ssh server:
3031ca86bcf2SDag-Erling Smørgrav  * 1) Handling downstream messages is straightforward and happens
3032ca86bcf2SDag-Erling Smørgrav  *    in channel_proxy_downstream():
3033ca86bcf2SDag-Erling Smørgrav  *    - We forward all messages (mostly) unmodified to the server.
3034ca86bcf2SDag-Erling Smørgrav  *    - However, in order to route messages from upstream to the correct
3035ca86bcf2SDag-Erling Smørgrav  *      downstream client, we have to replace the channel IDs used by the
3036ca86bcf2SDag-Erling Smørgrav  *      mux clients with a unique channel ID because the mux clients might
3037ca86bcf2SDag-Erling Smørgrav  *      use conflicting channel IDs.
3038ca86bcf2SDag-Erling Smørgrav  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
3039ca86bcf2SDag-Erling Smørgrav  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
3040ca86bcf2SDag-Erling Smørgrav  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
3041ca86bcf2SDag-Erling Smørgrav  *      with the newly allocated channel ID.
3042ca86bcf2SDag-Erling Smørgrav  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
3043190cef3dSDag-Erling Smørgrav  *    channels and processed by channel_proxy_upstream(). The local channel ID
3044ca86bcf2SDag-Erling Smørgrav  *    is then translated back to the original mux client ID.
3045ca86bcf2SDag-Erling Smørgrav  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
3046ca86bcf2SDag-Erling Smørgrav  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
3047ca86bcf2SDag-Erling Smørgrav  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
3048ca86bcf2SDag-Erling Smørgrav  *    downstream mux client are removed.
3049ca86bcf2SDag-Erling Smørgrav  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
3050ca86bcf2SDag-Erling Smørgrav  *    requires more work, because they are not addressed to a specific
3051ca86bcf2SDag-Erling Smørgrav  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
3052ca86bcf2SDag-Erling Smørgrav  *    out whether the request is addressed to the local client or a
3053ca86bcf2SDag-Erling Smørgrav  *    specific downstream client based on the listen-address/port.
3054190cef3dSDag-Erling Smørgrav  * 6) Agent and X11-Forwarding have a similar problem and are currently
3055ca86bcf2SDag-Erling Smørgrav  *    not supported as the matching session/channel cannot be identified
3056ca86bcf2SDag-Erling Smørgrav  *    easily.
3057ca86bcf2SDag-Erling Smørgrav  */
3058ca86bcf2SDag-Erling Smørgrav 
3059ca86bcf2SDag-Erling Smørgrav /*
3060ca86bcf2SDag-Erling Smørgrav  * receive packets from downstream mux clients:
3061ca86bcf2SDag-Erling Smørgrav  * channel callback fired on read from mux client, creates
3062ca86bcf2SDag-Erling Smørgrav  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
3063ca86bcf2SDag-Erling Smørgrav  * on channel creation.
3064ca86bcf2SDag-Erling Smørgrav  */
3065ca86bcf2SDag-Erling Smørgrav int
30664f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
3067ca86bcf2SDag-Erling Smørgrav {
3068ca86bcf2SDag-Erling Smørgrav 	Channel *c = NULL;
3069ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *original = NULL, *modified = NULL;
3070ca86bcf2SDag-Erling Smørgrav 	const u_char *cp;
3071ca86bcf2SDag-Erling Smørgrav 	char *ctype = NULL, *listen_host = NULL;
3072ca86bcf2SDag-Erling Smørgrav 	u_char type;
3073ca86bcf2SDag-Erling Smørgrav 	size_t have;
30744f52dfbbSDag-Erling Smørgrav 	int ret = -1, r;
3075ca86bcf2SDag-Erling Smørgrav 	u_int id, remote_id, listen_port;
3076ca86bcf2SDag-Erling Smørgrav 
30774f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(downstream->input, stderr); */
30784f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
3079ca86bcf2SDag-Erling Smørgrav 	    != 0) {
308019261079SEd Maste 		error_fr(r, "parse");
3081ca86bcf2SDag-Erling Smørgrav 		return -1;
3082ca86bcf2SDag-Erling Smørgrav 	}
3083ca86bcf2SDag-Erling Smørgrav 	if (have < 2) {
308419261079SEd Maste 		error_f("short message");
3085ca86bcf2SDag-Erling Smørgrav 		return -1;
3086ca86bcf2SDag-Erling Smørgrav 	}
3087ca86bcf2SDag-Erling Smørgrav 	type = cp[1];
3088ca86bcf2SDag-Erling Smørgrav 	/* skip padlen + type */
3089ca86bcf2SDag-Erling Smørgrav 	cp += 2;
3090ca86bcf2SDag-Erling Smørgrav 	have -= 2;
3091ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
309219261079SEd Maste 		debug3_f("channel %u: down->up: type %u",
3093ca86bcf2SDag-Erling Smørgrav 		    downstream->self, type);
3094ca86bcf2SDag-Erling Smørgrav 
3095ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3096ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN:
3097ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3098ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
309919261079SEd Maste 			error_f("alloc");
3100ca86bcf2SDag-Erling Smørgrav 			goto out;
3101ca86bcf2SDag-Erling Smørgrav 		}
3102ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
3103ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
310419261079SEd Maste 			error_fr(r, "parse");
3105ca86bcf2SDag-Erling Smørgrav 			goto out;
3106ca86bcf2SDag-Erling Smørgrav 		}
3107*f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3108ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, ctype, 1);
3109ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3110ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;	/* original downstream id */
3111ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
3112ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3113ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
311419261079SEd Maste 			error_fr(r, "compose");
31154f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3116ca86bcf2SDag-Erling Smørgrav 			goto out;
3117ca86bcf2SDag-Erling Smørgrav 		}
3118ca86bcf2SDag-Erling Smørgrav 		break;
3119ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3120ca86bcf2SDag-Erling Smørgrav 		/*
3121ca86bcf2SDag-Erling Smørgrav 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
3122ca86bcf2SDag-Erling Smørgrav 		 * need to parse 'remote_id' instead of 'ctype'.
3123ca86bcf2SDag-Erling Smørgrav 		 */
3124ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3125ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
312619261079SEd Maste 			error_f("alloc");
3127ca86bcf2SDag-Erling Smørgrav 			goto out;
3128ca86bcf2SDag-Erling Smørgrav 		}
3129ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
3130ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
313119261079SEd Maste 			error_fr(r, "parse");
3132ca86bcf2SDag-Erling Smørgrav 			goto out;
3133ca86bcf2SDag-Erling Smørgrav 		}
3134*f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3135ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
3136ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3137ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;
3138ca86bcf2SDag-Erling Smørgrav 		c->remote_id = remote_id;
31394f52dfbbSDag-Erling Smørgrav 		c->have_remote_id = 1;
3140ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
3141ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3142ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
314319261079SEd Maste 			error_fr(r, "compose");
31444f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3145ca86bcf2SDag-Erling Smørgrav 			goto out;
3146ca86bcf2SDag-Erling Smørgrav 		}
3147ca86bcf2SDag-Erling Smørgrav 		break;
3148ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_GLOBAL_REQUEST:
3149ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL) {
315019261079SEd Maste 			error_f("alloc");
3151ca86bcf2SDag-Erling Smørgrav 			goto out;
3152ca86bcf2SDag-Erling Smørgrav 		}
3153ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
315419261079SEd Maste 			error_fr(r, "parse");
3155ca86bcf2SDag-Erling Smørgrav 			goto out;
3156ca86bcf2SDag-Erling Smørgrav 		}
3157ca86bcf2SDag-Erling Smørgrav 		if (strcmp(ctype, "tcpip-forward") != 0) {
315819261079SEd Maste 			error_f("unsupported request %s", ctype);
3159ca86bcf2SDag-Erling Smørgrav 			goto out;
3160ca86bcf2SDag-Erling Smørgrav 		}
3161ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
3162ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
3163ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
316419261079SEd Maste 			error_fr(r, "parse");
3165ca86bcf2SDag-Erling Smørgrav 			goto out;
3166ca86bcf2SDag-Erling Smørgrav 		}
3167ca86bcf2SDag-Erling Smørgrav 		if (listen_port > 65535) {
316819261079SEd Maste 			error_f("tcpip-forward for %s: bad port %u",
316919261079SEd Maste 			    listen_host, listen_port);
3170ca86bcf2SDag-Erling Smørgrav 			goto out;
3171ca86bcf2SDag-Erling Smørgrav 		}
3172ca86bcf2SDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
3173190cef3dSDag-Erling Smørgrav 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1,
31744f52dfbbSDag-Erling Smørgrav 		    listen_host, NULL, (int)listen_port, downstream);
3175ca86bcf2SDag-Erling Smørgrav 		listen_host = NULL;
3176ca86bcf2SDag-Erling Smørgrav 		break;
3177ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3178ca86bcf2SDag-Erling Smørgrav 		if (have < 4)
3179ca86bcf2SDag-Erling Smørgrav 			break;
3180ca86bcf2SDag-Erling Smørgrav 		remote_id = PEEK_U32(cp);
31814f52dfbbSDag-Erling Smørgrav 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
3182ca86bcf2SDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD)
31834f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
3184ca86bcf2SDag-Erling Smørgrav 			else
3185ca86bcf2SDag-Erling Smørgrav 				c->flags |= CHAN_CLOSE_SENT;
3186ca86bcf2SDag-Erling Smørgrav 		}
3187ca86bcf2SDag-Erling Smørgrav 		break;
3188ca86bcf2SDag-Erling Smørgrav 	}
3189ca86bcf2SDag-Erling Smørgrav 	if (modified) {
3190ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3191ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
3192ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
319319261079SEd Maste 			error_fr(r, "send");
3194ca86bcf2SDag-Erling Smørgrav 			goto out;
3195ca86bcf2SDag-Erling Smørgrav 		}
3196ca86bcf2SDag-Erling Smørgrav 	} else {
3197ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3198ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
3199ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
320019261079SEd Maste 			error_fr(r, "send");
3201ca86bcf2SDag-Erling Smørgrav 			goto out;
3202ca86bcf2SDag-Erling Smørgrav 		}
3203ca86bcf2SDag-Erling Smørgrav 	}
3204ca86bcf2SDag-Erling Smørgrav 	ret = 0;
3205ca86bcf2SDag-Erling Smørgrav  out:
3206ca86bcf2SDag-Erling Smørgrav 	free(ctype);
3207ca86bcf2SDag-Erling Smørgrav 	free(listen_host);
3208ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(original);
3209ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(modified);
3210ca86bcf2SDag-Erling Smørgrav 	return ret;
3211ca86bcf2SDag-Erling Smørgrav }
3212ca86bcf2SDag-Erling Smørgrav 
3213ca86bcf2SDag-Erling Smørgrav /*
3214ca86bcf2SDag-Erling Smørgrav  * receive packets from upstream server and de-multiplex packets
3215ca86bcf2SDag-Erling Smørgrav  * to correct downstream:
3216ca86bcf2SDag-Erling Smørgrav  * implemented as a helper for channel input handlers,
3217ca86bcf2SDag-Erling Smørgrav  * replaces local (proxy) channel ID with downstream channel ID.
3218ca86bcf2SDag-Erling Smørgrav  */
3219ca86bcf2SDag-Erling Smørgrav int
32204f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
3221ca86bcf2SDag-Erling Smørgrav {
3222ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *b = NULL;
3223ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;
3224ca86bcf2SDag-Erling Smørgrav 	const u_char *cp = NULL;
3225ca86bcf2SDag-Erling Smørgrav 	size_t len;
3226ca86bcf2SDag-Erling Smørgrav 	int r;
3227ca86bcf2SDag-Erling Smørgrav 
3228ca86bcf2SDag-Erling Smørgrav 	/*
3229ca86bcf2SDag-Erling Smørgrav 	 * When receiving packets from the peer we need to check whether we
3230ca86bcf2SDag-Erling Smørgrav 	 * need to forward the packets to the mux client. In this case we
3231190cef3dSDag-Erling Smørgrav 	 * restore the original channel id and keep track of CLOSE messages,
3232ca86bcf2SDag-Erling Smørgrav 	 * so we can cleanup the channel.
3233ca86bcf2SDag-Erling Smørgrav 	 */
3234ca86bcf2SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
3235ca86bcf2SDag-Erling Smørgrav 		return 0;
3236ca86bcf2SDag-Erling Smørgrav 	if ((downstream = c->mux_ctx) == NULL)
3237ca86bcf2SDag-Erling Smørgrav 		return 0;
3238ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3239ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3240ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_DATA:
3241ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EOF:
3242ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
3243ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3244ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
3245ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3246ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_SUCCESS:
3247ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_FAILURE:
3248ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_REQUEST:
3249ca86bcf2SDag-Erling Smørgrav 		break;
3250ca86bcf2SDag-Erling Smørgrav 	default:
325119261079SEd Maste 		debug2_f("channel %u: unsupported type %u", c->self, type);
3252ca86bcf2SDag-Erling Smørgrav 		return 0;
3253ca86bcf2SDag-Erling Smørgrav 	}
3254ca86bcf2SDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
325519261079SEd Maste 		error_f("alloc reply");
3256ca86bcf2SDag-Erling Smørgrav 		goto out;
3257ca86bcf2SDag-Erling Smørgrav 	}
3258ca86bcf2SDag-Erling Smørgrav 	/* get remaining payload (after id) */
3259ca86bcf2SDag-Erling Smørgrav 	cp = sshpkt_ptr(ssh, &len);
3260ca86bcf2SDag-Erling Smørgrav 	if (cp == NULL) {
326119261079SEd Maste 		error_f("no packet");
3262ca86bcf2SDag-Erling Smørgrav 		goto out;
3263ca86bcf2SDag-Erling Smørgrav 	}
3264ca86bcf2SDag-Erling Smørgrav 	/* translate id and send to muxclient */
3265ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
3266ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u8(b, type)) != 0 ||
3267ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
3268ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put(b, cp, len)) != 0 ||
32694f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
327019261079SEd Maste 		error_fr(r, "compose muxclient");
3271ca86bcf2SDag-Erling Smørgrav 		goto out;
3272ca86bcf2SDag-Erling Smørgrav 	}
3273ca86bcf2SDag-Erling Smørgrav 	/* sshbuf_dump(b, stderr); */
3274ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
327519261079SEd Maste 		debug3_f("channel %u: up->down: type %u", c->self, type);
3276ca86bcf2SDag-Erling Smørgrav  out:
3277ca86bcf2SDag-Erling Smørgrav 	/* update state */
3278ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3279ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3280ca86bcf2SDag-Erling Smørgrav 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
32814f52dfbbSDag-Erling Smørgrav 		if (cp && len > 4) {
3282ca86bcf2SDag-Erling Smørgrav 			c->remote_id = PEEK_U32(cp);
32834f52dfbbSDag-Erling Smørgrav 			c->have_remote_id = 1;
32844f52dfbbSDag-Erling Smørgrav 		}
3285ca86bcf2SDag-Erling Smørgrav 		break;
3286ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3287ca86bcf2SDag-Erling Smørgrav 		if (c->flags & CHAN_CLOSE_SENT)
32884f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3289ca86bcf2SDag-Erling Smørgrav 		else
3290ca86bcf2SDag-Erling Smørgrav 			c->flags |= CHAN_CLOSE_RCVD;
3291ca86bcf2SDag-Erling Smørgrav 		break;
3292ca86bcf2SDag-Erling Smørgrav 	}
3293ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(b);
3294ca86bcf2SDag-Erling Smørgrav 	return 1;
3295ca86bcf2SDag-Erling Smørgrav }
3296af12a3e7SDag-Erling Smørgrav 
3297af12a3e7SDag-Erling Smørgrav /* -- protocol input */
3298511b41d2SMark Murray 
32994f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */
33004f52dfbbSDag-Erling Smørgrav static int
33014f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what)
3302511b41d2SMark Murray {
33034f52dfbbSDag-Erling Smørgrav 	u_int32_t id;
33044f52dfbbSDag-Erling Smørgrav 	int r;
33054f52dfbbSDag-Erling Smørgrav 
33064f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
330719261079SEd Maste 		error_r(r, "%s: parse id", where);
33084f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
33094f52dfbbSDag-Erling Smørgrav 	}
33104f52dfbbSDag-Erling Smørgrav 	if (id > INT_MAX) {
331119261079SEd Maste 		error_r(r, "%s: bad channel id %u", where, id);
33124f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
33134f52dfbbSDag-Erling Smørgrav 	}
33144f52dfbbSDag-Erling Smørgrav 	return (int)id;
33154f52dfbbSDag-Erling Smørgrav }
33164f52dfbbSDag-Erling Smørgrav 
33174f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */
33184f52dfbbSDag-Erling Smørgrav static Channel *
33194f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
33204f52dfbbSDag-Erling Smørgrav {
33214f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, where, what);
3322a04a10f8SKris Kennaway 	Channel *c;
3323511b41d2SMark Murray 
33244f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
33254f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh,
33264f52dfbbSDag-Erling Smørgrav 		    "%s packet referred to nonexistent channel %d", what, id);
33274f52dfbbSDag-Erling Smørgrav 	}
33284f52dfbbSDag-Erling Smørgrav 	return c;
33294f52dfbbSDag-Erling Smørgrav }
33304f52dfbbSDag-Erling Smørgrav 
33314f52dfbbSDag-Erling Smørgrav int
33324f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
33334f52dfbbSDag-Erling Smørgrav {
33344f52dfbbSDag-Erling Smørgrav 	const u_char *data;
33354f52dfbbSDag-Erling Smørgrav 	size_t data_len, win_len;
33364f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
33374f52dfbbSDag-Erling Smørgrav 	int r;
33384f52dfbbSDag-Erling Smørgrav 
33394f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3340ca86bcf2SDag-Erling Smørgrav 		return 0;
3341511b41d2SMark Murray 
3342511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
3343a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
33444f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
33454f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
3346a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
3347bc5531deSDag-Erling Smørgrav 		return 0;
3348511b41d2SMark Murray 
3349511b41d2SMark Murray 	/* Get the data. */
335019261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
335119261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0)
335219261079SEd Maste 		fatal_fr(r, "channel %i: get data", c->self);
33534f52dfbbSDag-Erling Smørgrav 
3354e2f6069cSDag-Erling Smørgrav 	win_len = data_len;
3355e2f6069cSDag-Erling Smørgrav 	if (c->datagram)
3356e2f6069cSDag-Erling Smørgrav 		win_len += 4;  /* string length header */
3357a04a10f8SKris Kennaway 
3358476cd3b2SDag-Erling Smørgrav 	/*
33594f52dfbbSDag-Erling Smørgrav 	 * The sending side reduces its window as it sends data, so we
33604f52dfbbSDag-Erling Smørgrav 	 * must 'fake' consumption of the data in order to ensure that window
33614f52dfbbSDag-Erling Smørgrav 	 * updates are sent back. Otherwise the connection might deadlock.
3362476cd3b2SDag-Erling Smørgrav 	 */
33634f52dfbbSDag-Erling Smørgrav 	if (c->ostate != CHAN_OUTPUT_OPEN) {
3364e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3365e2f6069cSDag-Erling Smørgrav 		c->local_consumed += win_len;
3366bc5531deSDag-Erling Smørgrav 		return 0;
3367476cd3b2SDag-Erling Smørgrav 	}
3368476cd3b2SDag-Erling Smørgrav 
3369e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_maxpacket) {
33704f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd big packet %zu, maxpack %u",
3371e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_maxpacket);
33724f52dfbbSDag-Erling Smørgrav 		return 0;
3373a04a10f8SKris Kennaway 	}
3374e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_window) {
33754f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much data %zu, win %u",
3376e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_window);
3377bc5531deSDag-Erling Smørgrav 		return 0;
3378a04a10f8SKris Kennaway 	}
3379e2f6069cSDag-Erling Smørgrav 	c->local_window -= win_len;
33804f52dfbbSDag-Erling Smørgrav 
33814f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
33824f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
338319261079SEd Maste 			fatal_fr(r, "channel %i: append datagram", c->self);
33844f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
338519261079SEd Maste 		fatal_fr(r, "channel %i: append data", c->self);
33864f52dfbbSDag-Erling Smørgrav 
3387bc5531deSDag-Erling Smørgrav 	return 0;
3388511b41d2SMark Murray }
3389af12a3e7SDag-Erling Smørgrav 
3390bc5531deSDag-Erling Smørgrav int
33914f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3392a04a10f8SKris Kennaway {
33934f52dfbbSDag-Erling Smørgrav 	const u_char *data;
33944f52dfbbSDag-Erling Smørgrav 	size_t data_len;
33954f52dfbbSDag-Erling Smørgrav 	u_int32_t tcode;
33964f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
33974f52dfbbSDag-Erling Smørgrav 	int r;
3398a04a10f8SKris Kennaway 
33994f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3400ca86bcf2SDag-Erling Smørgrav 		return 0;
3401a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
34024f52dfbbSDag-Erling Smørgrav 		logit("channel %d: ext data for non open", c->self);
3403bc5531deSDag-Erling Smørgrav 		return 0;
3404a04a10f8SKris Kennaway 	}
340580628bacSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
340619261079SEd Maste 		if (ssh->compat & SSH_BUG_EXTEOF)
34074f52dfbbSDag-Erling Smørgrav 			debug("channel %d: accepting ext data after eof",
34084f52dfbbSDag-Erling Smørgrav 			    c->self);
340980628bacSDag-Erling Smørgrav 		else
34104f52dfbbSDag-Erling Smørgrav 			ssh_packet_disconnect(ssh, "Received extended_data "
34114f52dfbbSDag-Erling Smørgrav 			    "after EOF on channel %d.", c->self);
341280628bacSDag-Erling Smørgrav 	}
34134f52dfbbSDag-Erling Smørgrav 
34144f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
341519261079SEd Maste 		error_fr(r, "parse tcode");
34164f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
34174f52dfbbSDag-Erling Smørgrav 	}
3418a04a10f8SKris Kennaway 	if (c->efd == -1 ||
3419a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3420a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3421221552e4SDag-Erling Smørgrav 		logit("channel %d: bad ext data", c->self);
3422bc5531deSDag-Erling Smørgrav 		return 0;
3423a04a10f8SKris Kennaway 	}
342419261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
342519261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
342619261079SEd Maste 		error_fr(r, "parse data");
34274f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
34284f52dfbbSDag-Erling Smørgrav 	}
34294f52dfbbSDag-Erling Smørgrav 
3430a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
34314f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3432a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
3433bc5531deSDag-Erling Smørgrav 		return 0;
3434a04a10f8SKris Kennaway 	}
34354f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
34364f52dfbbSDag-Erling Smørgrav 	/* XXX sshpkt_getb? */
34374f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
343819261079SEd Maste 		error_fr(r, "append");
3439a04a10f8SKris Kennaway 	c->local_window -= data_len;
3440bc5531deSDag-Erling Smørgrav 	return 0;
3441a04a10f8SKris Kennaway }
3442a04a10f8SKris Kennaway 
3443bc5531deSDag-Erling Smørgrav int
34444f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3445a04a10f8SKris Kennaway {
34464f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
344719261079SEd Maste 	int r;
3448a04a10f8SKris Kennaway 
344919261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
345019261079SEd Maste 		error_fr(r, "parse data");
345119261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid ieof message");
345219261079SEd Maste 	}
34534f52dfbbSDag-Erling Smørgrav 
34544f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3455ca86bcf2SDag-Erling Smørgrav 		return 0;
34564f52dfbbSDag-Erling Smørgrav 	chan_rcvd_ieof(ssh, c);
3457af12a3e7SDag-Erling Smørgrav 
3458af12a3e7SDag-Erling Smørgrav 	/* XXX force input close */
3459af12a3e7SDag-Erling Smørgrav 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3460af12a3e7SDag-Erling Smørgrav 		debug("channel %d: FORCE input drain", c->self);
3461af12a3e7SDag-Erling Smørgrav 		c->istate = CHAN_INPUT_WAIT_DRAIN;
34624f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->input) == 0)
34634f52dfbbSDag-Erling Smørgrav 			chan_ibuf_empty(ssh, c);
3464af12a3e7SDag-Erling Smørgrav 	}
3465bc5531deSDag-Erling Smørgrav 	return 0;
3466a04a10f8SKris Kennaway }
3467511b41d2SMark Murray 
3468bc5531deSDag-Erling Smørgrav int
34694f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3470511b41d2SMark Murray {
34714f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
347219261079SEd Maste 	int r;
3473511b41d2SMark Murray 
34744f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3475ca86bcf2SDag-Erling Smørgrav 		return 0;
347619261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
347719261079SEd Maste 		error_fr(r, "parse data");
347819261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid oclose message");
347919261079SEd Maste 	}
34804f52dfbbSDag-Erling Smørgrav 	chan_rcvd_oclose(ssh, c);
3481bc5531deSDag-Erling Smørgrav 	return 0;
3482511b41d2SMark Murray }
3483511b41d2SMark Murray 
3484bc5531deSDag-Erling Smørgrav int
34854f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3486a04a10f8SKris Kennaway {
34874f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
34884f52dfbbSDag-Erling Smørgrav 	u_int32_t remote_window, remote_maxpacket;
34894f52dfbbSDag-Erling Smørgrav 	int r;
3490af12a3e7SDag-Erling Smørgrav 
34914f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3492ca86bcf2SDag-Erling Smørgrav 		return 0;
3493ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
349419261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open confirmation for "
34954f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
34964f52dfbbSDag-Erling Smørgrav 	/*
34974f52dfbbSDag-Erling Smørgrav 	 * Record the remote channel number and mark that the channel
34984f52dfbbSDag-Erling Smørgrav 	 * is now open.
34994f52dfbbSDag-Erling Smørgrav 	 */
35004f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
35014f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
350219261079SEd Maste 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 ||
350319261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
350419261079SEd Maste 		error_fr(r, "window/maxpacket");
350519261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open confirmation message");
35064f52dfbbSDag-Erling Smørgrav 	}
3507a04a10f8SKris Kennaway 
35084f52dfbbSDag-Erling Smørgrav 	c->have_remote_id = 1;
35094f52dfbbSDag-Erling Smørgrav 	c->remote_window = remote_window;
35104f52dfbbSDag-Erling Smørgrav 	c->remote_maxpacket = remote_maxpacket;
35114f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
3512d4af9e69SDag-Erling Smørgrav 	if (c->open_confirm) {
351319261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
35144f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
351519261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3516a04a10f8SKris Kennaway 	}
3517*f374ba41SEd Maste 	c->lastused = monotime();
3518221552e4SDag-Erling Smørgrav 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3519a04a10f8SKris Kennaway 	    c->remote_window, c->remote_maxpacket);
3520bc5531deSDag-Erling Smørgrav 	return 0;
3521af12a3e7SDag-Erling Smørgrav }
3522af12a3e7SDag-Erling Smørgrav 
3523af12a3e7SDag-Erling Smørgrav static char *
3524af12a3e7SDag-Erling Smørgrav reason2txt(int reason)
3525af12a3e7SDag-Erling Smørgrav {
3526af12a3e7SDag-Erling Smørgrav 	switch (reason) {
3527af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3528af12a3e7SDag-Erling Smørgrav 		return "administratively prohibited";
3529af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_CONNECT_FAILED:
3530af12a3e7SDag-Erling Smørgrav 		return "connect failed";
3531af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3532af12a3e7SDag-Erling Smørgrav 		return "unknown channel type";
3533af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3534af12a3e7SDag-Erling Smørgrav 		return "resource shortage";
3535af12a3e7SDag-Erling Smørgrav 	}
3536af12a3e7SDag-Erling Smørgrav 	return "unknown reason";
3537a04a10f8SKris Kennaway }
3538a04a10f8SKris Kennaway 
3539bc5531deSDag-Erling Smørgrav int
35404f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3541a04a10f8SKris Kennaway {
35424f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
35434f52dfbbSDag-Erling Smørgrav 	u_int32_t reason;
35444f52dfbbSDag-Erling Smørgrav 	char *msg = NULL;
35454f52dfbbSDag-Erling Smørgrav 	int r;
3546a04a10f8SKris Kennaway 
35474f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3548ca86bcf2SDag-Erling Smørgrav 		return 0;
3549ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
355019261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open failure for "
35514f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
35524f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
355319261079SEd Maste 		error_fr(r, "parse reason");
355419261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
3555ca3176e7SBrian Feldman 	}
35564f52dfbbSDag-Erling Smørgrav 	/* skip language */
35574f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
355819261079SEd Maste 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 ||
355919261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
356019261079SEd Maste 		error_fr(r, "parse msg/lang");
356119261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
35624f52dfbbSDag-Erling Smørgrav 	}
35634f52dfbbSDag-Erling Smørgrav 	logit("channel %d: open failed: %s%s%s", c->self,
3564af12a3e7SDag-Erling Smørgrav 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
3565e4a9863fSDag-Erling Smørgrav 	free(msg);
3566e2f6069cSDag-Erling Smørgrav 	if (c->open_confirm) {
356719261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
35684f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
356919261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3570e2f6069cSDag-Erling Smørgrav 	}
3571cce7d346SDag-Erling Smørgrav 	/* Schedule the channel for cleanup/deletion. */
35724f52dfbbSDag-Erling Smørgrav 	chan_mark_dead(ssh, c);
3573bc5531deSDag-Erling Smørgrav 	return 0;
3574a04a10f8SKris Kennaway }
3575a04a10f8SKris Kennaway 
3576bc5531deSDag-Erling Smørgrav int
35774f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3578a04a10f8SKris Kennaway {
35794f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "window adjust");
3580a04a10f8SKris Kennaway 	Channel *c;
35814f52dfbbSDag-Erling Smørgrav 	u_int32_t adjust;
35824f52dfbbSDag-Erling Smørgrav 	u_int new_rwin;
35834f52dfbbSDag-Erling Smørgrav 	int r;
3584a04a10f8SKris Kennaway 
35854f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
3586b74df5b2SDag-Erling Smørgrav 		logit("Received window adjust for non-open channel %d.", id);
3587bc5531deSDag-Erling Smørgrav 		return 0;
3588511b41d2SMark Murray 	}
35894f52dfbbSDag-Erling Smørgrav 
35904f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3591ca86bcf2SDag-Erling Smørgrav 		return 0;
359219261079SEd Maste 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 ||
359319261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
359419261079SEd Maste 		error_fr(r, "parse adjust");
359519261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid window adjust message");
35964f52dfbbSDag-Erling Smørgrav 	}
35974f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
35984f52dfbbSDag-Erling Smørgrav 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
3599557f75e5SDag-Erling Smørgrav 		fatal("channel %d: adjust %u overflows remote window %u",
36004f52dfbbSDag-Erling Smørgrav 		    c->self, adjust, c->remote_window);
36014f52dfbbSDag-Erling Smørgrav 	}
36024f52dfbbSDag-Erling Smørgrav 	c->remote_window = new_rwin;
3603bc5531deSDag-Erling Smørgrav 	return 0;
3604511b41d2SMark Murray }
3605511b41d2SMark Murray 
3606bc5531deSDag-Erling Smørgrav int
36074f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3608af12a3e7SDag-Erling Smørgrav {
36094f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "status confirm");
3610d4af9e69SDag-Erling Smørgrav 	Channel *c;
3611d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
3612d4af9e69SDag-Erling Smørgrav 
3613d4af9e69SDag-Erling Smørgrav 	/* Reset keepalive timeout */
361419261079SEd Maste 	ssh_packet_set_alive_timeouts(ssh, 0);
3615d4af9e69SDag-Erling Smørgrav 
361619261079SEd Maste 	debug2_f("type %d id %d", type, id);
3617d4af9e69SDag-Erling Smørgrav 
36184f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
361919261079SEd Maste 		logit_f("%d: unknown", id);
3620bc5531deSDag-Erling Smørgrav 		return 0;
3621d4af9e69SDag-Erling Smørgrav 	}
36224f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3623ca86bcf2SDag-Erling Smørgrav 		return 0;
362419261079SEd Maste         if (sshpkt_get_end(ssh) != 0)
362519261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid status confirm message");
3626d4af9e69SDag-Erling Smørgrav 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3627bc5531deSDag-Erling Smørgrav 		return 0;
36284f52dfbbSDag-Erling Smørgrav 	cc->cb(ssh, type, c, cc->ctx);
3629d4af9e69SDag-Erling Smørgrav 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
363019261079SEd Maste 	freezero(cc, sizeof(*cc));
3631bc5531deSDag-Erling Smørgrav 	return 0;
3632d4af9e69SDag-Erling Smørgrav }
3633af12a3e7SDag-Erling Smørgrav 
3634af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
3635511b41d2SMark Murray 
3636511b41d2SMark Murray void
36374f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af)
3638511b41d2SMark Murray {
36394f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->IPv4or6 = af;
3640511b41d2SMark Murray }
3641511b41d2SMark Murray 
364289986192SBrooks Davis 
3643462c32cbSDag-Erling Smørgrav /*
3644462c32cbSDag-Erling Smørgrav  * Determine whether or not a port forward listens to loopback, the
3645462c32cbSDag-Erling Smørgrav  * specified address or wildcard. On the client, a specified bind
3646462c32cbSDag-Erling Smørgrav  * address will always override gateway_ports. On the server, a
3647462c32cbSDag-Erling Smørgrav  * gateway_ports of 1 (``yes'') will override the client's specification
3648462c32cbSDag-Erling Smørgrav  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3649462c32cbSDag-Erling Smørgrav  * will bind to whatever address the client asked for.
3650462c32cbSDag-Erling Smørgrav  *
3651462c32cbSDag-Erling Smørgrav  * Special-case listen_addrs are:
3652462c32cbSDag-Erling Smørgrav  *
3653462c32cbSDag-Erling Smørgrav  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3654462c32cbSDag-Erling Smørgrav  * "" (empty string), "*"  -> wildcard v4/v6
3655462c32cbSDag-Erling Smørgrav  * "localhost"             -> loopback v4/v6
3656a0ee8cc6SDag-Erling Smørgrav  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3657462c32cbSDag-Erling Smørgrav  */
3658462c32cbSDag-Erling Smørgrav static const char *
365919261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp,
3660a0ee8cc6SDag-Erling Smørgrav     int is_client, struct ForwardOptions *fwd_opts)
3661462c32cbSDag-Erling Smørgrav {
3662462c32cbSDag-Erling Smørgrav 	const char *addr = NULL;
3663462c32cbSDag-Erling Smørgrav 	int wildcard = 0;
3664462c32cbSDag-Erling Smørgrav 
3665462c32cbSDag-Erling Smørgrav 	if (listen_addr == NULL) {
3666462c32cbSDag-Erling Smørgrav 		/* No address specified: default to gateway_ports setting */
3667a0ee8cc6SDag-Erling Smørgrav 		if (fwd_opts->gateway_ports)
3668462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3669a0ee8cc6SDag-Erling Smørgrav 	} else if (fwd_opts->gateway_ports || is_client) {
367019261079SEd Maste 		if (((ssh->compat & SSH_OLD_FORWARD_ADDR) &&
3671462c32cbSDag-Erling Smørgrav 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3672462c32cbSDag-Erling Smørgrav 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
3673a0ee8cc6SDag-Erling Smørgrav 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3674462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3675f7167e0eSDag-Erling Smørgrav 			/*
3676f7167e0eSDag-Erling Smørgrav 			 * Notify client if they requested a specific listen
3677f7167e0eSDag-Erling Smørgrav 			 * address and it was overridden.
3678f7167e0eSDag-Erling Smørgrav 			 */
3679f7167e0eSDag-Erling Smørgrav 			if (*listen_addr != '\0' &&
3680f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
3681f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "*") != 0) {
368219261079SEd Maste 				ssh_packet_send_debug(ssh,
368319261079SEd Maste 				    "Forwarding listen address "
3684f7167e0eSDag-Erling Smørgrav 				    "\"%s\" overridden by server "
3685f7167e0eSDag-Erling Smørgrav 				    "GatewayPorts", listen_addr);
3686f7167e0eSDag-Erling Smørgrav 			}
3687a0ee8cc6SDag-Erling Smørgrav 		} else if (strcmp(listen_addr, "localhost") != 0 ||
3688a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
3689a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "::1") == 0) {
369019261079SEd Maste 			/*
369119261079SEd Maste 			 * Accept explicit localhost address when
369219261079SEd Maste 			 * GatewayPorts=yes. The "localhost" hostname is
369319261079SEd Maste 			 * deliberately skipped here so it will listen on all
369419261079SEd Maste 			 * available local address families.
369519261079SEd Maste 			 */
3696a0ee8cc6SDag-Erling Smørgrav 			addr = listen_addr;
3697f7167e0eSDag-Erling Smørgrav 		}
3698a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
3699a0ee8cc6SDag-Erling Smørgrav 	    strcmp(listen_addr, "::1") == 0) {
3700a0ee8cc6SDag-Erling Smørgrav 		/*
3701a0ee8cc6SDag-Erling Smørgrav 		 * If a specific IPv4/IPv6 localhost address has been
3702a0ee8cc6SDag-Erling Smørgrav 		 * requested then accept it even if gateway_ports is in
3703a0ee8cc6SDag-Erling Smørgrav 		 * effect. This allows the client to prefer IPv4 or IPv6.
3704a0ee8cc6SDag-Erling Smørgrav 		 */
3705462c32cbSDag-Erling Smørgrav 		addr = listen_addr;
3706462c32cbSDag-Erling Smørgrav 	}
3707462c32cbSDag-Erling Smørgrav 	if (wildcardp != NULL)
3708462c32cbSDag-Erling Smørgrav 		*wildcardp = wildcard;
3709462c32cbSDag-Erling Smørgrav 	return addr;
3710462c32cbSDag-Erling Smørgrav }
3711462c32cbSDag-Erling Smørgrav 
3712af12a3e7SDag-Erling Smørgrav static int
37134f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
37144f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int *allocated_listen_port,
37154f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3716511b41d2SMark Murray {
3717af12a3e7SDag-Erling Smørgrav 	Channel *c;
3718b74df5b2SDag-Erling Smørgrav 	int sock, r, success = 0, wildcard = 0, is_client;
3719511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
3720aa49c926SDag-Erling Smørgrav 	const char *host, *addr;
3721af12a3e7SDag-Erling Smørgrav 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3722cce7d346SDag-Erling Smørgrav 	in_port_t *lport_p;
3723511b41d2SMark Murray 
3724aa49c926SDag-Erling Smørgrav 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3725511b41d2SMark Murray 
3726557f75e5SDag-Erling Smørgrav 	if (is_client && fwd->connect_path != NULL) {
3727557f75e5SDag-Erling Smørgrav 		host = fwd->connect_path;
3728557f75e5SDag-Erling Smørgrav 	} else {
3729557f75e5SDag-Erling Smørgrav 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
3730557f75e5SDag-Erling Smørgrav 		    fwd->listen_host : fwd->connect_host;
3731af12a3e7SDag-Erling Smørgrav 		if (host == NULL) {
3732af12a3e7SDag-Erling Smørgrav 			error("No forward host name.");
3733d4ecd108SDag-Erling Smørgrav 			return 0;
3734ca3176e7SBrian Feldman 		}
3735cce7d346SDag-Erling Smørgrav 		if (strlen(host) >= NI_MAXHOST) {
3736ca3176e7SBrian Feldman 			error("Forward host name too long.");
3737d4ecd108SDag-Erling Smørgrav 			return 0;
3738ca3176e7SBrian Feldman 		}
3739557f75e5SDag-Erling Smørgrav 	}
3740ca3176e7SBrian Feldman 
3741462c32cbSDag-Erling Smørgrav 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
374219261079SEd Maste 	addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard,
3743a0ee8cc6SDag-Erling Smørgrav 	    is_client, fwd_opts);
374419261079SEd Maste 	debug3_f("type %d wildcard %d addr %s", type, wildcard,
374519261079SEd Maste 	    (addr == NULL) ? "NULL" : addr);
3746aa49c926SDag-Erling Smørgrav 
3747aa49c926SDag-Erling Smørgrav 	/*
3748511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
3749511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3750511b41d2SMark Murray 	 */
3751511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
37524f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
3753aa49c926SDag-Erling Smørgrav 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3754511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
3755a0ee8cc6SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3756aa49c926SDag-Erling Smørgrav 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3757aa49c926SDag-Erling Smørgrav 		if (addr == NULL) {
3758aa49c926SDag-Erling Smørgrav 			/* This really shouldn't happen */
375919261079SEd Maste 			ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s",
3760d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3761aa49c926SDag-Erling Smørgrav 		} else {
376219261079SEd Maste 			error_f("getaddrinfo(%.64s): %s", addr,
3763d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3764aa49c926SDag-Erling Smørgrav 		}
3765d4ecd108SDag-Erling Smørgrav 		return 0;
3766aa49c926SDag-Erling Smørgrav 	}
3767cce7d346SDag-Erling Smørgrav 	if (allocated_listen_port != NULL)
3768cce7d346SDag-Erling Smørgrav 		*allocated_listen_port = 0;
3769511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
3770cce7d346SDag-Erling Smørgrav 		switch (ai->ai_family) {
3771cce7d346SDag-Erling Smørgrav 		case AF_INET:
3772cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3773cce7d346SDag-Erling Smørgrav 			    sin_port;
3774cce7d346SDag-Erling Smørgrav 			break;
3775cce7d346SDag-Erling Smørgrav 		case AF_INET6:
3776cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3777cce7d346SDag-Erling Smørgrav 			    sin6_port;
3778cce7d346SDag-Erling Smørgrav 			break;
3779cce7d346SDag-Erling Smørgrav 		default:
3780511b41d2SMark Murray 			continue;
3781cce7d346SDag-Erling Smørgrav 		}
3782cce7d346SDag-Erling Smørgrav 		/*
3783cce7d346SDag-Erling Smørgrav 		 * If allocating a port for -R forwards, then use the
3784cce7d346SDag-Erling Smørgrav 		 * same port for all address families.
3785cce7d346SDag-Erling Smørgrav 		 */
37864f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
37874f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
37884f52dfbbSDag-Erling Smørgrav 		    *allocated_listen_port > 0)
3789cce7d346SDag-Erling Smørgrav 			*lport_p = htons(*allocated_listen_port);
3790cce7d346SDag-Erling Smørgrav 
3791511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
37924f52dfbbSDag-Erling Smørgrav 		    strport, sizeof(strport),
37934f52dfbbSDag-Erling Smørgrav 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
379419261079SEd Maste 			error_f("getnameinfo failed");
3795511b41d2SMark Murray 			continue;
3796511b41d2SMark Murray 		}
3797511b41d2SMark Murray 		/* Create a port to listen for the host. */
3798221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
379919261079SEd Maste 		if (sock == -1) {
3800511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
380147dd1d1bSDag-Erling Smørgrav 			verbose("socket [%s]:%s: %.100s", ntop, strport,
380247dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3803511b41d2SMark Murray 			continue;
3804511b41d2SMark Murray 		}
3805b74df5b2SDag-Erling Smørgrav 
380647dd1d1bSDag-Erling Smørgrav 		set_reuseaddr(sock);
3807b15c8340SDag-Erling Smørgrav 		if (ai->ai_family == AF_INET6)
3808b15c8340SDag-Erling Smørgrav 			sock_set_v6only(sock);
3809f388f5efSDag-Erling Smørgrav 
3810cce7d346SDag-Erling Smørgrav 		debug("Local forwarding listening on %s port %s.",
3811cce7d346SDag-Erling Smørgrav 		    ntop, strport);
3812511b41d2SMark Murray 
3813511b41d2SMark Murray 		/* Bind the socket to the address. */
381419261079SEd Maste 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
38154f52dfbbSDag-Erling Smørgrav 			/*
38164f52dfbbSDag-Erling Smørgrav 			 * address can be in if use ipv6 address is
38174f52dfbbSDag-Erling Smørgrav 			 * already bound
38184f52dfbbSDag-Erling Smørgrav 			 */
3819989dd127SDag-Erling Smørgrav 			if (!ai->ai_next)
382047dd1d1bSDag-Erling Smørgrav 				error("bind [%s]:%s: %.100s",
382147dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3822989dd127SDag-Erling Smørgrav 			else
382347dd1d1bSDag-Erling Smørgrav 				verbose("bind [%s]:%s: %.100s",
382447dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3825989dd127SDag-Erling Smørgrav 
3826511b41d2SMark Murray 			close(sock);
3827511b41d2SMark Murray 			continue;
3828511b41d2SMark Murray 		}
3829511b41d2SMark Murray 		/* Start listening for connections on the socket. */
383019261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
383147dd1d1bSDag-Erling Smørgrav 			error("listen [%s]:%s: %.100s", ntop, strport,
383247dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3833511b41d2SMark Murray 			close(sock);
3834511b41d2SMark Murray 			continue;
3835511b41d2SMark Murray 		}
3836cce7d346SDag-Erling Smørgrav 
3837cce7d346SDag-Erling Smørgrav 		/*
3838a0ee8cc6SDag-Erling Smørgrav 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3839cce7d346SDag-Erling Smørgrav 		 * record what we got.
3840cce7d346SDag-Erling Smørgrav 		 */
38414f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
38424f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 &&
3843cce7d346SDag-Erling Smørgrav 		    allocated_listen_port != NULL &&
3844cce7d346SDag-Erling Smørgrav 		    *allocated_listen_port == 0) {
3845076ad2f8SDag-Erling Smørgrav 			*allocated_listen_port = get_local_port(sock);
3846cce7d346SDag-Erling Smørgrav 			debug("Allocated listen port %d",
3847cce7d346SDag-Erling Smørgrav 			    *allocated_listen_port);
3848cce7d346SDag-Erling Smørgrav 		}
3849cce7d346SDag-Erling Smørgrav 
385060c59fadSDag-Erling Smørgrav 		/* Allocate a channel number for the socket. */
3851*f374ba41SEd Maste 		c = channel_new(ssh, "port-listener", type, sock, sock, -1,
3852a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3853221552e4SDag-Erling Smørgrav 		    0, "port listener", 1);
3854cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
3855a0ee8cc6SDag-Erling Smørgrav 		c->host_port = fwd->connect_port;
3856462c32cbSDag-Erling Smørgrav 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
3857a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
385819261079SEd Maste 		    !(ssh->compat & SSH_BUG_DYNAMIC_RPORT))
3859462c32cbSDag-Erling Smørgrav 			c->listening_port = *allocated_listen_port;
3860462c32cbSDag-Erling Smørgrav 		else
3861a0ee8cc6SDag-Erling Smørgrav 			c->listening_port = fwd->listen_port;
3862511b41d2SMark Murray 		success = 1;
3863511b41d2SMark Murray 	}
3864511b41d2SMark Murray 	if (success == 0)
386519261079SEd Maste 		error_f("cannot listen to port: %d", fwd->listen_port);
3866511b41d2SMark Murray 	freeaddrinfo(aitop);
3867ca3176e7SBrian Feldman 	return success;
3868511b41d2SMark Murray }
3869511b41d2SMark Murray 
3870a0ee8cc6SDag-Erling Smørgrav static int
38714f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
38724f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
3873a0ee8cc6SDag-Erling Smørgrav {
3874a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
3875a0ee8cc6SDag-Erling Smørgrav 	const char *path;
3876a0ee8cc6SDag-Erling Smørgrav 	Channel *c;
3877a0ee8cc6SDag-Erling Smørgrav 	int port, sock;
3878a0ee8cc6SDag-Erling Smørgrav 	mode_t omask;
3879a0ee8cc6SDag-Erling Smørgrav 
3880a0ee8cc6SDag-Erling Smørgrav 	switch (type) {
3881a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_UNIX_LISTENER:
3882a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
3883a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
3884a0ee8cc6SDag-Erling Smørgrav 				error("Local connecting path too long: %s",
3885a0ee8cc6SDag-Erling Smørgrav 				    fwd->connect_path);
3886a0ee8cc6SDag-Erling Smørgrav 				return 0;
3887a0ee8cc6SDag-Erling Smørgrav 			}
3888a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_path;
3889a0ee8cc6SDag-Erling Smørgrav 			port = PORT_STREAMLOCAL;
3890a0ee8cc6SDag-Erling Smørgrav 		} else {
3891a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_host == NULL) {
3892a0ee8cc6SDag-Erling Smørgrav 				error("No forward host name.");
3893a0ee8cc6SDag-Erling Smørgrav 				return 0;
3894a0ee8cc6SDag-Erling Smørgrav 			}
3895a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
3896a0ee8cc6SDag-Erling Smørgrav 				error("Forward host name too long.");
3897a0ee8cc6SDag-Erling Smørgrav 				return 0;
3898a0ee8cc6SDag-Erling Smørgrav 			}
3899a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_host;
3900a0ee8cc6SDag-Erling Smørgrav 			port = fwd->connect_port;
3901a0ee8cc6SDag-Erling Smørgrav 		}
3902a0ee8cc6SDag-Erling Smørgrav 		break;
3903a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_RUNIX_LISTENER:
3904a0ee8cc6SDag-Erling Smørgrav 		path = fwd->listen_path;
3905a0ee8cc6SDag-Erling Smørgrav 		port = PORT_STREAMLOCAL;
3906a0ee8cc6SDag-Erling Smørgrav 		break;
3907a0ee8cc6SDag-Erling Smørgrav 	default:
390819261079SEd Maste 		error_f("unexpected channel type %d", type);
3909a0ee8cc6SDag-Erling Smørgrav 		return 0;
3910a0ee8cc6SDag-Erling Smørgrav 	}
3911a0ee8cc6SDag-Erling Smørgrav 
3912a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path == NULL) {
3913a0ee8cc6SDag-Erling Smørgrav 		error("No forward path name.");
3914a0ee8cc6SDag-Erling Smørgrav 		return 0;
3915a0ee8cc6SDag-Erling Smørgrav 	}
3916a0ee8cc6SDag-Erling Smørgrav 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
3917a0ee8cc6SDag-Erling Smørgrav 		error("Local listening path too long: %s", fwd->listen_path);
3918a0ee8cc6SDag-Erling Smørgrav 		return 0;
3919a0ee8cc6SDag-Erling Smørgrav 	}
3920a0ee8cc6SDag-Erling Smørgrav 
392119261079SEd Maste 	debug3_f("type %d path %s", type, fwd->listen_path);
3922a0ee8cc6SDag-Erling Smørgrav 
3923a0ee8cc6SDag-Erling Smørgrav 	/* Start a Unix domain listener. */
3924a0ee8cc6SDag-Erling Smørgrav 	omask = umask(fwd_opts->streamlocal_bind_mask);
3925a0ee8cc6SDag-Erling Smørgrav 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
3926a0ee8cc6SDag-Erling Smørgrav 	    fwd_opts->streamlocal_bind_unlink);
3927a0ee8cc6SDag-Erling Smørgrav 	umask(omask);
3928a0ee8cc6SDag-Erling Smørgrav 	if (sock < 0)
3929a0ee8cc6SDag-Erling Smørgrav 		return 0;
3930a0ee8cc6SDag-Erling Smørgrav 
3931a0ee8cc6SDag-Erling Smørgrav 	debug("Local forwarding listening on path %s.", fwd->listen_path);
3932a0ee8cc6SDag-Erling Smørgrav 
3933a0ee8cc6SDag-Erling Smørgrav 	/* Allocate a channel number for the socket. */
3934*f374ba41SEd Maste 	c = channel_new(ssh, "unix-listener", type, sock, sock, -1,
3935a0ee8cc6SDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3936a0ee8cc6SDag-Erling Smørgrav 	    0, "unix listener", 1);
3937a0ee8cc6SDag-Erling Smørgrav 	c->path = xstrdup(path);
3938a0ee8cc6SDag-Erling Smørgrav 	c->host_port = port;
3939a0ee8cc6SDag-Erling Smørgrav 	c->listening_port = PORT_STREAMLOCAL;
3940a0ee8cc6SDag-Erling Smørgrav 	c->listening_addr = xstrdup(fwd->listen_path);
3941a0ee8cc6SDag-Erling Smørgrav 	return 1;
3942a0ee8cc6SDag-Erling Smørgrav }
3943a0ee8cc6SDag-Erling Smørgrav 
3944a0ee8cc6SDag-Erling Smørgrav static int
39454f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh,
39464f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
394721e764dfSDag-Erling Smørgrav {
394821e764dfSDag-Erling Smørgrav 	u_int i;
394921e764dfSDag-Erling Smørgrav 	int found = 0;
395021e764dfSDag-Erling Smørgrav 
39514f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
39524f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3953462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
3954462c32cbSDag-Erling Smørgrav 			continue;
3955462c32cbSDag-Erling Smørgrav 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
395619261079SEd Maste 			debug2_f("close channel %d", i);
39574f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3958462c32cbSDag-Erling Smørgrav 			found = 1;
3959462c32cbSDag-Erling Smørgrav 		}
3960462c32cbSDag-Erling Smørgrav 	}
396121e764dfSDag-Erling Smørgrav 
39624f52dfbbSDag-Erling Smørgrav 	return found;
3963462c32cbSDag-Erling Smørgrav }
3964462c32cbSDag-Erling Smørgrav 
3965a0ee8cc6SDag-Erling Smørgrav static int
39664f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
3967462c32cbSDag-Erling Smørgrav {
3968462c32cbSDag-Erling Smørgrav 	u_int i;
3969462c32cbSDag-Erling Smørgrav 	int found = 0;
3970a0ee8cc6SDag-Erling Smørgrav 
39714f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
39724f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3973a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
3974a0ee8cc6SDag-Erling Smørgrav 			continue;
3975a0ee8cc6SDag-Erling Smørgrav 		if (c->path == NULL)
3976a0ee8cc6SDag-Erling Smørgrav 			continue;
3977a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->path, path) == 0) {
397819261079SEd Maste 			debug2_f("close channel %d", i);
39794f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3980a0ee8cc6SDag-Erling Smørgrav 			found = 1;
3981a0ee8cc6SDag-Erling Smørgrav 		}
3982a0ee8cc6SDag-Erling Smørgrav 	}
3983a0ee8cc6SDag-Erling Smørgrav 
39844f52dfbbSDag-Erling Smørgrav 	return found;
3985a0ee8cc6SDag-Erling Smørgrav }
3986a0ee8cc6SDag-Erling Smørgrav 
3987a0ee8cc6SDag-Erling Smørgrav int
39884f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
3989a0ee8cc6SDag-Erling Smørgrav {
39904f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
39914f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_streamlocal(ssh,
39924f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
39934f52dfbbSDag-Erling Smørgrav 	} else {
39944f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_tcpip(ssh,
39954f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port);
39964f52dfbbSDag-Erling Smørgrav 	}
3997a0ee8cc6SDag-Erling Smørgrav }
3998a0ee8cc6SDag-Erling Smørgrav 
3999a0ee8cc6SDag-Erling Smørgrav static int
40004f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh,
40014f52dfbbSDag-Erling Smørgrav     const char *lhost, u_short lport, int cport,
40024f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
4003a0ee8cc6SDag-Erling Smørgrav {
4004a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4005a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
400619261079SEd Maste 	const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts);
4007462c32cbSDag-Erling Smørgrav 
40084f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40094f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4010462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
4011462c32cbSDag-Erling Smørgrav 			continue;
4012462c32cbSDag-Erling Smørgrav 		if (c->listening_port != lport)
4013462c32cbSDag-Erling Smørgrav 			continue;
4014462c32cbSDag-Erling Smørgrav 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
4015462c32cbSDag-Erling Smørgrav 			/* skip dynamic forwardings */
4016462c32cbSDag-Erling Smørgrav 			if (c->host_port == 0)
4017462c32cbSDag-Erling Smørgrav 				continue;
4018462c32cbSDag-Erling Smørgrav 		} else {
4019462c32cbSDag-Erling Smørgrav 			if (c->host_port != cport)
4020462c32cbSDag-Erling Smørgrav 				continue;
4021462c32cbSDag-Erling Smørgrav 		}
4022462c32cbSDag-Erling Smørgrav 		if ((c->listening_addr == NULL && addr != NULL) ||
4023462c32cbSDag-Erling Smørgrav 		    (c->listening_addr != NULL && addr == NULL))
4024462c32cbSDag-Erling Smørgrav 			continue;
4025462c32cbSDag-Erling Smørgrav 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
402619261079SEd Maste 			debug2_f("close channel %d", i);
40274f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
402821e764dfSDag-Erling Smørgrav 			found = 1;
402921e764dfSDag-Erling Smørgrav 		}
403021e764dfSDag-Erling Smørgrav 	}
403121e764dfSDag-Erling Smørgrav 
40324f52dfbbSDag-Erling Smørgrav 	return found;
403321e764dfSDag-Erling Smørgrav }
403421e764dfSDag-Erling Smørgrav 
4035a0ee8cc6SDag-Erling Smørgrav static int
40364f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
4037a0ee8cc6SDag-Erling Smørgrav {
4038a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4039a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
4040a0ee8cc6SDag-Erling Smørgrav 
4041a0ee8cc6SDag-Erling Smørgrav 	if (path == NULL) {
404219261079SEd Maste 		error_f("no path specified.");
4043a0ee8cc6SDag-Erling Smørgrav 		return 0;
4044a0ee8cc6SDag-Erling Smørgrav 	}
4045a0ee8cc6SDag-Erling Smørgrav 
40464f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40474f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4048a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
4049a0ee8cc6SDag-Erling Smørgrav 			continue;
4050a0ee8cc6SDag-Erling Smørgrav 		if (c->listening_addr == NULL)
4051a0ee8cc6SDag-Erling Smørgrav 			continue;
4052a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->listening_addr, path) == 0) {
405319261079SEd Maste 			debug2_f("close channel %d", i);
40544f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4055a0ee8cc6SDag-Erling Smørgrav 			found = 1;
4056a0ee8cc6SDag-Erling Smørgrav 		}
4057a0ee8cc6SDag-Erling Smørgrav 	}
4058a0ee8cc6SDag-Erling Smørgrav 
40594f52dfbbSDag-Erling Smørgrav 	return found;
4060a0ee8cc6SDag-Erling Smørgrav }
4061a0ee8cc6SDag-Erling Smørgrav 
4062a0ee8cc6SDag-Erling Smørgrav int
40634f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh,
40644f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
4065af12a3e7SDag-Erling Smørgrav {
4066a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40674f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_streamlocal(ssh,
40684f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
40694f52dfbbSDag-Erling Smørgrav 	} else {
40704f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_tcpip(ssh,
40714f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
40724f52dfbbSDag-Erling Smørgrav 	}
40734f52dfbbSDag-Erling Smørgrav }
40744f52dfbbSDag-Erling Smørgrav 
40754f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */
40764f52dfbbSDag-Erling Smørgrav int
40774f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh,
40784f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
40794f52dfbbSDag-Erling Smørgrav {
40804f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40814f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4082a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
4083a0ee8cc6SDag-Erling Smørgrav 	} else {
40844f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
40854f52dfbbSDag-Erling Smørgrav 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
4086a0ee8cc6SDag-Erling Smørgrav 	}
4087af12a3e7SDag-Erling Smørgrav }
4088af12a3e7SDag-Erling Smørgrav 
4089190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */
4090190cef3dSDag-Erling Smørgrav static int
4091190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd)
4092190cef3dSDag-Erling Smørgrav {
4093190cef3dSDag-Erling Smørgrav 	int ret;
4094190cef3dSDag-Erling Smørgrav 	char *lhost;
4095190cef3dSDag-Erling Smørgrav 
4096190cef3dSDag-Erling Smørgrav 	/* XXX add ACLs for streamlocal */
4097190cef3dSDag-Erling Smørgrav 	if (fwd->listen_path != NULL)
4098190cef3dSDag-Erling Smørgrav 		return 1;
4099190cef3dSDag-Erling Smørgrav 
4100190cef3dSDag-Erling Smørgrav 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
4101190cef3dSDag-Erling Smørgrav 		return 0;
4102190cef3dSDag-Erling Smørgrav 
4103190cef3dSDag-Erling Smørgrav 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
4104190cef3dSDag-Erling Smørgrav 	    allowed_open->listen_port != fwd->listen_port)
4105190cef3dSDag-Erling Smørgrav 		return 0;
4106190cef3dSDag-Erling Smørgrav 
4107190cef3dSDag-Erling Smørgrav 	/* Match hostnames case-insensitively */
4108190cef3dSDag-Erling Smørgrav 	lhost = xstrdup(fwd->listen_host);
4109190cef3dSDag-Erling Smørgrav 	lowercase(lhost);
4110190cef3dSDag-Erling Smørgrav 	ret = match_pattern(lhost, allowed_open->listen_host);
4111190cef3dSDag-Erling Smørgrav 	free(lhost);
4112190cef3dSDag-Erling Smørgrav 
4113190cef3dSDag-Erling Smørgrav 	return ret;
4114190cef3dSDag-Erling Smørgrav }
4115190cef3dSDag-Erling Smørgrav 
4116190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */
4117190cef3dSDag-Erling Smørgrav static int
4118190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
4119190cef3dSDag-Erling Smørgrav {
4120190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4121190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->remote_perms;
4122190cef3dSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4123190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4124190cef3dSDag-Erling Smørgrav 
4125190cef3dSDag-Erling Smørgrav 	/* XXX apply GatewayPorts override before checking? */
4126190cef3dSDag-Erling Smørgrav 
4127190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4128190cef3dSDag-Erling Smørgrav 	if (!permit) {
4129190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4130190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4131190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4132190cef3dSDag-Erling Smørgrav 				permit = 1;
4133190cef3dSDag-Erling Smørgrav 				break;
4134190cef3dSDag-Erling Smørgrav 			}
4135190cef3dSDag-Erling Smørgrav 		}
4136190cef3dSDag-Erling Smørgrav 	}
4137190cef3dSDag-Erling Smørgrav 
4138190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4139190cef3dSDag-Erling Smørgrav 		permit_adm = 0;
4140190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4141190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4142190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4143190cef3dSDag-Erling Smørgrav 				permit_adm = 1;
4144190cef3dSDag-Erling Smørgrav 				break;
4145190cef3dSDag-Erling Smørgrav 			}
4146190cef3dSDag-Erling Smørgrav 		}
4147190cef3dSDag-Erling Smørgrav 	}
4148190cef3dSDag-Erling Smørgrav 
4149190cef3dSDag-Erling Smørgrav 	return permit && permit_adm;
4150190cef3dSDag-Erling Smørgrav }
4151190cef3dSDag-Erling Smørgrav 
4152af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */
4153af12a3e7SDag-Erling Smørgrav int
41544f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
4155a0ee8cc6SDag-Erling Smørgrav     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
4156af12a3e7SDag-Erling Smørgrav {
4157190cef3dSDag-Erling Smørgrav 	if (!check_rfwd_permission(ssh, fwd)) {
415819261079SEd Maste 		ssh_packet_send_debug(ssh, "port forwarding refused");
415919261079SEd Maste 		if (fwd->listen_path != NULL)
416019261079SEd Maste 			/* XXX always allowed, see remote_open_match() */
416119261079SEd Maste 			logit("Received request from %.100s port %d to "
416219261079SEd Maste 			    "remote forward to path \"%.100s\", "
416319261079SEd Maste 			    "but the request was denied.",
416419261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
416519261079SEd Maste 			    fwd->listen_path);
416619261079SEd Maste 		else if(fwd->listen_host != NULL)
416719261079SEd Maste 			logit("Received request from %.100s port %d to "
416819261079SEd Maste 			    "remote forward to host %.100s port %d, "
416919261079SEd Maste 			    "but the request was denied.",
417019261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
417119261079SEd Maste 			    fwd->listen_host, fwd->listen_port );
417219261079SEd Maste 		else
417319261079SEd Maste 			logit("Received request from %.100s port %d to remote "
417419261079SEd Maste 			    "forward, but the request was denied.",
417519261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
4176190cef3dSDag-Erling Smørgrav 		return 0;
4177190cef3dSDag-Erling Smørgrav 	}
4178a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
41794f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4180a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
4181a0ee8cc6SDag-Erling Smørgrav 	} else {
41824f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
4183a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
4184a0ee8cc6SDag-Erling Smørgrav 		    fwd_opts);
4185a0ee8cc6SDag-Erling Smørgrav 	}
4186af12a3e7SDag-Erling Smørgrav }
4187af12a3e7SDag-Erling Smørgrav 
4188511b41d2SMark Murray /*
4189462c32cbSDag-Erling Smørgrav  * Translate the requested rfwd listen host to something usable for
4190462c32cbSDag-Erling Smørgrav  * this server.
4191462c32cbSDag-Erling Smørgrav  */
4192462c32cbSDag-Erling Smørgrav static const char *
4193462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host)
4194462c32cbSDag-Erling Smørgrav {
4195462c32cbSDag-Erling Smørgrav 	if (listen_host == NULL) {
4196462c32cbSDag-Erling Smørgrav 		return "localhost";
4197462c32cbSDag-Erling Smørgrav 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
4198462c32cbSDag-Erling Smørgrav 		return "";
4199462c32cbSDag-Erling Smørgrav 	} else
4200462c32cbSDag-Erling Smørgrav 		return listen_host;
4201462c32cbSDag-Erling Smørgrav }
4202462c32cbSDag-Erling Smørgrav 
4203462c32cbSDag-Erling Smørgrav /*
4204511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
4205511b41d2SMark Murray  * the secure channel to host:port from local side.
4206462c32cbSDag-Erling Smørgrav  * Returns handle (index) for updating the dynamic listen port with
4207190cef3dSDag-Erling Smørgrav  * channel_update_permission().
4208511b41d2SMark Murray  */
4209333ee039SDag-Erling Smørgrav int
42104f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
4211511b41d2SMark Murray {
42124f52dfbbSDag-Erling Smørgrav 	int r, success = 0, idx = -1;
4213*f374ba41SEd Maste 	const char *host_to_connect, *listen_host, *listen_path;
42144f52dfbbSDag-Erling Smørgrav 	int port_to_connect, listen_port;
4215ca3176e7SBrian Feldman 
4216511b41d2SMark Murray 	/* Send the forward request to the remote side. */
4217a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
42184f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
42194f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
42204f52dfbbSDag-Erling Smørgrav 		    "streamlocal-forward@openssh.com")) != 0 ||
42214f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
42224f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
42234f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
42244f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
422519261079SEd Maste 			fatal_fr(r, "request streamlocal");
4226a0ee8cc6SDag-Erling Smørgrav 	} else {
42274f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
42284f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
42294f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
42304f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
42314f52dfbbSDag-Erling Smørgrav 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
42324f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
42334f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
42344f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
423519261079SEd Maste 			fatal_fr(r, "request tcpip-forward");
4236a0ee8cc6SDag-Erling Smørgrav 	}
4237ca3176e7SBrian Feldman 	/* Assume that server accepts the request */
4238ca3176e7SBrian Feldman 	success = 1;
4239ca3176e7SBrian Feldman 	if (success) {
4240e2f6069cSDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
42414f52dfbbSDag-Erling Smørgrav 		host_to_connect = listen_host = listen_path = NULL;
42424f52dfbbSDag-Erling Smørgrav 		port_to_connect = listen_port = 0;
4243a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
4244*f374ba41SEd Maste 			host_to_connect = fwd->connect_path;
42454f52dfbbSDag-Erling Smørgrav 			port_to_connect = PORT_STREAMLOCAL;
4246a0ee8cc6SDag-Erling Smørgrav 		} else {
4247*f374ba41SEd Maste 			host_to_connect = fwd->connect_host;
42484f52dfbbSDag-Erling Smørgrav 			port_to_connect = fwd->connect_port;
4249a0ee8cc6SDag-Erling Smørgrav 		}
4250a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_path != NULL) {
4251*f374ba41SEd Maste 			listen_path = fwd->listen_path;
42524f52dfbbSDag-Erling Smørgrav 			listen_port = PORT_STREAMLOCAL;
4253a0ee8cc6SDag-Erling Smørgrav 		} else {
4254*f374ba41SEd Maste 			listen_host = fwd->listen_host;
42554f52dfbbSDag-Erling Smørgrav 			listen_port = fwd->listen_port;
4256a0ee8cc6SDag-Erling Smørgrav 		}
4257190cef3dSDag-Erling Smørgrav 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
42584f52dfbbSDag-Erling Smørgrav 		    host_to_connect, port_to_connect,
42594f52dfbbSDag-Erling Smørgrav 		    listen_host, listen_path, listen_port, NULL);
4260511b41d2SMark Murray 	}
42614f52dfbbSDag-Erling Smørgrav 	return idx;
4262a04a10f8SKris Kennaway }
4263511b41d2SMark Murray 
4264a0ee8cc6SDag-Erling Smørgrav static int
4265190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost,
4266a0ee8cc6SDag-Erling Smørgrav     int requestedport)
4267a0ee8cc6SDag-Erling Smørgrav {
4268a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4269a0ee8cc6SDag-Erling Smørgrav 		return 0;
4270a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
4271a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->port_to_connect != requestedport)
4272a0ee8cc6SDag-Erling Smørgrav 		return 0;
4273076ad2f8SDag-Erling Smørgrav 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
4274076ad2f8SDag-Erling Smørgrav 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
4275a0ee8cc6SDag-Erling Smørgrav 		return 0;
4276a0ee8cc6SDag-Erling Smørgrav 	return 1;
4277a0ee8cc6SDag-Erling Smørgrav }
4278a0ee8cc6SDag-Erling Smørgrav 
4279a0ee8cc6SDag-Erling Smørgrav /*
4280a0ee8cc6SDag-Erling Smørgrav  * Note that in the listen host/port case
4281a0ee8cc6SDag-Erling Smørgrav  * we don't support FWD_PERMIT_ANY_PORT and
4282a0ee8cc6SDag-Erling Smørgrav  * need to translate between the configured-host (listen_host)
4283a0ee8cc6SDag-Erling Smørgrav  * and what we've sent to the remote server (channel_rfwd_bind_host)
4284a0ee8cc6SDag-Erling Smørgrav  */
4285a0ee8cc6SDag-Erling Smørgrav static int
4286190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open,
4287a0ee8cc6SDag-Erling Smørgrav     const char *requestedhost, u_short requestedport, int translate)
4288a0ee8cc6SDag-Erling Smørgrav {
4289a0ee8cc6SDag-Erling Smørgrav 	const char *allowed_host;
4290a0ee8cc6SDag-Erling Smørgrav 
4291a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4292a0ee8cc6SDag-Erling Smørgrav 		return 0;
4293a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != requestedport)
4294a0ee8cc6SDag-Erling Smørgrav 		return 0;
4295a0ee8cc6SDag-Erling Smørgrav 	if (!translate && allowed_open->listen_host == NULL &&
4296a0ee8cc6SDag-Erling Smørgrav 	    requestedhost == NULL)
4297a0ee8cc6SDag-Erling Smørgrav 		return 1;
4298a0ee8cc6SDag-Erling Smørgrav 	allowed_host = translate ?
4299a0ee8cc6SDag-Erling Smørgrav 	    channel_rfwd_bind_host(allowed_open->listen_host) :
4300a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->listen_host;
4301190cef3dSDag-Erling Smørgrav 	if (allowed_host == NULL || requestedhost == NULL ||
4302a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_host, requestedhost) != 0)
4303a0ee8cc6SDag-Erling Smørgrav 		return 0;
4304a0ee8cc6SDag-Erling Smørgrav 	return 1;
4305a0ee8cc6SDag-Erling Smørgrav }
4306a0ee8cc6SDag-Erling Smørgrav 
4307a0ee8cc6SDag-Erling Smørgrav static int
4308190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open,
4309a0ee8cc6SDag-Erling Smørgrav     const char *requestedpath)
4310a0ee8cc6SDag-Erling Smørgrav {
4311a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4312a0ee8cc6SDag-Erling Smørgrav 		return 0;
4313a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
4314a0ee8cc6SDag-Erling Smørgrav 		return 0;
4315a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_path == NULL ||
4316a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
4317a0ee8cc6SDag-Erling Smørgrav 		return 0;
4318a0ee8cc6SDag-Erling Smørgrav 	return 1;
4319a0ee8cc6SDag-Erling Smørgrav }
4320a0ee8cc6SDag-Erling Smørgrav 
4321511b41d2SMark Murray /*
432221e764dfSDag-Erling Smørgrav  * Request cancellation of remote forwarding of connection host:port from
432321e764dfSDag-Erling Smørgrav  * local side.
432421e764dfSDag-Erling Smørgrav  */
4325a0ee8cc6SDag-Erling Smørgrav static int
43264f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh,
43274f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
432821e764dfSDag-Erling Smørgrav {
43294f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4330190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
43314f52dfbbSDag-Erling Smørgrav 	int r;
43324f52dfbbSDag-Erling Smørgrav 	u_int i;
433319261079SEd Maste 	struct permission *perm = NULL;
433421e764dfSDag-Erling Smørgrav 
4335190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4336190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4337190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm, host, port, 0))
433821e764dfSDag-Erling Smørgrav 			break;
4339190cef3dSDag-Erling Smørgrav 		perm = NULL;
434021e764dfSDag-Erling Smørgrav 	}
4341190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
434219261079SEd Maste 		debug_f("requested forward not found");
4343462c32cbSDag-Erling Smørgrav 		return -1;
434421e764dfSDag-Erling Smørgrav 	}
43454f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43464f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
43474f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
43484f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
43494f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
43504f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
435119261079SEd Maste 		fatal_fr(r, "send cancel");
435221e764dfSDag-Erling Smørgrav 
4353190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4354462c32cbSDag-Erling Smørgrav 
4355462c32cbSDag-Erling Smørgrav 	return 0;
435621e764dfSDag-Erling Smørgrav }
435721e764dfSDag-Erling Smørgrav 
435821e764dfSDag-Erling Smørgrav /*
4359a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of Unix domain socket
4360a0ee8cc6SDag-Erling Smørgrav  * path from local side.
4361a0ee8cc6SDag-Erling Smørgrav  */
4362a0ee8cc6SDag-Erling Smørgrav static int
43634f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
4364a0ee8cc6SDag-Erling Smørgrav {
43654f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4366190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
43674f52dfbbSDag-Erling Smørgrav 	int r;
43684f52dfbbSDag-Erling Smørgrav 	u_int i;
436919261079SEd Maste 	struct permission *perm = NULL;
4370a0ee8cc6SDag-Erling Smørgrav 
4371190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4372190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4373190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path))
4374a0ee8cc6SDag-Erling Smørgrav 			break;
4375190cef3dSDag-Erling Smørgrav 		perm = NULL;
4376a0ee8cc6SDag-Erling Smørgrav 	}
4377190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
437819261079SEd Maste 		debug_f("requested forward not found");
4379a0ee8cc6SDag-Erling Smørgrav 		return -1;
4380a0ee8cc6SDag-Erling Smørgrav 	}
43814f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43824f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh,
43834f52dfbbSDag-Erling Smørgrav 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
43844f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
43854f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
43864f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
438719261079SEd Maste 		fatal_fr(r, "send cancel");
4388a0ee8cc6SDag-Erling Smørgrav 
4389190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4390a0ee8cc6SDag-Erling Smørgrav 
4391a0ee8cc6SDag-Erling Smørgrav 	return 0;
4392a0ee8cc6SDag-Erling Smørgrav }
4393a0ee8cc6SDag-Erling Smørgrav 
4394a0ee8cc6SDag-Erling Smørgrav /*
4395a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of a connection from local side.
4396a0ee8cc6SDag-Erling Smørgrav  */
4397a0ee8cc6SDag-Erling Smørgrav int
43984f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
4399a0ee8cc6SDag-Erling Smørgrav {
4400a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
44014f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_streamlocal(ssh,
44024f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
4403a0ee8cc6SDag-Erling Smørgrav 	} else {
44044f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_tcpip(ssh,
44054f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host,
44064f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
4407a0ee8cc6SDag-Erling Smørgrav 	}
4408a0ee8cc6SDag-Erling Smørgrav }
4409a0ee8cc6SDag-Erling Smørgrav 
4410a0ee8cc6SDag-Erling Smørgrav /*
4411190cef3dSDag-Erling Smørgrav  * Permits opening to any host/port if permitted_user[] is empty.  This is
4412ca3176e7SBrian Feldman  * usually called by the server, because the user could connect to any port
4413ca3176e7SBrian Feldman  * anyway, and the server has no way to know but to trust the client anyway.
4414ca3176e7SBrian Feldman  */
4415ca3176e7SBrian Feldman void
4416190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where)
4417ca3176e7SBrian Feldman {
4418190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
4419190cef3dSDag-Erling Smørgrav 
4420190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_user == 0)
4421190cef3dSDag-Erling Smørgrav 		pset->all_permitted = 1;
4422ca3176e7SBrian Feldman }
4423ca3176e7SBrian Feldman 
4424190cef3dSDag-Erling Smørgrav /*
4425190cef3dSDag-Erling Smørgrav  * Permit the specified host/port for forwarding.
4426190cef3dSDag-Erling Smørgrav  */
4427ca3176e7SBrian Feldman void
4428190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where,
4429190cef3dSDag-Erling Smørgrav     char *host, int port)
4430ca3176e7SBrian Feldman {
4431190cef3dSDag-Erling Smørgrav 	int local = where == FORWARD_LOCAL;
4432190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
44334f52dfbbSDag-Erling Smørgrav 
4434190cef3dSDag-Erling Smørgrav 	debug("allow %s forwarding to host %s port %d",
4435190cef3dSDag-Erling Smørgrav 	    fwd_ident(who, where), host, port);
4436190cef3dSDag-Erling Smørgrav 	/*
4437190cef3dSDag-Erling Smørgrav 	 * Remote forwards set listen_host/port, local forwards set
4438190cef3dSDag-Erling Smørgrav 	 * host/port_to_connect.
4439190cef3dSDag-Erling Smørgrav 	 */
4440190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, who, where,
4441190cef3dSDag-Erling Smørgrav 	    local ? host : 0, local ? port : 0,
4442190cef3dSDag-Erling Smørgrav 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
4443190cef3dSDag-Erling Smørgrav 	pset->all_permitted = 0;
4444190cef3dSDag-Erling Smørgrav }
4445190cef3dSDag-Erling Smørgrav 
4446190cef3dSDag-Erling Smørgrav /*
4447190cef3dSDag-Erling Smørgrav  * Administratively disable forwarding.
4448190cef3dSDag-Erling Smørgrav  */
4449190cef3dSDag-Erling Smørgrav void
4450190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where)
4451190cef3dSDag-Erling Smørgrav {
4452190cef3dSDag-Erling Smørgrav 	channel_clear_permission(ssh, FORWARD_ADM, where);
4453190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, FORWARD_ADM, where,
4454190cef3dSDag-Erling Smørgrav 	    NULL, 0, NULL, NULL, 0, NULL);
4455190cef3dSDag-Erling Smørgrav }
4456190cef3dSDag-Erling Smørgrav 
4457190cef3dSDag-Erling Smørgrav /*
4458190cef3dSDag-Erling Smørgrav  * Clear a list of permitted opens.
4459190cef3dSDag-Erling Smørgrav  */
4460190cef3dSDag-Erling Smørgrav void
4461190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where)
4462190cef3dSDag-Erling Smørgrav {
4463190cef3dSDag-Erling Smørgrav 	struct permission **permp;
4464190cef3dSDag-Erling Smørgrav 	u_int *npermp;
4465190cef3dSDag-Erling Smørgrav 
4466190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
4467190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
4468190cef3dSDag-Erling Smørgrav 	*npermp = 0;
4469ca3176e7SBrian Feldman }
4470ca3176e7SBrian Feldman 
4471462c32cbSDag-Erling Smørgrav /*
4472462c32cbSDag-Erling Smørgrav  * Update the listen port for a dynamic remote forward, after
4473462c32cbSDag-Erling Smørgrav  * the actual 'newport' has been allocated. If 'newport' < 0 is
4474462c32cbSDag-Erling Smørgrav  * passed then they entry will be invalidated.
4475462c32cbSDag-Erling Smørgrav  */
4476462c32cbSDag-Erling Smørgrav void
4477190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport)
4478462c32cbSDag-Erling Smørgrav {
4479190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &ssh->chanctxt->local_perms;
44804f52dfbbSDag-Erling Smørgrav 
4481190cef3dSDag-Erling Smørgrav 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
448219261079SEd Maste 		debug_f("index out of range: %d num_permitted_user %d",
448319261079SEd Maste 		    idx, pset->num_permitted_user);
4484462c32cbSDag-Erling Smørgrav 		return;
4485462c32cbSDag-Erling Smørgrav 	}
4486462c32cbSDag-Erling Smørgrav 	debug("%s allowed port %d for forwarding to host %s port %d",
4487462c32cbSDag-Erling Smørgrav 	    newport > 0 ? "Updating" : "Removing",
4488462c32cbSDag-Erling Smørgrav 	    newport,
4489190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].host_to_connect,
4490190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].port_to_connect);
44914f52dfbbSDag-Erling Smørgrav 	if (newport <= 0)
4492190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(&pset->permitted_user[idx]);
44934f52dfbbSDag-Erling Smørgrav 	else {
4494190cef3dSDag-Erling Smørgrav 		pset->permitted_user[idx].listen_port =
449519261079SEd Maste 		    (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4496462c32cbSDag-Erling Smørgrav 	}
4497462c32cbSDag-Erling Smørgrav }
4498462c32cbSDag-Erling Smørgrav 
4499462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
4500462c32cbSDag-Erling Smørgrav int
4501462c32cbSDag-Erling Smørgrav permitopen_port(const char *p)
4502462c32cbSDag-Erling Smørgrav {
4503462c32cbSDag-Erling Smørgrav 	int port;
4504462c32cbSDag-Erling Smørgrav 
4505462c32cbSDag-Erling Smørgrav 	if (strcmp(p, "*") == 0)
4506462c32cbSDag-Erling Smørgrav 		return FWD_PERMIT_ANY_PORT;
4507462c32cbSDag-Erling Smørgrav 	if ((port = a2port(p)) > 0)
4508462c32cbSDag-Erling Smørgrav 		return port;
4509462c32cbSDag-Erling Smørgrav 	return -1;
4510462c32cbSDag-Erling Smørgrav }
4511462c32cbSDag-Erling Smørgrav 
4512d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */
4513d4af9e69SDag-Erling Smørgrav static int
4514d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx)
4515d4af9e69SDag-Erling Smørgrav {
4516d4af9e69SDag-Erling Smørgrav 	int sock, saved_errno;
4517a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un *sunaddr;
45184f52dfbbSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
45194f52dfbbSDag-Erling Smørgrav 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4520d4af9e69SDag-Erling Smørgrav 
4521d4af9e69SDag-Erling Smørgrav 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
4522a0ee8cc6SDag-Erling Smørgrav 		switch (cctx->ai->ai_family) {
4523a0ee8cc6SDag-Erling Smørgrav 		case AF_UNIX:
4524a0ee8cc6SDag-Erling Smørgrav 			/* unix:pathname instead of host:port */
4525a0ee8cc6SDag-Erling Smørgrav 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
4526a0ee8cc6SDag-Erling Smørgrav 			strlcpy(ntop, "unix", sizeof(ntop));
4527a0ee8cc6SDag-Erling Smørgrav 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
4528a0ee8cc6SDag-Erling Smørgrav 			break;
4529a0ee8cc6SDag-Erling Smørgrav 		case AF_INET:
4530a0ee8cc6SDag-Erling Smørgrav 		case AF_INET6:
4531d4af9e69SDag-Erling Smørgrav 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4532d4af9e69SDag-Erling Smørgrav 			    ntop, sizeof(ntop), strport, sizeof(strport),
4533d4af9e69SDag-Erling Smørgrav 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
453438a52bd3SEd Maste 				error_f("getnameinfo failed");
4535511b41d2SMark Murray 				continue;
4536511b41d2SMark Murray 			}
4537a0ee8cc6SDag-Erling Smørgrav 			break;
4538a0ee8cc6SDag-Erling Smørgrav 		default:
4539a0ee8cc6SDag-Erling Smørgrav 			continue;
4540a0ee8cc6SDag-Erling Smørgrav 		}
454138a52bd3SEd Maste 		debug_f("start for host %.100s ([%.100s]:%s)",
454238a52bd3SEd Maste 		    cctx->host, ntop, strport);
4543d4af9e69SDag-Erling Smørgrav 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4544d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_protocol)) == -1) {
4545d4af9e69SDag-Erling Smørgrav 			if (cctx->ai->ai_next == NULL)
4546511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
4547e73e9afaSDag-Erling Smørgrav 			else
4548e73e9afaSDag-Erling Smørgrav 				verbose("socket: %.100s", strerror(errno));
4549511b41d2SMark Murray 			continue;
4550511b41d2SMark Murray 		}
455121e764dfSDag-Erling Smørgrav 		if (set_nonblock(sock) == -1)
455219261079SEd Maste 			fatal_f("set_nonblock(%d)", sock);
4553d4af9e69SDag-Erling Smørgrav 		if (connect(sock, cctx->ai->ai_addr,
4554d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
455538a52bd3SEd Maste 			debug_f("host %.100s ([%.100s]:%s): %.100s",
455638a52bd3SEd Maste 			    cctx->host, ntop, strport, strerror(errno));
4557d4af9e69SDag-Erling Smørgrav 			saved_errno = errno;
4558511b41d2SMark Murray 			close(sock);
4559d4af9e69SDag-Erling Smørgrav 			errno = saved_errno;
4560511b41d2SMark Murray 			continue;	/* fail -- try next */
4561511b41d2SMark Murray 		}
4562a0ee8cc6SDag-Erling Smørgrav 		if (cctx->ai->ai_family != AF_UNIX)
4563a0ee8cc6SDag-Erling Smørgrav 			set_nodelay(sock);
456438a52bd3SEd Maste 		debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d",
456538a52bd3SEd Maste 		    cctx->host, ntop, strport, sock);
4566d4af9e69SDag-Erling Smørgrav 		cctx->ai = cctx->ai->ai_next;
4567a04a10f8SKris Kennaway 		return sock;
4568a04a10f8SKris Kennaway 	}
4569ca3176e7SBrian Feldman 	return -1;
4570ca3176e7SBrian Feldman }
4571ca3176e7SBrian Feldman 
4572d4af9e69SDag-Erling Smørgrav static void
4573d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx)
4574d4af9e69SDag-Erling Smørgrav {
4575e4a9863fSDag-Erling Smørgrav 	free(cctx->host);
4576a0ee8cc6SDag-Erling Smørgrav 	if (cctx->aitop) {
4577a0ee8cc6SDag-Erling Smørgrav 		if (cctx->aitop->ai_family == AF_UNIX)
4578a0ee8cc6SDag-Erling Smørgrav 			free(cctx->aitop);
4579a0ee8cc6SDag-Erling Smørgrav 		else
4580d4af9e69SDag-Erling Smørgrav 			freeaddrinfo(cctx->aitop);
4581a0ee8cc6SDag-Erling Smørgrav 	}
4582b83788ffSDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
4583d4af9e69SDag-Erling Smørgrav }
4584d4af9e69SDag-Erling Smørgrav 
4585d93a896eSDag-Erling Smørgrav /*
45864f52dfbbSDag-Erling Smørgrav  * Return connecting socket to remote host:port or local socket path,
4587d93a896eSDag-Erling Smørgrav  * passing back the failure reason if appropriate.
4588d93a896eSDag-Erling Smørgrav  */
45894f52dfbbSDag-Erling Smørgrav static int
45904f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
45914f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, struct channel_connect *cctx,
4592d93a896eSDag-Erling Smørgrav     int *reason, const char **errmsg)
4593d4af9e69SDag-Erling Smørgrav {
4594d4af9e69SDag-Erling Smørgrav 	struct addrinfo hints;
4595d4af9e69SDag-Erling Smørgrav 	int gaierr;
4596d4af9e69SDag-Erling Smørgrav 	int sock = -1;
4597d4af9e69SDag-Erling Smørgrav 	char strport[NI_MAXSERV];
4598a0ee8cc6SDag-Erling Smørgrav 
4599a0ee8cc6SDag-Erling Smørgrav 	if (port == PORT_STREAMLOCAL) {
4600a0ee8cc6SDag-Erling Smørgrav 		struct sockaddr_un *sunaddr;
4601a0ee8cc6SDag-Erling Smørgrav 		struct addrinfo *ai;
4602a0ee8cc6SDag-Erling Smørgrav 
4603a0ee8cc6SDag-Erling Smørgrav 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
4604a0ee8cc6SDag-Erling Smørgrav 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
46054f52dfbbSDag-Erling Smørgrav 			return -1;
4606a0ee8cc6SDag-Erling Smørgrav 		}
4607a0ee8cc6SDag-Erling Smørgrav 
4608a0ee8cc6SDag-Erling Smørgrav 		/*
4609a0ee8cc6SDag-Erling Smørgrav 		 * Fake up a struct addrinfo for AF_UNIX connections.
4610a0ee8cc6SDag-Erling Smørgrav 		 * channel_connect_ctx_free() must check ai_family
4611a0ee8cc6SDag-Erling Smørgrav 		 * and use free() not freeaddirinfo() for AF_UNIX.
4612a0ee8cc6SDag-Erling Smørgrav 		 */
4613a0ee8cc6SDag-Erling Smørgrav 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
4614a0ee8cc6SDag-Erling Smørgrav 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
4615a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addr = (struct sockaddr *)(ai + 1);
4616a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addrlen = sizeof(*sunaddr);
4617a0ee8cc6SDag-Erling Smørgrav 		ai->ai_family = AF_UNIX;
46184f52dfbbSDag-Erling Smørgrav 		ai->ai_socktype = socktype;
4619a0ee8cc6SDag-Erling Smørgrav 		ai->ai_protocol = PF_UNSPEC;
4620a0ee8cc6SDag-Erling Smørgrav 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
4621a0ee8cc6SDag-Erling Smørgrav 		sunaddr->sun_family = AF_UNIX;
4622a0ee8cc6SDag-Erling Smørgrav 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
46234f52dfbbSDag-Erling Smørgrav 		cctx->aitop = ai;
4624a0ee8cc6SDag-Erling Smørgrav 	} else {
4625d4af9e69SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
46264f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
46274f52dfbbSDag-Erling Smørgrav 		hints.ai_socktype = socktype;
4628d4af9e69SDag-Erling Smørgrav 		snprintf(strport, sizeof strport, "%d", port);
46294f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
4630d93a896eSDag-Erling Smørgrav 		    != 0) {
4631d93a896eSDag-Erling Smørgrav 			if (errmsg != NULL)
4632d93a896eSDag-Erling Smørgrav 				*errmsg = ssh_gai_strerror(gaierr);
4633d93a896eSDag-Erling Smørgrav 			if (reason != NULL)
4634d93a896eSDag-Erling Smørgrav 				*reason = SSH2_OPEN_CONNECT_FAILED;
4635a0ee8cc6SDag-Erling Smørgrav 			error("connect_to %.100s: unknown host (%s)", name,
4636d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
46374f52dfbbSDag-Erling Smørgrav 			return -1;
4638d4af9e69SDag-Erling Smørgrav 		}
4639a0ee8cc6SDag-Erling Smørgrav 	}
4640d4af9e69SDag-Erling Smørgrav 
46414f52dfbbSDag-Erling Smørgrav 	cctx->host = xstrdup(name);
46424f52dfbbSDag-Erling Smørgrav 	cctx->port = port;
46434f52dfbbSDag-Erling Smørgrav 	cctx->ai = cctx->aitop;
4644d4af9e69SDag-Erling Smørgrav 
46454f52dfbbSDag-Erling Smørgrav 	if ((sock = connect_next(cctx)) == -1) {
4646d4af9e69SDag-Erling Smørgrav 		error("connect to %.100s port %d failed: %s",
4647a0ee8cc6SDag-Erling Smørgrav 		    name, port, strerror(errno));
46484f52dfbbSDag-Erling Smørgrav 		return -1;
4649d4af9e69SDag-Erling Smørgrav 	}
46504f52dfbbSDag-Erling Smørgrav 
46514f52dfbbSDag-Erling Smørgrav 	return sock;
4652d4af9e69SDag-Erling Smørgrav }
4653d4af9e69SDag-Erling Smørgrav 
4654d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */
4655d93a896eSDag-Erling Smørgrav static Channel *
46564f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port,
46574f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4658d93a896eSDag-Erling Smørgrav {
46594f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
46604f52dfbbSDag-Erling Smørgrav 	Channel *c;
46614f52dfbbSDag-Erling Smørgrav 	int sock;
46624f52dfbbSDag-Erling Smørgrav 
46634f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
46644f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
46654f52dfbbSDag-Erling Smørgrav 	    &cctx, NULL, NULL);
46664f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
46674f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
46684f52dfbbSDag-Erling Smørgrav 		return NULL;
46694f52dfbbSDag-Erling Smørgrav 	}
46704f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
46714f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
46724f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
46734f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
46744f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
46754f52dfbbSDag-Erling Smørgrav 
46764f52dfbbSDag-Erling Smørgrav 	return c;
4677d93a896eSDag-Erling Smørgrav }
4678d93a896eSDag-Erling Smørgrav 
4679ca86bcf2SDag-Erling Smørgrav /*
4680ca86bcf2SDag-Erling Smørgrav  * returns either the newly connected channel or the downstream channel
4681ca86bcf2SDag-Erling Smørgrav  * that needs to deal with this connection.
4682ca86bcf2SDag-Erling Smørgrav  */
4683d4af9e69SDag-Erling Smørgrav Channel *
46844f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
4685a0ee8cc6SDag-Erling Smørgrav     u_short listen_port, char *ctype, char *rname)
4686d4af9e69SDag-Erling Smørgrav {
46874f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4688190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
46894f52dfbbSDag-Erling Smørgrav 	u_int i;
4690190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4691d4af9e69SDag-Erling Smørgrav 
4692190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4693190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4694190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm,
4695190cef3dSDag-Erling Smørgrav 		    listen_host, listen_port, 1)) {
4696190cef3dSDag-Erling Smørgrav 			if (perm->downstream)
4697190cef3dSDag-Erling Smørgrav 				return perm->downstream;
4698190cef3dSDag-Erling Smørgrav 			if (perm->port_to_connect == 0)
46994f52dfbbSDag-Erling Smørgrav 				return rdynamic_connect_prepare(ssh,
47004f52dfbbSDag-Erling Smørgrav 				    ctype, rname);
47014f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4702190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
47034f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4704d4af9e69SDag-Erling Smørgrav 		}
4705d4af9e69SDag-Erling Smørgrav 	}
4706d4af9e69SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4707d4af9e69SDag-Erling Smørgrav 	    listen_port);
4708d4af9e69SDag-Erling Smørgrav 	return NULL;
4709d4af9e69SDag-Erling Smørgrav }
4710d4af9e69SDag-Erling Smørgrav 
4711a0ee8cc6SDag-Erling Smørgrav Channel *
47124f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path,
47134f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4714a0ee8cc6SDag-Erling Smørgrav {
47154f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4716190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
47174f52dfbbSDag-Erling Smørgrav 	u_int i;
4718190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4719a0ee8cc6SDag-Erling Smørgrav 
4720190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4721190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4722190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path)) {
47234f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4724190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
47254f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4726a0ee8cc6SDag-Erling Smørgrav 		}
4727a0ee8cc6SDag-Erling Smørgrav 	}
4728a0ee8cc6SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown path %.100s",
4729a0ee8cc6SDag-Erling Smørgrav 	    path);
4730a0ee8cc6SDag-Erling Smørgrav 	return NULL;
4731a0ee8cc6SDag-Erling Smørgrav }
4732a0ee8cc6SDag-Erling Smørgrav 
4733ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */
4734d4af9e69SDag-Erling Smørgrav Channel *
47354f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
47364f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, int *reason, const char **errmsg)
4737ca3176e7SBrian Feldman {
47384f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4739190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
47404f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
47414f52dfbbSDag-Erling Smørgrav 	Channel *c;
47424f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
47434f52dfbbSDag-Erling Smørgrav 	int sock;
4744190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4745ca3176e7SBrian Feldman 
4746190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4747ca3176e7SBrian Feldman 	if (!permit) {
4748190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4749190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4750190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4751ca3176e7SBrian Feldman 				permit = 1;
4752a0ee8cc6SDag-Erling Smørgrav 				break;
4753a0ee8cc6SDag-Erling Smørgrav 			}
4754ca3176e7SBrian Feldman 		}
47554f52dfbbSDag-Erling Smørgrav 	}
4756333ee039SDag-Erling Smørgrav 
4757190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4758333ee039SDag-Erling Smørgrav 		permit_adm = 0;
4759190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4760190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4761190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4762333ee039SDag-Erling Smørgrav 				permit_adm = 1;
4763a0ee8cc6SDag-Erling Smørgrav 				break;
4764a0ee8cc6SDag-Erling Smørgrav 			}
4765333ee039SDag-Erling Smørgrav 		}
47664f52dfbbSDag-Erling Smørgrav 	}
4767333ee039SDag-Erling Smørgrav 
4768333ee039SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
476919261079SEd Maste 		logit("Received request from %.100s port %d to connect to "
477019261079SEd Maste 		    "host %.100s port %d, but the request was denied.",
477119261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port);
4772d93a896eSDag-Erling Smørgrav 		if (reason != NULL)
4773d93a896eSDag-Erling Smørgrav 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4774d4af9e69SDag-Erling Smørgrav 		return NULL;
4775ca3176e7SBrian Feldman 	}
47764f52dfbbSDag-Erling Smørgrav 
47774f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
47784f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
47794f52dfbbSDag-Erling Smørgrav 	    &cctx, reason, errmsg);
47804f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
47814f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
47824f52dfbbSDag-Erling Smørgrav 		return NULL;
47834f52dfbbSDag-Erling Smørgrav 	}
47844f52dfbbSDag-Erling Smørgrav 
47854f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
47864f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
47874f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
47884f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
47894f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
47904f52dfbbSDag-Erling Smørgrav 
47914f52dfbbSDag-Erling Smørgrav 	return c;
4792ca3176e7SBrian Feldman }
4793ca3176e7SBrian Feldman 
4794a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */
4795a0ee8cc6SDag-Erling Smørgrav Channel *
47964f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path,
47974f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4798a0ee8cc6SDag-Erling Smørgrav {
47994f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4800190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48014f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4802190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4803a0ee8cc6SDag-Erling Smørgrav 
4804190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4805a0ee8cc6SDag-Erling Smørgrav 	if (!permit) {
4806190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4807190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4808190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4809a0ee8cc6SDag-Erling Smørgrav 				permit = 1;
4810a0ee8cc6SDag-Erling Smørgrav 				break;
4811a0ee8cc6SDag-Erling Smørgrav 			}
4812a0ee8cc6SDag-Erling Smørgrav 		}
48134f52dfbbSDag-Erling Smørgrav 	}
4814a0ee8cc6SDag-Erling Smørgrav 
4815190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4816a0ee8cc6SDag-Erling Smørgrav 		permit_adm = 0;
4817190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4818190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4819190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4820a0ee8cc6SDag-Erling Smørgrav 				permit_adm = 1;
4821a0ee8cc6SDag-Erling Smørgrav 				break;
4822a0ee8cc6SDag-Erling Smørgrav 			}
4823a0ee8cc6SDag-Erling Smørgrav 		}
48244f52dfbbSDag-Erling Smørgrav 	}
4825a0ee8cc6SDag-Erling Smørgrav 
4826a0ee8cc6SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4827a0ee8cc6SDag-Erling Smørgrav 		logit("Received request to connect to path %.100s, "
4828a0ee8cc6SDag-Erling Smørgrav 		    "but the request was denied.", path);
4829a0ee8cc6SDag-Erling Smørgrav 		return NULL;
4830a0ee8cc6SDag-Erling Smørgrav 	}
48314f52dfbbSDag-Erling Smørgrav 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
4832a0ee8cc6SDag-Erling Smørgrav }
4833a0ee8cc6SDag-Erling Smørgrav 
483421e764dfSDag-Erling Smørgrav void
48354f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh)
483621e764dfSDag-Erling Smørgrav {
48374f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
483821e764dfSDag-Erling Smørgrav 	struct winsize ws;
48394f52dfbbSDag-Erling Smørgrav 	int r;
48404f52dfbbSDag-Erling Smørgrav 	u_int i;
484121e764dfSDag-Erling Smørgrav 
48424f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
48434f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
48444f52dfbbSDag-Erling Smørgrav 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
484521e764dfSDag-Erling Smørgrav 			continue;
484619261079SEd Maste 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1)
484721e764dfSDag-Erling Smørgrav 			continue;
48484f52dfbbSDag-Erling Smørgrav 		channel_request_start(ssh, i, "window-change", 0);
48494f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
48504f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
48514f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
48524f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
48534f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0)
485419261079SEd Maste 			fatal_fr(r, "channel %u; send window-change", i);
485521e764dfSDag-Erling Smørgrav 	}
485621e764dfSDag-Erling Smørgrav }
485721e764dfSDag-Erling Smørgrav 
48584f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
48594f52dfbbSDag-Erling Smørgrav static Channel *
48604f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
48614f52dfbbSDag-Erling Smørgrav {
48624f52dfbbSDag-Erling Smørgrav 	Channel *c;
48634f52dfbbSDag-Erling Smørgrav 	int r;
48644f52dfbbSDag-Erling Smørgrav 
48654f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
48664f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
48674f52dfbbSDag-Erling Smørgrav 	c->host_port = 0;
48684f52dfbbSDag-Erling Smørgrav 	c->path = NULL;
48694f52dfbbSDag-Erling Smørgrav 
48704f52dfbbSDag-Erling Smørgrav 	/*
48714f52dfbbSDag-Erling Smørgrav 	 * We need to open the channel before we have a FD,
48724f52dfbbSDag-Erling Smørgrav 	 * so that we can get SOCKS header from peer.
48734f52dfbbSDag-Erling Smørgrav 	 */
48744f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
48754f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
48764f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
48774f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
487819261079SEd Maste 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0)
487919261079SEd Maste 		fatal_fr(r, "channel %i; confirm", c->self);
48804f52dfbbSDag-Erling Smørgrav 	return c;
48814f52dfbbSDag-Erling Smørgrav }
48824f52dfbbSDag-Erling Smørgrav 
48834f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */
48844f52dfbbSDag-Erling Smørgrav static int
48854f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c)
48864f52dfbbSDag-Erling Smørgrav {
488719261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
488819261079SEd Maste 	struct permission_set *pset = &sc->local_perms;
488919261079SEd Maste 	struct permission *perm;
48904f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
489119261079SEd Maste 	u_int i, permit_adm = 1;
48924f52dfbbSDag-Erling Smørgrav 	int sock;
48934f52dfbbSDag-Erling Smørgrav 
489419261079SEd Maste 	if (pset->num_permitted_admin > 0) {
489519261079SEd Maste 		permit_adm = 0;
489619261079SEd Maste 		for (i = 0; i < pset->num_permitted_admin; i++) {
489719261079SEd Maste 			perm = &pset->permitted_admin[i];
489819261079SEd Maste 			if (open_match(perm, c->path, c->host_port)) {
489919261079SEd Maste 				permit_adm = 1;
490019261079SEd Maste 				break;
490119261079SEd Maste 			}
490219261079SEd Maste 		}
490319261079SEd Maste 	}
490419261079SEd Maste 	if (!permit_adm) {
490519261079SEd Maste 		debug_f("requested forward not permitted");
490619261079SEd Maste 		return -1;
490719261079SEd Maste 	}
490819261079SEd Maste 
49094f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
49104f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
49114f52dfbbSDag-Erling Smørgrav 	    NULL, &cctx, NULL, NULL);
49124f52dfbbSDag-Erling Smørgrav 	if (sock == -1)
49134f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
49144f52dfbbSDag-Erling Smørgrav 	else {
49154f52dfbbSDag-Erling Smørgrav 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
49164f52dfbbSDag-Erling Smørgrav 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
49174f52dfbbSDag-Erling Smørgrav 		c->connect_ctx = cctx;
49184f52dfbbSDag-Erling Smørgrav 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
49194f52dfbbSDag-Erling Smørgrav 	}
49204f52dfbbSDag-Erling Smørgrav 	return sock;
49214f52dfbbSDag-Erling Smørgrav }
49224f52dfbbSDag-Erling Smørgrav 
4923af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */
4924511b41d2SMark Murray 
4925511b41d2SMark Murray /*
4926511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
4927a82e551fSDag-Erling Smørgrav  * Returns 0 and a suitable display number for the DISPLAY variable
4928a82e551fSDag-Erling Smørgrav  * stored in display_numberp , or -1 if an error occurs.
4929511b41d2SMark Murray  */
4930af12a3e7SDag-Erling Smørgrav int
49314f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
49324f52dfbbSDag-Erling Smørgrav     int x11_use_localhost, int single_connection,
49334f52dfbbSDag-Erling Smørgrav     u_int *display_numberp, int **chanids)
4934511b41d2SMark Murray {
4935af12a3e7SDag-Erling Smørgrav 	Channel *nc = NULL;
4936511b41d2SMark Murray 	int display_number, sock;
4937511b41d2SMark Murray 	u_short port;
4938511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
4939511b41d2SMark Murray 	char strport[NI_MAXSERV];
4940511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
4941511b41d2SMark Murray 
4942b74df5b2SDag-Erling Smørgrav 	if (chanids == NULL)
4943b74df5b2SDag-Erling Smørgrav 		return -1;
4944b74df5b2SDag-Erling Smørgrav 
4945511b41d2SMark Murray 	for (display_number = x11_display_offset;
4946511b41d2SMark Murray 	    display_number < MAX_DISPLAYS;
4947511b41d2SMark Murray 	    display_number++) {
4948511b41d2SMark Murray 		port = 6000 + display_number;
4949511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
49504f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
4951af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
4952511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
4953511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
49544f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(NULL, strport,
49554f52dfbbSDag-Erling Smørgrav 		    &hints, &aitop)) != 0) {
4956d4af9e69SDag-Erling Smørgrav 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
4957af12a3e7SDag-Erling Smørgrav 			return -1;
4958511b41d2SMark Murray 		}
4959511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
49604f52dfbbSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
49614f52dfbbSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
4962511b41d2SMark Murray 				continue;
4963221552e4SDag-Erling Smørgrav 			sock = socket(ai->ai_family, ai->ai_socktype,
4964221552e4SDag-Erling Smørgrav 			    ai->ai_protocol);
496519261079SEd Maste 			if (sock == -1) {
49668ad9b54aSDag-Erling Smørgrav 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
49678ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT
49688ad9b54aSDag-Erling Smørgrav 				    && (errno != EPFNOSUPPORT)
49698ad9b54aSDag-Erling Smørgrav #endif
49708ad9b54aSDag-Erling Smørgrav 				    ) {
4971511b41d2SMark Murray 					error("socket: %.100s", strerror(errno));
497221e764dfSDag-Erling Smørgrav 					freeaddrinfo(aitop);
4973af12a3e7SDag-Erling Smørgrav 					return -1;
4974989dd127SDag-Erling Smørgrav 				} else {
4975989dd127SDag-Erling Smørgrav 					debug("x11_create_display_inet: Socket family %d not supported",
4976989dd127SDag-Erling Smørgrav 						 ai->ai_family);
4977989dd127SDag-Erling Smørgrav 					continue;
4978511b41d2SMark Murray 				}
4979989dd127SDag-Erling Smørgrav 			}
4980b15c8340SDag-Erling Smørgrav 			if (ai->ai_family == AF_INET6)
4981b15c8340SDag-Erling Smørgrav 				sock_set_v6only(sock);
4982d4af9e69SDag-Erling Smørgrav 			if (x11_use_localhost)
498347dd1d1bSDag-Erling Smørgrav 				set_reuseaddr(sock);
498419261079SEd Maste 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
498519261079SEd Maste 				debug2_f("bind port %d: %.100s", port,
498619261079SEd Maste 				    strerror(errno));
4987511b41d2SMark Murray 				close(sock);
49884f52dfbbSDag-Erling Smørgrav 				for (n = 0; n < num_socks; n++)
4989511b41d2SMark Murray 					close(socks[n]);
4990511b41d2SMark Murray 				num_socks = 0;
4991511b41d2SMark Murray 				break;
4992511b41d2SMark Murray 			}
4993511b41d2SMark Murray 			socks[num_socks++] = sock;
4994511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
4995511b41d2SMark Murray 				break;
4996511b41d2SMark Murray 		}
4997ca3176e7SBrian Feldman 		freeaddrinfo(aitop);
4998511b41d2SMark Murray 		if (num_socks > 0)
4999511b41d2SMark Murray 			break;
5000511b41d2SMark Murray 	}
5001511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
5002511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
5003af12a3e7SDag-Erling Smørgrav 		return -1;
5004511b41d2SMark Murray 	}
5005511b41d2SMark Murray 	/* Start listening for connections on the socket. */
5006511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5007511b41d2SMark Murray 		sock = socks[n];
500819261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
5009511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
5010511b41d2SMark Murray 			close(sock);
5011af12a3e7SDag-Erling Smørgrav 			return -1;
5012511b41d2SMark Murray 		}
5013511b41d2SMark Murray 	}
5014511b41d2SMark Murray 
5015511b41d2SMark Murray 	/* Allocate a channel for each socket. */
5016333ee039SDag-Erling Smørgrav 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
5017511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5018511b41d2SMark Murray 		sock = socks[n];
5019*f374ba41SEd Maste 		nc = channel_new(ssh, "x11-listener",
5020a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
5021a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
5022221552e4SDag-Erling Smørgrav 		    0, "X11 inet listener", 1);
5023af12a3e7SDag-Erling Smørgrav 		nc->single_connection = single_connection;
5024d4ecd108SDag-Erling Smørgrav 		(*chanids)[n] = nc->self;
5025511b41d2SMark Murray 	}
5026d4ecd108SDag-Erling Smørgrav 	(*chanids)[n] = -1;
5027511b41d2SMark Murray 
5028af12a3e7SDag-Erling Smørgrav 	/* Return the display number for the DISPLAY environment variable. */
5029a82e551fSDag-Erling Smørgrav 	*display_numberp = display_number;
50304f52dfbbSDag-Erling Smørgrav 	return 0;
5031511b41d2SMark Murray }
5032511b41d2SMark Murray 
5033af12a3e7SDag-Erling Smørgrav static int
5034cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname)
5035511b41d2SMark Murray {
5036511b41d2SMark Murray 	int sock;
5037511b41d2SMark Murray 	struct sockaddr_un addr;
5038511b41d2SMark Murray 
5039511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
504019261079SEd Maste 	if (sock == -1)
5041511b41d2SMark Murray 		error("socket: %.100s", strerror(errno));
5042511b41d2SMark Murray 	memset(&addr, 0, sizeof(addr));
5043511b41d2SMark Murray 	addr.sun_family = AF_UNIX;
5044cce7d346SDag-Erling Smørgrav 	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
5045511b41d2SMark Murray 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
5046511b41d2SMark Murray 		return sock;
5047511b41d2SMark Murray 	close(sock);
5048511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
5049511b41d2SMark Murray 	return -1;
5050511b41d2SMark Murray }
5051511b41d2SMark Murray 
5052cce7d346SDag-Erling Smørgrav static int
5053cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr)
5054cce7d346SDag-Erling Smørgrav {
5055cce7d346SDag-Erling Smørgrav 	char buf[1024];
5056cce7d346SDag-Erling Smørgrav 	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
5057cce7d346SDag-Erling Smørgrav 	return connect_local_xsocket_path(buf);
5058cce7d346SDag-Erling Smørgrav }
5059cce7d346SDag-Erling Smørgrav 
5060d93a896eSDag-Erling Smørgrav #ifdef __APPLE__
5061d93a896eSDag-Erling Smørgrav static int
5062d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen)
5063d93a896eSDag-Erling Smørgrav {
5064d93a896eSDag-Erling Smørgrav 	struct stat sbuf;
5065d93a896eSDag-Erling Smørgrav 
5066d93a896eSDag-Erling Smørgrav 	if (strlcpy(path, display, pathlen) >= pathlen) {
5067d93a896eSDag-Erling Smørgrav 		error("%s: display path too long", __func__);
5068d93a896eSDag-Erling Smørgrav 		return 0;
5069d93a896eSDag-Erling Smørgrav 	}
5070d93a896eSDag-Erling Smørgrav 	if (display[0] != '/')
5071d93a896eSDag-Erling Smørgrav 		return 0;
5072d93a896eSDag-Erling Smørgrav 	if (stat(path, &sbuf) == 0) {
5073d93a896eSDag-Erling Smørgrav 		return 1;
5074d93a896eSDag-Erling Smørgrav 	} else {
5075d93a896eSDag-Erling Smørgrav 		char *dot = strrchr(path, '.');
5076d93a896eSDag-Erling Smørgrav 		if (dot != NULL) {
5077d93a896eSDag-Erling Smørgrav 			*dot = '\0';
5078d93a896eSDag-Erling Smørgrav 			if (stat(path, &sbuf) == 0) {
5079d93a896eSDag-Erling Smørgrav 				return 1;
5080d93a896eSDag-Erling Smørgrav 			}
5081d93a896eSDag-Erling Smørgrav 		}
5082d93a896eSDag-Erling Smørgrav 	}
5083d93a896eSDag-Erling Smørgrav 	return 0;
5084d93a896eSDag-Erling Smørgrav }
5085d93a896eSDag-Erling Smørgrav #endif
5086d93a896eSDag-Erling Smørgrav 
5087a04a10f8SKris Kennaway int
50884f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh)
5089511b41d2SMark Murray {
5090333ee039SDag-Erling Smørgrav 	u_int display_number;
5091511b41d2SMark Murray 	const char *display;
5092a04a10f8SKris Kennaway 	char buf[1024], *cp;
5093511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
5094511b41d2SMark Murray 	char strport[NI_MAXSERV];
5095333ee039SDag-Erling Smørgrav 	int gaierr, sock = 0;
5096511b41d2SMark Murray 
5097511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
5098511b41d2SMark Murray 	display = getenv("DISPLAY");
5099511b41d2SMark Murray 	if (!display) {
5100511b41d2SMark Murray 		error("DISPLAY not set.");
5101a04a10f8SKris Kennaway 		return -1;
5102511b41d2SMark Murray 	}
5103511b41d2SMark Murray 	/*
5104511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
5105511b41d2SMark Murray 	 * connection to the real X server.
5106511b41d2SMark Murray 	 */
5107511b41d2SMark Murray 
5108cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
5109d93a896eSDag-Erling Smørgrav 	/* Check if display is a path to a socket (as set by launchd). */
5110d93a896eSDag-Erling Smørgrav 	{
5111d93a896eSDag-Erling Smørgrav 		char path[PATH_MAX];
5112d93a896eSDag-Erling Smørgrav 
5113d93a896eSDag-Erling Smørgrav 		if (is_path_to_xsocket(display, path, sizeof(path))) {
5114d93a896eSDag-Erling Smørgrav 			debug("x11_connect_display: $DISPLAY is launchd");
5115d93a896eSDag-Erling Smørgrav 
5116d93a896eSDag-Erling Smørgrav 			/* Create a socket. */
5117d93a896eSDag-Erling Smørgrav 			sock = connect_local_xsocket_path(path);
5118cce7d346SDag-Erling Smørgrav 			if (sock < 0)
5119cce7d346SDag-Erling Smørgrav 				return -1;
5120cce7d346SDag-Erling Smørgrav 
5121cce7d346SDag-Erling Smørgrav 			/* OK, we now have a connection to the display. */
5122cce7d346SDag-Erling Smørgrav 			return sock;
5123cce7d346SDag-Erling Smørgrav 		}
5124d93a896eSDag-Erling Smørgrav 	}
5125cce7d346SDag-Erling Smørgrav #endif
5126511b41d2SMark Murray 	/*
5127511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
5128511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
5129511b41d2SMark Murray 	 */
5130511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
5131511b41d2SMark Murray 	    display[0] == ':') {
5132511b41d2SMark Murray 		/* Connect to the unix domain socket. */
51334f52dfbbSDag-Erling Smørgrav 		if (sscanf(strrchr(display, ':') + 1, "%u",
51344f52dfbbSDag-Erling Smørgrav 		    &display_number) != 1) {
51354f52dfbbSDag-Erling Smørgrav 			error("Could not parse display number from DISPLAY: "
51364f52dfbbSDag-Erling Smørgrav 			    "%.100s", display);
5137a04a10f8SKris Kennaway 			return -1;
5138511b41d2SMark Murray 		}
5139511b41d2SMark Murray 		/* Create a socket. */
5140511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
5141511b41d2SMark Murray 		if (sock < 0)
5142a04a10f8SKris Kennaway 			return -1;
5143511b41d2SMark Murray 
5144511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
5145a04a10f8SKris Kennaway 		return sock;
5146511b41d2SMark Murray 	}
5147511b41d2SMark Murray 	/*
5148511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
5149511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
5150511b41d2SMark Murray 	 */
5151af12a3e7SDag-Erling Smørgrav 	strlcpy(buf, display, sizeof(buf));
5152511b41d2SMark Murray 	cp = strchr(buf, ':');
5153511b41d2SMark Murray 	if (!cp) {
5154511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
5155a04a10f8SKris Kennaway 		return -1;
5156511b41d2SMark Murray 	}
5157511b41d2SMark Murray 	*cp = 0;
51584f52dfbbSDag-Erling Smørgrav 	/*
51594f52dfbbSDag-Erling Smørgrav 	 * buf now contains the host name.  But first we parse the
51604f52dfbbSDag-Erling Smørgrav 	 * display number.
51614f52dfbbSDag-Erling Smørgrav 	 */
5162333ee039SDag-Erling Smørgrav 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
5163511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
5164511b41d2SMark Murray 		    display);
5165a04a10f8SKris Kennaway 		return -1;
5166511b41d2SMark Murray 	}
5167511b41d2SMark Murray 
5168511b41d2SMark Murray 	/* Look up the host address */
5169511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
51704f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
5171511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
5172333ee039SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
5173511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
5174d4af9e69SDag-Erling Smørgrav 		error("%.100s: unknown host. (%s)", buf,
5175d4af9e69SDag-Erling Smørgrav 		ssh_gai_strerror(gaierr));
5176a04a10f8SKris Kennaway 		return -1;
5177511b41d2SMark Murray 	}
5178511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
5179511b41d2SMark Murray 		/* Create a socket. */
5180221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
518119261079SEd Maste 		if (sock == -1) {
5182221552e4SDag-Erling Smørgrav 			debug2("socket: %.100s", strerror(errno));
5183511b41d2SMark Murray 			continue;
5184511b41d2SMark Murray 		}
5185511b41d2SMark Murray 		/* Connect it to the display. */
518619261079SEd Maste 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
5187333ee039SDag-Erling Smørgrav 			debug2("connect %.100s port %u: %.100s", buf,
5188a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
5189511b41d2SMark Murray 			close(sock);
5190511b41d2SMark Murray 			continue;
5191511b41d2SMark Murray 		}
5192511b41d2SMark Murray 		/* Success */
5193511b41d2SMark Murray 		break;
5194a04a10f8SKris Kennaway 	}
5195511b41d2SMark Murray 	freeaddrinfo(aitop);
5196511b41d2SMark Murray 	if (!ai) {
51974f52dfbbSDag-Erling Smørgrav 		error("connect %.100s port %u: %.100s", buf,
51984f52dfbbSDag-Erling Smørgrav 		    6000 + display_number, strerror(errno));
5199a04a10f8SKris Kennaway 		return -1;
5200511b41d2SMark Murray 	}
5201af12a3e7SDag-Erling Smørgrav 	set_nodelay(sock);
5202a04a10f8SKris Kennaway 	return sock;
5203a04a10f8SKris Kennaway }
5204511b41d2SMark Murray 
5205a04a10f8SKris Kennaway /*
5206511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
5207511b41d2SMark Murray  * data, and enables authentication spoofing.
5208af12a3e7SDag-Erling Smørgrav  * This should be called in the client only.
5209511b41d2SMark Murray  */
5210511b41d2SMark Murray void
52114f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
52124f52dfbbSDag-Erling Smørgrav     const char *disp, const char *proto, const char *data, int want_reply)
5213511b41d2SMark Murray {
52144f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
5215ca3176e7SBrian Feldman 	u_int data_len = (u_int) strlen(data) / 2;
5216d4ecd108SDag-Erling Smørgrav 	u_int i, value;
5217511b41d2SMark Murray 	const char *cp;
52184f52dfbbSDag-Erling Smørgrav 	char *new_data;
52194f52dfbbSDag-Erling Smørgrav 	int r, screen_number;
5220511b41d2SMark Murray 
52214f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_display == NULL)
52224f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_display = xstrdup(disp);
52234f52dfbbSDag-Erling Smørgrav 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
5224d4ecd108SDag-Erling Smørgrav 		error("x11_request_forwarding_with_spoofing: different "
5225d4ecd108SDag-Erling Smørgrav 		    "$DISPLAY already forwarded");
5226d4ecd108SDag-Erling Smørgrav 		return;
5227d4ecd108SDag-Erling Smørgrav 	}
5228d4ecd108SDag-Erling Smørgrav 
5229d4ecd108SDag-Erling Smørgrav 	cp = strchr(disp, ':');
5230511b41d2SMark Murray 	if (cp)
5231511b41d2SMark Murray 		cp = strchr(cp, '.');
5232511b41d2SMark Murray 	if (cp)
5233333ee039SDag-Erling Smørgrav 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
5234511b41d2SMark Murray 	else
5235511b41d2SMark Murray 		screen_number = 0;
5236511b41d2SMark Murray 
52374f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_proto == NULL) {
5238511b41d2SMark Murray 		/* Save protocol name. */
52394f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_proto = xstrdup(proto);
5240ca86bcf2SDag-Erling Smørgrav 
5241ca86bcf2SDag-Erling Smørgrav 		/* Extract real authentication data. */
52424f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data = xmalloc(data_len);
5243511b41d2SMark Murray 		for (i = 0; i < data_len; i++) {
524419261079SEd Maste 			if (sscanf(data + 2 * i, "%2x", &value) != 1) {
5245d4ecd108SDag-Erling Smørgrav 				fatal("x11_request_forwarding: bad "
5246d4ecd108SDag-Erling Smørgrav 				    "authentication data: %.100s", data);
524719261079SEd Maste 			}
52484f52dfbbSDag-Erling Smørgrav 			sc->x11_saved_data[i] = value;
5249511b41d2SMark Murray 		}
52504f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data_len = data_len;
5251ca86bcf2SDag-Erling Smørgrav 
5252ca86bcf2SDag-Erling Smørgrav 		/* Generate fake data of the same length. */
52534f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data = xmalloc(data_len);
52544f52dfbbSDag-Erling Smørgrav 		arc4random_buf(sc->x11_fake_data, data_len);
52554f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data_len = data_len;
5256d4ecd108SDag-Erling Smørgrav 	}
5257511b41d2SMark Murray 
5258511b41d2SMark Murray 	/* Convert the fake data into hex. */
52594f52dfbbSDag-Erling Smørgrav 	new_data = tohex(sc->x11_fake_data, data_len);
5260511b41d2SMark Murray 
5261511b41d2SMark Murray 	/* Send the request packet. */
52624f52dfbbSDag-Erling Smørgrav 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
52634f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
52644f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
52654f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
52664f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
52674f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0 ||
52684f52dfbbSDag-Erling Smørgrav 	    (r = ssh_packet_write_wait(ssh)) != 0)
526919261079SEd Maste 		fatal_fr(r, "send x11-req");
5270e4a9863fSDag-Erling Smørgrav 	free(new_data);
5271511b41d2SMark Murray }
5272