xref: /freebsd/crypto/openssh/channels.c (revision a91a246563dffa876a52f53a98de4af9fa364c52)
1*a91a2465SEd Maste /* $OpenBSD: channels.c,v 1.437 2024/03/06 02:59:59 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 
154f374ba41SEd Maste /* Used to record timeouts per channel type */
155f374ba41SEd Maste struct ssh_channel_timeout {
156f374ba41SEd Maste 	char *type_pattern;
157535af610SEd Maste 	int timeout_secs;
158f374ba41SEd Maste };
159f374ba41SEd 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 */
2014d3fc8b0SEd Maste 	time_t 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;
213f374ba41SEd Maste 
214f374ba41SEd Maste 	/* Channel timeouts by type */
215f374ba41SEd Maste 	struct ssh_channel_timeout *timeouts;
216f374ba41SEd Maste 	size_t ntimeouts;
217*a91a2465SEd Maste 	/* Global timeout for all OPEN channels */
218*a91a2465SEd Maste 	int global_deadline;
219*a91a2465SEd Maste 	time_t lastused;
2204f52dfbbSDag-Erling Smørgrav };
221ca3176e7SBrian Feldman 
222af12a3e7SDag-Erling Smørgrav /* helper */
2234f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
224ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host);
225ca3176e7SBrian Feldman 
226d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */
227d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *);
228d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *);
2294f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
2304f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *);
2314f52dfbbSDag-Erling Smørgrav 
2324f52dfbbSDag-Erling Smørgrav /* Setup helper */
2334f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc);
234d4af9e69SDag-Erling Smørgrav 
235af12a3e7SDag-Erling Smørgrav /* -- channel core */
236a04a10f8SKris Kennaway 
2374f52dfbbSDag-Erling Smørgrav void
2384f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh)
2394f52dfbbSDag-Erling Smørgrav {
2404f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc;
2414f52dfbbSDag-Erling Smørgrav 
24219261079SEd Maste 	if ((sc = calloc(1, sizeof(*sc))) == NULL)
24319261079SEd Maste 		fatal_f("allocation failed");
2444f52dfbbSDag-Erling Smørgrav 	sc->channels_alloc = 10;
2454f52dfbbSDag-Erling Smørgrav 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2464f52dfbbSDag-Erling Smørgrav 	sc->IPv4or6 = AF_UNSPEC;
2474f52dfbbSDag-Erling Smørgrav 	channel_handler_init(sc);
2484f52dfbbSDag-Erling Smørgrav 
2494f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt = sc;
2504f52dfbbSDag-Erling Smørgrav }
2514f52dfbbSDag-Erling Smørgrav 
252a04a10f8SKris Kennaway Channel *
2534f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id)
254a04a10f8SKris Kennaway {
255a04a10f8SKris Kennaway 	Channel *c;
256af12a3e7SDag-Erling Smørgrav 
2574f52dfbbSDag-Erling Smørgrav 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
25819261079SEd Maste 		logit_f("%d: bad id", id);
259a04a10f8SKris Kennaway 		return NULL;
260a04a10f8SKris Kennaway 	}
2614f52dfbbSDag-Erling Smørgrav 	c = ssh->chanctxt->channels[id];
262af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
26319261079SEd Maste 		logit_f("%d: bad id: channel free", id);
264a04a10f8SKris Kennaway 		return NULL;
265a04a10f8SKris Kennaway 	}
266a04a10f8SKris Kennaway 	return c;
267a04a10f8SKris Kennaway }
268a04a10f8SKris Kennaway 
269ca86bcf2SDag-Erling Smørgrav Channel *
2704f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id)
271ca86bcf2SDag-Erling Smørgrav {
272ca86bcf2SDag-Erling Smørgrav 	Channel *c;
273ca86bcf2SDag-Erling Smørgrav 	u_int i;
274ca86bcf2SDag-Erling Smørgrav 
2754f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2764f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
2774f52dfbbSDag-Erling Smørgrav 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
278ca86bcf2SDag-Erling Smørgrav 			return c;
279ca86bcf2SDag-Erling Smørgrav 	}
280ca86bcf2SDag-Erling Smørgrav 	return NULL;
281ca86bcf2SDag-Erling Smørgrav }
282ca86bcf2SDag-Erling Smørgrav 
283a04a10f8SKris Kennaway /*
284b74df5b2SDag-Erling Smørgrav  * Returns the channel if it is allowed to receive protocol messages.
285b74df5b2SDag-Erling Smørgrav  * Private channels, like listening sockets, may not receive messages.
286b74df5b2SDag-Erling Smørgrav  */
287b74df5b2SDag-Erling Smørgrav Channel *
2884f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id)
289b74df5b2SDag-Erling Smørgrav {
290b74df5b2SDag-Erling Smørgrav 	Channel *c;
291b74df5b2SDag-Erling Smørgrav 
2924f52dfbbSDag-Erling Smørgrav 	if ((c = channel_by_id(ssh, id)) == NULL)
2934f52dfbbSDag-Erling Smørgrav 		return NULL;
294b74df5b2SDag-Erling Smørgrav 
295b74df5b2SDag-Erling Smørgrav 	switch (c->type) {
296b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_X11_OPEN:
297b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_LARVAL:
298b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_CONNECTING:
299b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_DYNAMIC:
3004f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_OPEN:
3014f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_FINISH:
302b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPENING:
303b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPEN:
304e4a9863fSDag-Erling Smørgrav 	case SSH_CHANNEL_ABANDONED:
305ca86bcf2SDag-Erling Smørgrav 	case SSH_CHANNEL_MUX_PROXY:
3064f52dfbbSDag-Erling Smørgrav 		return c;
307b74df5b2SDag-Erling Smørgrav 	}
308b74df5b2SDag-Erling Smørgrav 	logit("Non-public channel %d, type %d.", id, c->type);
3094f52dfbbSDag-Erling Smørgrav 	return NULL;
310b74df5b2SDag-Erling Smørgrav }
311b74df5b2SDag-Erling Smørgrav 
312b74df5b2SDag-Erling Smørgrav /*
313f374ba41SEd Maste  * Add a timeout for open channels whose c->ctype (or c->xctype if it is set)
314f374ba41SEd Maste  * match type_pattern.
315f374ba41SEd Maste  */
316f374ba41SEd Maste void
317f374ba41SEd Maste channel_add_timeout(struct ssh *ssh, const char *type_pattern,
318535af610SEd Maste     int timeout_secs)
319f374ba41SEd Maste {
320f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
321f374ba41SEd Maste 
322*a91a2465SEd Maste 	if (strcmp(type_pattern, "global") == 0) {
323*a91a2465SEd Maste 		debug2_f("global channel timeout %d seconds", timeout_secs);
324*a91a2465SEd Maste 		sc->global_deadline = timeout_secs;
325*a91a2465SEd Maste 		return;
326*a91a2465SEd Maste 	}
327535af610SEd Maste 	debug2_f("channel type \"%s\" timeout %d seconds",
328f374ba41SEd Maste 	    type_pattern, timeout_secs);
329f374ba41SEd Maste 	sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts,
330f374ba41SEd Maste 	    sc->ntimeouts + 1, sizeof(*sc->timeouts));
331f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern);
332f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs;
333f374ba41SEd Maste 	sc->ntimeouts++;
334f374ba41SEd Maste }
335f374ba41SEd Maste 
336f374ba41SEd Maste /* Clears all previously-added channel timeouts */
337f374ba41SEd Maste void
338f374ba41SEd Maste channel_clear_timeouts(struct ssh *ssh)
339f374ba41SEd Maste {
340f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
341f374ba41SEd Maste 	size_t i;
342f374ba41SEd Maste 
343f374ba41SEd Maste 	debug3_f("clearing");
344f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++)
345f374ba41SEd Maste 		free(sc->timeouts[i].type_pattern);
346f374ba41SEd Maste 	free(sc->timeouts);
347f374ba41SEd Maste 	sc->timeouts = NULL;
348f374ba41SEd Maste 	sc->ntimeouts = 0;
349f374ba41SEd Maste }
350f374ba41SEd Maste 
351535af610SEd Maste static int
352f374ba41SEd Maste lookup_timeout(struct ssh *ssh, const char *type)
353f374ba41SEd Maste {
354f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
355f374ba41SEd Maste 	size_t i;
356f374ba41SEd Maste 
357f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++) {
358f374ba41SEd Maste 		if (match_pattern(type, sc->timeouts[i].type_pattern))
359f374ba41SEd Maste 			return sc->timeouts[i].timeout_secs;
360f374ba41SEd Maste 	}
361f374ba41SEd Maste 
362f374ba41SEd Maste 	return 0;
363f374ba41SEd Maste }
364f374ba41SEd Maste 
365f374ba41SEd Maste /*
366f374ba41SEd Maste  * Sets "extended type" of a channel; used by session layer to add additional
367f374ba41SEd Maste  * information about channel types (e.g. shell, login, subsystem) that can then
368f374ba41SEd Maste  * be used to select timeouts.
369f374ba41SEd Maste  * Will reset c->inactive_deadline as a side-effect.
370f374ba41SEd Maste  */
371f374ba41SEd Maste void
372f374ba41SEd Maste channel_set_xtype(struct ssh *ssh, int id, const char *xctype)
373f374ba41SEd Maste {
374f374ba41SEd Maste 	Channel *c;
375f374ba41SEd Maste 
376f374ba41SEd Maste 	if ((c = channel_by_id(ssh, id)) == NULL)
377f374ba41SEd Maste 		fatal_f("missing channel %d", id);
378f374ba41SEd Maste 	if (c->xctype != NULL)
379f374ba41SEd Maste 		free(c->xctype);
380f374ba41SEd Maste 	c->xctype = xstrdup(xctype);
381f374ba41SEd Maste 	/* Type has changed, so look up inactivity deadline again */
382f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->xctype);
383f374ba41SEd Maste 	debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype,
384f374ba41SEd Maste 	    c->inactive_deadline);
385f374ba41SEd Maste }
386f374ba41SEd Maste 
387f374ba41SEd Maste /*
388*a91a2465SEd Maste  * update "last used" time on a channel.
389*a91a2465SEd Maste  * NB. nothing else should update lastused except to clear it.
390*a91a2465SEd Maste  */
391*a91a2465SEd Maste static void
392*a91a2465SEd Maste channel_set_used_time(struct ssh *ssh, Channel *c)
393*a91a2465SEd Maste {
394*a91a2465SEd Maste 	ssh->chanctxt->lastused = monotime();
395*a91a2465SEd Maste 	if (c != NULL)
396*a91a2465SEd Maste 		c->lastused = ssh->chanctxt->lastused;
397*a91a2465SEd Maste }
398*a91a2465SEd Maste 
399*a91a2465SEd Maste /*
400*a91a2465SEd Maste  * Get the time at which a channel is due to time out for inactivity.
401*a91a2465SEd Maste  * Returns 0 if the channel is not due to time out ever.
402*a91a2465SEd Maste  */
403*a91a2465SEd Maste static time_t
404*a91a2465SEd Maste channel_get_expiry(struct ssh *ssh, Channel *c)
405*a91a2465SEd Maste {
406*a91a2465SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
407*a91a2465SEd Maste 	time_t expiry = 0, channel_expiry;
408*a91a2465SEd Maste 
409*a91a2465SEd Maste 	if (sc->lastused != 0 && sc->global_deadline != 0)
410*a91a2465SEd Maste 		expiry = sc->lastused + sc->global_deadline;
411*a91a2465SEd Maste 	if (c->lastused != 0 && c->inactive_deadline != 0) {
412*a91a2465SEd Maste 		channel_expiry = c->lastused + c->inactive_deadline;
413*a91a2465SEd Maste 		if (expiry == 0 || channel_expiry < expiry)
414*a91a2465SEd Maste 			expiry = channel_expiry;
415*a91a2465SEd Maste 	}
416*a91a2465SEd Maste 	return expiry;
417*a91a2465SEd Maste }
418*a91a2465SEd Maste 
419*a91a2465SEd Maste /*
420a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
421a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
422a04a10f8SKris Kennaway  */
423af12a3e7SDag-Erling Smørgrav static void
4244f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
425d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty)
426a04a10f8SKris Kennaway {
42738a52bd3SEd Maste 	int val;
42838a52bd3SEd Maste 
429b15c8340SDag-Erling Smørgrav 	if (rfd != -1)
4304d3fc8b0SEd Maste 		(void)fcntl(rfd, F_SETFD, FD_CLOEXEC);
431b15c8340SDag-Erling Smørgrav 	if (wfd != -1 && wfd != rfd)
4324d3fc8b0SEd Maste 		(void)fcntl(wfd, F_SETFD, FD_CLOEXEC);
433b15c8340SDag-Erling Smørgrav 	if (efd != -1 && efd != rfd && efd != wfd)
4344d3fc8b0SEd Maste 		(void)fcntl(efd, F_SETFD, FD_CLOEXEC);
435a04a10f8SKris Kennaway 
436a04a10f8SKris Kennaway 	c->rfd = rfd;
437a04a10f8SKris Kennaway 	c->wfd = wfd;
438a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
439a04a10f8SKris Kennaway 	c->efd = efd;
440a04a10f8SKris Kennaway 	c->extended_usage = extusage;
4415b9b2fafSBrian Feldman 
442d4af9e69SDag-Erling Smørgrav 	if ((c->isatty = is_tty) != 0)
443221552e4SDag-Erling Smørgrav 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
444e4a9863fSDag-Erling Smørgrav #ifdef _AIX
445e4a9863fSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
446d4af9e69SDag-Erling Smørgrav 	c->wfd_isatty = is_tty || isatty(c->wfd);
447e4a9863fSDag-Erling Smørgrav #endif
448e0fbb1d2SBrian Feldman 
4495b9b2fafSBrian Feldman 	/* enable nonblocking mode */
45019261079SEd Maste 	c->restore_block = 0;
45119261079SEd Maste 	if (nonblock == CHANNEL_NONBLOCK_STDIO) {
45219261079SEd Maste 		/*
45319261079SEd Maste 		 * Special handling for stdio file descriptors: do not set
45419261079SEd Maste 		 * non-blocking mode if they are TTYs. Otherwise prepare to
45519261079SEd Maste 		 * restore their blocking state on exit to avoid interfering
45619261079SEd Maste 		 * with other programs that follow.
45719261079SEd Maste 		 */
45838a52bd3SEd Maste 		if (rfd != -1 && !isatty(rfd) &&
45938a52bd3SEd Maste 		    (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
460f374ba41SEd Maste 			c->restore_flags[0] = val;
46119261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_RFD;
46219261079SEd Maste 			set_nonblock(rfd);
46319261079SEd Maste 		}
46438a52bd3SEd Maste 		if (wfd != -1 && !isatty(wfd) &&
46538a52bd3SEd Maste 		    (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
466f374ba41SEd Maste 			c->restore_flags[1] = val;
46719261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_WFD;
46819261079SEd Maste 			set_nonblock(wfd);
46919261079SEd Maste 		}
47038a52bd3SEd Maste 		if (efd != -1 && !isatty(efd) &&
47138a52bd3SEd Maste 		    (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
472f374ba41SEd Maste 			c->restore_flags[2] = val;
47319261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_EFD;
47419261079SEd Maste 			set_nonblock(efd);
47519261079SEd Maste 		}
47619261079SEd Maste 	} else if (nonblock) {
477a04a10f8SKris Kennaway 		if (rfd != -1)
478a04a10f8SKris Kennaway 			set_nonblock(rfd);
479a04a10f8SKris Kennaway 		if (wfd != -1)
480a04a10f8SKris Kennaway 			set_nonblock(wfd);
481a04a10f8SKris Kennaway 		if (efd != -1)
482a04a10f8SKris Kennaway 			set_nonblock(efd);
483a04a10f8SKris Kennaway 	}
484*a91a2465SEd Maste 	/* channel might be entering a larval state, so reset global timeout */
485*a91a2465SEd Maste 	channel_set_used_time(ssh, NULL);
4865b9b2fafSBrian Feldman }
487a04a10f8SKris Kennaway 
488511b41d2SMark Murray /*
48938a52bd3SEd Maste  * Allocate a new channel object and set its type and socket.
490511b41d2SMark Murray  */
491af12a3e7SDag-Erling Smørgrav Channel *
4924f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
49338a52bd3SEd Maste     u_int window, u_int maxpack, int extusage, const char *remote_name,
49438a52bd3SEd Maste     int nonblock)
495511b41d2SMark Murray {
4964f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
49738a52bd3SEd Maste 	u_int i, found = 0;
498511b41d2SMark Murray 	Channel *c;
49919261079SEd Maste 	int r;
500511b41d2SMark Murray 
501511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
5024f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
5034f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL) {
504511b41d2SMark Murray 			/* Found a free slot. */
5054f52dfbbSDag-Erling Smørgrav 			found = i;
506511b41d2SMark Murray 			break;
507511b41d2SMark Murray 		}
5084f52dfbbSDag-Erling Smørgrav 	}
5094f52dfbbSDag-Erling Smørgrav 	if (i >= sc->channels_alloc) {
5104f52dfbbSDag-Erling Smørgrav 		/*
5114f52dfbbSDag-Erling Smørgrav 		 * There are no free slots. Take last+1 slot and expand
5124f52dfbbSDag-Erling Smørgrav 		 * the array.
5134f52dfbbSDag-Erling Smørgrav 		 */
5144f52dfbbSDag-Erling Smørgrav 		found = sc->channels_alloc;
5154f52dfbbSDag-Erling Smørgrav 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
51619261079SEd Maste 			fatal_f("internal error: channels_alloc %d too big",
51719261079SEd Maste 			    sc->channels_alloc);
5184f52dfbbSDag-Erling Smørgrav 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
5194f52dfbbSDag-Erling Smørgrav 		    sc->channels_alloc + 10, sizeof(*sc->channels));
5204f52dfbbSDag-Erling Smørgrav 		sc->channels_alloc += 10;
5214f52dfbbSDag-Erling Smørgrav 		debug2("channel: expanding %d", sc->channels_alloc);
522511b41d2SMark Murray 	}
523af12a3e7SDag-Erling Smørgrav 	/* Initialize and return new channel. */
5244f52dfbbSDag-Erling Smørgrav 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
5254f52dfbbSDag-Erling Smørgrav 	if ((c->input = sshbuf_new()) == NULL ||
5264f52dfbbSDag-Erling Smørgrav 	    (c->output = sshbuf_new()) == NULL ||
5274f52dfbbSDag-Erling Smørgrav 	    (c->extended = sshbuf_new()) == NULL)
52819261079SEd Maste 		fatal_f("sshbuf_new failed");
52919261079SEd Maste 	if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
53019261079SEd Maste 		fatal_fr(r, "sshbuf_set_max_size");
531af12a3e7SDag-Erling Smørgrav 	c->ostate = CHAN_OUTPUT_OPEN;
532af12a3e7SDag-Erling Smørgrav 	c->istate = CHAN_INPUT_OPEN;
5334f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
534511b41d2SMark Murray 	c->self = found;
535511b41d2SMark Murray 	c->type = type;
536a04a10f8SKris Kennaway 	c->ctype = ctype;
537a04a10f8SKris Kennaway 	c->local_window = window;
538a04a10f8SKris Kennaway 	c->local_window_max = window;
539a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
540221552e4SDag-Erling Smørgrav 	c->remote_name = xstrdup(remote_name);
541b15c8340SDag-Erling Smørgrav 	c->ctl_chan = -1;
542b15c8340SDag-Erling Smørgrav 	c->delayed = 1;		/* prevent call to channel_post handler */
543f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->ctype);
544d4af9e69SDag-Erling Smørgrav 	TAILQ_INIT(&c->status_confirms);
545f374ba41SEd Maste 	debug("channel %d: new %s [%s] (inactive timeout: %u)",
546f374ba41SEd Maste 	    found, c->ctype, remote_name, c->inactive_deadline);
547af12a3e7SDag-Erling Smørgrav 	return c;
548a04a10f8SKris Kennaway }
549511b41d2SMark Murray 
550af12a3e7SDag-Erling Smørgrav int
55119261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp)
552af12a3e7SDag-Erling Smørgrav {
55319261079SEd Maste 	int ret, fd = *fdp;
554af12a3e7SDag-Erling Smørgrav 
55519261079SEd Maste 	if (fd == -1)
55619261079SEd Maste 		return 0;
55719261079SEd Maste 
558f374ba41SEd Maste 	/* restore blocking */
559f374ba41SEd Maste 	if (*fdp == c->rfd &&
560f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_RFD) != 0)
561f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[0]);
562f374ba41SEd Maste 	else if (*fdp == c->wfd &&
563f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_WFD) != 0)
564f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[1]);
565f374ba41SEd Maste 	else if (*fdp == c->efd &&
566f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_EFD) != 0)
567f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[2]);
56819261079SEd Maste 
5691323ec57SEd Maste 	if (*fdp == c->rfd) {
5701323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_RFD;
5711323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_RFD;
5721323ec57SEd Maste 		c->rfd = -1;
57387c1498dSEd Maste 		c->pfds[0] = -1;
5741323ec57SEd Maste 	}
5751323ec57SEd Maste 	if (*fdp == c->wfd) {
5761323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_WFD;
5771323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_WFD;
5781323ec57SEd Maste 		c->wfd = -1;
57987c1498dSEd Maste 		c->pfds[1] = -1;
5801323ec57SEd Maste 	}
5811323ec57SEd Maste 	if (*fdp == c->efd) {
5821323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_EFD;
5831323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_EFD;
5841323ec57SEd Maste 		c->efd = -1;
58587c1498dSEd Maste 		c->pfds[2] = -1;
5861323ec57SEd Maste 	}
5871323ec57SEd Maste 	if (*fdp == c->sock) {
5881323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_SOCK;
5891323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_SOCK;
5901323ec57SEd Maste 		c->sock = -1;
59187c1498dSEd Maste 		c->pfds[3] = -1;
5921323ec57SEd Maste 	}
5931323ec57SEd Maste 
594af12a3e7SDag-Erling Smørgrav 	ret = close(fd);
5951323ec57SEd Maste 	*fdp = -1; /* probably redundant */
596af12a3e7SDag-Erling Smørgrav 	return ret;
597af12a3e7SDag-Erling Smørgrav }
598a04a10f8SKris Kennaway 
599a04a10f8SKris Kennaway /* Close all channel fd/socket. */
600af12a3e7SDag-Erling Smørgrav static void
6014f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c)
602511b41d2SMark Murray {
60347dd1d1bSDag-Erling Smørgrav 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
60447dd1d1bSDag-Erling Smørgrav 
60519261079SEd Maste 	channel_close_fd(ssh, c, &c->sock);
60647dd1d1bSDag-Erling Smørgrav 	if (rfd != sock)
60719261079SEd Maste 		channel_close_fd(ssh, c, &c->rfd);
60847dd1d1bSDag-Erling Smørgrav 	if (wfd != sock && wfd != rfd)
60919261079SEd Maste 		channel_close_fd(ssh, c, &c->wfd);
61047dd1d1bSDag-Erling Smørgrav 	if (efd != sock && efd != rfd && efd != wfd)
61119261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
6124f52dfbbSDag-Erling Smørgrav }
6134f52dfbbSDag-Erling Smørgrav 
6144f52dfbbSDag-Erling Smørgrav static void
615190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm)
6164f52dfbbSDag-Erling Smørgrav {
617190cef3dSDag-Erling Smørgrav 	free(perm->host_to_connect);
618190cef3dSDag-Erling Smørgrav 	free(perm->listen_host);
619190cef3dSDag-Erling Smørgrav 	free(perm->listen_path);
62019261079SEd Maste 	memset(perm, 0, sizeof(*perm));
6214f52dfbbSDag-Erling Smørgrav }
6224f52dfbbSDag-Erling Smørgrav 
623190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */
624190cef3dSDag-Erling Smørgrav static const char *
625190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where)
626190cef3dSDag-Erling Smørgrav {
627190cef3dSDag-Erling Smørgrav 	if (who == FORWARD_ADM) {
628190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
629190cef3dSDag-Erling Smørgrav 			return "admin local";
630190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
631190cef3dSDag-Erling Smørgrav 			return "admin remote";
632190cef3dSDag-Erling Smørgrav 	} else if (who == FORWARD_USER) {
633190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
634190cef3dSDag-Erling Smørgrav 			return "user local";
635190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
636190cef3dSDag-Erling Smørgrav 			return "user remote";
637190cef3dSDag-Erling Smørgrav 	}
638190cef3dSDag-Erling Smørgrav 	fatal("Unknown forward permission list %d/%d", who, where);
639190cef3dSDag-Erling Smørgrav }
6404f52dfbbSDag-Erling Smørgrav 
641190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */
642190cef3dSDag-Erling Smørgrav static struct permission_set *
643190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where)
644190cef3dSDag-Erling Smørgrav {
645190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
646190cef3dSDag-Erling Smørgrav 
647190cef3dSDag-Erling Smørgrav 	switch (where) {
648190cef3dSDag-Erling Smørgrav 	case FORWARD_LOCAL:
649190cef3dSDag-Erling Smørgrav 		return &sc->local_perms;
650190cef3dSDag-Erling Smørgrav 		break;
651190cef3dSDag-Erling Smørgrav 	case FORWARD_REMOTE:
652190cef3dSDag-Erling Smørgrav 		return &sc->remote_perms;
653190cef3dSDag-Erling Smørgrav 		break;
654190cef3dSDag-Erling Smørgrav 	default:
65519261079SEd Maste 		fatal_f("invalid forwarding direction %d", where);
656190cef3dSDag-Erling Smørgrav 	}
657190cef3dSDag-Erling Smørgrav }
658190cef3dSDag-Erling Smørgrav 
65919261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */
660190cef3dSDag-Erling Smørgrav static void
661190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where,
662190cef3dSDag-Erling Smørgrav     struct permission ***permpp, u_int **npermpp)
663190cef3dSDag-Erling Smørgrav {
664190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
665190cef3dSDag-Erling Smørgrav 
666190cef3dSDag-Erling Smørgrav 	switch (who) {
667190cef3dSDag-Erling Smørgrav 	case FORWARD_USER:
668190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_user;
669190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_user;
670190cef3dSDag-Erling Smørgrav 		break;
671190cef3dSDag-Erling Smørgrav 	case FORWARD_ADM:
672190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_admin;
673190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_admin;
674190cef3dSDag-Erling Smørgrav 		break;
675190cef3dSDag-Erling Smørgrav 	default:
67619261079SEd Maste 		fatal_f("invalid forwarding client %d", who);
677190cef3dSDag-Erling Smørgrav 	}
678190cef3dSDag-Erling Smørgrav }
679190cef3dSDag-Erling Smørgrav 
6801323ec57SEd Maste /* Adds an entry to the specified forwarding list */
6814f52dfbbSDag-Erling Smørgrav static int
682190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where,
6834f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, int port_to_connect,
6844f52dfbbSDag-Erling Smørgrav     const char *listen_host, const char *listen_path, int listen_port,
6854f52dfbbSDag-Erling Smørgrav     Channel *downstream)
6864f52dfbbSDag-Erling Smørgrav {
687190cef3dSDag-Erling Smørgrav 	struct permission **permp;
688190cef3dSDag-Erling Smørgrav 	u_int n, *npermp;
6894f52dfbbSDag-Erling Smørgrav 
690190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
6914f52dfbbSDag-Erling Smørgrav 
692190cef3dSDag-Erling Smørgrav 	if (*npermp >= INT_MAX)
69319261079SEd Maste 		fatal_f("%s overflow", fwd_ident(who, where));
6944f52dfbbSDag-Erling Smørgrav 
695190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
696190cef3dSDag-Erling Smørgrav 	n = (*npermp)++;
6974f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
698190cef3dSDag-Erling Smørgrav 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
699190cef3dSDag-Erling Smørgrav 	(*permp)[n].port_to_connect = port_to_connect;
700190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
701190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
702190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_port = listen_port;
703190cef3dSDag-Erling Smørgrav 	(*permp)[n].downstream = downstream;
7044f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP
7054f52dfbbSDag-Erling Smørgrav 	return (int)n;
7064f52dfbbSDag-Erling Smørgrav }
7074f52dfbbSDag-Erling Smørgrav 
7084f52dfbbSDag-Erling Smørgrav static void
7094f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
7104f52dfbbSDag-Erling Smørgrav {
7114f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
712190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
713190cef3dSDag-Erling Smørgrav 	struct permission *perm;
7144f52dfbbSDag-Erling Smørgrav 	int r;
7154f52dfbbSDag-Erling Smørgrav 	u_int i;
7164f52dfbbSDag-Erling Smørgrav 
717190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
718190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
719190cef3dSDag-Erling Smørgrav 		if (perm->downstream != c)
7204f52dfbbSDag-Erling Smørgrav 			continue;
7214f52dfbbSDag-Erling Smørgrav 
7224f52dfbbSDag-Erling Smørgrav 		/* cancel on the server, since mux client is gone */
7234f52dfbbSDag-Erling Smørgrav 		debug("channel %d: cleanup remote forward for %s:%u",
724190cef3dSDag-Erling Smørgrav 		    c->self, perm->listen_host, perm->listen_port);
7254f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
7264f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
7274f52dfbbSDag-Erling Smørgrav 		    "cancel-tcpip-forward")) != 0 ||
7284f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
7294f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
730190cef3dSDag-Erling Smørgrav 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
731190cef3dSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
7324f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
73319261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
7344f52dfbbSDag-Erling Smørgrav 		}
735190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(perm); /* unregister */
7364f52dfbbSDag-Erling Smørgrav 	}
737a04a10f8SKris Kennaway }
738511b41d2SMark Murray 
739a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
740a04a10f8SKris Kennaway void
7414f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c)
742a04a10f8SKris Kennaway {
7434f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
744af12a3e7SDag-Erling Smørgrav 	char *s;
74521e764dfSDag-Erling Smørgrav 	u_int i, n;
746ca86bcf2SDag-Erling Smørgrav 	Channel *other;
747d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
748ca3176e7SBrian Feldman 
7494f52dfbbSDag-Erling Smørgrav 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
7504f52dfbbSDag-Erling Smørgrav 		if ((other = sc->channels[i]) == NULL)
7514f52dfbbSDag-Erling Smørgrav 			continue;
752af12a3e7SDag-Erling Smørgrav 		n++;
753ca86bcf2SDag-Erling Smørgrav 		/* detach from mux client and prepare for closing */
754ca86bcf2SDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
755ca86bcf2SDag-Erling Smørgrav 		    other->type == SSH_CHANNEL_MUX_PROXY &&
756ca86bcf2SDag-Erling Smørgrav 		    other->mux_ctx == c) {
757ca86bcf2SDag-Erling Smørgrav 			other->mux_ctx = NULL;
758ca86bcf2SDag-Erling Smørgrav 			other->type = SSH_CHANNEL_OPEN;
759ca86bcf2SDag-Erling Smørgrav 			other->istate = CHAN_INPUT_CLOSED;
760ca86bcf2SDag-Erling Smørgrav 			other->ostate = CHAN_OUTPUT_CLOSED;
761ca86bcf2SDag-Erling Smørgrav 		}
762ca86bcf2SDag-Erling Smørgrav 	}
76321e764dfSDag-Erling Smørgrav 	debug("channel %d: free: %s, nchannels %u", c->self,
764af12a3e7SDag-Erling Smørgrav 	    c->remote_name ? c->remote_name : "???", n);
765af12a3e7SDag-Erling Smørgrav 
766e9e8876aSEd Maste 	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
7674f52dfbbSDag-Erling Smørgrav 		mux_remove_remote_forwardings(ssh, c);
768e9e8876aSEd Maste 		free(c->mux_ctx);
769e9e8876aSEd Maste 		c->mux_ctx = NULL;
770e9e8876aSEd Maste 	} else if (c->type == SSH_CHANNEL_MUX_LISTENER) {
77119261079SEd Maste 		free(c->mux_ctx);
77219261079SEd Maste 		c->mux_ctx = NULL;
77319261079SEd Maste 	}
774ca86bcf2SDag-Erling Smørgrav 
775190cef3dSDag-Erling Smørgrav 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
7764f52dfbbSDag-Erling Smørgrav 		s = channel_open_message(ssh);
777221552e4SDag-Erling Smørgrav 		debug3("channel %d: status: %s", c->self, s);
778e4a9863fSDag-Erling Smørgrav 		free(s);
779190cef3dSDag-Erling Smørgrav 	}
780ca3176e7SBrian Feldman 
7814f52dfbbSDag-Erling Smørgrav 	channel_close_fds(ssh, c);
7824f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->input);
7834f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->output);
7844f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->extended);
7854f52dfbbSDag-Erling Smørgrav 	c->input = c->output = c->extended = NULL;
786e4a9863fSDag-Erling Smørgrav 	free(c->remote_name);
787a04a10f8SKris Kennaway 	c->remote_name = NULL;
788e4a9863fSDag-Erling Smørgrav 	free(c->path);
789cce7d346SDag-Erling Smørgrav 	c->path = NULL;
790e4a9863fSDag-Erling Smørgrav 	free(c->listening_addr);
791462c32cbSDag-Erling Smørgrav 	c->listening_addr = NULL;
792f374ba41SEd Maste 	free(c->xctype);
793f374ba41SEd Maste 	c->xctype = NULL;
794d4af9e69SDag-Erling Smørgrav 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
795d4af9e69SDag-Erling Smørgrav 		if (cc->abandon_cb != NULL)
7964f52dfbbSDag-Erling Smørgrav 			cc->abandon_cb(ssh, c, cc->ctx);
797d4af9e69SDag-Erling Smørgrav 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
79819261079SEd Maste 		freezero(cc, sizeof(*cc));
799d4af9e69SDag-Erling Smørgrav 	}
800d4af9e69SDag-Erling Smørgrav 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
8014f52dfbbSDag-Erling Smørgrav 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
8024f52dfbbSDag-Erling Smørgrav 	sc->channels[c->self] = NULL;
80319261079SEd Maste 	freezero(c, sizeof(*c));
804af12a3e7SDag-Erling Smørgrav }
805af12a3e7SDag-Erling Smørgrav 
806af12a3e7SDag-Erling Smørgrav void
8074f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh)
808af12a3e7SDag-Erling Smørgrav {
80921e764dfSDag-Erling Smørgrav 	u_int i;
81019261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
811af12a3e7SDag-Erling Smørgrav 
81219261079SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
81319261079SEd Maste 		if (sc->channels[i] != NULL)
81419261079SEd Maste 			channel_free(ssh, sc->channels[i]);
81519261079SEd Maste 
81619261079SEd Maste 	free(sc->channels);
81719261079SEd Maste 	sc->channels = NULL;
81819261079SEd Maste 	sc->channels_alloc = 0;
81919261079SEd Maste 
82019261079SEd Maste 	free(sc->x11_saved_display);
82119261079SEd Maste 	sc->x11_saved_display = NULL;
82219261079SEd Maste 
82319261079SEd Maste 	free(sc->x11_saved_proto);
82419261079SEd Maste 	sc->x11_saved_proto = NULL;
82519261079SEd Maste 
82619261079SEd Maste 	free(sc->x11_saved_data);
82719261079SEd Maste 	sc->x11_saved_data = NULL;
82819261079SEd Maste 	sc->x11_saved_data_len = 0;
82919261079SEd Maste 
83019261079SEd Maste 	free(sc->x11_fake_data);
83119261079SEd Maste 	sc->x11_fake_data = NULL;
83219261079SEd Maste 	sc->x11_fake_data_len = 0;
833af12a3e7SDag-Erling Smørgrav }
834af12a3e7SDag-Erling Smørgrav 
835af12a3e7SDag-Erling Smørgrav /*
836af12a3e7SDag-Erling Smørgrav  * Closes the sockets/fds of all channels.  This is used to close extra file
837af12a3e7SDag-Erling Smørgrav  * descriptors after a fork.
838af12a3e7SDag-Erling Smørgrav  */
839af12a3e7SDag-Erling Smørgrav void
8404f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh)
841af12a3e7SDag-Erling Smørgrav {
84221e764dfSDag-Erling Smørgrav 	u_int i;
843af12a3e7SDag-Erling Smørgrav 
8444f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
8454f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
8464f52dfbbSDag-Erling Smørgrav 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
847af12a3e7SDag-Erling Smørgrav }
848af12a3e7SDag-Erling Smørgrav 
849af12a3e7SDag-Erling Smørgrav /*
850af12a3e7SDag-Erling Smørgrav  * Stop listening to channels.
851af12a3e7SDag-Erling Smørgrav  */
852af12a3e7SDag-Erling Smørgrav void
8534f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh)
854af12a3e7SDag-Erling Smørgrav {
85521e764dfSDag-Erling Smørgrav 	u_int i;
856af12a3e7SDag-Erling Smørgrav 	Channel *c;
857af12a3e7SDag-Erling Smørgrav 
8584f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8594f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
860af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
861af12a3e7SDag-Erling Smørgrav 			switch (c->type) {
862af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_AUTH_SOCKET:
863af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_PORT_LISTENER:
864af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_RPORT_LISTENER:
865af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_X11_LISTENER:
866a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_UNIX_LISTENER:
867a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_RUNIX_LISTENER:
86819261079SEd Maste 				channel_close_fd(ssh, c, &c->sock);
8694f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
870af12a3e7SDag-Erling Smørgrav 				break;
871af12a3e7SDag-Erling Smørgrav 			}
872af12a3e7SDag-Erling Smørgrav 		}
873af12a3e7SDag-Erling Smørgrav 	}
874af12a3e7SDag-Erling Smørgrav }
875af12a3e7SDag-Erling Smørgrav 
876af12a3e7SDag-Erling Smørgrav /*
877af12a3e7SDag-Erling Smørgrav  * Returns true if no channel has too much buffered data, and false if one or
878af12a3e7SDag-Erling Smørgrav  * more channel is overfull.
879af12a3e7SDag-Erling Smørgrav  */
880af12a3e7SDag-Erling Smørgrav int
8814f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh)
882af12a3e7SDag-Erling Smørgrav {
883af12a3e7SDag-Erling Smørgrav 	u_int i;
8844f52dfbbSDag-Erling Smørgrav 	u_int maxsize = ssh_packet_get_maxsize(ssh);
885af12a3e7SDag-Erling Smørgrav 	Channel *c;
886af12a3e7SDag-Erling Smørgrav 
8874f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8884f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
8894f52dfbbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
8904f52dfbbSDag-Erling Smørgrav 			continue;
8914f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > maxsize) {
8924f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: big output buffer %zu > %u",
8934f52dfbbSDag-Erling Smørgrav 			    c->self, sshbuf_len(c->output), maxsize);
894af12a3e7SDag-Erling Smørgrav 			return 0;
895af12a3e7SDag-Erling Smørgrav 		}
896af12a3e7SDag-Erling Smørgrav 	}
897af12a3e7SDag-Erling Smørgrav 	return 1;
898af12a3e7SDag-Erling Smørgrav }
899af12a3e7SDag-Erling Smørgrav 
900af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */
901af12a3e7SDag-Erling Smørgrav int
9024f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh)
903af12a3e7SDag-Erling Smørgrav {
90421e764dfSDag-Erling Smørgrav 	u_int i;
905af12a3e7SDag-Erling Smørgrav 	Channel *c;
906af12a3e7SDag-Erling Smørgrav 
9074f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9084f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
909af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
910af12a3e7SDag-Erling Smørgrav 			continue;
911af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
912af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
913af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
914af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
915b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
916af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
917af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
918af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9194f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
920af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
921af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
922e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
923a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
924a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
925af12a3e7SDag-Erling Smørgrav 			continue;
926af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
927af12a3e7SDag-Erling Smørgrav 			continue;
928af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
929af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
9304f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
931af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
932b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
933ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
934af12a3e7SDag-Erling Smørgrav 			return 1;
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 0;
941af12a3e7SDag-Erling Smørgrav }
942af12a3e7SDag-Erling Smørgrav 
943069ac184SEd Maste /* Returns true if a channel with a TTY is open. */
944069ac184SEd Maste int
945069ac184SEd Maste channel_tty_open(struct ssh *ssh)
946069ac184SEd Maste {
947069ac184SEd Maste 	u_int i;
948069ac184SEd Maste 	Channel *c;
949069ac184SEd Maste 
950069ac184SEd Maste 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
951069ac184SEd Maste 		c = ssh->chanctxt->channels[i];
952069ac184SEd Maste 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
953069ac184SEd Maste 			continue;
954069ac184SEd Maste 		if (c->client_tty)
955069ac184SEd Maste 			return 1;
956069ac184SEd Maste 	}
957069ac184SEd Maste 	return 0;
958069ac184SEd Maste }
959069ac184SEd Maste 
960af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */
961af12a3e7SDag-Erling Smørgrav int
9624f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh)
963af12a3e7SDag-Erling Smørgrav {
96421e764dfSDag-Erling Smørgrav 	u_int i;
965af12a3e7SDag-Erling Smørgrav 	Channel *c;
966af12a3e7SDag-Erling Smørgrav 
9674f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9684f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
9694f52dfbbSDag-Erling Smørgrav 		if (c == NULL || !c->have_remote_id)
970af12a3e7SDag-Erling Smørgrav 			continue;
971af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
972af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
973af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9744f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
9754f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
976af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
977af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
978af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
979b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
980b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
981ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
982af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
983af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
984af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
985e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
986a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
987a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
988af12a3e7SDag-Erling Smørgrav 			continue;
989af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
990af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
991af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
992af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
993af12a3e7SDag-Erling Smørgrav 			return i;
994af12a3e7SDag-Erling Smørgrav 		default:
99519261079SEd Maste 			fatal_f("bad channel type %d", c->type);
996af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
997af12a3e7SDag-Erling Smørgrav 		}
998af12a3e7SDag-Erling Smørgrav 	}
999af12a3e7SDag-Erling Smørgrav 	return -1;
1000af12a3e7SDag-Erling Smørgrav }
1001af12a3e7SDag-Erling Smørgrav 
10022f513db7SEd Maste /* Returns the state of the channel's extended usage flag */
10032f513db7SEd Maste const char *
10042f513db7SEd Maste channel_format_extended_usage(const Channel *c)
10052f513db7SEd Maste {
10062f513db7SEd Maste 	if (c->efd == -1)
10072f513db7SEd Maste 		return "closed";
10082f513db7SEd Maste 
10092f513db7SEd Maste 	switch (c->extended_usage) {
10102f513db7SEd Maste 	case CHAN_EXTENDED_WRITE:
10112f513db7SEd Maste 		return "write";
10122f513db7SEd Maste 	case CHAN_EXTENDED_READ:
10132f513db7SEd Maste 		return "read";
10142f513db7SEd Maste 	case CHAN_EXTENDED_IGNORE:
10152f513db7SEd Maste 		return "ignore";
10162f513db7SEd Maste 	default:
10172f513db7SEd Maste 		return "UNKNOWN";
10182f513db7SEd Maste 	}
10192f513db7SEd Maste }
10202f513db7SEd Maste 
10212f513db7SEd Maste static char *
10222f513db7SEd Maste channel_format_status(const Channel *c)
10232f513db7SEd Maste {
10242f513db7SEd Maste 	char *ret = NULL;
10252f513db7SEd Maste 
1026f374ba41SEd Maste 	xasprintf(&ret, "t%d [%s] %s%u i%u/%zu o%u/%zu e[%s]/%zu "
10271323ec57SEd Maste 	    "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x",
1028f374ba41SEd Maste 	    c->type, c->xctype != NULL ? c->xctype : c->ctype,
10292f513db7SEd Maste 	    c->have_remote_id ? "r" : "nr", c->remote_id,
10302f513db7SEd Maste 	    c->istate, sshbuf_len(c->input),
10312f513db7SEd Maste 	    c->ostate, sshbuf_len(c->output),
10322f513db7SEd Maste 	    channel_format_extended_usage(c), sshbuf_len(c->extended),
10331323ec57SEd Maste 	    c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan,
10341323ec57SEd Maste 	    c->io_want, c->io_ready);
10352f513db7SEd Maste 	return ret;
10362f513db7SEd Maste }
10372f513db7SEd Maste 
1038af12a3e7SDag-Erling Smørgrav /*
1039af12a3e7SDag-Erling Smørgrav  * Returns a message describing the currently open forwarded connections,
1040af12a3e7SDag-Erling Smørgrav  * suitable for sending to the client.  The message contains crlf pairs for
1041af12a3e7SDag-Erling Smørgrav  * newlines.
1042af12a3e7SDag-Erling Smørgrav  */
1043af12a3e7SDag-Erling Smørgrav char *
10444f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh)
1045af12a3e7SDag-Erling Smørgrav {
10464f52dfbbSDag-Erling Smørgrav 	struct sshbuf *buf;
1047af12a3e7SDag-Erling Smørgrav 	Channel *c;
104821e764dfSDag-Erling Smørgrav 	u_int i;
10494f52dfbbSDag-Erling Smørgrav 	int r;
10502f513db7SEd Maste 	char *cp, *ret;
1051af12a3e7SDag-Erling Smørgrav 
10524f52dfbbSDag-Erling Smørgrav 	if ((buf = sshbuf_new()) == NULL)
105319261079SEd Maste 		fatal_f("sshbuf_new");
10544f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_putf(buf,
10554f52dfbbSDag-Erling Smørgrav 	    "The following connections are open:\r\n")) != 0)
105619261079SEd Maste 		fatal_fr(r, "sshbuf_putf");
10574f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
10584f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
1059af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
1060af12a3e7SDag-Erling Smørgrav 			continue;
1061af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
1062af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
1063af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
1064af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
1065af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
1066af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
1067af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
1068e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
1069b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
1070a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
1071a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
1072af12a3e7SDag-Erling Smørgrav 			continue;
1073af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
1074af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
1075af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
1076af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
10774f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
10784f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
1079af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
1080af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
1081ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
1082ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
10832f513db7SEd Maste 			cp = channel_format_status(c);
10842f513db7SEd Maste 			if ((r = sshbuf_putf(buf, "  #%d %.300s (%s)\r\n",
10852f513db7SEd Maste 			    c->self, c->remote_name, cp)) != 0) {
10862f513db7SEd Maste 				free(cp);
108719261079SEd Maste 				fatal_fr(r, "sshbuf_putf");
10882f513db7SEd Maste 			}
10892f513db7SEd Maste 			free(cp);
1090af12a3e7SDag-Erling Smørgrav 			continue;
1091af12a3e7SDag-Erling Smørgrav 		default:
109219261079SEd Maste 			fatal_f("bad channel type %d", c->type);
1093af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
1094af12a3e7SDag-Erling Smørgrav 		}
1095af12a3e7SDag-Erling Smørgrav 	}
10964f52dfbbSDag-Erling Smørgrav 	if ((ret = sshbuf_dup_string(buf)) == NULL)
109719261079SEd Maste 		fatal_f("sshbuf_dup_string");
10984f52dfbbSDag-Erling Smørgrav 	sshbuf_free(buf);
10994f52dfbbSDag-Erling Smørgrav 	return ret;
11004f52dfbbSDag-Erling Smørgrav }
11014f52dfbbSDag-Erling Smørgrav 
11024f52dfbbSDag-Erling Smørgrav static void
11034f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
11044f52dfbbSDag-Erling Smørgrav {
11054f52dfbbSDag-Erling Smørgrav 	int r;
11064f52dfbbSDag-Erling Smørgrav 
11074f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
11084f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
11094f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
11104f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
11114f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
111219261079SEd Maste 		fatal_r(r, "%s: channel %i: open", where, c->self);
11134f52dfbbSDag-Erling Smørgrav 	}
1114af12a3e7SDag-Erling Smørgrav }
1115af12a3e7SDag-Erling Smørgrav 
1116af12a3e7SDag-Erling Smørgrav void
11174f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id)
1118af12a3e7SDag-Erling Smørgrav {
11194f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
11204f52dfbbSDag-Erling Smørgrav 	int r;
1121f388f5efSDag-Erling Smørgrav 
1122af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
1123221552e4SDag-Erling Smørgrav 		logit("channel_send_open: %d: bad id", id);
1124af12a3e7SDag-Erling Smørgrav 		return;
1125af12a3e7SDag-Erling Smørgrav 	}
1126e73e9afaSDag-Erling Smørgrav 	debug2("channel %d: send open", id);
11274f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, c->ctype);
11284f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
112919261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1130af12a3e7SDag-Erling Smørgrav }
1131af12a3e7SDag-Erling Smørgrav 
1132af12a3e7SDag-Erling Smørgrav void
11334f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
1134af12a3e7SDag-Erling Smørgrav {
11354f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
11364f52dfbbSDag-Erling Smørgrav 	int r;
1137f388f5efSDag-Erling Smørgrav 
1138af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
113919261079SEd Maste 		logit_f("%d: unknown channel id", id);
1140af12a3e7SDag-Erling Smørgrav 		return;
1141af12a3e7SDag-Erling Smørgrav 	}
11424f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
114319261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
11444f52dfbbSDag-Erling Smørgrav 
114521e764dfSDag-Erling Smørgrav 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
11464f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
11474f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
11484f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
11494f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
115019261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
11514f52dfbbSDag-Erling Smørgrav 	}
1152af12a3e7SDag-Erling Smørgrav }
1153333ee039SDag-Erling Smørgrav 
1154af12a3e7SDag-Erling Smørgrav void
11554f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id,
11564f52dfbbSDag-Erling Smørgrav     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
1157d4af9e69SDag-Erling Smørgrav {
1158d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
1159d4af9e69SDag-Erling Smørgrav 	Channel *c;
1160d4af9e69SDag-Erling Smørgrav 
11614f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL)
116219261079SEd Maste 		fatal_f("%d: bad id", id);
1163d4af9e69SDag-Erling Smørgrav 
11640a37d4a3SXin LI 	cc = xcalloc(1, sizeof(*cc));
1165d4af9e69SDag-Erling Smørgrav 	cc->cb = cb;
1166d4af9e69SDag-Erling Smørgrav 	cc->abandon_cb = abandon_cb;
1167d4af9e69SDag-Erling Smørgrav 	cc->ctx = ctx;
1168d4af9e69SDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
1169d4af9e69SDag-Erling Smørgrav }
1170d4af9e69SDag-Erling Smørgrav 
1171d4af9e69SDag-Erling Smørgrav void
11724f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id,
11734f52dfbbSDag-Erling Smørgrav     channel_open_fn *fn, void *ctx)
1174af12a3e7SDag-Erling Smørgrav {
11754f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1176f388f5efSDag-Erling Smørgrav 
1177af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
117819261079SEd Maste 		logit_f("%d: bad id", id);
1179af12a3e7SDag-Erling Smørgrav 		return;
1180af12a3e7SDag-Erling Smørgrav 	}
1181d4af9e69SDag-Erling Smørgrav 	c->open_confirm = fn;
1182d4af9e69SDag-Erling Smørgrav 	c->open_confirm_ctx = ctx;
1183af12a3e7SDag-Erling Smørgrav }
1184333ee039SDag-Erling Smørgrav 
1185af12a3e7SDag-Erling Smørgrav void
11864f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id,
11874f52dfbbSDag-Erling Smørgrav     channel_callback_fn *fn, int do_close)
1188af12a3e7SDag-Erling Smørgrav {
11894f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1190f388f5efSDag-Erling Smørgrav 
1191af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
119219261079SEd Maste 		logit_f("%d: bad id", id);
1193af12a3e7SDag-Erling Smørgrav 		return;
1194af12a3e7SDag-Erling Smørgrav 	}
1195af12a3e7SDag-Erling Smørgrav 	c->detach_user = fn;
1196b74df5b2SDag-Erling Smørgrav 	c->detach_close = do_close;
1197af12a3e7SDag-Erling Smørgrav }
1198333ee039SDag-Erling Smørgrav 
1199af12a3e7SDag-Erling Smørgrav void
12004f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id)
1201af12a3e7SDag-Erling Smørgrav {
12024f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1203f388f5efSDag-Erling Smørgrav 
1204af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
120519261079SEd Maste 		logit_f("%d: bad id", id);
1206af12a3e7SDag-Erling Smørgrav 		return;
1207af12a3e7SDag-Erling Smørgrav 	}
1208af12a3e7SDag-Erling Smørgrav 	c->detach_user = NULL;
1209b74df5b2SDag-Erling Smørgrav 	c->detach_close = 0;
1210af12a3e7SDag-Erling Smørgrav }
1211333ee039SDag-Erling Smørgrav 
1212af12a3e7SDag-Erling Smørgrav void
12134f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
1214d4af9e69SDag-Erling Smørgrav     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
1215af12a3e7SDag-Erling Smørgrav {
12164f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1217f388f5efSDag-Erling Smørgrav 
1218af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
121919261079SEd Maste 		logit_f("%d: bad id", id);
1220af12a3e7SDag-Erling Smørgrav 		return;
1221af12a3e7SDag-Erling Smørgrav 	}
1222b74df5b2SDag-Erling Smørgrav 	c->input_filter = ifn;
1223b74df5b2SDag-Erling Smørgrav 	c->output_filter = ofn;
1224d4af9e69SDag-Erling Smørgrav 	c->filter_ctx = ctx;
1225d4af9e69SDag-Erling Smørgrav 	c->filter_cleanup = cfn;
1226af12a3e7SDag-Erling Smørgrav }
1227af12a3e7SDag-Erling Smørgrav 
1228af12a3e7SDag-Erling Smørgrav void
12294f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
1230d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty, u_int window_max)
1231af12a3e7SDag-Erling Smørgrav {
12324f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
12334f52dfbbSDag-Erling Smørgrav 	int r;
1234f388f5efSDag-Erling Smørgrav 
1235af12a3e7SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1236af12a3e7SDag-Erling Smørgrav 		fatal("channel_activate for non-larval channel %d.", id);
12374f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
123819261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
12394f52dfbbSDag-Erling Smørgrav 
12404f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1241af12a3e7SDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1242*a91a2465SEd Maste 	channel_set_used_time(ssh, c);
1243af12a3e7SDag-Erling Smørgrav 	c->local_window = c->local_window_max = window_max;
12444f52dfbbSDag-Erling Smørgrav 
12454f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
12464f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
12474f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
12484f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
124919261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1250511b41d2SMark Murray }
1251511b41d2SMark Murray 
1252af12a3e7SDag-Erling Smørgrav static void
12531323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c)
1254511b41d2SMark Murray {
12551323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_R;
1256a04a10f8SKris Kennaway }
1257a04a10f8SKris Kennaway 
1258af12a3e7SDag-Erling Smørgrav static void
12591323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c)
1260ca3176e7SBrian Feldman {
1261ca3176e7SBrian Feldman 	debug3("channel %d: waiting for connection", c->self);
12621323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_W;
1263ca3176e7SBrian Feldman }
1264ca3176e7SBrian Feldman 
1265af12a3e7SDag-Erling Smørgrav static void
12661323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c)
1267a04a10f8SKris Kennaway {
12681323ec57SEd Maste 	c->io_want = 0;
1269a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
12704f52dfbbSDag-Erling Smørgrav 	    c->remote_window > 0 &&
12714f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->input) < c->remote_window &&
12724f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
12731323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1274a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1275a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
12764f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0) {
12771323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1278a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
127980628bacSDag-Erling Smørgrav 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
12804f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
12814f52dfbbSDag-Erling Smørgrav 				    "obuf_empty delayed efd %d/(%zu)", c->self,
12824f52dfbbSDag-Erling Smørgrav 				    c->efd, sshbuf_len(c->extended));
128380628bacSDag-Erling Smørgrav 			else
12844f52dfbbSDag-Erling Smørgrav 				chan_obuf_empty(ssh, c);
1285a04a10f8SKris Kennaway 		}
1286a04a10f8SKris Kennaway 	}
1287a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
12884f52dfbbSDag-Erling Smørgrav 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
12894f52dfbbSDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1290a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
12914f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) > 0)
12921323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_W;
1293e2f6069cSDag-Erling Smørgrav 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
1294e2f6069cSDag-Erling Smørgrav 		    (c->extended_usage == CHAN_EXTENDED_READ ||
1295e2f6069cSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
12964f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) < c->remote_window)
12971323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_R;
1298a04a10f8SKris Kennaway 	}
129921e764dfSDag-Erling Smørgrav 	/* XXX: What about efd? races? */
1300a04a10f8SKris Kennaway }
1301a04a10f8SKris Kennaway 
1302a04a10f8SKris Kennaway /*
1303a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
1304a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
1305a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
1306a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
1307a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
1308a04a10f8SKris Kennaway  * XXX All this happens at the client side.
1309af12a3e7SDag-Erling Smørgrav  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1310a04a10f8SKris Kennaway  */
1311af12a3e7SDag-Erling Smørgrav static int
13124f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1313a04a10f8SKris Kennaway {
13144f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
1315ca3176e7SBrian Feldman 	u_char *ucp;
1316ca3176e7SBrian Feldman 	u_int proto_len, data_len;
1317511b41d2SMark Murray 
1318557f75e5SDag-Erling Smørgrav 	/* Is this being called after the refusal deadline? */
13194f52dfbbSDag-Erling Smørgrav 	if (sc->x11_refuse_time != 0 &&
13204d3fc8b0SEd Maste 	    monotime() >= sc->x11_refuse_time) {
1321557f75e5SDag-Erling Smørgrav 		verbose("Rejected X11 connection after ForwardX11Timeout "
1322557f75e5SDag-Erling Smørgrav 		    "expired");
1323557f75e5SDag-Erling Smørgrav 		return -1;
1324557f75e5SDag-Erling Smørgrav 	}
1325557f75e5SDag-Erling Smørgrav 
1326511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
13274f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) < 12)
1328a04a10f8SKris Kennaway 		return 0;
1329511b41d2SMark Murray 
1330511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
13314f52dfbbSDag-Erling Smørgrav 	ucp = sshbuf_mutable_ptr(b);
1332511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1333511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
1334511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
1335511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1336511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
1337511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
1338511b41d2SMark Murray 	} else {
1339221552e4SDag-Erling Smørgrav 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1340511b41d2SMark Murray 		    ucp[0]);
1341a04a10f8SKris Kennaway 		return -1;
1342511b41d2SMark Murray 	}
1343511b41d2SMark Murray 
1344511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
13454f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) <
1346511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1347a04a10f8SKris Kennaway 		return 0;
1348511b41d2SMark Murray 
1349511b41d2SMark Murray 	/* Check if authentication protocol matches. */
13504f52dfbbSDag-Erling Smørgrav 	if (proto_len != strlen(sc->x11_saved_proto) ||
13514f52dfbbSDag-Erling Smørgrav 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1352221552e4SDag-Erling Smørgrav 		debug2("X11 connection uses different authentication protocol.");
1353a04a10f8SKris Kennaway 		return -1;
1354511b41d2SMark Murray 	}
1355511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
13564f52dfbbSDag-Erling Smørgrav 	if (data_len != sc->x11_fake_data_len ||
1357e2f6069cSDag-Erling Smørgrav 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
13584f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1359221552e4SDag-Erling Smørgrav 		debug2("X11 auth data does not match fake data.");
1360a04a10f8SKris Kennaway 		return -1;
1361511b41d2SMark Murray 	}
1362511b41d2SMark Murray 	/* Check fake data length */
13634f52dfbbSDag-Erling Smørgrav 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1364511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
13654f52dfbbSDag-Erling Smørgrav 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1366a04a10f8SKris Kennaway 		return -1;
1367511b41d2SMark Murray 	}
1368511b41d2SMark Murray 	/*
1369511b41d2SMark Murray 	 * Received authentication protocol and data match
1370511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
1371511b41d2SMark Murray 	 * data.
1372511b41d2SMark Murray 	 */
1373511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
13744f52dfbbSDag-Erling Smørgrav 	    sc->x11_saved_data, sc->x11_saved_data_len);
1375a04a10f8SKris Kennaway 	return 1;
1376a04a10f8SKris Kennaway }
1377511b41d2SMark Murray 
1378f374ba41SEd Maste void
1379f374ba41SEd Maste channel_force_close(struct ssh *ssh, Channel *c, int abandon)
1380f374ba41SEd Maste {
1381f374ba41SEd Maste 	debug3_f("channel %d: forcibly closing", c->self);
1382f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_OPEN)
1383f374ba41SEd Maste 		chan_read_failed(ssh, c);
1384f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1385f374ba41SEd Maste 		sshbuf_reset(c->input);
1386f374ba41SEd Maste 		chan_ibuf_empty(ssh, c);
1387f374ba41SEd Maste 	}
1388f374ba41SEd Maste 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1389f374ba41SEd Maste 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1390f374ba41SEd Maste 		sshbuf_reset(c->output);
1391f374ba41SEd Maste 		chan_write_failed(ssh, c);
1392f374ba41SEd Maste 	}
1393f374ba41SEd Maste 	if (c->detach_user)
1394f374ba41SEd Maste 		c->detach_user(ssh, c->self, 1, NULL);
1395f374ba41SEd Maste 	if (c->efd != -1)
1396f374ba41SEd Maste 		channel_close_fd(ssh, c, &c->efd);
1397f374ba41SEd Maste 	if (abandon)
1398f374ba41SEd Maste 		c->type = SSH_CHANNEL_ABANDONED;
1399f374ba41SEd Maste 	/* exempt from inactivity timeouts */
1400f374ba41SEd Maste 	c->inactive_deadline = 0;
1401f374ba41SEd Maste 	c->lastused = 0;
1402f374ba41SEd Maste }
1403f374ba41SEd Maste 
1404af12a3e7SDag-Erling Smørgrav static void
14051323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c)
1406a04a10f8SKris Kennaway {
14074f52dfbbSDag-Erling Smørgrav 	int ret = x11_open_helper(ssh, c->output);
1408af12a3e7SDag-Erling Smørgrav 
1409af12a3e7SDag-Erling Smørgrav 	/* c->force_drain = 1; */
1410af12a3e7SDag-Erling Smørgrav 
1411a04a10f8SKris Kennaway 	if (ret == 1) {
1412a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
1413*a91a2465SEd Maste 		channel_set_used_time(ssh, c);
14141323ec57SEd Maste 		channel_pre_open(ssh, c);
1415a04a10f8SKris Kennaway 	} else if (ret == -1) {
1416f374ba41SEd Maste 		logit("X11 connection rejected because of wrong "
1417f374ba41SEd Maste 		    "authentication.");
14184f52dfbbSDag-Erling Smørgrav 		debug2("X11 rejected %d i%d/o%d",
14194f52dfbbSDag-Erling Smørgrav 		    c->self, c->istate, c->ostate);
1420f374ba41SEd Maste 		channel_force_close(ssh, c, 0);
1421a04a10f8SKris Kennaway 	}
1422a04a10f8SKris Kennaway }
1423a04a10f8SKris Kennaway 
1424b15c8340SDag-Erling Smørgrav static void
14251323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c)
1426b15c8340SDag-Erling Smørgrav {
14271323ec57SEd Maste 	c->io_want = 0;
1428e2f6069cSDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
14294f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
14301323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1431b15c8340SDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1432b15c8340SDag-Erling Smørgrav 		/* clear buffer immediately (discard any partial packet) */
14334f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
14344f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
1435b15c8340SDag-Erling Smørgrav 		/* Start output drain. XXX just kill chan? */
14364f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
1437b15c8340SDag-Erling Smørgrav 	}
1438b15c8340SDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1439b15c8340SDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
14404f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0)
14411323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1442b15c8340SDag-Erling Smørgrav 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
14434f52dfbbSDag-Erling Smørgrav 			chan_obuf_empty(ssh, c);
1444b15c8340SDag-Erling Smørgrav 	}
1445b15c8340SDag-Erling Smørgrav }
1446b15c8340SDag-Erling Smørgrav 
1447ca3176e7SBrian Feldman /* try to decode a socks4 header */
1448af12a3e7SDag-Erling Smørgrav static int
14494f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1450ca3176e7SBrian Feldman {
14514f52dfbbSDag-Erling Smørgrav 	const u_char *p;
14524f52dfbbSDag-Erling Smørgrav 	char *host;
1453cce7d346SDag-Erling Smørgrav 	u_int len, have, i, found, need;
1454ca3176e7SBrian Feldman 	char username[256];
1455ca3176e7SBrian Feldman 	struct {
1456ca3176e7SBrian Feldman 		u_int8_t version;
1457ca3176e7SBrian Feldman 		u_int8_t command;
1458ca3176e7SBrian Feldman 		u_int16_t dest_port;
1459ca3176e7SBrian Feldman 		struct in_addr dest_addr;
1460ca3176e7SBrian Feldman 	} s4_req, s4_rsp;
14614f52dfbbSDag-Erling Smørgrav 	int r;
1462ca3176e7SBrian Feldman 
1463ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4", c->self);
1464ca3176e7SBrian Feldman 
14654f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1466ca3176e7SBrian Feldman 	len = sizeof(s4_req);
1467ca3176e7SBrian Feldman 	if (have < len)
1468ca3176e7SBrian Feldman 		return 0;
14694f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1470cce7d346SDag-Erling Smørgrav 
1471cce7d346SDag-Erling Smørgrav 	need = 1;
1472cce7d346SDag-Erling Smørgrav 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1473cce7d346SDag-Erling Smørgrav 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1474cce7d346SDag-Erling Smørgrav 		debug2("channel %d: socks4a request", c->self);
1475cce7d346SDag-Erling Smørgrav 		/* ... and needs an extra string (the hostname) */
1476cce7d346SDag-Erling Smørgrav 		need = 2;
1477cce7d346SDag-Erling Smørgrav 	}
1478cce7d346SDag-Erling Smørgrav 	/* Check for terminating NUL on the string(s) */
1479ca3176e7SBrian Feldman 	for (found = 0, i = len; i < have; i++) {
1480ca3176e7SBrian Feldman 		if (p[i] == '\0') {
1481cce7d346SDag-Erling Smørgrav 			found++;
1482cce7d346SDag-Erling Smørgrav 			if (found == need)
1483ca3176e7SBrian Feldman 				break;
1484ca3176e7SBrian Feldman 		}
1485ca3176e7SBrian Feldman 		if (i > 1024) {
1486ca3176e7SBrian Feldman 			/* the peer is probably sending garbage */
1487ca3176e7SBrian Feldman 			debug("channel %d: decode socks4: too long",
1488ca3176e7SBrian Feldman 			    c->self);
1489ca3176e7SBrian Feldman 			return -1;
1490ca3176e7SBrian Feldman 		}
1491ca3176e7SBrian Feldman 	}
1492cce7d346SDag-Erling Smørgrav 	if (found < need)
1493ca3176e7SBrian Feldman 		return 0;
14944f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
14954f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
14964f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
14974f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
149819261079SEd Maste 		debug_r(r, "channels %d: decode socks4", c->self);
14994f52dfbbSDag-Erling Smørgrav 		return -1;
15004f52dfbbSDag-Erling Smørgrav 	}
15014f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
15024f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
15034f52dfbbSDag-Erling Smørgrav 	if (memchr(p, '\0', have) == NULL) {
150419261079SEd Maste 		error("channel %d: decode socks4: unterminated user", c->self);
15054f52dfbbSDag-Erling Smørgrav 		return -1;
15064f52dfbbSDag-Erling Smørgrav 	}
1507ca3176e7SBrian Feldman 	len = strlen(p);
1508ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1509cce7d346SDag-Erling Smørgrav 	len++; /* trailing '\0' */
1510ca3176e7SBrian Feldman 	strlcpy(username, p, sizeof(username));
151119261079SEd Maste 	if ((r = sshbuf_consume(input, len)) != 0)
151219261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
1513e4a9863fSDag-Erling Smørgrav 	free(c->path);
1514cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1515cce7d346SDag-Erling Smørgrav 	if (need == 1) {			/* SOCKS4: one string */
1516ca3176e7SBrian Feldman 		host = inet_ntoa(s4_req.dest_addr);
1517cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
1518cce7d346SDag-Erling Smørgrav 	} else {				/* SOCKS4A: two strings */
15194f52dfbbSDag-Erling Smørgrav 		have = sshbuf_len(input);
15204f52dfbbSDag-Erling Smørgrav 		p = sshbuf_ptr(input);
15214f52dfbbSDag-Erling Smørgrav 		if (memchr(p, '\0', have) == NULL) {
15224f52dfbbSDag-Erling Smørgrav 			error("channel %d: decode socks4a: host not nul "
15234f52dfbbSDag-Erling Smørgrav 			    "terminated", c->self);
15244f52dfbbSDag-Erling Smørgrav 			return -1;
15254f52dfbbSDag-Erling Smørgrav 		}
1526cce7d346SDag-Erling Smørgrav 		len = strlen(p);
1527cce7d346SDag-Erling Smørgrav 		debug2("channel %d: decode socks4a: host %s/%d",
1528cce7d346SDag-Erling Smørgrav 		    c->self, p, len);
1529cce7d346SDag-Erling Smørgrav 		len++;				/* trailing '\0' */
1530cce7d346SDag-Erling Smørgrav 		if (len > NI_MAXHOST) {
1531cce7d346SDag-Erling Smørgrav 			error("channel %d: hostname \"%.100s\" too long",
1532cce7d346SDag-Erling Smørgrav 			    c->self, p);
1533cce7d346SDag-Erling Smørgrav 			return -1;
1534cce7d346SDag-Erling Smørgrav 		}
1535cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(p);
153619261079SEd Maste 		if ((r = sshbuf_consume(input, len)) != 0)
153719261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
1538cce7d346SDag-Erling Smørgrav 	}
1539ca3176e7SBrian Feldman 	c->host_port = ntohs(s4_req.dest_port);
1540ca3176e7SBrian Feldman 
1541221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1542cce7d346SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s4_req.command);
1543ca3176e7SBrian Feldman 
1544ca3176e7SBrian Feldman 	if (s4_req.command != 1) {
1545cce7d346SDag-Erling Smørgrav 		debug("channel %d: cannot handle: %s cn %d",
1546cce7d346SDag-Erling Smørgrav 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1547ca3176e7SBrian Feldman 		return -1;
1548ca3176e7SBrian Feldman 	}
1549ca3176e7SBrian Feldman 	s4_rsp.version = 0;			/* vn: 0 for reply */
1550ca3176e7SBrian Feldman 	s4_rsp.command = 90;			/* cd: req granted */
1551ca3176e7SBrian Feldman 	s4_rsp.dest_port = 0;			/* ignored */
1552ca3176e7SBrian Feldman 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
155319261079SEd Maste 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0)
155419261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1555ca3176e7SBrian Feldman 	return 1;
1556ca3176e7SBrian Feldman }
1557ca3176e7SBrian Feldman 
1558221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */
1559221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE	0x1000
1560221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH	0x00
1561221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4		0x01
1562221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN	0x03
1563221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6		0x04
1564221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT	0x01
1565221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS	0x00
1566221552e4SDag-Erling Smørgrav 
1567221552e4SDag-Erling Smørgrav static int
15684f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1569221552e4SDag-Erling Smørgrav {
15704f52dfbbSDag-Erling Smørgrav 	/* XXX use get/put_u8 instead of trusting struct padding */
1571221552e4SDag-Erling Smørgrav 	struct {
1572221552e4SDag-Erling Smørgrav 		u_int8_t version;
1573221552e4SDag-Erling Smørgrav 		u_int8_t command;
1574221552e4SDag-Erling Smørgrav 		u_int8_t reserved;
1575221552e4SDag-Erling Smørgrav 		u_int8_t atyp;
1576221552e4SDag-Erling Smørgrav 	} s5_req, s5_rsp;
1577221552e4SDag-Erling Smørgrav 	u_int16_t dest_port;
1578e4a9863fSDag-Erling Smørgrav 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
15794f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1580333ee039SDag-Erling Smørgrav 	u_int have, need, i, found, nmethods, addrlen, af;
15814f52dfbbSDag-Erling Smørgrav 	int r;
1582221552e4SDag-Erling Smørgrav 
1583221552e4SDag-Erling Smørgrav 	debug2("channel %d: decode socks5", c->self);
15844f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1585221552e4SDag-Erling Smørgrav 	if (p[0] != 0x05)
1586221552e4SDag-Erling Smørgrav 		return -1;
15874f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1588221552e4SDag-Erling Smørgrav 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1589221552e4SDag-Erling Smørgrav 		/* format: ver | nmethods | methods */
1590221552e4SDag-Erling Smørgrav 		if (have < 2)
1591221552e4SDag-Erling Smørgrav 			return 0;
1592221552e4SDag-Erling Smørgrav 		nmethods = p[1];
1593221552e4SDag-Erling Smørgrav 		if (have < nmethods + 2)
1594221552e4SDag-Erling Smørgrav 			return 0;
1595221552e4SDag-Erling Smørgrav 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1596221552e4SDag-Erling Smørgrav 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1597221552e4SDag-Erling Smørgrav 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1598221552e4SDag-Erling Smørgrav 				found = 1;
1599221552e4SDag-Erling Smørgrav 				break;
1600221552e4SDag-Erling Smørgrav 			}
1601221552e4SDag-Erling Smørgrav 		}
1602221552e4SDag-Erling Smørgrav 		if (!found) {
1603221552e4SDag-Erling Smørgrav 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1604221552e4SDag-Erling Smørgrav 			    c->self);
1605221552e4SDag-Erling Smørgrav 			return -1;
1606221552e4SDag-Erling Smørgrav 		}
160719261079SEd Maste 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0)
160819261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
16094f52dfbbSDag-Erling Smørgrav 		/* version, method */
16104f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
161119261079SEd Maste 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0)
161219261079SEd Maste 			fatal_fr(r, "channel %d: append reply", c->self);
1613221552e4SDag-Erling Smørgrav 		c->flags |= SSH_SOCKS5_AUTHDONE;
1614221552e4SDag-Erling Smørgrav 		debug2("channel %d: socks5 auth done", c->self);
1615221552e4SDag-Erling Smørgrav 		return 0;				/* need more */
1616221552e4SDag-Erling Smørgrav 	}
1617221552e4SDag-Erling Smørgrav 	debug2("channel %d: socks5 post auth", c->self);
1618221552e4SDag-Erling Smørgrav 	if (have < sizeof(s5_req)+1)
1619221552e4SDag-Erling Smørgrav 		return 0;			/* need more */
1620333ee039SDag-Erling Smørgrav 	memcpy(&s5_req, p, sizeof(s5_req));
1621221552e4SDag-Erling Smørgrav 	if (s5_req.version != 0x05 ||
1622221552e4SDag-Erling Smørgrav 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1623221552e4SDag-Erling Smørgrav 	    s5_req.reserved != 0x00) {
1624221552e4SDag-Erling Smørgrav 		debug2("channel %d: only socks5 connect supported", c->self);
1625221552e4SDag-Erling Smørgrav 		return -1;
1626221552e4SDag-Erling Smørgrav 	}
1627221552e4SDag-Erling Smørgrav 	switch (s5_req.atyp){
1628221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV4:
1629221552e4SDag-Erling Smørgrav 		addrlen = 4;
1630221552e4SDag-Erling Smørgrav 		af = AF_INET;
1631221552e4SDag-Erling Smørgrav 		break;
1632221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_DOMAIN:
1633221552e4SDag-Erling Smørgrav 		addrlen = p[sizeof(s5_req)];
1634221552e4SDag-Erling Smørgrav 		af = -1;
1635221552e4SDag-Erling Smørgrav 		break;
1636221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV6:
1637221552e4SDag-Erling Smørgrav 		addrlen = 16;
1638221552e4SDag-Erling Smørgrav 		af = AF_INET6;
1639221552e4SDag-Erling Smørgrav 		break;
1640221552e4SDag-Erling Smørgrav 	default:
1641221552e4SDag-Erling Smørgrav 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1642221552e4SDag-Erling Smørgrav 		return -1;
1643221552e4SDag-Erling Smørgrav 	}
1644333ee039SDag-Erling Smørgrav 	need = sizeof(s5_req) + addrlen + 2;
1645333ee039SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1646333ee039SDag-Erling Smørgrav 		need++;
1647333ee039SDag-Erling Smørgrav 	if (have < need)
1648221552e4SDag-Erling Smørgrav 		return 0;
164919261079SEd Maste 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0)
165019261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
16514f52dfbbSDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
16524f52dfbbSDag-Erling Smørgrav 		/* host string length */
165319261079SEd Maste 		if ((r = sshbuf_consume(input, 1)) != 0)
165419261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
16554f52dfbbSDag-Erling Smørgrav 	}
16564f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
16574f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
165819261079SEd Maste 		debug_r(r, "channel %d: parse addr/port", c->self);
16594f52dfbbSDag-Erling Smørgrav 		return -1;
16604f52dfbbSDag-Erling Smørgrav 	}
1661221552e4SDag-Erling Smørgrav 	dest_addr[addrlen] = '\0';
1662e4a9863fSDag-Erling Smørgrav 	free(c->path);
1663cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1664cce7d346SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1665cce7d346SDag-Erling Smørgrav 		if (addrlen >= NI_MAXHOST) {
1666cce7d346SDag-Erling Smørgrav 			error("channel %d: dynamic request: socks5 hostname "
1667cce7d346SDag-Erling Smørgrav 			    "\"%.100s\" too long", c->self, dest_addr);
1668221552e4SDag-Erling Smørgrav 			return -1;
1669cce7d346SDag-Erling Smørgrav 		}
1670cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(dest_addr);
1671cce7d346SDag-Erling Smørgrav 	} else {
1672cce7d346SDag-Erling Smørgrav 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1673cce7d346SDag-Erling Smørgrav 			return -1;
1674cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(ntop);
1675cce7d346SDag-Erling Smørgrav 	}
1676221552e4SDag-Erling Smørgrav 	c->host_port = ntohs(dest_port);
1677221552e4SDag-Erling Smørgrav 
1678221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1679221552e4SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s5_req.command);
1680221552e4SDag-Erling Smørgrav 
1681221552e4SDag-Erling Smørgrav 	s5_rsp.version = 0x05;
1682221552e4SDag-Erling Smørgrav 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1683221552e4SDag-Erling Smørgrav 	s5_rsp.reserved = 0;			/* ignored */
1684221552e4SDag-Erling Smørgrav 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1685221552e4SDag-Erling Smørgrav 	dest_port = 0;				/* ignored */
1686221552e4SDag-Erling Smørgrav 
16874f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
16884f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
16894f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
169019261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1691221552e4SDag-Erling Smørgrav 	return 1;
1692221552e4SDag-Erling Smørgrav }
1693221552e4SDag-Erling Smørgrav 
1694b15c8340SDag-Erling Smørgrav Channel *
16954f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh,
1696535af610SEd Maste     const char *host_to_connect, int port_to_connect,
169719261079SEd Maste     int in, int out, int nonblock)
1698b15c8340SDag-Erling Smørgrav {
1699b15c8340SDag-Erling Smørgrav 	Channel *c;
1700b15c8340SDag-Erling Smørgrav 
170119261079SEd Maste 	debug_f("%s:%d", host_to_connect, port_to_connect);
1702b15c8340SDag-Erling Smørgrav 
17034f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
1704b15c8340SDag-Erling Smørgrav 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
170519261079SEd Maste 	    0, "stdio-forward", nonblock);
1706b15c8340SDag-Erling Smørgrav 
1707b15c8340SDag-Erling Smørgrav 	c->path = xstrdup(host_to_connect);
1708b15c8340SDag-Erling Smørgrav 	c->host_port = port_to_connect;
1709b15c8340SDag-Erling Smørgrav 	c->listening_port = 0;
1710b15c8340SDag-Erling Smørgrav 	c->force_drain = 1;
1711b15c8340SDag-Erling Smørgrav 
17124f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
1713535af610SEd Maste 	port_open_helper(ssh, c, port_to_connect == PORT_STREAMLOCAL ?
1714535af610SEd Maste 	    "direct-streamlocal@openssh.com" : "direct-tcpip");
1715b15c8340SDag-Erling Smørgrav 
1716b15c8340SDag-Erling Smørgrav 	return c;
1717b15c8340SDag-Erling Smørgrav }
1718b15c8340SDag-Erling Smørgrav 
1719ca3176e7SBrian Feldman /* dynamic port forwarding */
1720af12a3e7SDag-Erling Smørgrav static void
17211323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c)
1722ca3176e7SBrian Feldman {
17234f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1724d4ecd108SDag-Erling Smørgrav 	u_int have;
1725d4ecd108SDag-Erling Smørgrav 	int ret;
1726ca3176e7SBrian Feldman 
17271323ec57SEd Maste 	c->io_want = 0;
17284f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->input);
1729ca3176e7SBrian Feldman 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
17304f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->input, stderr); */
1731ca3176e7SBrian Feldman 	/* check if the fixed size part of the packet is in buffer. */
1732221552e4SDag-Erling Smørgrav 	if (have < 3) {
1733ca3176e7SBrian Feldman 		/* need more */
17341323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1735ca3176e7SBrian Feldman 		return;
1736ca3176e7SBrian Feldman 	}
1737ca3176e7SBrian Feldman 	/* try to guess the protocol */
17384f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->input);
17394f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u8? */
1740ca3176e7SBrian Feldman 	switch (p[0]) {
1741ca3176e7SBrian Feldman 	case 0x04:
17424f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->input, c->output);
1743ca3176e7SBrian Feldman 		break;
1744221552e4SDag-Erling Smørgrav 	case 0x05:
17454f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->input, c->output);
1746221552e4SDag-Erling Smørgrav 		break;
1747ca3176e7SBrian Feldman 	default:
1748ca3176e7SBrian Feldman 		ret = -1;
1749ca3176e7SBrian Feldman 		break;
1750ca3176e7SBrian Feldman 	}
1751ca3176e7SBrian Feldman 	if (ret < 0) {
17524f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1753ca3176e7SBrian Feldman 	} else if (ret == 0) {
1754ca3176e7SBrian Feldman 		debug2("channel %d: pre_dynamic: need more", c->self);
1755ca3176e7SBrian Feldman 		/* need more */
17561323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
17574f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output))
17581323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1759ca3176e7SBrian Feldman 	} else {
1760ca3176e7SBrian Feldman 		/* switch to the next state */
1761ca3176e7SBrian Feldman 		c->type = SSH_CHANNEL_OPENING;
17624f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, c, "direct-tcpip");
17634f52dfbbSDag-Erling Smørgrav 	}
17644f52dfbbSDag-Erling Smørgrav }
17654f52dfbbSDag-Erling Smørgrav 
17664f52dfbbSDag-Erling Smørgrav /* simulate read-error */
17674f52dfbbSDag-Erling Smørgrav static void
17684f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c)
17694f52dfbbSDag-Erling Smørgrav {
17704f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1771f374ba41SEd Maste 	channel_force_close(ssh, c, 0);
17724f52dfbbSDag-Erling Smørgrav }
17734f52dfbbSDag-Erling Smørgrav 
17744f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */
17754f52dfbbSDag-Erling Smørgrav static void
17761323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c)
17774f52dfbbSDag-Erling Smørgrav {
17784f52dfbbSDag-Erling Smørgrav 	const u_char *p;
17794f52dfbbSDag-Erling Smørgrav 	u_int have, len;
17804f52dfbbSDag-Erling Smørgrav 	int r, ret;
17814f52dfbbSDag-Erling Smørgrav 
17824f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->output);
17834f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
17844f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->output, stderr); */
17854f52dfbbSDag-Erling Smørgrav 	/* EOF received */
17864f52dfbbSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
178719261079SEd Maste 		if ((r = sshbuf_consume(c->output, have)) != 0)
178819261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
17894f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
17904f52dfbbSDag-Erling Smørgrav 		return;
17914f52dfbbSDag-Erling Smørgrav 	}
17924f52dfbbSDag-Erling Smørgrav 	/* check if the fixed size part of the packet is in buffer. */
17934f52dfbbSDag-Erling Smørgrav 	if (have < 3)
17944f52dfbbSDag-Erling Smørgrav 		return;
17954f52dfbbSDag-Erling Smørgrav 	/* try to guess the protocol */
17964f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->output);
17974f52dfbbSDag-Erling Smørgrav 	switch (p[0]) {
17984f52dfbbSDag-Erling Smørgrav 	case 0x04:
17994f52dfbbSDag-Erling Smørgrav 		/* switch input/output for reverse forwarding */
18004f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->output, c->input);
18014f52dfbbSDag-Erling Smørgrav 		break;
18024f52dfbbSDag-Erling Smørgrav 	case 0x05:
18034f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->output, c->input);
18044f52dfbbSDag-Erling Smørgrav 		break;
18054f52dfbbSDag-Erling Smørgrav 	default:
18064f52dfbbSDag-Erling Smørgrav 		ret = -1;
18074f52dfbbSDag-Erling Smørgrav 		break;
18084f52dfbbSDag-Erling Smørgrav 	}
18094f52dfbbSDag-Erling Smørgrav 	if (ret < 0) {
18104f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
18114f52dfbbSDag-Erling Smørgrav 	} else if (ret == 0) {
18124f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: pre_rdynamic: need more", c->self);
18134f52dfbbSDag-Erling Smørgrav 		/* send socks request to peer */
18144f52dfbbSDag-Erling Smørgrav 		len = sshbuf_len(c->input);
18154f52dfbbSDag-Erling Smørgrav 		if (len > 0 && len < c->remote_window) {
18164f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
18174f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
18184f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
18194f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0) {
182019261079SEd Maste 				fatal_fr(r, "channel %i: rdynamic", c->self);
18214f52dfbbSDag-Erling Smørgrav 			}
182219261079SEd Maste 			if ((r = sshbuf_consume(c->input, len)) != 0)
182319261079SEd Maste 				fatal_fr(r, "channel %d: consume", c->self);
18244f52dfbbSDag-Erling Smørgrav 			c->remote_window -= len;
18254f52dfbbSDag-Erling Smørgrav 		}
18264f52dfbbSDag-Erling Smørgrav 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
18274f52dfbbSDag-Erling Smørgrav 		/* the connect failed */
18284f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
1829ca3176e7SBrian Feldman 	}
1830ca3176e7SBrian Feldman }
1831ca3176e7SBrian Feldman 
1832a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
1833af12a3e7SDag-Erling Smørgrav static void
18341323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c)
1835511b41d2SMark Murray {
1836af12a3e7SDag-Erling Smørgrav 	Channel *nc;
1837d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
18384f52dfbbSDag-Erling Smørgrav 	int r, newsock, oerrno, remote_port;
1839511b41d2SMark Murray 	socklen_t addrlen;
1840ca3176e7SBrian Feldman 	char buf[16384], *remote_ipaddr;
1841511b41d2SMark Murray 
18421323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
18434f52dfbbSDag-Erling Smørgrav 		return;
18444f52dfbbSDag-Erling Smørgrav 
1845511b41d2SMark Murray 	debug("X11 connection requested.");
1846511b41d2SMark Murray 	addrlen = sizeof(addr);
1847d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1848af12a3e7SDag-Erling Smørgrav 	if (c->single_connection) {
1849e4a9863fSDag-Erling Smørgrav 		oerrno = errno;
1850221552e4SDag-Erling Smørgrav 		debug2("single_connection: closing X11 listener.");
185119261079SEd Maste 		channel_close_fd(ssh, c, &c->sock);
18524f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1853e4a9863fSDag-Erling Smørgrav 		errno = oerrno;
1854af12a3e7SDag-Erling Smørgrav 	}
185519261079SEd Maste 	if (newsock == -1) {
1856e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1857e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1858511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1859462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1860e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1861a04a10f8SKris Kennaway 		return;
1862511b41d2SMark Murray 	}
1863af12a3e7SDag-Erling Smørgrav 	set_nodelay(newsock);
1864ca3176e7SBrian Feldman 	remote_ipaddr = get_peer_ipaddr(newsock);
1865a04a10f8SKris Kennaway 	remote_port = get_peer_port(newsock);
1866511b41d2SMark Murray 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1867ca3176e7SBrian Feldman 	    remote_ipaddr, remote_port);
1868a04a10f8SKris Kennaway 
1869f374ba41SEd Maste 	nc = channel_new(ssh, "x11-connection",
1870a04a10f8SKris Kennaway 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1871221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
18724f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "x11");
187347dd1d1bSDag-Erling Smørgrav 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
187447dd1d1bSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
187519261079SEd Maste 		fatal_fr(r, "channel %i: reply", c->self);
1876511b41d2SMark Murray 	}
18774f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
187819261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1879e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1880a04a10f8SKris Kennaway }
1881511b41d2SMark Murray 
1882af12a3e7SDag-Erling Smørgrav static void
18834f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
1884ca3176e7SBrian Feldman {
1885f7167e0eSDag-Erling Smørgrav 	char *local_ipaddr = get_local_ipaddr(c->sock);
1886076ad2f8SDag-Erling Smørgrav 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1887ca3176e7SBrian Feldman 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1888d4ecd108SDag-Erling Smørgrav 	int remote_port = get_peer_port(c->sock);
18894f52dfbbSDag-Erling Smørgrav 	int r;
1890ca3176e7SBrian Feldman 
1891b15c8340SDag-Erling Smørgrav 	if (remote_port == -1) {
1892b15c8340SDag-Erling Smørgrav 		/* Fake addr/port to appease peers that validate it (Tectia) */
1893e4a9863fSDag-Erling Smørgrav 		free(remote_ipaddr);
1894b15c8340SDag-Erling Smørgrav 		remote_ipaddr = xstrdup("127.0.0.1");
1895b15c8340SDag-Erling Smørgrav 		remote_port = 65535;
1896b15c8340SDag-Erling Smørgrav 	}
1897b15c8340SDag-Erling Smørgrav 
18984f52dfbbSDag-Erling Smørgrav 	free(c->remote_name);
18994f52dfbbSDag-Erling Smørgrav 	xasprintf(&c->remote_name,
1900ca3176e7SBrian Feldman 	    "%s: listening port %d for %.100s port %d, "
1901f7167e0eSDag-Erling Smørgrav 	    "connect from %.200s port %d to %.100s port %d",
1902ca3176e7SBrian Feldman 	    rtype, c->listening_port, c->path, c->host_port,
1903f7167e0eSDag-Erling Smørgrav 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1904ca3176e7SBrian Feldman 
19054f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, rtype);
1906a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "direct-tcpip") == 0) {
1907ca3176e7SBrian Feldman 		/* target host, port */
19084f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
190919261079SEd Maste 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0)
191019261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1911a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
1912a0ee8cc6SDag-Erling Smørgrav 		/* target path */
191319261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
191419261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1915a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1916a0ee8cc6SDag-Erling Smørgrav 		/* listen path */
191719261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
191819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1919ca3176e7SBrian Feldman 	} else {
1920ca3176e7SBrian Feldman 		/* listen address, port */
19214f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
192219261079SEd Maste 		    (r = sshpkt_put_u32(ssh, local_port)) != 0)
192319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1924ca3176e7SBrian Feldman 	}
1925a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1926a0ee8cc6SDag-Erling Smørgrav 		/* reserved for future owner/mode info */
192719261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, "")) != 0)
192819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1929a0ee8cc6SDag-Erling Smørgrav 	} else {
1930ca3176e7SBrian Feldman 		/* originator host and port */
19314f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
193219261079SEd Maste 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0)
193319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1934ca3176e7SBrian Feldman 	}
19354f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
193619261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1937e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1938f7167e0eSDag-Erling Smørgrav 	free(local_ipaddr);
1939ca3176e7SBrian Feldman }
1940ca3176e7SBrian Feldman 
1941557f75e5SDag-Erling Smørgrav void
19424d3fc8b0SEd Maste channel_set_x11_refuse_time(struct ssh *ssh, time_t refuse_time)
1943557f75e5SDag-Erling Smørgrav {
19444f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->x11_refuse_time = refuse_time;
1945557f75e5SDag-Erling Smørgrav }
1946557f75e5SDag-Erling Smørgrav 
1947511b41d2SMark Murray /*
1948a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
1949511b41d2SMark Murray  */
1950af12a3e7SDag-Erling Smørgrav static void
19511323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c)
1952a04a10f8SKris Kennaway {
1953ca3176e7SBrian Feldman 	Channel *nc;
1954d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1955af12a3e7SDag-Erling Smørgrav 	int newsock, nextstate;
1956a04a10f8SKris Kennaway 	socklen_t addrlen;
1957ca3176e7SBrian Feldman 	char *rtype;
1958a04a10f8SKris Kennaway 
19591323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
19604f52dfbbSDag-Erling Smørgrav 		return;
19614f52dfbbSDag-Erling Smørgrav 
19624f52dfbbSDag-Erling Smørgrav 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1963a04a10f8SKris Kennaway 	    c->listening_port, c->path, c->host_port);
1964ca3176e7SBrian Feldman 
1965af12a3e7SDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1966af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1967af12a3e7SDag-Erling Smørgrav 		rtype = "forwarded-tcpip";
1968a0ee8cc6SDag-Erling Smørgrav 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
1969a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1970a0ee8cc6SDag-Erling Smørgrav 		rtype = "forwarded-streamlocal@openssh.com";
1971a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == PORT_STREAMLOCAL) {
1972a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1973a0ee8cc6SDag-Erling Smørgrav 		rtype = "direct-streamlocal@openssh.com";
1974a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == 0) {
1975af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_DYNAMIC;
1976af12a3e7SDag-Erling Smørgrav 		rtype = "dynamic-tcpip";
1977af12a3e7SDag-Erling Smørgrav 	} else {
1978af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1979af12a3e7SDag-Erling Smørgrav 		rtype = "direct-tcpip";
1980af12a3e7SDag-Erling Smørgrav 	}
1981ca3176e7SBrian Feldman 
1982511b41d2SMark Murray 	addrlen = sizeof(addr);
1983d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
198419261079SEd Maste 	if (newsock == -1) {
1985e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1986e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1987511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1988462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1989e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1990a04a10f8SKris Kennaway 		return;
1991511b41d2SMark Murray 	}
1992a0ee8cc6SDag-Erling Smørgrav 	if (c->host_port != PORT_STREAMLOCAL)
1993af12a3e7SDag-Erling Smørgrav 		set_nodelay(newsock);
19944f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
1995221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
1996ca3176e7SBrian Feldman 	nc->listening_port = c->listening_port;
1997ca3176e7SBrian Feldman 	nc->host_port = c->host_port;
1998cce7d346SDag-Erling Smørgrav 	if (c->path != NULL)
1999cce7d346SDag-Erling Smørgrav 		nc->path = xstrdup(c->path);
2000ca3176e7SBrian Feldman 
2001b15c8340SDag-Erling Smørgrav 	if (nextstate != SSH_CHANNEL_DYNAMIC)
20024f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, nc, rtype);
2003a04a10f8SKris Kennaway }
2004511b41d2SMark Murray 
2005511b41d2SMark Murray /*
2006a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
2007a04a10f8SKris Kennaway  * clients.
2008511b41d2SMark Murray  */
2009af12a3e7SDag-Erling Smørgrav static void
20101323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c)
2011a04a10f8SKris Kennaway {
2012af12a3e7SDag-Erling Smørgrav 	Channel *nc;
20134f52dfbbSDag-Erling Smørgrav 	int r, newsock;
2014d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2015a04a10f8SKris Kennaway 	socklen_t addrlen;
2016a04a10f8SKris Kennaway 
20171323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
20184f52dfbbSDag-Erling Smørgrav 		return;
20194f52dfbbSDag-Erling Smørgrav 
2020511b41d2SMark Murray 	addrlen = sizeof(addr);
2021d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
202219261079SEd Maste 	if (newsock == -1) {
20234f52dfbbSDag-Erling Smørgrav 		error("accept from auth socket: %.100s", strerror(errno));
2024462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2025e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2026a04a10f8SKris Kennaway 		return;
2027511b41d2SMark Murray 	}
2028f374ba41SEd Maste 	nc = channel_new(ssh, "agent-connection",
2029ca3176e7SBrian Feldman 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
2030ca3176e7SBrian Feldman 	    c->local_window_max, c->local_maxpacket,
2031221552e4SDag-Erling Smørgrav 	    0, "accepted auth socket", 1);
20324f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
20334f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
203419261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
2035a04a10f8SKris Kennaway }
2036511b41d2SMark Murray 
2037af12a3e7SDag-Erling Smørgrav static void
20381323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c)
2039ca3176e7SBrian Feldman {
20404f52dfbbSDag-Erling Smørgrav 	int err = 0, sock, isopen, r;
2041af12a3e7SDag-Erling Smørgrav 	socklen_t sz = sizeof(err);
2042af12a3e7SDag-Erling Smørgrav 
20431323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
20444f52dfbbSDag-Erling Smørgrav 		return;
20454f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
204619261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
20474f52dfbbSDag-Erling Smørgrav 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
20484f52dfbbSDag-Erling Smørgrav 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
20494d3fc8b0SEd Maste 
205019261079SEd Maste 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) {
2051af12a3e7SDag-Erling Smørgrav 		err = errno;
2052af12a3e7SDag-Erling Smørgrav 		error("getsockopt SO_ERROR failed");
2053af12a3e7SDag-Erling Smørgrav 	}
20544d3fc8b0SEd Maste 
2055ca3176e7SBrian Feldman 	if (err == 0) {
20564d3fc8b0SEd Maste 		/* Non-blocking connection completed */
2057d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connected to %s port %d",
2058d4af9e69SDag-Erling Smørgrav 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
2059d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
2060af12a3e7SDag-Erling Smørgrav 		c->type = SSH_CHANNEL_OPEN;
2061*a91a2465SEd Maste 		channel_set_used_time(ssh, c);
20624f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20634f52dfbbSDag-Erling Smørgrav 			/* no message necessary */
2064af12a3e7SDag-Erling Smørgrav 		} else {
20654f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20664f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
20674f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
20684f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
20694f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
207019261079SEd Maste 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
207119261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
207219261079SEd Maste 				fatal_fr(r, "channel %i open confirm", c->self);
2073af12a3e7SDag-Erling Smørgrav 		}
2074d4af9e69SDag-Erling Smørgrav 		return;
2075d4af9e69SDag-Erling Smørgrav 	}
20764d3fc8b0SEd Maste 	if (err == EINTR || err == EAGAIN || err == EINPROGRESS)
20774d3fc8b0SEd Maste 		return;
20784d3fc8b0SEd Maste 
20794d3fc8b0SEd Maste 	/* Non-blocking connection failed */
20804d3fc8b0SEd Maste 	debug("channel %d: connection failed: %s", c->self, strerror(err));
20814d3fc8b0SEd Maste 
20824d3fc8b0SEd Maste 	/* Try next address, if any */
20834d3fc8b0SEd Maste 	if ((sock = connect_next(&c->connect_ctx)) == -1) {
20844d3fc8b0SEd Maste 		/* Exhausted all addresses for this destination */
2085d4af9e69SDag-Erling Smørgrav 		error("connect_to %.100s port %d: failed.",
2086d4af9e69SDag-Erling Smørgrav 		    c->connect_ctx.host, c->connect_ctx.port);
2087d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
20884f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20894f52dfbbSDag-Erling Smørgrav 			rdynamic_close(ssh, c);
2090af12a3e7SDag-Erling Smørgrav 		} else {
20914f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20924f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
20934f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
209447dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh,
209547dd1d1bSDag-Erling Smørgrav 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
209647dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
209719261079SEd Maste 			    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
209819261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
209919261079SEd Maste 				fatal_fr(r, "channel %i: failure", c->self);
21004f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2101ca3176e7SBrian Feldman 		}
2102ca3176e7SBrian Feldman 	}
21034d3fc8b0SEd Maste 
21044d3fc8b0SEd Maste 	/* New non-blocking connection in progress */
21054d3fc8b0SEd Maste 	close(c->sock);
21064d3fc8b0SEd Maste 	c->sock = c->rfd = c->wfd = sock;
2107ca3176e7SBrian Feldman }
2108ca3176e7SBrian Feldman 
2109af12a3e7SDag-Erling Smørgrav static int
21101323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c)
2111a04a10f8SKris Kennaway {
2112aa49c926SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
21134f52dfbbSDag-Erling Smørgrav 	ssize_t len;
21144f52dfbbSDag-Erling Smørgrav 	int r, force;
2115f374ba41SEd Maste 	size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ;
21161323ec57SEd Maste 	int pty_zeroread = 0;
21171323ec57SEd Maste 
21181323ec57SEd Maste #ifdef PTY_ZEROREAD
21191323ec57SEd Maste 	/* Bug on AIX: read(1) can return 0 for a non-closed fd */
21201323ec57SEd Maste 	pty_zeroread = c->isatty;
21211323ec57SEd Maste #endif
2122511b41d2SMark Murray 
2123d4af9e69SDag-Erling Smørgrav 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
21244f52dfbbSDag-Erling Smørgrav 
21251323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0)
21264f52dfbbSDag-Erling Smørgrav 		return 1;
21271323ec57SEd Maste 	if ((avail = sshbuf_avail(c->input)) == 0)
21281323ec57SEd Maste 		return 1; /* Shouldn't happen */
21291323ec57SEd Maste 
21301323ec57SEd Maste 	/*
21311323ec57SEd Maste 	 * For "simple" channels (i.e. not datagram or filtered), we can
21321323ec57SEd Maste 	 * read directly to the channel buffer.
21331323ec57SEd Maste 	 */
21341323ec57SEd Maste 	if (!pty_zeroread && c->input_filter == NULL && !c->datagram) {
21351323ec57SEd Maste 		/* Only OPEN channels have valid rwin */
21361323ec57SEd Maste 		if (c->type == SSH_CHANNEL_OPEN) {
21371323ec57SEd Maste 			if ((have = sshbuf_len(c->input)) >= c->remote_window)
21381323ec57SEd Maste 				return 1; /* shouldn't happen */
21391323ec57SEd Maste 			if (maxlen > c->remote_window - have)
21401323ec57SEd Maste 				maxlen = c->remote_window - have;
21411323ec57SEd Maste 		}
21421323ec57SEd Maste 		if (maxlen > avail)
21431323ec57SEd Maste 			maxlen = avail;
2144f374ba41SEd Maste 		if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) {
21451323ec57SEd Maste 			if (errno == EINTR || (!force &&
21461323ec57SEd Maste 			    (errno == EAGAIN || errno == EWOULDBLOCK)))
21471323ec57SEd Maste 				return 1;
21481323ec57SEd Maste 			debug2("channel %d: read failed rfd %d maxlen %zu: %s",
21491323ec57SEd Maste 			    c->self, c->rfd, maxlen, ssh_err(r));
21501323ec57SEd Maste 			goto rfail;
21511323ec57SEd Maste 		}
2152f374ba41SEd Maste 		if (nr != 0)
2153*a91a2465SEd Maste 			channel_set_used_time(ssh, c);
21541323ec57SEd Maste 		return 1;
21551323ec57SEd Maste 	}
21564f52dfbbSDag-Erling Smørgrav 
2157333ee039SDag-Erling Smørgrav 	errno = 0;
2158a04a10f8SKris Kennaway 	len = read(c->rfd, buf, sizeof(buf));
21591323ec57SEd Maste 	/* fixup AIX zero-length read with errno set to look more like errors */
21601323ec57SEd Maste 	if (pty_zeroread && len == 0 && errno != 0)
21611323ec57SEd Maste 		len = -1;
216219261079SEd Maste 	if (len == -1 && (errno == EINTR ||
2163d4af9e69SDag-Erling Smørgrav 	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
2164a04a10f8SKris Kennaway 		return 1;
21651323ec57SEd Maste 	if (len < 0 || (!pty_zeroread && len == 0)) {
21661323ec57SEd Maste 		debug2("channel %d: read<=0 rfd %d len %zd: %s",
21671323ec57SEd Maste 		    c->self, c->rfd, len,
21681323ec57SEd Maste 		    len == 0 ? "closed" : strerror(errno));
21691323ec57SEd Maste  rfail:
2170ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2171221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
21724f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2173ca3176e7SBrian Feldman 			return -1;
2174a04a10f8SKris Kennaway 		} else {
21754f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2176a04a10f8SKris Kennaway 		}
2177a04a10f8SKris Kennaway 		return -1;
2178a04a10f8SKris Kennaway 	}
2179*a91a2465SEd Maste 	channel_set_used_time(ssh, c);
2180b66f2d16SKris Kennaway 	if (c->input_filter != NULL) {
21814f52dfbbSDag-Erling Smørgrav 		if (c->input_filter(ssh, c, buf, len) == -1) {
2182221552e4SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
21834f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2184b66f2d16SKris Kennaway 		}
2185b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
21864f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
218719261079SEd Maste 			fatal_fr(r, "channel %i: put datagram", c->self);
218819261079SEd Maste 	} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
218919261079SEd Maste 		fatal_fr(r, "channel %i: put data", c->self);
21901323ec57SEd Maste 
2191a04a10f8SKris Kennaway 	return 1;
2192a04a10f8SKris Kennaway }
2193333ee039SDag-Erling Smørgrav 
2194af12a3e7SDag-Erling Smørgrav static int
21951323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c)
2196a04a10f8SKris Kennaway {
2197ca3176e7SBrian Feldman 	struct termios tio;
21984f52dfbbSDag-Erling Smørgrav 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
21994f52dfbbSDag-Erling Smørgrav 	size_t dlen, olen = 0;
22004f52dfbbSDag-Erling Smørgrav 	int r, len;
22014f52dfbbSDag-Erling Smørgrav 
22021323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
22031323ec57SEd Maste 		return 1;
22041323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
22054f52dfbbSDag-Erling Smørgrav 		return 1;
2206a04a10f8SKris Kennaway 
2207a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
22084f52dfbbSDag-Erling Smørgrav 	olen = sshbuf_len(c->output);
2209b74df5b2SDag-Erling Smørgrav 	if (c->output_filter != NULL) {
22104f52dfbbSDag-Erling Smørgrav 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
2211b74df5b2SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
2212b74df5b2SDag-Erling Smørgrav 			if (c->type != SSH_CHANNEL_OPEN)
22134f52dfbbSDag-Erling Smørgrav 				chan_mark_dead(ssh, c);
2214b74df5b2SDag-Erling Smørgrav 			else
22154f52dfbbSDag-Erling Smørgrav 				chan_write_failed(ssh, c);
2216b74df5b2SDag-Erling Smørgrav 			return -1;
2217b74df5b2SDag-Erling Smørgrav 		}
2218b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
22194f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
222019261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
22214f52dfbbSDag-Erling Smørgrav 		buf = data;
2222b74df5b2SDag-Erling Smørgrav 	} else {
22234f52dfbbSDag-Erling Smørgrav 		buf = data = sshbuf_mutable_ptr(c->output);
22244f52dfbbSDag-Erling Smørgrav 		dlen = sshbuf_len(c->output);
2225b74df5b2SDag-Erling Smørgrav 	}
2226b74df5b2SDag-Erling Smørgrav 
2227b74df5b2SDag-Erling Smørgrav 	if (c->datagram) {
2228b74df5b2SDag-Erling Smørgrav 		/* ignore truncated writes, datagrams might get lost */
2229b74df5b2SDag-Erling Smørgrav 		len = write(c->wfd, buf, dlen);
2230e4a9863fSDag-Erling Smørgrav 		free(data);
223119261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2232d4af9e69SDag-Erling Smørgrav 		    errno == EWOULDBLOCK))
2233b74df5b2SDag-Erling Smørgrav 			return 1;
22344f52dfbbSDag-Erling Smørgrav 		if (len <= 0)
22354f52dfbbSDag-Erling Smørgrav 			goto write_fail;
2236e2f6069cSDag-Erling Smørgrav 		goto out;
2237b74df5b2SDag-Erling Smørgrav 	}
22384f52dfbbSDag-Erling Smørgrav 
2239f388f5efSDag-Erling Smørgrav #ifdef _AIX
2240f388f5efSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
22414f52dfbbSDag-Erling Smørgrav 	if (c->wfd_isatty)
22421323ec57SEd Maste 		dlen = MINIMUM(dlen, 8*1024);
2243f388f5efSDag-Erling Smørgrav #endif
2244b74df5b2SDag-Erling Smørgrav 
2245b74df5b2SDag-Erling Smørgrav 	len = write(c->wfd, buf, dlen);
224619261079SEd Maste 	if (len == -1 &&
2247d4af9e69SDag-Erling Smørgrav 	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
2248a04a10f8SKris Kennaway 		return 1;
2249511b41d2SMark Murray 	if (len <= 0) {
22504f52dfbbSDag-Erling Smørgrav  write_fail:
2251ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2252221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
22534f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2254ca3176e7SBrian Feldman 			return -1;
2255511b41d2SMark Murray 		} else {
22564f52dfbbSDag-Erling Smørgrav 			chan_write_failed(ssh, c);
2257511b41d2SMark Murray 		}
2258a04a10f8SKris Kennaway 		return -1;
2259511b41d2SMark Murray 	}
2260*a91a2465SEd Maste 	channel_set_used_time(ssh, c);
22617aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON
22624f52dfbbSDag-Erling Smørgrav 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2263e0fbb1d2SBrian Feldman 		if (tcgetattr(c->wfd, &tio) == 0 &&
2264e0fbb1d2SBrian Feldman 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2265e0fbb1d2SBrian Feldman 			/*
2266e0fbb1d2SBrian Feldman 			 * Simulate echo to reduce the impact of
2267ca3176e7SBrian Feldman 			 * traffic analysis. We need to match the
2268ca3176e7SBrian Feldman 			 * size of a SSH2_MSG_CHANNEL_DATA message
2269b74df5b2SDag-Erling Smørgrav 			 * (4 byte channel id + buf)
2270e0fbb1d2SBrian Feldman 			 */
22714f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
22724f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0)
227319261079SEd Maste 				fatal_fr(r, "channel %i: ignore", c->self);
2274e0fbb1d2SBrian Feldman 		}
2275e0fbb1d2SBrian Feldman 	}
22764f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */
227719261079SEd Maste 	if ((r = sshbuf_consume(c->output, len)) != 0)
227819261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
2279e2f6069cSDag-Erling Smørgrav  out:
22804f52dfbbSDag-Erling Smørgrav 	c->local_consumed += olen - sshbuf_len(c->output);
22814f52dfbbSDag-Erling Smørgrav 
2282a04a10f8SKris Kennaway 	return 1;
2283511b41d2SMark Murray }
2284333ee039SDag-Erling Smørgrav 
2285af12a3e7SDag-Erling Smørgrav static int
22861323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c)
2287a04a10f8SKris Kennaway {
22884f52dfbbSDag-Erling Smørgrav 	int r;
22894f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2290511b41d2SMark Murray 
22911323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0)
22921323ec57SEd Maste 		return 1;
22931323ec57SEd Maste 	if (sshbuf_len(c->extended) == 0)
22944f52dfbbSDag-Erling Smørgrav 		return 1;
22954f52dfbbSDag-Erling Smørgrav 
22964f52dfbbSDag-Erling Smørgrav 	len = write(c->efd, sshbuf_ptr(c->extended),
22974f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->extended));
22984f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
229919261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2300d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK))
2301ca3176e7SBrian Feldman 		return 1;
2302ca3176e7SBrian Feldman 	if (len <= 0) {
23034f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
230419261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2305ca3176e7SBrian Feldman 	} else {
230619261079SEd Maste 		if ((r = sshbuf_consume(c->extended, len)) != 0)
230719261079SEd Maste 			fatal_fr(r, "channel %i: consume", c->self);
2308a04a10f8SKris Kennaway 		c->local_consumed += len;
2309*a91a2465SEd Maste 		channel_set_used_time(ssh, c);
2310a04a10f8SKris Kennaway 	}
23114f52dfbbSDag-Erling Smørgrav 	return 1;
23124f52dfbbSDag-Erling Smørgrav }
23134f52dfbbSDag-Erling Smørgrav 
23144f52dfbbSDag-Erling Smørgrav static int
23151323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c)
23164f52dfbbSDag-Erling Smørgrav {
23174f52dfbbSDag-Erling Smørgrav 	char buf[CHAN_RBUF];
23184f52dfbbSDag-Erling Smørgrav 	ssize_t len;
231919261079SEd Maste 	int r, force;
23204f52dfbbSDag-Erling Smørgrav 
232119261079SEd Maste 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
232219261079SEd Maste 
23231323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
23244f52dfbbSDag-Erling Smørgrav 		return 1;
23254f52dfbbSDag-Erling Smørgrav 
2326a04a10f8SKris Kennaway 	len = read(c->efd, buf, sizeof(buf));
23274f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
232819261079SEd Maste 	if (len == -1 && (errno == EINTR || ((errno == EAGAIN ||
232919261079SEd Maste 	    errno == EWOULDBLOCK) && !force)))
2330ca3176e7SBrian Feldman 		return 1;
2331ca3176e7SBrian Feldman 	if (len <= 0) {
233219261079SEd Maste 		debug2("channel %d: closing read-efd %d", c->self, c->efd);
233319261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2334f374ba41SEd Maste 		return 1;
2335f374ba41SEd Maste 	}
2336*a91a2465SEd Maste 	channel_set_used_time(ssh, c);
2337f374ba41SEd Maste 	if (c->extended_usage == CHAN_EXTENDED_IGNORE)
233819261079SEd Maste 		debug3("channel %d: discard efd", c->self);
233919261079SEd Maste 	else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
234019261079SEd Maste 		fatal_fr(r, "channel %i: append", c->self);
2341a04a10f8SKris Kennaway 	return 1;
2342a04a10f8SKris Kennaway }
2343333ee039SDag-Erling Smørgrav 
234421e764dfSDag-Erling Smørgrav static int
23451323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c)
2346a04a10f8SKris Kennaway {
23474f52dfbbSDag-Erling Smørgrav 	if (c->efd == -1)
23484f52dfbbSDag-Erling Smørgrav 		return 1;
23494f52dfbbSDag-Erling Smørgrav 
23504f52dfbbSDag-Erling Smørgrav 	/** XXX handle drain efd, too */
23514f52dfbbSDag-Erling Smørgrav 
23524f52dfbbSDag-Erling Smørgrav 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
23531323ec57SEd Maste 		return channel_handle_efd_write(ssh, c);
23544f52dfbbSDag-Erling Smørgrav 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
23554f52dfbbSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
23561323ec57SEd Maste 		return channel_handle_efd_read(ssh, c);
23574f52dfbbSDag-Erling Smørgrav 
23584f52dfbbSDag-Erling Smørgrav 	return 1;
23594f52dfbbSDag-Erling Smørgrav }
23604f52dfbbSDag-Erling Smørgrav 
23614f52dfbbSDag-Erling Smørgrav static int
23624f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c)
23634f52dfbbSDag-Erling Smørgrav {
23644f52dfbbSDag-Erling Smørgrav 	int r;
23654f52dfbbSDag-Erling Smørgrav 
2366ca3176e7SBrian Feldman 	if (c->type == SSH_CHANNEL_OPEN &&
2367ca3176e7SBrian Feldman 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2368d4af9e69SDag-Erling Smørgrav 	    ((c->local_window_max - c->local_window >
2369d4af9e69SDag-Erling Smørgrav 	    c->local_maxpacket*3) ||
2370d4af9e69SDag-Erling Smørgrav 	    c->local_window < c->local_window_max/2) &&
2371a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
23724f52dfbbSDag-Erling Smørgrav 		if (!c->have_remote_id)
237319261079SEd Maste 			fatal_f("channel %d: no remote id", c->self);
23744f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh,
23754f52dfbbSDag-Erling Smørgrav 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
23764f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
23774f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
23784f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
237919261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
23804f52dfbbSDag-Erling Smørgrav 		}
238119261079SEd Maste 		debug2("channel %d: window %d sent adjust %d", c->self,
238219261079SEd Maste 		    c->local_window, c->local_consumed);
238360c59fadSDag-Erling Smørgrav 		c->local_window += c->local_consumed;
2384a04a10f8SKris Kennaway 		c->local_consumed = 0;
2385a04a10f8SKris Kennaway 	}
2386a04a10f8SKris Kennaway 	return 1;
2387a04a10f8SKris Kennaway }
2388a04a10f8SKris Kennaway 
2389af12a3e7SDag-Erling Smørgrav static void
23901323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c)
2391a04a10f8SKris Kennaway {
23921323ec57SEd Maste 	channel_handle_rfd(ssh, c);
23931323ec57SEd Maste 	channel_handle_wfd(ssh, c);
23941323ec57SEd Maste 	channel_handle_efd(ssh, c);
23954f52dfbbSDag-Erling Smørgrav 	channel_check_window(ssh, c);
2396a04a10f8SKris Kennaway }
2397a04a10f8SKris Kennaway 
2398b15c8340SDag-Erling Smørgrav static u_int
23994f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need)
2400b15c8340SDag-Erling Smørgrav {
2401b15c8340SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
24024f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2403b15c8340SDag-Erling Smørgrav 	u_int rlen;
24044f52dfbbSDag-Erling Smørgrav 	int r;
2405b15c8340SDag-Erling Smørgrav 
24064f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(c->input) < need) {
24074f52dfbbSDag-Erling Smørgrav 		rlen = need - sshbuf_len(c->input);
2408ca86bcf2SDag-Erling Smørgrav 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
240919261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
24104f52dfbbSDag-Erling Smørgrav 			return sshbuf_len(c->input);
2411b15c8340SDag-Erling Smørgrav 		if (len <= 0) {
24124f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
2413b15c8340SDag-Erling Smørgrav 			    c->self, c->rfd, len);
24144f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2415b15c8340SDag-Erling Smørgrav 			return 0;
241619261079SEd Maste 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
241719261079SEd Maste 			fatal_fr(r, "channel %i: append", c->self);
24184f52dfbbSDag-Erling Smørgrav 	}
24194f52dfbbSDag-Erling Smørgrav 	return sshbuf_len(c->input);
2420b15c8340SDag-Erling Smørgrav }
2421b15c8340SDag-Erling Smørgrav 
2422b15c8340SDag-Erling Smørgrav static void
24231323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c)
2424b15c8340SDag-Erling Smørgrav {
2425b15c8340SDag-Erling Smørgrav 	u_int need;
2426b15c8340SDag-Erling Smørgrav 
24271323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
24284f52dfbbSDag-Erling Smørgrav 		return;
24294f52dfbbSDag-Erling Smørgrav 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
24304f52dfbbSDag-Erling Smørgrav 		return;
24314f52dfbbSDag-Erling Smørgrav 	if (c->mux_pause)
24324f52dfbbSDag-Erling Smørgrav 		return;
2433b15c8340SDag-Erling Smørgrav 
2434b15c8340SDag-Erling Smørgrav 	/*
2435b15c8340SDag-Erling Smørgrav 	 * Don't not read past the precise end of packets to
2436b15c8340SDag-Erling Smørgrav 	 * avoid disrupting fd passing.
2437b15c8340SDag-Erling Smørgrav 	 */
24384f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, 4) < 4) /* read header */
2439b15c8340SDag-Erling Smørgrav 		return;
24404f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u32 */
24414f52dfbbSDag-Erling Smørgrav 	need = PEEK_U32(sshbuf_ptr(c->input));
2442b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
2443b15c8340SDag-Erling Smørgrav 	if (need > CHANNEL_MUX_MAX_PACKET) {
2444b15c8340SDag-Erling Smørgrav 		debug2("channel %d: packet too big %u > %u",
2445b15c8340SDag-Erling Smørgrav 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
24464f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
2447b15c8340SDag-Erling Smørgrav 		return;
2448b15c8340SDag-Erling Smørgrav 	}
24494f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
2450b15c8340SDag-Erling Smørgrav 		return;
24514f52dfbbSDag-Erling Smørgrav 	if (c->mux_rcb(ssh, c) != 0) {
2452b15c8340SDag-Erling Smørgrav 		debug("channel %d: mux_rcb failed", c->self);
24534f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
2454b15c8340SDag-Erling Smørgrav 		return;
2455b15c8340SDag-Erling Smørgrav 	}
2456b15c8340SDag-Erling Smørgrav }
2457b15c8340SDag-Erling Smørgrav 
2458b15c8340SDag-Erling Smørgrav static void
24591323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c)
24604f52dfbbSDag-Erling Smørgrav {
24614f52dfbbSDag-Erling Smørgrav 	ssize_t len;
24624f52dfbbSDag-Erling Smørgrav 	int r;
24634f52dfbbSDag-Erling Smørgrav 
24641323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
24651323ec57SEd Maste 		return;
24661323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
24674f52dfbbSDag-Erling Smørgrav 		return;
24684f52dfbbSDag-Erling Smørgrav 
24694f52dfbbSDag-Erling Smørgrav 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
247019261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
24714f52dfbbSDag-Erling Smørgrav 		return;
24724f52dfbbSDag-Erling Smørgrav 	if (len <= 0) {
24734f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
24744f52dfbbSDag-Erling Smørgrav 		return;
24754f52dfbbSDag-Erling Smørgrav 	}
24764f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0)
247719261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
24784f52dfbbSDag-Erling Smørgrav }
24794f52dfbbSDag-Erling Smørgrav 
24804f52dfbbSDag-Erling Smørgrav static void
24811323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c)
24824f52dfbbSDag-Erling Smørgrav {
24831323ec57SEd Maste 	channel_post_mux_client_read(ssh, c);
24841323ec57SEd Maste 	channel_post_mux_client_write(ssh, c);
24854f52dfbbSDag-Erling Smørgrav }
24864f52dfbbSDag-Erling Smørgrav 
24874f52dfbbSDag-Erling Smørgrav static void
24881323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c)
2489b15c8340SDag-Erling Smørgrav {
2490b15c8340SDag-Erling Smørgrav 	Channel *nc;
2491b15c8340SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2492b15c8340SDag-Erling Smørgrav 	socklen_t addrlen;
2493b15c8340SDag-Erling Smørgrav 	int newsock;
2494b15c8340SDag-Erling Smørgrav 	uid_t euid;
2495b15c8340SDag-Erling Smørgrav 	gid_t egid;
2496b15c8340SDag-Erling Smørgrav 
24971323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
2498b15c8340SDag-Erling Smørgrav 		return;
2499b15c8340SDag-Erling Smørgrav 
2500b15c8340SDag-Erling Smørgrav 	debug("multiplexing control connection");
2501b15c8340SDag-Erling Smørgrav 
2502b15c8340SDag-Erling Smørgrav 	/*
2503b15c8340SDag-Erling Smørgrav 	 * Accept connection on control socket
2504b15c8340SDag-Erling Smørgrav 	 */
2505b15c8340SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
2506b15c8340SDag-Erling Smørgrav 	addrlen = sizeof(addr);
2507b15c8340SDag-Erling Smørgrav 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
2508b15c8340SDag-Erling Smørgrav 	    &addrlen)) == -1) {
250919261079SEd Maste 		error_f("accept: %s", strerror(errno));
2510462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2511e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2512b15c8340SDag-Erling Smørgrav 		return;
2513b15c8340SDag-Erling Smørgrav 	}
2514b15c8340SDag-Erling Smørgrav 
251519261079SEd Maste 	if (getpeereid(newsock, &euid, &egid) == -1) {
251619261079SEd Maste 		error_f("getpeereid failed: %s", strerror(errno));
2517b15c8340SDag-Erling Smørgrav 		close(newsock);
2518b15c8340SDag-Erling Smørgrav 		return;
2519b15c8340SDag-Erling Smørgrav 	}
2520b15c8340SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
2521b15c8340SDag-Erling Smørgrav 		error("multiplex uid mismatch: peer euid %u != uid %u",
2522b15c8340SDag-Erling Smørgrav 		    (u_int)euid, (u_int)getuid());
2523b15c8340SDag-Erling Smørgrav 		close(newsock);
2524b15c8340SDag-Erling Smørgrav 		return;
2525b15c8340SDag-Erling Smørgrav 	}
2526f374ba41SEd Maste 	nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT,
2527b15c8340SDag-Erling Smørgrav 	    newsock, newsock, -1, c->local_window_max,
2528b15c8340SDag-Erling Smørgrav 	    c->local_maxpacket, 0, "mux-control", 1);
2529b15c8340SDag-Erling Smørgrav 	nc->mux_rcb = c->mux_rcb;
253019261079SEd Maste 	debug3_f("new mux channel %d fd %d", nc->self, nc->sock);
2531b15c8340SDag-Erling Smørgrav 	/* establish state */
25324f52dfbbSDag-Erling Smørgrav 	nc->mux_rcb(ssh, nc);
2533b15c8340SDag-Erling Smørgrav 	/* mux state transitions must not elicit protocol messages */
2534b15c8340SDag-Erling Smørgrav 	nc->flags |= CHAN_LOCAL;
2535b15c8340SDag-Erling Smørgrav }
2536b15c8340SDag-Erling Smørgrav 
2537af12a3e7SDag-Erling Smørgrav static void
25384f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc)
2539a04a10f8SKris Kennaway {
25404f52dfbbSDag-Erling Smørgrav 	chan_fn **pre, **post;
2541f388f5efSDag-Erling Smørgrav 
25424f52dfbbSDag-Erling Smørgrav 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
25434f52dfbbSDag-Erling Smørgrav 	    (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
254419261079SEd Maste 		fatal_f("allocation failed");
2545511b41d2SMark Murray 
25464f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
25474f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
25484f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
25494f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
25504f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
25514f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
25524f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
25534f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
25544f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
25554f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
25564f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
25574f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
25584f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2559a04a10f8SKris Kennaway 
25604f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
25614f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
25624f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
25634f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
25644f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
25654f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
25664f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
25674f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
25684f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
25694f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
25704f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
25714f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2572a04a10f8SKris Kennaway 
25734f52dfbbSDag-Erling Smørgrav 	sc->channel_pre = pre;
25744f52dfbbSDag-Erling Smørgrav 	sc->channel_post = post;
2575a04a10f8SKris Kennaway }
2576a04a10f8SKris Kennaway 
2577af12a3e7SDag-Erling Smørgrav /* gc dead channels */
2578af12a3e7SDag-Erling Smørgrav static void
25794f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c)
2580af12a3e7SDag-Erling Smørgrav {
2581af12a3e7SDag-Erling Smørgrav 	if (c == NULL)
2582af12a3e7SDag-Erling Smørgrav 		return;
2583af12a3e7SDag-Erling Smørgrav 	if (c->detach_user != NULL) {
25844f52dfbbSDag-Erling Smørgrav 		if (!chan_is_dead(ssh, c, c->detach_close))
2585af12a3e7SDag-Erling Smørgrav 			return;
25862f513db7SEd Maste 
2587221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: notify user", c->self);
2588f374ba41SEd Maste 		c->detach_user(ssh, c->self, 0, NULL);
2589af12a3e7SDag-Erling Smørgrav 		/* if we still have a callback */
2590af12a3e7SDag-Erling Smørgrav 		if (c->detach_user != NULL)
2591af12a3e7SDag-Erling Smørgrav 			return;
2592221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: user detached", c->self);
2593af12a3e7SDag-Erling Smørgrav 	}
25944f52dfbbSDag-Erling Smørgrav 	if (!chan_is_dead(ssh, c, 1))
2595af12a3e7SDag-Erling Smørgrav 		return;
2596221552e4SDag-Erling Smørgrav 	debug2("channel %d: garbage collecting", c->self);
25974f52dfbbSDag-Erling Smørgrav 	channel_free(ssh, c);
2598af12a3e7SDag-Erling Smørgrav }
2599af12a3e7SDag-Erling Smørgrav 
26004f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST };
26014f52dfbbSDag-Erling Smørgrav 
2602af12a3e7SDag-Erling Smørgrav static void
2603f374ba41SEd Maste channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
2604a04a10f8SKris Kennaway {
26054f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
26064f52dfbbSDag-Erling Smørgrav 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
2607b15c8340SDag-Erling Smørgrav 	u_int i, oalloc;
2608a04a10f8SKris Kennaway 	Channel *c;
2609462c32cbSDag-Erling Smørgrav 	time_t now;
2610a04a10f8SKris Kennaway 
2611e4a9863fSDag-Erling Smørgrav 	now = monotime();
26124f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26134f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2614af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2615511b41d2SMark Murray 			continue;
261638a52bd3SEd Maste 		/* Try to keep IO going while rekeying */
261738a52bd3SEd Maste 		if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN)
261838a52bd3SEd Maste 			continue;
2619b15c8340SDag-Erling Smørgrav 		if (c->delayed) {
26204f52dfbbSDag-Erling Smørgrav 			if (table == CHAN_PRE)
2621b15c8340SDag-Erling Smørgrav 				c->delayed = 0;
2622b15c8340SDag-Erling Smørgrav 			else
2623b15c8340SDag-Erling Smørgrav 				continue;
2624b15c8340SDag-Erling Smørgrav 		}
2625462c32cbSDag-Erling Smørgrav 		if (ftab[c->type] != NULL) {
2626*a91a2465SEd Maste 			if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN &&
2627*a91a2465SEd Maste 			    channel_get_expiry(ssh, c) != 0 &&
2628*a91a2465SEd Maste 			    now >= channel_get_expiry(ssh, c)) {
2629f374ba41SEd Maste 				/* channel closed for inactivity */
2630f374ba41SEd Maste 				verbose("channel %d: closing after %u seconds "
2631f374ba41SEd Maste 				    "of inactivity", c->self,
2632f374ba41SEd Maste 				    c->inactive_deadline);
2633f374ba41SEd Maste 				channel_force_close(ssh, c, 1);
2634f374ba41SEd Maste 			} else if (c->notbefore <= now) {
2635f374ba41SEd Maste 				/* Run handlers that are not paused. */
26361323ec57SEd Maste 				(*ftab[c->type])(ssh, c);
2637f374ba41SEd Maste 				/* inactivity timeouts must interrupt poll() */
2638f374ba41SEd Maste 				if (timeout != NULL &&
2639f374ba41SEd Maste 				    c->type == SSH_CHANNEL_OPEN &&
2640*a91a2465SEd Maste 				    channel_get_expiry(ssh, c) != 0) {
2641f374ba41SEd Maste 					ptimeout_deadline_monotime(timeout,
2642*a91a2465SEd Maste 					    channel_get_expiry(ssh, c));
2643f374ba41SEd Maste 				}
2644f374ba41SEd Maste 			} else if (timeout != NULL) {
2645462c32cbSDag-Erling Smørgrav 				/*
2646f374ba41SEd Maste 				 * Arrange for poll() wakeup when channel pause
2647f374ba41SEd Maste 				 * timer expires.
2648462c32cbSDag-Erling Smørgrav 				 */
2649f374ba41SEd Maste 				ptimeout_deadline_monotime(timeout,
2650f374ba41SEd Maste 				    c->notbefore);
2651462c32cbSDag-Erling Smørgrav 			}
2652462c32cbSDag-Erling Smørgrav 		}
26534f52dfbbSDag-Erling Smørgrav 		channel_garbage_collect(ssh, c);
2654511b41d2SMark Murray 	}
2655511b41d2SMark Murray }
2656a04a10f8SKris Kennaway 
2657af12a3e7SDag-Erling Smørgrav /*
26581323ec57SEd Maste  * Create sockets before preparing IO.
26594f52dfbbSDag-Erling Smørgrav  * This is necessary for things that need to happen after reading
26601323ec57SEd Maste  * the network-input but need to be completed before IO event setup, e.g.
26611323ec57SEd Maste  * because they may create new channels.
26624f52dfbbSDag-Erling Smørgrav  */
26634f52dfbbSDag-Erling Smørgrav static void
26641323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh)
26654f52dfbbSDag-Erling Smørgrav {
26664f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
26674f52dfbbSDag-Erling Smørgrav 	Channel *c;
26684f52dfbbSDag-Erling Smørgrav 	u_int i, oalloc;
26694f52dfbbSDag-Erling Smørgrav 
26704f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26714f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
26724f52dfbbSDag-Erling Smørgrav 		if (c == NULL)
26734f52dfbbSDag-Erling Smørgrav 			continue;
26744f52dfbbSDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
26751323ec57SEd Maste 			channel_before_prepare_io_rdynamic(ssh, c);
26764f52dfbbSDag-Erling Smørgrav 	}
26774f52dfbbSDag-Erling Smørgrav }
26784f52dfbbSDag-Erling Smørgrav 
26791323ec57SEd Maste static void
26801323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c,
26811323ec57SEd Maste     u_int pollfd_offset, struct pollfd *pfd)
2682a04a10f8SKris Kennaway {
26831323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
268487c1498dSEd Maste 	debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] "
268587c1498dSEd Maste 	    "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d "
268687c1498dSEd Maste 	    "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what,
268787c1498dSEd Maste 	    c->rfd, c->wfd, c->efd, c->sock,
268887c1498dSEd Maste 	    c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3],
268987c1498dSEd Maste 	    c->io_want, c->io_ready,
269087c1498dSEd Maste 	    pollfd_offset, pfd->fd, pfd->events, pfd->revents);
26911323ec57SEd Maste #endif
2692ca3176e7SBrian Feldman }
2693ca3176e7SBrian Feldman 
26941323ec57SEd Maste /* Prepare pollfd entries for a single channel */
26951323ec57SEd Maste static void
26961323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd,
26971323ec57SEd Maste     struct pollfd *pfd, u_int npfd)
26981323ec57SEd Maste {
269987c1498dSEd Maste 	u_int ev, p = *next_pollfd;
27001323ec57SEd Maste 
27011323ec57SEd Maste 	if (c == NULL)
27021323ec57SEd Maste 		return;
27031323ec57SEd Maste 	if (p + 4 > npfd) {
27041323ec57SEd Maste 		/* Shouldn't happen */
27051323ec57SEd Maste 		fatal_f("channel %d: bad pfd offset %u (max %u)",
27061323ec57SEd Maste 		    c->self, p, npfd);
27071323ec57SEd Maste 	}
270887c1498dSEd Maste 	c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1;
27091323ec57SEd Maste 	/*
27101323ec57SEd Maste 	 * prepare c->rfd
27111323ec57SEd Maste 	 *
27121323ec57SEd Maste 	 * This is a special case, since c->rfd might be the same as
27131323ec57SEd Maste 	 * c->wfd, c->efd and/or c->sock. Handle those here if they want
27141323ec57SEd Maste 	 * IO too.
27151323ec57SEd Maste 	 */
27161323ec57SEd Maste 	if (c->rfd != -1) {
271787c1498dSEd Maste 		ev = 0;
27181323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_RFD) != 0)
271987c1498dSEd Maste 			ev |= POLLIN;
27201323ec57SEd Maste 		/* rfd == wfd */
272187c1498dSEd Maste 		if (c->wfd == c->rfd) {
272287c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_WFD) != 0)
272387c1498dSEd Maste 				ev |= POLLOUT;
272487c1498dSEd Maste 		}
27251323ec57SEd Maste 		/* rfd == efd */
272687c1498dSEd Maste 		if (c->efd == c->rfd) {
272787c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
272887c1498dSEd Maste 				ev |= POLLIN;
272987c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
273087c1498dSEd Maste 				ev |= POLLOUT;
273187c1498dSEd Maste 		}
27321323ec57SEd Maste 		/* rfd == sock */
273387c1498dSEd Maste 		if (c->sock == c->rfd) {
273487c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
273587c1498dSEd Maste 				ev |= POLLIN;
273687c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
273787c1498dSEd Maste 				ev |= POLLOUT;
273887c1498dSEd Maste 		}
273987c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
274087c1498dSEd Maste 		if (ev != 0) {
274187c1498dSEd Maste 			c->pfds[0] = p;
274287c1498dSEd Maste 			pfd[p].fd = c->rfd;
274387c1498dSEd Maste 			pfd[p].events = ev;
27441323ec57SEd Maste 			dump_channel_poll(__func__, "rfd", c, p, &pfd[p]);
27451323ec57SEd Maste 			p++;
27461323ec57SEd Maste 		}
274787c1498dSEd Maste 	}
274887c1498dSEd Maste 	/* prepare c->wfd if wanting IO and not already handled above */
27491323ec57SEd Maste 	if (c->wfd != -1 && c->rfd != c->wfd) {
275087c1498dSEd Maste 		ev = 0;
275187c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_WFD))
275287c1498dSEd Maste 			ev |= POLLOUT;
275387c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
275487c1498dSEd Maste 		if (ev != 0) {
275587c1498dSEd Maste 			c->pfds[1] = p;
27561323ec57SEd Maste 			pfd[p].fd = c->wfd;
275787c1498dSEd Maste 			pfd[p].events = ev;
27581323ec57SEd Maste 			dump_channel_poll(__func__, "wfd", c, p, &pfd[p]);
27591323ec57SEd Maste 			p++;
27601323ec57SEd Maste 		}
276187c1498dSEd Maste 	}
276287c1498dSEd Maste 	/* prepare c->efd if wanting IO and not already handled above */
27631323ec57SEd Maste 	if (c->efd != -1 && c->rfd != c->efd) {
276487c1498dSEd Maste 		ev = 0;
27651323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
276687c1498dSEd Maste 			ev |= POLLIN;
27671323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
276887c1498dSEd Maste 			ev |= POLLOUT;
276987c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
277087c1498dSEd Maste 		if (ev != 0) {
277187c1498dSEd Maste 			c->pfds[2] = p;
277287c1498dSEd Maste 			pfd[p].fd = c->efd;
277387c1498dSEd Maste 			pfd[p].events = ev;
27741323ec57SEd Maste 			dump_channel_poll(__func__, "efd", c, p, &pfd[p]);
27751323ec57SEd Maste 			p++;
27761323ec57SEd Maste 		}
277787c1498dSEd Maste 	}
277887c1498dSEd Maste 	/* prepare c->sock if wanting IO and not already handled above */
27791323ec57SEd Maste 	if (c->sock != -1 && c->rfd != c->sock) {
278087c1498dSEd Maste 		ev = 0;
278187c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
278287c1498dSEd Maste 			ev |= POLLIN;
278387c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
278487c1498dSEd Maste 			ev |= POLLOUT;
278587c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
278687c1498dSEd Maste 		if (ev != 0) {
278787c1498dSEd Maste 			c->pfds[3] = p;
27881323ec57SEd Maste 			pfd[p].fd = c->sock;
27891323ec57SEd Maste 			pfd[p].events = 0;
27901323ec57SEd Maste 			dump_channel_poll(__func__, "sock", c, p, &pfd[p]);
27911323ec57SEd Maste 			p++;
27921323ec57SEd Maste 		}
279387c1498dSEd Maste 	}
27941323ec57SEd Maste 	*next_pollfd = p;
27951323ec57SEd Maste }
27961323ec57SEd Maste 
27971323ec57SEd Maste /* * Allocate/prepare poll structure */
27981323ec57SEd Maste void
27991323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
2800f374ba41SEd Maste     u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout)
28011323ec57SEd Maste {
28021323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
28031323ec57SEd Maste 	u_int i, oalloc, p, npfd = npfd_reserved;
28041323ec57SEd Maste 
28051323ec57SEd Maste 	channel_before_prepare_io(ssh); /* might create a new channel */
28069fce8d41SEd Maste 	/* clear out I/O flags from last poll */
28079fce8d41SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
28089fce8d41SEd Maste 		if (sc->channels[i] == NULL)
28099fce8d41SEd Maste 			continue;
28109fce8d41SEd Maste 		sc->channels[i]->io_want = sc->channels[i]->io_ready = 0;
28119fce8d41SEd Maste 	}
28121323ec57SEd Maste 	/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */
28131323ec57SEd Maste 	if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved)
28141323ec57SEd Maste 		fatal_f("too many channels"); /* shouldn't happen */
28151323ec57SEd Maste 	npfd += sc->channels_alloc * 4;
28161323ec57SEd Maste 	if (npfd > *npfd_allocp) {
28171323ec57SEd Maste 		*pfdp = xrecallocarray(*pfdp, *npfd_allocp,
28181323ec57SEd Maste 		    npfd, sizeof(**pfdp));
28191323ec57SEd Maste 		*npfd_allocp = npfd;
28201323ec57SEd Maste 	}
28211323ec57SEd Maste 	*npfd_activep = npfd_reserved;
28221323ec57SEd Maste 	oalloc = sc->channels_alloc;
28231323ec57SEd Maste 
2824f374ba41SEd Maste 	channel_handler(ssh, CHAN_PRE, timeout);
28251323ec57SEd Maste 
28261323ec57SEd Maste 	if (oalloc != sc->channels_alloc) {
28271323ec57SEd Maste 		/* shouldn't happen */
28281323ec57SEd Maste 		fatal_f("channels_alloc changed during CHAN_PRE "
28291323ec57SEd Maste 		    "(was %u, now %u)", oalloc, sc->channels_alloc);
28301323ec57SEd Maste 	}
28311323ec57SEd Maste 
28321323ec57SEd Maste 	/* Prepare pollfd */
28331323ec57SEd Maste 	p = npfd_reserved;
28341323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
28351323ec57SEd Maste 		channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
28361323ec57SEd Maste 	*npfd_activep = p;
28371323ec57SEd Maste }
28381323ec57SEd Maste 
28391323ec57SEd Maste static void
284087c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd,
28411323ec57SEd Maste     const char *what, u_int revents_mask, u_int ready)
28421323ec57SEd Maste {
28431323ec57SEd Maste 	struct pollfd *pfd = &pfds[p];
28441323ec57SEd Maste 
28451323ec57SEd Maste 	if (fd == -1)
28461323ec57SEd Maste 		return;
284787c1498dSEd Maste 	if (p == -1 || (u_int)p >= npfd)
284887c1498dSEd Maste 		fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd);
28491323ec57SEd Maste 	dump_channel_poll(__func__, what, c, p, pfd);
28501323ec57SEd Maste 	if (pfd->fd != fd) {
28511323ec57SEd Maste 		fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
28521323ec57SEd Maste 		    "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
28531323ec57SEd Maste 		    c->rfd, c->wfd, c->efd, c->sock);
28541323ec57SEd Maste 	}
28551323ec57SEd Maste 	if ((pfd->revents & POLLNVAL) != 0) {
28561323ec57SEd Maste 		fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d",
28571323ec57SEd Maste 		    c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock);
28581323ec57SEd Maste 	}
28591323ec57SEd Maste 	if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0)
28601323ec57SEd Maste 		c->io_ready |= ready & c->io_want;
2861a04a10f8SKris Kennaway }
2862a04a10f8SKris Kennaway 
2863af12a3e7SDag-Erling Smørgrav /*
28641323ec57SEd Maste  * After poll, perform any appropriate operations for channels which have
2865af12a3e7SDag-Erling Smørgrav  * events pending.
2866af12a3e7SDag-Erling Smørgrav  */
2867a04a10f8SKris Kennaway void
28681323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
2869a04a10f8SKris Kennaway {
28701323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
287187c1498dSEd Maste 	u_int i;
287287c1498dSEd Maste 	int p;
28731323ec57SEd Maste 	Channel *c;
28741323ec57SEd Maste 
28751323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
287687c1498dSEd Maste 	for (p = 0; p < (int)npfd; p++) {
28771323ec57SEd Maste 		if (pfd[p].revents == 0)
28781323ec57SEd Maste 			continue;
28791323ec57SEd Maste 		debug_f("pfd[%u].fd %d rev 0x%04x",
28801323ec57SEd Maste 		    p, pfd[p].fd, pfd[p].revents);
28811323ec57SEd Maste 	}
28821323ec57SEd Maste #endif
28831323ec57SEd Maste 
28841323ec57SEd Maste 	/* Convert pollfd into c->io_ready */
28851323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
28861323ec57SEd Maste 		c = sc->channels[i];
288787c1498dSEd Maste 		if (c == NULL)
28881323ec57SEd Maste 			continue;
28891323ec57SEd Maste 		/* if rfd is shared with efd/sock then wfd should be too */
28901323ec57SEd Maste 		if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd &&
28911323ec57SEd Maste 		    (c->rfd == c->efd || c->rfd == c->sock)) {
28921323ec57SEd Maste 			/* Shouldn't happen */
28931323ec57SEd Maste 			fatal_f("channel %d: unexpected fds r%d w%d e%d s%d",
28941323ec57SEd Maste 			    c->self, c->rfd, c->wfd, c->efd, c->sock);
28951323ec57SEd Maste 		}
28961323ec57SEd Maste 		c->io_ready = 0;
28971323ec57SEd Maste 		/* rfd, potentially shared with wfd, efd and sock */
289887c1498dSEd Maste 		if (c->rfd != -1 && (p = c->pfds[0]) != -1) {
289987c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->rfd,
290087c1498dSEd Maste 			    "rfd", POLLIN, SSH_CHAN_IO_RFD);
29011323ec57SEd Maste 			if (c->rfd == c->wfd) {
290287c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->wfd,
290387c1498dSEd Maste 				    "wfd/r", POLLOUT, SSH_CHAN_IO_WFD);
29041323ec57SEd Maste 			}
29051323ec57SEd Maste 			if (c->rfd == c->efd) {
290687c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
290787c1498dSEd Maste 				    "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R);
290887c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
290987c1498dSEd Maste 				    "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W);
29101323ec57SEd Maste 			}
29111323ec57SEd Maste 			if (c->rfd == c->sock) {
291287c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
291387c1498dSEd Maste 				    "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R);
291487c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
291587c1498dSEd Maste 				    "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W);
29161323ec57SEd Maste 			}
291787c1498dSEd Maste 			dump_channel_poll(__func__, "rfd", c, p, pfd);
29181323ec57SEd Maste 		}
29191323ec57SEd Maste 		/* wfd */
292087c1498dSEd Maste 		if (c->wfd != -1 && c->wfd != c->rfd &&
292187c1498dSEd Maste 		    (p = c->pfds[1]) != -1) {
292287c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->wfd,
292387c1498dSEd Maste 			    "wfd", POLLOUT, SSH_CHAN_IO_WFD);
292487c1498dSEd Maste 			dump_channel_poll(__func__, "wfd", c, p, pfd);
29251323ec57SEd Maste 		}
29261323ec57SEd Maste 		/* efd */
292787c1498dSEd Maste 		if (c->efd != -1 && c->efd != c->rfd &&
292887c1498dSEd Maste 		    (p = c->pfds[2]) != -1) {
292987c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
293087c1498dSEd Maste 			    "efdr", POLLIN, SSH_CHAN_IO_EFD_R);
293187c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
293287c1498dSEd Maste 			    "efdw", POLLOUT, SSH_CHAN_IO_EFD_W);
293387c1498dSEd Maste 			dump_channel_poll(__func__, "efd", c, p, pfd);
29341323ec57SEd Maste 		}
29351323ec57SEd Maste 		/* sock */
293687c1498dSEd Maste 		if (c->sock != -1 && c->sock != c->rfd &&
293787c1498dSEd Maste 		    (p = c->pfds[3]) != -1) {
293887c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
293987c1498dSEd Maste 			    "sockr", POLLIN, SSH_CHAN_IO_SOCK_R);
294087c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
294187c1498dSEd Maste 			    "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W);
294287c1498dSEd Maste 			dump_channel_poll(__func__, "sock", c, p, pfd);
29431323ec57SEd Maste 		}
29441323ec57SEd Maste 	}
29451323ec57SEd Maste 	channel_handler(ssh, CHAN_POST, NULL);
2946511b41d2SMark Murray }
2947511b41d2SMark Murray 
29484f52dfbbSDag-Erling Smørgrav /*
29494f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open or draining c->input.
2950edf85781SEd Maste  * Returns non-zero if a packet was enqueued.
29514f52dfbbSDag-Erling Smørgrav  */
2952edf85781SEd Maste static int
29534f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c)
29544f52dfbbSDag-Erling Smørgrav {
29554f52dfbbSDag-Erling Smørgrav 	size_t len, plen;
29564f52dfbbSDag-Erling Smørgrav 	const u_char *pkt;
29574f52dfbbSDag-Erling Smørgrav 	int r;
29584f52dfbbSDag-Erling Smørgrav 
29594f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->input)) == 0) {
29604f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
29614f52dfbbSDag-Erling Smørgrav 			/*
29624f52dfbbSDag-Erling Smørgrav 			 * input-buffer is empty and read-socket shutdown:
29634f52dfbbSDag-Erling Smørgrav 			 * tell peer, that we will not send more data:
29644f52dfbbSDag-Erling Smørgrav 			 * send IEOF.
29654f52dfbbSDag-Erling Smørgrav 			 * hack for extended data: delay EOF if EFD still
29664f52dfbbSDag-Erling Smørgrav 			 * in use.
29674f52dfbbSDag-Erling Smørgrav 			 */
29684f52dfbbSDag-Erling Smørgrav 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
29694f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
29704f52dfbbSDag-Erling Smørgrav 				    "ibuf_empty delayed efd %d/(%zu)",
29714f52dfbbSDag-Erling Smørgrav 				    c->self, c->efd, sshbuf_len(c->extended));
29724f52dfbbSDag-Erling Smørgrav 			else
29734f52dfbbSDag-Erling Smørgrav 				chan_ibuf_empty(ssh, c);
29744f52dfbbSDag-Erling Smørgrav 		}
2975edf85781SEd Maste 		return 0;
29764f52dfbbSDag-Erling Smørgrav 	}
29774f52dfbbSDag-Erling Smørgrav 
29784f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
297919261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
29804f52dfbbSDag-Erling Smørgrav 
29814f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
29824f52dfbbSDag-Erling Smørgrav 		/* Check datagram will fit; drop if not */
29834f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
298419261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
29854f52dfbbSDag-Erling Smørgrav 		/*
29864f52dfbbSDag-Erling Smørgrav 		 * XXX this does tail-drop on the datagram queue which is
29874f52dfbbSDag-Erling Smørgrav 		 * usually suboptimal compared to head-drop. Better to have
29884f52dfbbSDag-Erling Smørgrav 		 * backpressure at read time? (i.e. read + discard)
29894f52dfbbSDag-Erling Smørgrav 		 */
29904f52dfbbSDag-Erling Smørgrav 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
29914f52dfbbSDag-Erling Smørgrav 			debug("channel %d: datagram too big", c->self);
2992edf85781SEd Maste 			return 0;
29934f52dfbbSDag-Erling Smørgrav 		}
29944f52dfbbSDag-Erling Smørgrav 		/* Enqueue it */
29954f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
29964f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29974f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
299819261079SEd Maste 		    (r = sshpkt_send(ssh)) != 0)
299919261079SEd Maste 			fatal_fr(r, "channel %i: send datagram", c->self);
30004f52dfbbSDag-Erling Smørgrav 		c->remote_window -= plen;
3001edf85781SEd Maste 		return 1;
30024f52dfbbSDag-Erling Smørgrav 	}
30034f52dfbbSDag-Erling Smørgrav 
30044f52dfbbSDag-Erling Smørgrav 	/* Enqueue packet for buffered data. */
30054f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
30064f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
30074f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
30084f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
30094f52dfbbSDag-Erling Smørgrav 	if (len == 0)
3010edf85781SEd Maste 		return 0;
30114f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
30124f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30134f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
301419261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
301519261079SEd Maste 		fatal_fr(r, "channel %i: send data", c->self);
30164f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->input, len)) != 0)
301719261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
30184f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
3019edf85781SEd Maste 	return 1;
30204f52dfbbSDag-Erling Smørgrav }
30214f52dfbbSDag-Erling Smørgrav 
30224f52dfbbSDag-Erling Smørgrav /*
30234f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open c->extended in read mode.
3024edf85781SEd Maste  * Returns non-zero if a packet was enqueued.
30254f52dfbbSDag-Erling Smørgrav  */
3026edf85781SEd Maste static int
30274f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
30284f52dfbbSDag-Erling Smørgrav {
30294f52dfbbSDag-Erling Smørgrav 	size_t len;
30304f52dfbbSDag-Erling Smørgrav 	int r;
30314f52dfbbSDag-Erling Smørgrav 
30324f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->extended)) == 0)
3033edf85781SEd Maste 		return 0;
30344f52dfbbSDag-Erling Smørgrav 
30354f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
30364f52dfbbSDag-Erling Smørgrav 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
30374f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
30384f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
30394f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
30404f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
30414f52dfbbSDag-Erling Smørgrav 	if (len == 0)
3042edf85781SEd Maste 		return 0;
30434f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
304419261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
30454f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
30464f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30474f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
30484f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
304919261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
305019261079SEd Maste 		fatal_fr(r, "channel %i: data", c->self);
30514f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->extended, len)) != 0)
305219261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
30534f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
30544f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: sent ext data %zu", c->self, len);
3055edf85781SEd Maste 	return 1;
30564f52dfbbSDag-Erling Smørgrav }
3057af12a3e7SDag-Erling Smørgrav 
3058edf85781SEd Maste /*
3059edf85781SEd Maste  * If there is data to send to the connection, enqueue some of it now.
3060edf85781SEd Maste  * Returns non-zero if data was enqueued.
3061edf85781SEd Maste  */
3062edf85781SEd Maste int
30634f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh)
3064511b41d2SMark Murray {
30654f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3066a04a10f8SKris Kennaway 	Channel *c;
30674f52dfbbSDag-Erling Smørgrav 	u_int i;
3068edf85781SEd Maste 	int ret = 0;
3069511b41d2SMark Murray 
30704f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
30714f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
3072af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
3073af12a3e7SDag-Erling Smørgrav 			continue;
3074511b41d2SMark Murray 
3075af12a3e7SDag-Erling Smørgrav 		/*
3076af12a3e7SDag-Erling Smørgrav 		 * We are only interested in channels that can have buffered
3077af12a3e7SDag-Erling Smørgrav 		 * incoming data.
3078af12a3e7SDag-Erling Smørgrav 		 */
3079a04a10f8SKris Kennaway 		if (c->type != SSH_CHANNEL_OPEN)
3080511b41d2SMark Murray 			continue;
30814f52dfbbSDag-Erling Smørgrav 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
3082ca3176e7SBrian Feldman 			/* XXX is this true? */
30834f52dfbbSDag-Erling Smørgrav 			debug3("channel %d: will not send data after close",
30844f52dfbbSDag-Erling Smørgrav 			    c->self);
3085511b41d2SMark Murray 			continue;
3086511b41d2SMark Murray 		}
3087511b41d2SMark Murray 
3088511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
30894f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_OPEN ||
30904f52dfbbSDag-Erling Smørgrav 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
3091edf85781SEd Maste 			ret |= channel_output_poll_input_open(ssh, c);
3092a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
30934f52dfbbSDag-Erling Smørgrav 		if (!(c->flags & CHAN_EOF_SENT) &&
30944f52dfbbSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_READ)
3095edf85781SEd Maste 			ret |= channel_output_poll_extended_read(ssh, c);
3096511b41d2SMark Murray 	}
3097edf85781SEd Maste 	return ret;
3098511b41d2SMark Murray }
3099511b41d2SMark Murray 
3100ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support  */
3101ca86bcf2SDag-Erling Smørgrav 
3102ca86bcf2SDag-Erling Smørgrav /*
3103ca86bcf2SDag-Erling Smørgrav  * When multiplexing channel messages for mux clients we have to deal
3104ca86bcf2SDag-Erling Smørgrav  * with downstream messages from the mux client and upstream messages
3105ca86bcf2SDag-Erling Smørgrav  * from the ssh server:
3106ca86bcf2SDag-Erling Smørgrav  * 1) Handling downstream messages is straightforward and happens
3107ca86bcf2SDag-Erling Smørgrav  *    in channel_proxy_downstream():
3108ca86bcf2SDag-Erling Smørgrav  *    - We forward all messages (mostly) unmodified to the server.
3109ca86bcf2SDag-Erling Smørgrav  *    - However, in order to route messages from upstream to the correct
3110ca86bcf2SDag-Erling Smørgrav  *      downstream client, we have to replace the channel IDs used by the
3111ca86bcf2SDag-Erling Smørgrav  *      mux clients with a unique channel ID because the mux clients might
3112ca86bcf2SDag-Erling Smørgrav  *      use conflicting channel IDs.
3113ca86bcf2SDag-Erling Smørgrav  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
3114ca86bcf2SDag-Erling Smørgrav  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
3115ca86bcf2SDag-Erling Smørgrav  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
3116ca86bcf2SDag-Erling Smørgrav  *      with the newly allocated channel ID.
3117ca86bcf2SDag-Erling Smørgrav  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
3118190cef3dSDag-Erling Smørgrav  *    channels and processed by channel_proxy_upstream(). The local channel ID
3119ca86bcf2SDag-Erling Smørgrav  *    is then translated back to the original mux client ID.
3120ca86bcf2SDag-Erling Smørgrav  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
3121ca86bcf2SDag-Erling Smørgrav  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
3122ca86bcf2SDag-Erling Smørgrav  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
3123ca86bcf2SDag-Erling Smørgrav  *    downstream mux client are removed.
3124ca86bcf2SDag-Erling Smørgrav  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
3125ca86bcf2SDag-Erling Smørgrav  *    requires more work, because they are not addressed to a specific
3126ca86bcf2SDag-Erling Smørgrav  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
3127ca86bcf2SDag-Erling Smørgrav  *    out whether the request is addressed to the local client or a
3128ca86bcf2SDag-Erling Smørgrav  *    specific downstream client based on the listen-address/port.
3129190cef3dSDag-Erling Smørgrav  * 6) Agent and X11-Forwarding have a similar problem and are currently
3130ca86bcf2SDag-Erling Smørgrav  *    not supported as the matching session/channel cannot be identified
3131ca86bcf2SDag-Erling Smørgrav  *    easily.
3132ca86bcf2SDag-Erling Smørgrav  */
3133ca86bcf2SDag-Erling Smørgrav 
3134ca86bcf2SDag-Erling Smørgrav /*
3135ca86bcf2SDag-Erling Smørgrav  * receive packets from downstream mux clients:
3136ca86bcf2SDag-Erling Smørgrav  * channel callback fired on read from mux client, creates
3137ca86bcf2SDag-Erling Smørgrav  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
3138ca86bcf2SDag-Erling Smørgrav  * on channel creation.
3139ca86bcf2SDag-Erling Smørgrav  */
3140ca86bcf2SDag-Erling Smørgrav int
31414f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
3142ca86bcf2SDag-Erling Smørgrav {
3143ca86bcf2SDag-Erling Smørgrav 	Channel *c = NULL;
3144ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *original = NULL, *modified = NULL;
3145ca86bcf2SDag-Erling Smørgrav 	const u_char *cp;
3146ca86bcf2SDag-Erling Smørgrav 	char *ctype = NULL, *listen_host = NULL;
3147ca86bcf2SDag-Erling Smørgrav 	u_char type;
3148ca86bcf2SDag-Erling Smørgrav 	size_t have;
31494f52dfbbSDag-Erling Smørgrav 	int ret = -1, r;
3150ca86bcf2SDag-Erling Smørgrav 	u_int id, remote_id, listen_port;
3151ca86bcf2SDag-Erling Smørgrav 
31524f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(downstream->input, stderr); */
31534f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
3154ca86bcf2SDag-Erling Smørgrav 	    != 0) {
315519261079SEd Maste 		error_fr(r, "parse");
3156ca86bcf2SDag-Erling Smørgrav 		return -1;
3157ca86bcf2SDag-Erling Smørgrav 	}
3158ca86bcf2SDag-Erling Smørgrav 	if (have < 2) {
315919261079SEd Maste 		error_f("short message");
3160ca86bcf2SDag-Erling Smørgrav 		return -1;
3161ca86bcf2SDag-Erling Smørgrav 	}
3162ca86bcf2SDag-Erling Smørgrav 	type = cp[1];
3163ca86bcf2SDag-Erling Smørgrav 	/* skip padlen + type */
3164ca86bcf2SDag-Erling Smørgrav 	cp += 2;
3165ca86bcf2SDag-Erling Smørgrav 	have -= 2;
3166ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
316719261079SEd Maste 		debug3_f("channel %u: down->up: type %u",
3168ca86bcf2SDag-Erling Smørgrav 		    downstream->self, type);
3169ca86bcf2SDag-Erling Smørgrav 
3170ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3171ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN:
3172ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3173ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
317419261079SEd Maste 			error_f("alloc");
3175ca86bcf2SDag-Erling Smørgrav 			goto out;
3176ca86bcf2SDag-Erling Smørgrav 		}
3177ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
3178ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
317919261079SEd Maste 			error_fr(r, "parse");
3180ca86bcf2SDag-Erling Smørgrav 			goto out;
3181ca86bcf2SDag-Erling Smørgrav 		}
3182f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3183ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, ctype, 1);
3184ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3185ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;	/* original downstream id */
3186ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
3187ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3188ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
318919261079SEd Maste 			error_fr(r, "compose");
31904f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3191ca86bcf2SDag-Erling Smørgrav 			goto out;
3192ca86bcf2SDag-Erling Smørgrav 		}
3193ca86bcf2SDag-Erling Smørgrav 		break;
3194ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3195ca86bcf2SDag-Erling Smørgrav 		/*
3196ca86bcf2SDag-Erling Smørgrav 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
3197ca86bcf2SDag-Erling Smørgrav 		 * need to parse 'remote_id' instead of 'ctype'.
3198ca86bcf2SDag-Erling Smørgrav 		 */
3199ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3200ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
320119261079SEd Maste 			error_f("alloc");
3202ca86bcf2SDag-Erling Smørgrav 			goto out;
3203ca86bcf2SDag-Erling Smørgrav 		}
3204ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
3205ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
320619261079SEd Maste 			error_fr(r, "parse");
3207ca86bcf2SDag-Erling Smørgrav 			goto out;
3208ca86bcf2SDag-Erling Smørgrav 		}
3209f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3210ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
3211ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3212ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;
3213ca86bcf2SDag-Erling Smørgrav 		c->remote_id = remote_id;
32144f52dfbbSDag-Erling Smørgrav 		c->have_remote_id = 1;
3215ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
3216ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3217ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
321819261079SEd Maste 			error_fr(r, "compose");
32194f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3220ca86bcf2SDag-Erling Smørgrav 			goto out;
3221ca86bcf2SDag-Erling Smørgrav 		}
3222ca86bcf2SDag-Erling Smørgrav 		break;
3223ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_GLOBAL_REQUEST:
3224ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL) {
322519261079SEd Maste 			error_f("alloc");
3226ca86bcf2SDag-Erling Smørgrav 			goto out;
3227ca86bcf2SDag-Erling Smørgrav 		}
3228ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
322919261079SEd Maste 			error_fr(r, "parse");
3230ca86bcf2SDag-Erling Smørgrav 			goto out;
3231ca86bcf2SDag-Erling Smørgrav 		}
3232ca86bcf2SDag-Erling Smørgrav 		if (strcmp(ctype, "tcpip-forward") != 0) {
323319261079SEd Maste 			error_f("unsupported request %s", ctype);
3234ca86bcf2SDag-Erling Smørgrav 			goto out;
3235ca86bcf2SDag-Erling Smørgrav 		}
3236ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
3237ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
3238ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
323919261079SEd Maste 			error_fr(r, "parse");
3240ca86bcf2SDag-Erling Smørgrav 			goto out;
3241ca86bcf2SDag-Erling Smørgrav 		}
3242ca86bcf2SDag-Erling Smørgrav 		if (listen_port > 65535) {
324319261079SEd Maste 			error_f("tcpip-forward for %s: bad port %u",
324419261079SEd Maste 			    listen_host, listen_port);
3245ca86bcf2SDag-Erling Smørgrav 			goto out;
3246ca86bcf2SDag-Erling Smørgrav 		}
3247ca86bcf2SDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
3248*a91a2465SEd Maste 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>",
3249*a91a2465SEd Maste 		    -1, listen_host, NULL, (int)listen_port, downstream);
3250ca86bcf2SDag-Erling Smørgrav 		break;
3251ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3252ca86bcf2SDag-Erling Smørgrav 		if (have < 4)
3253ca86bcf2SDag-Erling Smørgrav 			break;
3254ca86bcf2SDag-Erling Smørgrav 		remote_id = PEEK_U32(cp);
32554f52dfbbSDag-Erling Smørgrav 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
3256ca86bcf2SDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD)
32574f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
3258ca86bcf2SDag-Erling Smørgrav 			else
3259ca86bcf2SDag-Erling Smørgrav 				c->flags |= CHAN_CLOSE_SENT;
3260ca86bcf2SDag-Erling Smørgrav 		}
3261ca86bcf2SDag-Erling Smørgrav 		break;
3262ca86bcf2SDag-Erling Smørgrav 	}
3263ca86bcf2SDag-Erling Smørgrav 	if (modified) {
3264ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3265ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
3266ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
326719261079SEd Maste 			error_fr(r, "send");
3268ca86bcf2SDag-Erling Smørgrav 			goto out;
3269ca86bcf2SDag-Erling Smørgrav 		}
3270ca86bcf2SDag-Erling Smørgrav 	} else {
3271ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3272ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
3273ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
327419261079SEd Maste 			error_fr(r, "send");
3275ca86bcf2SDag-Erling Smørgrav 			goto out;
3276ca86bcf2SDag-Erling Smørgrav 		}
3277ca86bcf2SDag-Erling Smørgrav 	}
3278ca86bcf2SDag-Erling Smørgrav 	ret = 0;
3279ca86bcf2SDag-Erling Smørgrav  out:
3280ca86bcf2SDag-Erling Smørgrav 	free(ctype);
3281ca86bcf2SDag-Erling Smørgrav 	free(listen_host);
3282ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(original);
3283ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(modified);
3284ca86bcf2SDag-Erling Smørgrav 	return ret;
3285ca86bcf2SDag-Erling Smørgrav }
3286ca86bcf2SDag-Erling Smørgrav 
3287ca86bcf2SDag-Erling Smørgrav /*
3288ca86bcf2SDag-Erling Smørgrav  * receive packets from upstream server and de-multiplex packets
3289ca86bcf2SDag-Erling Smørgrav  * to correct downstream:
3290ca86bcf2SDag-Erling Smørgrav  * implemented as a helper for channel input handlers,
3291ca86bcf2SDag-Erling Smørgrav  * replaces local (proxy) channel ID with downstream channel ID.
3292ca86bcf2SDag-Erling Smørgrav  */
3293ca86bcf2SDag-Erling Smørgrav int
32944f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
3295ca86bcf2SDag-Erling Smørgrav {
3296ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *b = NULL;
3297ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;
3298ca86bcf2SDag-Erling Smørgrav 	const u_char *cp = NULL;
3299ca86bcf2SDag-Erling Smørgrav 	size_t len;
3300ca86bcf2SDag-Erling Smørgrav 	int r;
3301ca86bcf2SDag-Erling Smørgrav 
3302ca86bcf2SDag-Erling Smørgrav 	/*
3303ca86bcf2SDag-Erling Smørgrav 	 * When receiving packets from the peer we need to check whether we
3304ca86bcf2SDag-Erling Smørgrav 	 * need to forward the packets to the mux client. In this case we
3305190cef3dSDag-Erling Smørgrav 	 * restore the original channel id and keep track of CLOSE messages,
3306ca86bcf2SDag-Erling Smørgrav 	 * so we can cleanup the channel.
3307ca86bcf2SDag-Erling Smørgrav 	 */
3308ca86bcf2SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
3309ca86bcf2SDag-Erling Smørgrav 		return 0;
3310ca86bcf2SDag-Erling Smørgrav 	if ((downstream = c->mux_ctx) == NULL)
3311ca86bcf2SDag-Erling Smørgrav 		return 0;
3312ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3313ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3314ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_DATA:
3315ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EOF:
3316ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
3317ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3318ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
3319ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3320ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_SUCCESS:
3321ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_FAILURE:
3322ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_REQUEST:
3323ca86bcf2SDag-Erling Smørgrav 		break;
3324ca86bcf2SDag-Erling Smørgrav 	default:
332519261079SEd Maste 		debug2_f("channel %u: unsupported type %u", c->self, type);
3326ca86bcf2SDag-Erling Smørgrav 		return 0;
3327ca86bcf2SDag-Erling Smørgrav 	}
3328ca86bcf2SDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
332919261079SEd Maste 		error_f("alloc reply");
3330ca86bcf2SDag-Erling Smørgrav 		goto out;
3331ca86bcf2SDag-Erling Smørgrav 	}
3332ca86bcf2SDag-Erling Smørgrav 	/* get remaining payload (after id) */
3333ca86bcf2SDag-Erling Smørgrav 	cp = sshpkt_ptr(ssh, &len);
3334ca86bcf2SDag-Erling Smørgrav 	if (cp == NULL) {
333519261079SEd Maste 		error_f("no packet");
3336ca86bcf2SDag-Erling Smørgrav 		goto out;
3337ca86bcf2SDag-Erling Smørgrav 	}
3338ca86bcf2SDag-Erling Smørgrav 	/* translate id and send to muxclient */
3339ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
3340ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u8(b, type)) != 0 ||
3341ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
3342ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put(b, cp, len)) != 0 ||
33434f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
334419261079SEd Maste 		error_fr(r, "compose muxclient");
3345ca86bcf2SDag-Erling Smørgrav 		goto out;
3346ca86bcf2SDag-Erling Smørgrav 	}
3347ca86bcf2SDag-Erling Smørgrav 	/* sshbuf_dump(b, stderr); */
3348ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
334919261079SEd Maste 		debug3_f("channel %u: up->down: type %u", c->self, type);
3350ca86bcf2SDag-Erling Smørgrav  out:
3351ca86bcf2SDag-Erling Smørgrav 	/* update state */
3352ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3353ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3354ca86bcf2SDag-Erling Smørgrav 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
33554f52dfbbSDag-Erling Smørgrav 		if (cp && len > 4) {
3356ca86bcf2SDag-Erling Smørgrav 			c->remote_id = PEEK_U32(cp);
33574f52dfbbSDag-Erling Smørgrav 			c->have_remote_id = 1;
33584f52dfbbSDag-Erling Smørgrav 		}
3359ca86bcf2SDag-Erling Smørgrav 		break;
3360ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3361ca86bcf2SDag-Erling Smørgrav 		if (c->flags & CHAN_CLOSE_SENT)
33624f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3363ca86bcf2SDag-Erling Smørgrav 		else
3364ca86bcf2SDag-Erling Smørgrav 			c->flags |= CHAN_CLOSE_RCVD;
3365ca86bcf2SDag-Erling Smørgrav 		break;
3366ca86bcf2SDag-Erling Smørgrav 	}
3367ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(b);
3368ca86bcf2SDag-Erling Smørgrav 	return 1;
3369ca86bcf2SDag-Erling Smørgrav }
3370af12a3e7SDag-Erling Smørgrav 
3371af12a3e7SDag-Erling Smørgrav /* -- protocol input */
3372511b41d2SMark Murray 
33734f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */
33744f52dfbbSDag-Erling Smørgrav static int
33754f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what)
3376511b41d2SMark Murray {
33774f52dfbbSDag-Erling Smørgrav 	u_int32_t id;
33784f52dfbbSDag-Erling Smørgrav 	int r;
33794f52dfbbSDag-Erling Smørgrav 
33804f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
338119261079SEd Maste 		error_r(r, "%s: parse id", where);
33824f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
33834f52dfbbSDag-Erling Smørgrav 	}
33844f52dfbbSDag-Erling Smørgrav 	if (id > INT_MAX) {
338519261079SEd Maste 		error_r(r, "%s: bad channel id %u", where, id);
33864f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
33874f52dfbbSDag-Erling Smørgrav 	}
33884f52dfbbSDag-Erling Smørgrav 	return (int)id;
33894f52dfbbSDag-Erling Smørgrav }
33904f52dfbbSDag-Erling Smørgrav 
33914f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */
33924f52dfbbSDag-Erling Smørgrav static Channel *
33934f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
33944f52dfbbSDag-Erling Smørgrav {
33954f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, where, what);
3396a04a10f8SKris Kennaway 	Channel *c;
3397511b41d2SMark Murray 
33984f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
33994f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh,
34004f52dfbbSDag-Erling Smørgrav 		    "%s packet referred to nonexistent channel %d", what, id);
34014f52dfbbSDag-Erling Smørgrav 	}
34024f52dfbbSDag-Erling Smørgrav 	return c;
34034f52dfbbSDag-Erling Smørgrav }
34044f52dfbbSDag-Erling Smørgrav 
34054f52dfbbSDag-Erling Smørgrav int
34064f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
34074f52dfbbSDag-Erling Smørgrav {
34084f52dfbbSDag-Erling Smørgrav 	const u_char *data;
34094f52dfbbSDag-Erling Smørgrav 	size_t data_len, win_len;
34104f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
34114f52dfbbSDag-Erling Smørgrav 	int r;
34124f52dfbbSDag-Erling Smørgrav 
34134f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3414ca86bcf2SDag-Erling Smørgrav 		return 0;
3415511b41d2SMark Murray 
3416511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
3417a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
34184f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
34194f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
3420a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
3421bc5531deSDag-Erling Smørgrav 		return 0;
3422511b41d2SMark Murray 
3423511b41d2SMark Murray 	/* Get the data. */
342419261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
342519261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0)
342619261079SEd Maste 		fatal_fr(r, "channel %i: get data", c->self);
34274f52dfbbSDag-Erling Smørgrav 
3428e2f6069cSDag-Erling Smørgrav 	win_len = data_len;
3429e2f6069cSDag-Erling Smørgrav 	if (c->datagram)
3430e2f6069cSDag-Erling Smørgrav 		win_len += 4;  /* string length header */
3431a04a10f8SKris Kennaway 
3432476cd3b2SDag-Erling Smørgrav 	/*
34334f52dfbbSDag-Erling Smørgrav 	 * The sending side reduces its window as it sends data, so we
34344f52dfbbSDag-Erling Smørgrav 	 * must 'fake' consumption of the data in order to ensure that window
34354f52dfbbSDag-Erling Smørgrav 	 * updates are sent back. Otherwise the connection might deadlock.
3436476cd3b2SDag-Erling Smørgrav 	 */
34374f52dfbbSDag-Erling Smørgrav 	if (c->ostate != CHAN_OUTPUT_OPEN) {
3438e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3439e2f6069cSDag-Erling Smørgrav 		c->local_consumed += win_len;
3440bc5531deSDag-Erling Smørgrav 		return 0;
3441476cd3b2SDag-Erling Smørgrav 	}
3442476cd3b2SDag-Erling Smørgrav 
3443e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_maxpacket) {
34444f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd big packet %zu, maxpack %u",
3445e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_maxpacket);
34464f52dfbbSDag-Erling Smørgrav 		return 0;
3447a04a10f8SKris Kennaway 	}
3448e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_window) {
3449069ac184SEd Maste 		c->local_window_exceeded += win_len - c->local_window;
3450069ac184SEd Maste 		logit("channel %d: rcvd too much data %zu, win %u/%u "
3451069ac184SEd Maste 		    "(excess %u)", c->self, win_len, c->local_window,
3452069ac184SEd Maste 		    c->local_window_max, c->local_window_exceeded);
3453069ac184SEd Maste 		c->local_window = 0;
3454069ac184SEd Maste 		/* Allow 10% grace before bringing the hammer down */
3455069ac184SEd Maste 		if (c->local_window_exceeded > (c->local_window_max / 10)) {
3456069ac184SEd Maste 			ssh_packet_disconnect(ssh, "channel %d: peer ignored "
3457069ac184SEd Maste 			    "channel window", c->self);
3458a04a10f8SKris Kennaway 		}
3459069ac184SEd Maste 	} else {
3460e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3461069ac184SEd Maste 		c->local_window_exceeded = 0;
3462069ac184SEd Maste 	}
34634f52dfbbSDag-Erling Smørgrav 
34644f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
34654f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
346619261079SEd Maste 			fatal_fr(r, "channel %i: append datagram", c->self);
34674f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
346819261079SEd Maste 		fatal_fr(r, "channel %i: append data", c->self);
34694f52dfbbSDag-Erling Smørgrav 
3470bc5531deSDag-Erling Smørgrav 	return 0;
3471511b41d2SMark Murray }
3472af12a3e7SDag-Erling Smørgrav 
3473bc5531deSDag-Erling Smørgrav int
34744f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3475a04a10f8SKris Kennaway {
34764f52dfbbSDag-Erling Smørgrav 	const u_char *data;
34774f52dfbbSDag-Erling Smørgrav 	size_t data_len;
34784f52dfbbSDag-Erling Smørgrav 	u_int32_t tcode;
34794f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
34804f52dfbbSDag-Erling Smørgrav 	int r;
3481a04a10f8SKris Kennaway 
34824f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3483ca86bcf2SDag-Erling Smørgrav 		return 0;
3484a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
34854f52dfbbSDag-Erling Smørgrav 		logit("channel %d: ext data for non open", c->self);
3486bc5531deSDag-Erling Smørgrav 		return 0;
3487a04a10f8SKris Kennaway 	}
348880628bacSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
348919261079SEd Maste 		if (ssh->compat & SSH_BUG_EXTEOF)
34904f52dfbbSDag-Erling Smørgrav 			debug("channel %d: accepting ext data after eof",
34914f52dfbbSDag-Erling Smørgrav 			    c->self);
349280628bacSDag-Erling Smørgrav 		else
34934f52dfbbSDag-Erling Smørgrav 			ssh_packet_disconnect(ssh, "Received extended_data "
34944f52dfbbSDag-Erling Smørgrav 			    "after EOF on channel %d.", c->self);
349580628bacSDag-Erling Smørgrav 	}
34964f52dfbbSDag-Erling Smørgrav 
34974f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
349819261079SEd Maste 		error_fr(r, "parse tcode");
34994f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
35004f52dfbbSDag-Erling Smørgrav 	}
3501a04a10f8SKris Kennaway 	if (c->efd == -1 ||
3502a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3503a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3504221552e4SDag-Erling Smørgrav 		logit("channel %d: bad ext data", c->self);
3505bc5531deSDag-Erling Smørgrav 		return 0;
3506a04a10f8SKris Kennaway 	}
350719261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
350819261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
350919261079SEd Maste 		error_fr(r, "parse data");
35104f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
35114f52dfbbSDag-Erling Smørgrav 	}
35124f52dfbbSDag-Erling Smørgrav 
3513a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
35144f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3515a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
3516bc5531deSDag-Erling Smørgrav 		return 0;
3517a04a10f8SKris Kennaway 	}
35184f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
35194f52dfbbSDag-Erling Smørgrav 	/* XXX sshpkt_getb? */
35204f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
352119261079SEd Maste 		error_fr(r, "append");
3522a04a10f8SKris Kennaway 	c->local_window -= data_len;
3523bc5531deSDag-Erling Smørgrav 	return 0;
3524a04a10f8SKris Kennaway }
3525a04a10f8SKris Kennaway 
3526bc5531deSDag-Erling Smørgrav int
35274f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3528a04a10f8SKris Kennaway {
35294f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
353019261079SEd Maste 	int r;
3531a04a10f8SKris Kennaway 
353219261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
353319261079SEd Maste 		error_fr(r, "parse data");
353419261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid ieof message");
353519261079SEd Maste 	}
35364f52dfbbSDag-Erling Smørgrav 
35374f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3538ca86bcf2SDag-Erling Smørgrav 		return 0;
35394f52dfbbSDag-Erling Smørgrav 	chan_rcvd_ieof(ssh, c);
3540af12a3e7SDag-Erling Smørgrav 
3541af12a3e7SDag-Erling Smørgrav 	/* XXX force input close */
3542af12a3e7SDag-Erling Smørgrav 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3543af12a3e7SDag-Erling Smørgrav 		debug("channel %d: FORCE input drain", c->self);
3544af12a3e7SDag-Erling Smørgrav 		c->istate = CHAN_INPUT_WAIT_DRAIN;
35454f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->input) == 0)
35464f52dfbbSDag-Erling Smørgrav 			chan_ibuf_empty(ssh, c);
3547af12a3e7SDag-Erling Smørgrav 	}
3548bc5531deSDag-Erling Smørgrav 	return 0;
3549a04a10f8SKris Kennaway }
3550511b41d2SMark Murray 
3551bc5531deSDag-Erling Smørgrav int
35524f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3553511b41d2SMark Murray {
35544f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
355519261079SEd Maste 	int r;
3556511b41d2SMark Murray 
35574f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3558ca86bcf2SDag-Erling Smørgrav 		return 0;
355919261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
356019261079SEd Maste 		error_fr(r, "parse data");
356119261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid oclose message");
356219261079SEd Maste 	}
35634f52dfbbSDag-Erling Smørgrav 	chan_rcvd_oclose(ssh, c);
3564bc5531deSDag-Erling Smørgrav 	return 0;
3565511b41d2SMark Murray }
3566511b41d2SMark Murray 
3567bc5531deSDag-Erling Smørgrav int
35684f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3569a04a10f8SKris Kennaway {
35704f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
35714f52dfbbSDag-Erling Smørgrav 	u_int32_t remote_window, remote_maxpacket;
35724f52dfbbSDag-Erling Smørgrav 	int r;
3573af12a3e7SDag-Erling Smørgrav 
35744f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3575ca86bcf2SDag-Erling Smørgrav 		return 0;
3576ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
357719261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open confirmation for "
35784f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
35794f52dfbbSDag-Erling Smørgrav 	/*
35804f52dfbbSDag-Erling Smørgrav 	 * Record the remote channel number and mark that the channel
35814f52dfbbSDag-Erling Smørgrav 	 * is now open.
35824f52dfbbSDag-Erling Smørgrav 	 */
35834f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
35844f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
358519261079SEd Maste 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 ||
358619261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
358719261079SEd Maste 		error_fr(r, "window/maxpacket");
358819261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open confirmation message");
35894f52dfbbSDag-Erling Smørgrav 	}
3590a04a10f8SKris Kennaway 
35914f52dfbbSDag-Erling Smørgrav 	c->have_remote_id = 1;
35924f52dfbbSDag-Erling Smørgrav 	c->remote_window = remote_window;
35934f52dfbbSDag-Erling Smørgrav 	c->remote_maxpacket = remote_maxpacket;
35944f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
3595d4af9e69SDag-Erling Smørgrav 	if (c->open_confirm) {
359619261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
35974f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
359819261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3599a04a10f8SKris Kennaway 	}
3600*a91a2465SEd Maste 	channel_set_used_time(ssh, c);
3601221552e4SDag-Erling Smørgrav 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3602a04a10f8SKris Kennaway 	    c->remote_window, c->remote_maxpacket);
3603bc5531deSDag-Erling Smørgrav 	return 0;
3604af12a3e7SDag-Erling Smørgrav }
3605af12a3e7SDag-Erling Smørgrav 
3606af12a3e7SDag-Erling Smørgrav static char *
3607af12a3e7SDag-Erling Smørgrav reason2txt(int reason)
3608af12a3e7SDag-Erling Smørgrav {
3609af12a3e7SDag-Erling Smørgrav 	switch (reason) {
3610af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3611af12a3e7SDag-Erling Smørgrav 		return "administratively prohibited";
3612af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_CONNECT_FAILED:
3613af12a3e7SDag-Erling Smørgrav 		return "connect failed";
3614af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3615af12a3e7SDag-Erling Smørgrav 		return "unknown channel type";
3616af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3617af12a3e7SDag-Erling Smørgrav 		return "resource shortage";
3618af12a3e7SDag-Erling Smørgrav 	}
3619af12a3e7SDag-Erling Smørgrav 	return "unknown reason";
3620a04a10f8SKris Kennaway }
3621a04a10f8SKris Kennaway 
3622bc5531deSDag-Erling Smørgrav int
36234f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3624a04a10f8SKris Kennaway {
36254f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
36264f52dfbbSDag-Erling Smørgrav 	u_int32_t reason;
36274f52dfbbSDag-Erling Smørgrav 	char *msg = NULL;
36284f52dfbbSDag-Erling Smørgrav 	int r;
3629a04a10f8SKris Kennaway 
36304f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3631ca86bcf2SDag-Erling Smørgrav 		return 0;
3632ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
363319261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open failure for "
36344f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
36354f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
363619261079SEd Maste 		error_fr(r, "parse reason");
363719261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
3638ca3176e7SBrian Feldman 	}
36394f52dfbbSDag-Erling Smørgrav 	/* skip language */
36404f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
364119261079SEd Maste 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 ||
364219261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
364319261079SEd Maste 		error_fr(r, "parse msg/lang");
364419261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
36454f52dfbbSDag-Erling Smørgrav 	}
36464f52dfbbSDag-Erling Smørgrav 	logit("channel %d: open failed: %s%s%s", c->self,
3647af12a3e7SDag-Erling Smørgrav 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
3648e4a9863fSDag-Erling Smørgrav 	free(msg);
3649e2f6069cSDag-Erling Smørgrav 	if (c->open_confirm) {
365019261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
36514f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
365219261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3653e2f6069cSDag-Erling Smørgrav 	}
3654cce7d346SDag-Erling Smørgrav 	/* Schedule the channel for cleanup/deletion. */
36554f52dfbbSDag-Erling Smørgrav 	chan_mark_dead(ssh, c);
3656bc5531deSDag-Erling Smørgrav 	return 0;
3657a04a10f8SKris Kennaway }
3658a04a10f8SKris Kennaway 
3659bc5531deSDag-Erling Smørgrav int
36604f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3661a04a10f8SKris Kennaway {
36624f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "window adjust");
3663a04a10f8SKris Kennaway 	Channel *c;
36644f52dfbbSDag-Erling Smørgrav 	u_int32_t adjust;
36654f52dfbbSDag-Erling Smørgrav 	u_int new_rwin;
36664f52dfbbSDag-Erling Smørgrav 	int r;
3667a04a10f8SKris Kennaway 
36684f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
3669b74df5b2SDag-Erling Smørgrav 		logit("Received window adjust for non-open channel %d.", id);
3670bc5531deSDag-Erling Smørgrav 		return 0;
3671511b41d2SMark Murray 	}
36724f52dfbbSDag-Erling Smørgrav 
36734f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3674ca86bcf2SDag-Erling Smørgrav 		return 0;
367519261079SEd Maste 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 ||
367619261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
367719261079SEd Maste 		error_fr(r, "parse adjust");
367819261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid window adjust message");
36794f52dfbbSDag-Erling Smørgrav 	}
36804f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
36814f52dfbbSDag-Erling Smørgrav 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
3682557f75e5SDag-Erling Smørgrav 		fatal("channel %d: adjust %u overflows remote window %u",
36834f52dfbbSDag-Erling Smørgrav 		    c->self, adjust, c->remote_window);
36844f52dfbbSDag-Erling Smørgrav 	}
36854f52dfbbSDag-Erling Smørgrav 	c->remote_window = new_rwin;
3686bc5531deSDag-Erling Smørgrav 	return 0;
3687511b41d2SMark Murray }
3688511b41d2SMark Murray 
3689bc5531deSDag-Erling Smørgrav int
36904f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3691af12a3e7SDag-Erling Smørgrav {
36924f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "status confirm");
3693d4af9e69SDag-Erling Smørgrav 	Channel *c;
3694d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
3695d4af9e69SDag-Erling Smørgrav 
3696d4af9e69SDag-Erling Smørgrav 	/* Reset keepalive timeout */
369719261079SEd Maste 	ssh_packet_set_alive_timeouts(ssh, 0);
3698d4af9e69SDag-Erling Smørgrav 
369919261079SEd Maste 	debug2_f("type %d id %d", type, id);
3700d4af9e69SDag-Erling Smørgrav 
37014f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
370219261079SEd Maste 		logit_f("%d: unknown", id);
3703bc5531deSDag-Erling Smørgrav 		return 0;
3704d4af9e69SDag-Erling Smørgrav 	}
37054f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3706ca86bcf2SDag-Erling Smørgrav 		return 0;
370719261079SEd Maste         if (sshpkt_get_end(ssh) != 0)
370819261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid status confirm message");
3709d4af9e69SDag-Erling Smørgrav 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3710bc5531deSDag-Erling Smørgrav 		return 0;
37114f52dfbbSDag-Erling Smørgrav 	cc->cb(ssh, type, c, cc->ctx);
3712d4af9e69SDag-Erling Smørgrav 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
371319261079SEd Maste 	freezero(cc, sizeof(*cc));
3714bc5531deSDag-Erling Smørgrav 	return 0;
3715d4af9e69SDag-Erling Smørgrav }
3716af12a3e7SDag-Erling Smørgrav 
3717af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
3718511b41d2SMark Murray 
3719511b41d2SMark Murray void
37204f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af)
3721511b41d2SMark Murray {
37224f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->IPv4or6 = af;
3723511b41d2SMark Murray }
3724511b41d2SMark Murray 
372589986192SBrooks Davis 
3726462c32cbSDag-Erling Smørgrav /*
3727462c32cbSDag-Erling Smørgrav  * Determine whether or not a port forward listens to loopback, the
3728462c32cbSDag-Erling Smørgrav  * specified address or wildcard. On the client, a specified bind
3729462c32cbSDag-Erling Smørgrav  * address will always override gateway_ports. On the server, a
3730462c32cbSDag-Erling Smørgrav  * gateway_ports of 1 (``yes'') will override the client's specification
3731462c32cbSDag-Erling Smørgrav  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3732462c32cbSDag-Erling Smørgrav  * will bind to whatever address the client asked for.
3733462c32cbSDag-Erling Smørgrav  *
3734462c32cbSDag-Erling Smørgrav  * Special-case listen_addrs are:
3735462c32cbSDag-Erling Smørgrav  *
3736462c32cbSDag-Erling Smørgrav  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3737462c32cbSDag-Erling Smørgrav  * "" (empty string), "*"  -> wildcard v4/v6
3738462c32cbSDag-Erling Smørgrav  * "localhost"             -> loopback v4/v6
3739a0ee8cc6SDag-Erling Smørgrav  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3740462c32cbSDag-Erling Smørgrav  */
3741462c32cbSDag-Erling Smørgrav static const char *
374219261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp,
3743a0ee8cc6SDag-Erling Smørgrav     int is_client, struct ForwardOptions *fwd_opts)
3744462c32cbSDag-Erling Smørgrav {
3745462c32cbSDag-Erling Smørgrav 	const char *addr = NULL;
3746462c32cbSDag-Erling Smørgrav 	int wildcard = 0;
3747462c32cbSDag-Erling Smørgrav 
3748462c32cbSDag-Erling Smørgrav 	if (listen_addr == NULL) {
3749462c32cbSDag-Erling Smørgrav 		/* No address specified: default to gateway_ports setting */
3750a0ee8cc6SDag-Erling Smørgrav 		if (fwd_opts->gateway_ports)
3751462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3752a0ee8cc6SDag-Erling Smørgrav 	} else if (fwd_opts->gateway_ports || is_client) {
375319261079SEd Maste 		if (((ssh->compat & SSH_OLD_FORWARD_ADDR) &&
3754462c32cbSDag-Erling Smørgrav 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3755462c32cbSDag-Erling Smørgrav 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
3756a0ee8cc6SDag-Erling Smørgrav 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3757462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3758f7167e0eSDag-Erling Smørgrav 			/*
3759f7167e0eSDag-Erling Smørgrav 			 * Notify client if they requested a specific listen
3760f7167e0eSDag-Erling Smørgrav 			 * address and it was overridden.
3761f7167e0eSDag-Erling Smørgrav 			 */
3762f7167e0eSDag-Erling Smørgrav 			if (*listen_addr != '\0' &&
3763f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
3764f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "*") != 0) {
376519261079SEd Maste 				ssh_packet_send_debug(ssh,
376619261079SEd Maste 				    "Forwarding listen address "
3767f7167e0eSDag-Erling Smørgrav 				    "\"%s\" overridden by server "
3768f7167e0eSDag-Erling Smørgrav 				    "GatewayPorts", listen_addr);
3769f7167e0eSDag-Erling Smørgrav 			}
3770a0ee8cc6SDag-Erling Smørgrav 		} else if (strcmp(listen_addr, "localhost") != 0 ||
3771a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
3772a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "::1") == 0) {
377319261079SEd Maste 			/*
377419261079SEd Maste 			 * Accept explicit localhost address when
377519261079SEd Maste 			 * GatewayPorts=yes. The "localhost" hostname is
377619261079SEd Maste 			 * deliberately skipped here so it will listen on all
377719261079SEd Maste 			 * available local address families.
377819261079SEd Maste 			 */
3779a0ee8cc6SDag-Erling Smørgrav 			addr = listen_addr;
3780f7167e0eSDag-Erling Smørgrav 		}
3781a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
3782a0ee8cc6SDag-Erling Smørgrav 	    strcmp(listen_addr, "::1") == 0) {
3783a0ee8cc6SDag-Erling Smørgrav 		/*
3784a0ee8cc6SDag-Erling Smørgrav 		 * If a specific IPv4/IPv6 localhost address has been
3785a0ee8cc6SDag-Erling Smørgrav 		 * requested then accept it even if gateway_ports is in
3786a0ee8cc6SDag-Erling Smørgrav 		 * effect. This allows the client to prefer IPv4 or IPv6.
3787a0ee8cc6SDag-Erling Smørgrav 		 */
3788462c32cbSDag-Erling Smørgrav 		addr = listen_addr;
3789462c32cbSDag-Erling Smørgrav 	}
3790462c32cbSDag-Erling Smørgrav 	if (wildcardp != NULL)
3791462c32cbSDag-Erling Smørgrav 		*wildcardp = wildcard;
3792462c32cbSDag-Erling Smørgrav 	return addr;
3793462c32cbSDag-Erling Smørgrav }
3794462c32cbSDag-Erling Smørgrav 
3795af12a3e7SDag-Erling Smørgrav static int
37964f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
37974f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int *allocated_listen_port,
37984f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3799511b41d2SMark Murray {
3800af12a3e7SDag-Erling Smørgrav 	Channel *c;
3801b74df5b2SDag-Erling Smørgrav 	int sock, r, success = 0, wildcard = 0, is_client;
3802511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
3803aa49c926SDag-Erling Smørgrav 	const char *host, *addr;
3804af12a3e7SDag-Erling Smørgrav 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3805cce7d346SDag-Erling Smørgrav 	in_port_t *lport_p;
3806511b41d2SMark Murray 
3807aa49c926SDag-Erling Smørgrav 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3808511b41d2SMark Murray 
3809557f75e5SDag-Erling Smørgrav 	if (is_client && fwd->connect_path != NULL) {
3810557f75e5SDag-Erling Smørgrav 		host = fwd->connect_path;
3811557f75e5SDag-Erling Smørgrav 	} else {
3812557f75e5SDag-Erling Smørgrav 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
3813557f75e5SDag-Erling Smørgrav 		    fwd->listen_host : fwd->connect_host;
3814af12a3e7SDag-Erling Smørgrav 		if (host == NULL) {
3815af12a3e7SDag-Erling Smørgrav 			error("No forward host name.");
3816d4ecd108SDag-Erling Smørgrav 			return 0;
3817ca3176e7SBrian Feldman 		}
3818cce7d346SDag-Erling Smørgrav 		if (strlen(host) >= NI_MAXHOST) {
3819ca3176e7SBrian Feldman 			error("Forward host name too long.");
3820d4ecd108SDag-Erling Smørgrav 			return 0;
3821ca3176e7SBrian Feldman 		}
3822557f75e5SDag-Erling Smørgrav 	}
3823ca3176e7SBrian Feldman 
3824462c32cbSDag-Erling Smørgrav 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
382519261079SEd Maste 	addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard,
3826a0ee8cc6SDag-Erling Smørgrav 	    is_client, fwd_opts);
382719261079SEd Maste 	debug3_f("type %d wildcard %d addr %s", type, wildcard,
382819261079SEd Maste 	    (addr == NULL) ? "NULL" : addr);
3829aa49c926SDag-Erling Smørgrav 
3830aa49c926SDag-Erling Smørgrav 	/*
3831511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
3832511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3833511b41d2SMark Murray 	 */
3834511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
38354f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
3836aa49c926SDag-Erling Smørgrav 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3837511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
3838a0ee8cc6SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3839aa49c926SDag-Erling Smørgrav 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3840aa49c926SDag-Erling Smørgrav 		if (addr == NULL) {
3841aa49c926SDag-Erling Smørgrav 			/* This really shouldn't happen */
384219261079SEd Maste 			ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s",
3843d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3844aa49c926SDag-Erling Smørgrav 		} else {
384519261079SEd Maste 			error_f("getaddrinfo(%.64s): %s", addr,
3846d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3847aa49c926SDag-Erling Smørgrav 		}
3848d4ecd108SDag-Erling Smørgrav 		return 0;
3849aa49c926SDag-Erling Smørgrav 	}
3850cce7d346SDag-Erling Smørgrav 	if (allocated_listen_port != NULL)
3851cce7d346SDag-Erling Smørgrav 		*allocated_listen_port = 0;
3852511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
3853cce7d346SDag-Erling Smørgrav 		switch (ai->ai_family) {
3854cce7d346SDag-Erling Smørgrav 		case AF_INET:
3855cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3856cce7d346SDag-Erling Smørgrav 			    sin_port;
3857cce7d346SDag-Erling Smørgrav 			break;
3858cce7d346SDag-Erling Smørgrav 		case AF_INET6:
3859cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3860cce7d346SDag-Erling Smørgrav 			    sin6_port;
3861cce7d346SDag-Erling Smørgrav 			break;
3862cce7d346SDag-Erling Smørgrav 		default:
3863511b41d2SMark Murray 			continue;
3864cce7d346SDag-Erling Smørgrav 		}
3865cce7d346SDag-Erling Smørgrav 		/*
3866cce7d346SDag-Erling Smørgrav 		 * If allocating a port for -R forwards, then use the
3867cce7d346SDag-Erling Smørgrav 		 * same port for all address families.
3868cce7d346SDag-Erling Smørgrav 		 */
38694f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
38704f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
38714f52dfbbSDag-Erling Smørgrav 		    *allocated_listen_port > 0)
3872cce7d346SDag-Erling Smørgrav 			*lport_p = htons(*allocated_listen_port);
3873cce7d346SDag-Erling Smørgrav 
3874511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
38754f52dfbbSDag-Erling Smørgrav 		    strport, sizeof(strport),
38764f52dfbbSDag-Erling Smørgrav 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
387719261079SEd Maste 			error_f("getnameinfo failed");
3878511b41d2SMark Murray 			continue;
3879511b41d2SMark Murray 		}
3880511b41d2SMark Murray 		/* Create a port to listen for the host. */
3881221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
388219261079SEd Maste 		if (sock == -1) {
3883511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
388447dd1d1bSDag-Erling Smørgrav 			verbose("socket [%s]:%s: %.100s", ntop, strport,
388547dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3886511b41d2SMark Murray 			continue;
3887511b41d2SMark Murray 		}
3888b74df5b2SDag-Erling Smørgrav 
388947dd1d1bSDag-Erling Smørgrav 		set_reuseaddr(sock);
3890b15c8340SDag-Erling Smørgrav 		if (ai->ai_family == AF_INET6)
3891b15c8340SDag-Erling Smørgrav 			sock_set_v6only(sock);
3892f388f5efSDag-Erling Smørgrav 
3893cce7d346SDag-Erling Smørgrav 		debug("Local forwarding listening on %s port %s.",
3894cce7d346SDag-Erling Smørgrav 		    ntop, strport);
3895511b41d2SMark Murray 
3896511b41d2SMark Murray 		/* Bind the socket to the address. */
389719261079SEd Maste 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
38984f52dfbbSDag-Erling Smørgrav 			/*
38994f52dfbbSDag-Erling Smørgrav 			 * address can be in if use ipv6 address is
39004f52dfbbSDag-Erling Smørgrav 			 * already bound
39014f52dfbbSDag-Erling Smørgrav 			 */
3902989dd127SDag-Erling Smørgrav 			if (!ai->ai_next)
390347dd1d1bSDag-Erling Smørgrav 				error("bind [%s]:%s: %.100s",
390447dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3905989dd127SDag-Erling Smørgrav 			else
390647dd1d1bSDag-Erling Smørgrav 				verbose("bind [%s]:%s: %.100s",
390747dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3908989dd127SDag-Erling Smørgrav 
3909511b41d2SMark Murray 			close(sock);
3910511b41d2SMark Murray 			continue;
3911511b41d2SMark Murray 		}
3912511b41d2SMark Murray 		/* Start listening for connections on the socket. */
391319261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
391447dd1d1bSDag-Erling Smørgrav 			error("listen [%s]:%s: %.100s", ntop, strport,
391547dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3916511b41d2SMark Murray 			close(sock);
3917511b41d2SMark Murray 			continue;
3918511b41d2SMark Murray 		}
3919cce7d346SDag-Erling Smørgrav 
3920cce7d346SDag-Erling Smørgrav 		/*
3921a0ee8cc6SDag-Erling Smørgrav 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3922cce7d346SDag-Erling Smørgrav 		 * record what we got.
3923cce7d346SDag-Erling Smørgrav 		 */
39244f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
39254f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 &&
3926cce7d346SDag-Erling Smørgrav 		    allocated_listen_port != NULL &&
3927cce7d346SDag-Erling Smørgrav 		    *allocated_listen_port == 0) {
3928076ad2f8SDag-Erling Smørgrav 			*allocated_listen_port = get_local_port(sock);
3929cce7d346SDag-Erling Smørgrav 			debug("Allocated listen port %d",
3930cce7d346SDag-Erling Smørgrav 			    *allocated_listen_port);
3931cce7d346SDag-Erling Smørgrav 		}
3932cce7d346SDag-Erling Smørgrav 
393360c59fadSDag-Erling Smørgrav 		/* Allocate a channel number for the socket. */
3934f374ba41SEd Maste 		c = channel_new(ssh, "port-listener", type, sock, sock, -1,
3935a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3936221552e4SDag-Erling Smørgrav 		    0, "port listener", 1);
3937cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
3938a0ee8cc6SDag-Erling Smørgrav 		c->host_port = fwd->connect_port;
3939462c32cbSDag-Erling Smørgrav 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
3940a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
394119261079SEd Maste 		    !(ssh->compat & SSH_BUG_DYNAMIC_RPORT))
3942462c32cbSDag-Erling Smørgrav 			c->listening_port = *allocated_listen_port;
3943462c32cbSDag-Erling Smørgrav 		else
3944a0ee8cc6SDag-Erling Smørgrav 			c->listening_port = fwd->listen_port;
3945511b41d2SMark Murray 		success = 1;
3946511b41d2SMark Murray 	}
3947511b41d2SMark Murray 	if (success == 0)
394819261079SEd Maste 		error_f("cannot listen to port: %d", fwd->listen_port);
3949511b41d2SMark Murray 	freeaddrinfo(aitop);
3950ca3176e7SBrian Feldman 	return success;
3951511b41d2SMark Murray }
3952511b41d2SMark Murray 
3953a0ee8cc6SDag-Erling Smørgrav static int
39544f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
39554f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
3956a0ee8cc6SDag-Erling Smørgrav {
3957a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
3958a0ee8cc6SDag-Erling Smørgrav 	const char *path;
3959a0ee8cc6SDag-Erling Smørgrav 	Channel *c;
3960a0ee8cc6SDag-Erling Smørgrav 	int port, sock;
3961a0ee8cc6SDag-Erling Smørgrav 	mode_t omask;
3962a0ee8cc6SDag-Erling Smørgrav 
3963a0ee8cc6SDag-Erling Smørgrav 	switch (type) {
3964a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_UNIX_LISTENER:
3965a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
3966a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
3967a0ee8cc6SDag-Erling Smørgrav 				error("Local connecting path too long: %s",
3968a0ee8cc6SDag-Erling Smørgrav 				    fwd->connect_path);
3969a0ee8cc6SDag-Erling Smørgrav 				return 0;
3970a0ee8cc6SDag-Erling Smørgrav 			}
3971a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_path;
3972a0ee8cc6SDag-Erling Smørgrav 			port = PORT_STREAMLOCAL;
3973a0ee8cc6SDag-Erling Smørgrav 		} else {
3974a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_host == NULL) {
3975a0ee8cc6SDag-Erling Smørgrav 				error("No forward host name.");
3976a0ee8cc6SDag-Erling Smørgrav 				return 0;
3977a0ee8cc6SDag-Erling Smørgrav 			}
3978a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
3979a0ee8cc6SDag-Erling Smørgrav 				error("Forward host name too long.");
3980a0ee8cc6SDag-Erling Smørgrav 				return 0;
3981a0ee8cc6SDag-Erling Smørgrav 			}
3982a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_host;
3983a0ee8cc6SDag-Erling Smørgrav 			port = fwd->connect_port;
3984a0ee8cc6SDag-Erling Smørgrav 		}
3985a0ee8cc6SDag-Erling Smørgrav 		break;
3986a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_RUNIX_LISTENER:
3987a0ee8cc6SDag-Erling Smørgrav 		path = fwd->listen_path;
3988a0ee8cc6SDag-Erling Smørgrav 		port = PORT_STREAMLOCAL;
3989a0ee8cc6SDag-Erling Smørgrav 		break;
3990a0ee8cc6SDag-Erling Smørgrav 	default:
399119261079SEd Maste 		error_f("unexpected channel type %d", type);
3992a0ee8cc6SDag-Erling Smørgrav 		return 0;
3993a0ee8cc6SDag-Erling Smørgrav 	}
3994a0ee8cc6SDag-Erling Smørgrav 
3995a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path == NULL) {
3996a0ee8cc6SDag-Erling Smørgrav 		error("No forward path name.");
3997a0ee8cc6SDag-Erling Smørgrav 		return 0;
3998a0ee8cc6SDag-Erling Smørgrav 	}
3999a0ee8cc6SDag-Erling Smørgrav 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
4000a0ee8cc6SDag-Erling Smørgrav 		error("Local listening path too long: %s", fwd->listen_path);
4001a0ee8cc6SDag-Erling Smørgrav 		return 0;
4002a0ee8cc6SDag-Erling Smørgrav 	}
4003a0ee8cc6SDag-Erling Smørgrav 
400419261079SEd Maste 	debug3_f("type %d path %s", type, fwd->listen_path);
4005a0ee8cc6SDag-Erling Smørgrav 
4006a0ee8cc6SDag-Erling Smørgrav 	/* Start a Unix domain listener. */
4007a0ee8cc6SDag-Erling Smørgrav 	omask = umask(fwd_opts->streamlocal_bind_mask);
4008a0ee8cc6SDag-Erling Smørgrav 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
4009a0ee8cc6SDag-Erling Smørgrav 	    fwd_opts->streamlocal_bind_unlink);
4010a0ee8cc6SDag-Erling Smørgrav 	umask(omask);
4011a0ee8cc6SDag-Erling Smørgrav 	if (sock < 0)
4012a0ee8cc6SDag-Erling Smørgrav 		return 0;
4013a0ee8cc6SDag-Erling Smørgrav 
4014a0ee8cc6SDag-Erling Smørgrav 	debug("Local forwarding listening on path %s.", fwd->listen_path);
4015a0ee8cc6SDag-Erling Smørgrav 
4016a0ee8cc6SDag-Erling Smørgrav 	/* Allocate a channel number for the socket. */
4017f374ba41SEd Maste 	c = channel_new(ssh, "unix-listener", type, sock, sock, -1,
4018a0ee8cc6SDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
4019a0ee8cc6SDag-Erling Smørgrav 	    0, "unix listener", 1);
4020a0ee8cc6SDag-Erling Smørgrav 	c->path = xstrdup(path);
4021a0ee8cc6SDag-Erling Smørgrav 	c->host_port = port;
4022a0ee8cc6SDag-Erling Smørgrav 	c->listening_port = PORT_STREAMLOCAL;
4023a0ee8cc6SDag-Erling Smørgrav 	c->listening_addr = xstrdup(fwd->listen_path);
4024a0ee8cc6SDag-Erling Smørgrav 	return 1;
4025a0ee8cc6SDag-Erling Smørgrav }
4026a0ee8cc6SDag-Erling Smørgrav 
4027a0ee8cc6SDag-Erling Smørgrav static int
40284f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh,
40294f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
403021e764dfSDag-Erling Smørgrav {
403121e764dfSDag-Erling Smørgrav 	u_int i;
403221e764dfSDag-Erling Smørgrav 	int found = 0;
403321e764dfSDag-Erling Smørgrav 
40344f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40354f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4036462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
4037462c32cbSDag-Erling Smørgrav 			continue;
4038462c32cbSDag-Erling Smørgrav 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
403919261079SEd Maste 			debug2_f("close channel %d", i);
40404f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4041462c32cbSDag-Erling Smørgrav 			found = 1;
4042462c32cbSDag-Erling Smørgrav 		}
4043462c32cbSDag-Erling Smørgrav 	}
404421e764dfSDag-Erling Smørgrav 
40454f52dfbbSDag-Erling Smørgrav 	return found;
4046462c32cbSDag-Erling Smørgrav }
4047462c32cbSDag-Erling Smørgrav 
4048a0ee8cc6SDag-Erling Smørgrav static int
40494f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
4050462c32cbSDag-Erling Smørgrav {
4051462c32cbSDag-Erling Smørgrav 	u_int i;
4052462c32cbSDag-Erling Smørgrav 	int found = 0;
4053a0ee8cc6SDag-Erling Smørgrav 
40544f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40554f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4056a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
4057a0ee8cc6SDag-Erling Smørgrav 			continue;
4058a0ee8cc6SDag-Erling Smørgrav 		if (c->path == NULL)
4059a0ee8cc6SDag-Erling Smørgrav 			continue;
4060a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->path, path) == 0) {
406119261079SEd Maste 			debug2_f("close channel %d", i);
40624f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4063a0ee8cc6SDag-Erling Smørgrav 			found = 1;
4064a0ee8cc6SDag-Erling Smørgrav 		}
4065a0ee8cc6SDag-Erling Smørgrav 	}
4066a0ee8cc6SDag-Erling Smørgrav 
40674f52dfbbSDag-Erling Smørgrav 	return found;
4068a0ee8cc6SDag-Erling Smørgrav }
4069a0ee8cc6SDag-Erling Smørgrav 
4070a0ee8cc6SDag-Erling Smørgrav int
40714f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
4072a0ee8cc6SDag-Erling Smørgrav {
40734f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40744f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_streamlocal(ssh,
40754f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
40764f52dfbbSDag-Erling Smørgrav 	} else {
40774f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_tcpip(ssh,
40784f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port);
40794f52dfbbSDag-Erling Smørgrav 	}
4080a0ee8cc6SDag-Erling Smørgrav }
4081a0ee8cc6SDag-Erling Smørgrav 
4082a0ee8cc6SDag-Erling Smørgrav static int
40834f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh,
40844f52dfbbSDag-Erling Smørgrav     const char *lhost, u_short lport, int cport,
40854f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
4086a0ee8cc6SDag-Erling Smørgrav {
4087a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4088a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
408919261079SEd Maste 	const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts);
4090462c32cbSDag-Erling Smørgrav 
40914f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40924f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4093462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
4094462c32cbSDag-Erling Smørgrav 			continue;
4095462c32cbSDag-Erling Smørgrav 		if (c->listening_port != lport)
4096462c32cbSDag-Erling Smørgrav 			continue;
4097462c32cbSDag-Erling Smørgrav 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
4098462c32cbSDag-Erling Smørgrav 			/* skip dynamic forwardings */
4099462c32cbSDag-Erling Smørgrav 			if (c->host_port == 0)
4100462c32cbSDag-Erling Smørgrav 				continue;
4101462c32cbSDag-Erling Smørgrav 		} else {
4102462c32cbSDag-Erling Smørgrav 			if (c->host_port != cport)
4103462c32cbSDag-Erling Smørgrav 				continue;
4104462c32cbSDag-Erling Smørgrav 		}
4105462c32cbSDag-Erling Smørgrav 		if ((c->listening_addr == NULL && addr != NULL) ||
4106462c32cbSDag-Erling Smørgrav 		    (c->listening_addr != NULL && addr == NULL))
4107462c32cbSDag-Erling Smørgrav 			continue;
4108462c32cbSDag-Erling Smørgrav 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
410919261079SEd Maste 			debug2_f("close channel %d", i);
41104f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
411121e764dfSDag-Erling Smørgrav 			found = 1;
411221e764dfSDag-Erling Smørgrav 		}
411321e764dfSDag-Erling Smørgrav 	}
411421e764dfSDag-Erling Smørgrav 
41154f52dfbbSDag-Erling Smørgrav 	return found;
411621e764dfSDag-Erling Smørgrav }
411721e764dfSDag-Erling Smørgrav 
4118a0ee8cc6SDag-Erling Smørgrav static int
41194f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
4120a0ee8cc6SDag-Erling Smørgrav {
4121a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4122a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
4123a0ee8cc6SDag-Erling Smørgrav 
4124a0ee8cc6SDag-Erling Smørgrav 	if (path == NULL) {
412519261079SEd Maste 		error_f("no path specified.");
4126a0ee8cc6SDag-Erling Smørgrav 		return 0;
4127a0ee8cc6SDag-Erling Smørgrav 	}
4128a0ee8cc6SDag-Erling Smørgrav 
41294f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
41304f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4131a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
4132a0ee8cc6SDag-Erling Smørgrav 			continue;
4133a0ee8cc6SDag-Erling Smørgrav 		if (c->listening_addr == NULL)
4134a0ee8cc6SDag-Erling Smørgrav 			continue;
4135a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->listening_addr, path) == 0) {
413619261079SEd Maste 			debug2_f("close channel %d", i);
41374f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4138a0ee8cc6SDag-Erling Smørgrav 			found = 1;
4139a0ee8cc6SDag-Erling Smørgrav 		}
4140a0ee8cc6SDag-Erling Smørgrav 	}
4141a0ee8cc6SDag-Erling Smørgrav 
41424f52dfbbSDag-Erling Smørgrav 	return found;
4143a0ee8cc6SDag-Erling Smørgrav }
4144a0ee8cc6SDag-Erling Smørgrav 
4145a0ee8cc6SDag-Erling Smørgrav int
41464f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh,
41474f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
4148af12a3e7SDag-Erling Smørgrav {
4149a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
41504f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_streamlocal(ssh,
41514f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
41524f52dfbbSDag-Erling Smørgrav 	} else {
41534f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_tcpip(ssh,
41544f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
41554f52dfbbSDag-Erling Smørgrav 	}
41564f52dfbbSDag-Erling Smørgrav }
41574f52dfbbSDag-Erling Smørgrav 
41584f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */
41594f52dfbbSDag-Erling Smørgrav int
41604f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh,
41614f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
41624f52dfbbSDag-Erling Smørgrav {
41634f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
41644f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4165a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
4166a0ee8cc6SDag-Erling Smørgrav 	} else {
41674f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
41684f52dfbbSDag-Erling Smørgrav 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
4169a0ee8cc6SDag-Erling Smørgrav 	}
4170af12a3e7SDag-Erling Smørgrav }
4171af12a3e7SDag-Erling Smørgrav 
4172190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */
4173190cef3dSDag-Erling Smørgrav static int
4174190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd)
4175190cef3dSDag-Erling Smørgrav {
4176190cef3dSDag-Erling Smørgrav 	int ret;
4177190cef3dSDag-Erling Smørgrav 	char *lhost;
4178190cef3dSDag-Erling Smørgrav 
4179190cef3dSDag-Erling Smørgrav 	/* XXX add ACLs for streamlocal */
4180190cef3dSDag-Erling Smørgrav 	if (fwd->listen_path != NULL)
4181190cef3dSDag-Erling Smørgrav 		return 1;
4182190cef3dSDag-Erling Smørgrav 
4183190cef3dSDag-Erling Smørgrav 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
4184190cef3dSDag-Erling Smørgrav 		return 0;
4185190cef3dSDag-Erling Smørgrav 
4186190cef3dSDag-Erling Smørgrav 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
4187190cef3dSDag-Erling Smørgrav 	    allowed_open->listen_port != fwd->listen_port)
4188190cef3dSDag-Erling Smørgrav 		return 0;
4189190cef3dSDag-Erling Smørgrav 
4190190cef3dSDag-Erling Smørgrav 	/* Match hostnames case-insensitively */
4191190cef3dSDag-Erling Smørgrav 	lhost = xstrdup(fwd->listen_host);
4192190cef3dSDag-Erling Smørgrav 	lowercase(lhost);
4193190cef3dSDag-Erling Smørgrav 	ret = match_pattern(lhost, allowed_open->listen_host);
4194190cef3dSDag-Erling Smørgrav 	free(lhost);
4195190cef3dSDag-Erling Smørgrav 
4196190cef3dSDag-Erling Smørgrav 	return ret;
4197190cef3dSDag-Erling Smørgrav }
4198190cef3dSDag-Erling Smørgrav 
4199190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */
4200190cef3dSDag-Erling Smørgrav static int
4201190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
4202190cef3dSDag-Erling Smørgrav {
4203190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4204190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->remote_perms;
4205190cef3dSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4206190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4207190cef3dSDag-Erling Smørgrav 
4208190cef3dSDag-Erling Smørgrav 	/* XXX apply GatewayPorts override before checking? */
4209190cef3dSDag-Erling Smørgrav 
4210190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4211190cef3dSDag-Erling Smørgrav 	if (!permit) {
4212190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4213190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4214190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4215190cef3dSDag-Erling Smørgrav 				permit = 1;
4216190cef3dSDag-Erling Smørgrav 				break;
4217190cef3dSDag-Erling Smørgrav 			}
4218190cef3dSDag-Erling Smørgrav 		}
4219190cef3dSDag-Erling Smørgrav 	}
4220190cef3dSDag-Erling Smørgrav 
4221190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4222190cef3dSDag-Erling Smørgrav 		permit_adm = 0;
4223190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4224190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4225190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4226190cef3dSDag-Erling Smørgrav 				permit_adm = 1;
4227190cef3dSDag-Erling Smørgrav 				break;
4228190cef3dSDag-Erling Smørgrav 			}
4229190cef3dSDag-Erling Smørgrav 		}
4230190cef3dSDag-Erling Smørgrav 	}
4231190cef3dSDag-Erling Smørgrav 
4232190cef3dSDag-Erling Smørgrav 	return permit && permit_adm;
4233190cef3dSDag-Erling Smørgrav }
4234190cef3dSDag-Erling Smørgrav 
4235af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */
4236af12a3e7SDag-Erling Smørgrav int
42374f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
4238a0ee8cc6SDag-Erling Smørgrav     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
4239af12a3e7SDag-Erling Smørgrav {
4240190cef3dSDag-Erling Smørgrav 	if (!check_rfwd_permission(ssh, fwd)) {
424119261079SEd Maste 		ssh_packet_send_debug(ssh, "port forwarding refused");
424219261079SEd Maste 		if (fwd->listen_path != NULL)
424319261079SEd Maste 			/* XXX always allowed, see remote_open_match() */
424419261079SEd Maste 			logit("Received request from %.100s port %d to "
424519261079SEd Maste 			    "remote forward to path \"%.100s\", "
424619261079SEd Maste 			    "but the request was denied.",
424719261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
424819261079SEd Maste 			    fwd->listen_path);
424919261079SEd Maste 		else if(fwd->listen_host != NULL)
425019261079SEd Maste 			logit("Received request from %.100s port %d to "
425119261079SEd Maste 			    "remote forward to host %.100s port %d, "
425219261079SEd Maste 			    "but the request was denied.",
425319261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
425419261079SEd Maste 			    fwd->listen_host, fwd->listen_port );
425519261079SEd Maste 		else
425619261079SEd Maste 			logit("Received request from %.100s port %d to remote "
425719261079SEd Maste 			    "forward, but the request was denied.",
425819261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
4259190cef3dSDag-Erling Smørgrav 		return 0;
4260190cef3dSDag-Erling Smørgrav 	}
4261a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
42624f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4263a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
4264a0ee8cc6SDag-Erling Smørgrav 	} else {
42654f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
4266a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
4267a0ee8cc6SDag-Erling Smørgrav 		    fwd_opts);
4268a0ee8cc6SDag-Erling Smørgrav 	}
4269af12a3e7SDag-Erling Smørgrav }
4270af12a3e7SDag-Erling Smørgrav 
4271511b41d2SMark Murray /*
4272462c32cbSDag-Erling Smørgrav  * Translate the requested rfwd listen host to something usable for
4273462c32cbSDag-Erling Smørgrav  * this server.
4274462c32cbSDag-Erling Smørgrav  */
4275462c32cbSDag-Erling Smørgrav static const char *
4276462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host)
4277462c32cbSDag-Erling Smørgrav {
4278462c32cbSDag-Erling Smørgrav 	if (listen_host == NULL) {
4279462c32cbSDag-Erling Smørgrav 		return "localhost";
4280462c32cbSDag-Erling Smørgrav 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
4281462c32cbSDag-Erling Smørgrav 		return "";
4282462c32cbSDag-Erling Smørgrav 	} else
4283462c32cbSDag-Erling Smørgrav 		return listen_host;
4284462c32cbSDag-Erling Smørgrav }
4285462c32cbSDag-Erling Smørgrav 
4286462c32cbSDag-Erling Smørgrav /*
4287511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
4288511b41d2SMark Murray  * the secure channel to host:port from local side.
4289462c32cbSDag-Erling Smørgrav  * Returns handle (index) for updating the dynamic listen port with
4290190cef3dSDag-Erling Smørgrav  * channel_update_permission().
4291511b41d2SMark Murray  */
4292333ee039SDag-Erling Smørgrav int
42934f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
4294511b41d2SMark Murray {
42954f52dfbbSDag-Erling Smørgrav 	int r, success = 0, idx = -1;
4296f374ba41SEd Maste 	const char *host_to_connect, *listen_host, *listen_path;
42974f52dfbbSDag-Erling Smørgrav 	int port_to_connect, listen_port;
4298ca3176e7SBrian Feldman 
4299511b41d2SMark Murray 	/* Send the forward request to the remote side. */
4300a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
43014f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43024f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
43034f52dfbbSDag-Erling Smørgrav 		    "streamlocal-forward@openssh.com")) != 0 ||
43044f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43054f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
43064f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
43074f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
430819261079SEd Maste 			fatal_fr(r, "request streamlocal");
4309a0ee8cc6SDag-Erling Smørgrav 	} else {
43104f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43114f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
43124f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43134f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
43144f52dfbbSDag-Erling Smørgrav 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
43154f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
43164f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
43174f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
431819261079SEd Maste 			fatal_fr(r, "request tcpip-forward");
4319a0ee8cc6SDag-Erling Smørgrav 	}
4320ca3176e7SBrian Feldman 	/* Assume that server accepts the request */
4321ca3176e7SBrian Feldman 	success = 1;
4322ca3176e7SBrian Feldman 	if (success) {
4323e2f6069cSDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
43244f52dfbbSDag-Erling Smørgrav 		host_to_connect = listen_host = listen_path = NULL;
43254f52dfbbSDag-Erling Smørgrav 		port_to_connect = listen_port = 0;
4326a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
4327f374ba41SEd Maste 			host_to_connect = fwd->connect_path;
43284f52dfbbSDag-Erling Smørgrav 			port_to_connect = PORT_STREAMLOCAL;
4329a0ee8cc6SDag-Erling Smørgrav 		} else {
4330f374ba41SEd Maste 			host_to_connect = fwd->connect_host;
43314f52dfbbSDag-Erling Smørgrav 			port_to_connect = fwd->connect_port;
4332a0ee8cc6SDag-Erling Smørgrav 		}
4333a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_path != NULL) {
4334f374ba41SEd Maste 			listen_path = fwd->listen_path;
43354f52dfbbSDag-Erling Smørgrav 			listen_port = PORT_STREAMLOCAL;
4336a0ee8cc6SDag-Erling Smørgrav 		} else {
4337f374ba41SEd Maste 			listen_host = fwd->listen_host;
43384f52dfbbSDag-Erling Smørgrav 			listen_port = fwd->listen_port;
4339a0ee8cc6SDag-Erling Smørgrav 		}
4340190cef3dSDag-Erling Smørgrav 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
43414f52dfbbSDag-Erling Smørgrav 		    host_to_connect, port_to_connect,
43424f52dfbbSDag-Erling Smørgrav 		    listen_host, listen_path, listen_port, NULL);
4343511b41d2SMark Murray 	}
43444f52dfbbSDag-Erling Smørgrav 	return idx;
4345a04a10f8SKris Kennaway }
4346511b41d2SMark Murray 
4347a0ee8cc6SDag-Erling Smørgrav static int
4348190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost,
4349a0ee8cc6SDag-Erling Smørgrav     int requestedport)
4350a0ee8cc6SDag-Erling Smørgrav {
4351a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4352a0ee8cc6SDag-Erling Smørgrav 		return 0;
4353a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
4354a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->port_to_connect != requestedport)
4355a0ee8cc6SDag-Erling Smørgrav 		return 0;
4356076ad2f8SDag-Erling Smørgrav 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
4357076ad2f8SDag-Erling Smørgrav 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
4358a0ee8cc6SDag-Erling Smørgrav 		return 0;
4359a0ee8cc6SDag-Erling Smørgrav 	return 1;
4360a0ee8cc6SDag-Erling Smørgrav }
4361a0ee8cc6SDag-Erling Smørgrav 
4362a0ee8cc6SDag-Erling Smørgrav /*
4363a0ee8cc6SDag-Erling Smørgrav  * Note that in the listen host/port case
4364a0ee8cc6SDag-Erling Smørgrav  * we don't support FWD_PERMIT_ANY_PORT and
4365a0ee8cc6SDag-Erling Smørgrav  * need to translate between the configured-host (listen_host)
4366a0ee8cc6SDag-Erling Smørgrav  * and what we've sent to the remote server (channel_rfwd_bind_host)
4367a0ee8cc6SDag-Erling Smørgrav  */
4368a0ee8cc6SDag-Erling Smørgrav static int
4369190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open,
4370a0ee8cc6SDag-Erling Smørgrav     const char *requestedhost, u_short requestedport, int translate)
4371a0ee8cc6SDag-Erling Smørgrav {
4372a0ee8cc6SDag-Erling Smørgrav 	const char *allowed_host;
4373a0ee8cc6SDag-Erling Smørgrav 
4374a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4375a0ee8cc6SDag-Erling Smørgrav 		return 0;
4376a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != requestedport)
4377a0ee8cc6SDag-Erling Smørgrav 		return 0;
4378a0ee8cc6SDag-Erling Smørgrav 	if (!translate && allowed_open->listen_host == NULL &&
4379a0ee8cc6SDag-Erling Smørgrav 	    requestedhost == NULL)
4380a0ee8cc6SDag-Erling Smørgrav 		return 1;
4381a0ee8cc6SDag-Erling Smørgrav 	allowed_host = translate ?
4382a0ee8cc6SDag-Erling Smørgrav 	    channel_rfwd_bind_host(allowed_open->listen_host) :
4383a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->listen_host;
4384190cef3dSDag-Erling Smørgrav 	if (allowed_host == NULL || requestedhost == NULL ||
4385a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_host, requestedhost) != 0)
4386a0ee8cc6SDag-Erling Smørgrav 		return 0;
4387a0ee8cc6SDag-Erling Smørgrav 	return 1;
4388a0ee8cc6SDag-Erling Smørgrav }
4389a0ee8cc6SDag-Erling Smørgrav 
4390a0ee8cc6SDag-Erling Smørgrav static int
4391190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open,
4392a0ee8cc6SDag-Erling Smørgrav     const char *requestedpath)
4393a0ee8cc6SDag-Erling Smørgrav {
4394a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4395a0ee8cc6SDag-Erling Smørgrav 		return 0;
4396a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
4397a0ee8cc6SDag-Erling Smørgrav 		return 0;
4398a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_path == NULL ||
4399a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
4400a0ee8cc6SDag-Erling Smørgrav 		return 0;
4401a0ee8cc6SDag-Erling Smørgrav 	return 1;
4402a0ee8cc6SDag-Erling Smørgrav }
4403a0ee8cc6SDag-Erling Smørgrav 
4404511b41d2SMark Murray /*
440521e764dfSDag-Erling Smørgrav  * Request cancellation of remote forwarding of connection host:port from
440621e764dfSDag-Erling Smørgrav  * local side.
440721e764dfSDag-Erling Smørgrav  */
4408a0ee8cc6SDag-Erling Smørgrav static int
44094f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh,
44104f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
441121e764dfSDag-Erling Smørgrav {
44124f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4413190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
44144f52dfbbSDag-Erling Smørgrav 	int r;
44154f52dfbbSDag-Erling Smørgrav 	u_int i;
441619261079SEd Maste 	struct permission *perm = NULL;
441721e764dfSDag-Erling Smørgrav 
4418190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4419190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4420190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm, host, port, 0))
442121e764dfSDag-Erling Smørgrav 			break;
4422190cef3dSDag-Erling Smørgrav 		perm = NULL;
442321e764dfSDag-Erling Smørgrav 	}
4424190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
442519261079SEd Maste 		debug_f("requested forward not found");
4426462c32cbSDag-Erling Smørgrav 		return -1;
442721e764dfSDag-Erling Smørgrav 	}
44284f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44294f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
44304f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44314f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
44324f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
44334f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
443419261079SEd Maste 		fatal_fr(r, "send cancel");
443521e764dfSDag-Erling Smørgrav 
4436190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4437462c32cbSDag-Erling Smørgrav 
4438462c32cbSDag-Erling Smørgrav 	return 0;
443921e764dfSDag-Erling Smørgrav }
444021e764dfSDag-Erling Smørgrav 
444121e764dfSDag-Erling Smørgrav /*
4442a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of Unix domain socket
4443a0ee8cc6SDag-Erling Smørgrav  * path from local side.
4444a0ee8cc6SDag-Erling Smørgrav  */
4445a0ee8cc6SDag-Erling Smørgrav static int
44464f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
4447a0ee8cc6SDag-Erling Smørgrav {
44484f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4449190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
44504f52dfbbSDag-Erling Smørgrav 	int r;
44514f52dfbbSDag-Erling Smørgrav 	u_int i;
445219261079SEd Maste 	struct permission *perm = NULL;
4453a0ee8cc6SDag-Erling Smørgrav 
4454190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4455190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4456190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path))
4457a0ee8cc6SDag-Erling Smørgrav 			break;
4458190cef3dSDag-Erling Smørgrav 		perm = NULL;
4459a0ee8cc6SDag-Erling Smørgrav 	}
4460190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
446119261079SEd Maste 		debug_f("requested forward not found");
4462a0ee8cc6SDag-Erling Smørgrav 		return -1;
4463a0ee8cc6SDag-Erling Smørgrav 	}
44644f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44654f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh,
44664f52dfbbSDag-Erling Smørgrav 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
44674f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44684f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
44694f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
447019261079SEd Maste 		fatal_fr(r, "send cancel");
4471a0ee8cc6SDag-Erling Smørgrav 
4472190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4473a0ee8cc6SDag-Erling Smørgrav 
4474a0ee8cc6SDag-Erling Smørgrav 	return 0;
4475a0ee8cc6SDag-Erling Smørgrav }
4476a0ee8cc6SDag-Erling Smørgrav 
4477a0ee8cc6SDag-Erling Smørgrav /*
4478a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of a connection from local side.
4479a0ee8cc6SDag-Erling Smørgrav  */
4480a0ee8cc6SDag-Erling Smørgrav int
44814f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
4482a0ee8cc6SDag-Erling Smørgrav {
4483a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
44844f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_streamlocal(ssh,
44854f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
4486a0ee8cc6SDag-Erling Smørgrav 	} else {
44874f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_tcpip(ssh,
44884f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host,
44894f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
4490a0ee8cc6SDag-Erling Smørgrav 	}
4491a0ee8cc6SDag-Erling Smørgrav }
4492a0ee8cc6SDag-Erling Smørgrav 
4493a0ee8cc6SDag-Erling Smørgrav /*
4494190cef3dSDag-Erling Smørgrav  * Permits opening to any host/port if permitted_user[] is empty.  This is
4495ca3176e7SBrian Feldman  * usually called by the server, because the user could connect to any port
4496ca3176e7SBrian Feldman  * anyway, and the server has no way to know but to trust the client anyway.
4497ca3176e7SBrian Feldman  */
4498ca3176e7SBrian Feldman void
4499190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where)
4500ca3176e7SBrian Feldman {
4501190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
4502190cef3dSDag-Erling Smørgrav 
4503190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_user == 0)
4504190cef3dSDag-Erling Smørgrav 		pset->all_permitted = 1;
4505ca3176e7SBrian Feldman }
4506ca3176e7SBrian Feldman 
4507190cef3dSDag-Erling Smørgrav /*
4508190cef3dSDag-Erling Smørgrav  * Permit the specified host/port for forwarding.
4509190cef3dSDag-Erling Smørgrav  */
4510ca3176e7SBrian Feldman void
4511190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where,
4512190cef3dSDag-Erling Smørgrav     char *host, int port)
4513ca3176e7SBrian Feldman {
4514190cef3dSDag-Erling Smørgrav 	int local = where == FORWARD_LOCAL;
4515190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
45164f52dfbbSDag-Erling Smørgrav 
4517190cef3dSDag-Erling Smørgrav 	debug("allow %s forwarding to host %s port %d",
4518190cef3dSDag-Erling Smørgrav 	    fwd_ident(who, where), host, port);
4519190cef3dSDag-Erling Smørgrav 	/*
4520190cef3dSDag-Erling Smørgrav 	 * Remote forwards set listen_host/port, local forwards set
4521190cef3dSDag-Erling Smørgrav 	 * host/port_to_connect.
4522190cef3dSDag-Erling Smørgrav 	 */
4523190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, who, where,
4524190cef3dSDag-Erling Smørgrav 	    local ? host : 0, local ? port : 0,
4525190cef3dSDag-Erling Smørgrav 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
4526190cef3dSDag-Erling Smørgrav 	pset->all_permitted = 0;
4527190cef3dSDag-Erling Smørgrav }
4528190cef3dSDag-Erling Smørgrav 
4529190cef3dSDag-Erling Smørgrav /*
4530190cef3dSDag-Erling Smørgrav  * Administratively disable forwarding.
4531190cef3dSDag-Erling Smørgrav  */
4532190cef3dSDag-Erling Smørgrav void
4533190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where)
4534190cef3dSDag-Erling Smørgrav {
4535190cef3dSDag-Erling Smørgrav 	channel_clear_permission(ssh, FORWARD_ADM, where);
4536190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, FORWARD_ADM, where,
4537190cef3dSDag-Erling Smørgrav 	    NULL, 0, NULL, NULL, 0, NULL);
4538190cef3dSDag-Erling Smørgrav }
4539190cef3dSDag-Erling Smørgrav 
4540190cef3dSDag-Erling Smørgrav /*
4541190cef3dSDag-Erling Smørgrav  * Clear a list of permitted opens.
4542190cef3dSDag-Erling Smørgrav  */
4543190cef3dSDag-Erling Smørgrav void
4544190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where)
4545190cef3dSDag-Erling Smørgrav {
4546190cef3dSDag-Erling Smørgrav 	struct permission **permp;
4547190cef3dSDag-Erling Smørgrav 	u_int *npermp;
4548190cef3dSDag-Erling Smørgrav 
4549190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
4550190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
4551190cef3dSDag-Erling Smørgrav 	*npermp = 0;
4552ca3176e7SBrian Feldman }
4553ca3176e7SBrian Feldman 
4554462c32cbSDag-Erling Smørgrav /*
4555462c32cbSDag-Erling Smørgrav  * Update the listen port for a dynamic remote forward, after
4556462c32cbSDag-Erling Smørgrav  * the actual 'newport' has been allocated. If 'newport' < 0 is
4557462c32cbSDag-Erling Smørgrav  * passed then they entry will be invalidated.
4558462c32cbSDag-Erling Smørgrav  */
4559462c32cbSDag-Erling Smørgrav void
4560190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport)
4561462c32cbSDag-Erling Smørgrav {
4562190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &ssh->chanctxt->local_perms;
45634f52dfbbSDag-Erling Smørgrav 
4564190cef3dSDag-Erling Smørgrav 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
456519261079SEd Maste 		debug_f("index out of range: %d num_permitted_user %d",
456619261079SEd Maste 		    idx, pset->num_permitted_user);
4567462c32cbSDag-Erling Smørgrav 		return;
4568462c32cbSDag-Erling Smørgrav 	}
4569462c32cbSDag-Erling Smørgrav 	debug("%s allowed port %d for forwarding to host %s port %d",
4570462c32cbSDag-Erling Smørgrav 	    newport > 0 ? "Updating" : "Removing",
4571462c32cbSDag-Erling Smørgrav 	    newport,
4572190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].host_to_connect,
4573190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].port_to_connect);
45744f52dfbbSDag-Erling Smørgrav 	if (newport <= 0)
4575190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(&pset->permitted_user[idx]);
45764f52dfbbSDag-Erling Smørgrav 	else {
4577190cef3dSDag-Erling Smørgrav 		pset->permitted_user[idx].listen_port =
457819261079SEd Maste 		    (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4579462c32cbSDag-Erling Smørgrav 	}
4580462c32cbSDag-Erling Smørgrav }
4581462c32cbSDag-Erling Smørgrav 
4582462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
4583462c32cbSDag-Erling Smørgrav int
4584462c32cbSDag-Erling Smørgrav permitopen_port(const char *p)
4585462c32cbSDag-Erling Smørgrav {
4586462c32cbSDag-Erling Smørgrav 	int port;
4587462c32cbSDag-Erling Smørgrav 
4588462c32cbSDag-Erling Smørgrav 	if (strcmp(p, "*") == 0)
4589462c32cbSDag-Erling Smørgrav 		return FWD_PERMIT_ANY_PORT;
4590462c32cbSDag-Erling Smørgrav 	if ((port = a2port(p)) > 0)
4591462c32cbSDag-Erling Smørgrav 		return port;
4592462c32cbSDag-Erling Smørgrav 	return -1;
4593462c32cbSDag-Erling Smørgrav }
4594462c32cbSDag-Erling Smørgrav 
4595d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */
4596d4af9e69SDag-Erling Smørgrav static int
4597d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx)
4598d4af9e69SDag-Erling Smørgrav {
4599d4af9e69SDag-Erling Smørgrav 	int sock, saved_errno;
4600a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un *sunaddr;
46014f52dfbbSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
46024f52dfbbSDag-Erling Smørgrav 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4603d4af9e69SDag-Erling Smørgrav 
4604d4af9e69SDag-Erling Smørgrav 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
4605a0ee8cc6SDag-Erling Smørgrav 		switch (cctx->ai->ai_family) {
4606a0ee8cc6SDag-Erling Smørgrav 		case AF_UNIX:
4607a0ee8cc6SDag-Erling Smørgrav 			/* unix:pathname instead of host:port */
4608a0ee8cc6SDag-Erling Smørgrav 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
4609a0ee8cc6SDag-Erling Smørgrav 			strlcpy(ntop, "unix", sizeof(ntop));
4610a0ee8cc6SDag-Erling Smørgrav 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
4611a0ee8cc6SDag-Erling Smørgrav 			break;
4612a0ee8cc6SDag-Erling Smørgrav 		case AF_INET:
4613a0ee8cc6SDag-Erling Smørgrav 		case AF_INET6:
4614d4af9e69SDag-Erling Smørgrav 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4615d4af9e69SDag-Erling Smørgrav 			    ntop, sizeof(ntop), strport, sizeof(strport),
4616d4af9e69SDag-Erling Smørgrav 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
461738a52bd3SEd Maste 				error_f("getnameinfo failed");
4618511b41d2SMark Murray 				continue;
4619511b41d2SMark Murray 			}
4620a0ee8cc6SDag-Erling Smørgrav 			break;
4621a0ee8cc6SDag-Erling Smørgrav 		default:
4622a0ee8cc6SDag-Erling Smørgrav 			continue;
4623a0ee8cc6SDag-Erling Smørgrav 		}
462438a52bd3SEd Maste 		debug_f("start for host %.100s ([%.100s]:%s)",
462538a52bd3SEd Maste 		    cctx->host, ntop, strport);
4626d4af9e69SDag-Erling Smørgrav 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4627d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_protocol)) == -1) {
4628d4af9e69SDag-Erling Smørgrav 			if (cctx->ai->ai_next == NULL)
4629511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
4630e73e9afaSDag-Erling Smørgrav 			else
4631e73e9afaSDag-Erling Smørgrav 				verbose("socket: %.100s", strerror(errno));
4632511b41d2SMark Murray 			continue;
4633511b41d2SMark Murray 		}
463421e764dfSDag-Erling Smørgrav 		if (set_nonblock(sock) == -1)
463519261079SEd Maste 			fatal_f("set_nonblock(%d)", sock);
4636d4af9e69SDag-Erling Smørgrav 		if (connect(sock, cctx->ai->ai_addr,
4637d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
463838a52bd3SEd Maste 			debug_f("host %.100s ([%.100s]:%s): %.100s",
463938a52bd3SEd Maste 			    cctx->host, ntop, strport, strerror(errno));
4640d4af9e69SDag-Erling Smørgrav 			saved_errno = errno;
4641511b41d2SMark Murray 			close(sock);
4642d4af9e69SDag-Erling Smørgrav 			errno = saved_errno;
4643511b41d2SMark Murray 			continue;	/* fail -- try next */
4644511b41d2SMark Murray 		}
4645a0ee8cc6SDag-Erling Smørgrav 		if (cctx->ai->ai_family != AF_UNIX)
4646a0ee8cc6SDag-Erling Smørgrav 			set_nodelay(sock);
464738a52bd3SEd Maste 		debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d",
464838a52bd3SEd Maste 		    cctx->host, ntop, strport, sock);
4649d4af9e69SDag-Erling Smørgrav 		cctx->ai = cctx->ai->ai_next;
4650a04a10f8SKris Kennaway 		return sock;
4651a04a10f8SKris Kennaway 	}
4652ca3176e7SBrian Feldman 	return -1;
4653ca3176e7SBrian Feldman }
4654ca3176e7SBrian Feldman 
4655d4af9e69SDag-Erling Smørgrav static void
4656d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx)
4657d4af9e69SDag-Erling Smørgrav {
4658e4a9863fSDag-Erling Smørgrav 	free(cctx->host);
4659a0ee8cc6SDag-Erling Smørgrav 	if (cctx->aitop) {
4660a0ee8cc6SDag-Erling Smørgrav 		if (cctx->aitop->ai_family == AF_UNIX)
4661a0ee8cc6SDag-Erling Smørgrav 			free(cctx->aitop);
4662a0ee8cc6SDag-Erling Smørgrav 		else
4663d4af9e69SDag-Erling Smørgrav 			freeaddrinfo(cctx->aitop);
4664a0ee8cc6SDag-Erling Smørgrav 	}
4665b83788ffSDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
4666d4af9e69SDag-Erling Smørgrav }
4667d4af9e69SDag-Erling Smørgrav 
4668d93a896eSDag-Erling Smørgrav /*
46694f52dfbbSDag-Erling Smørgrav  * Return connecting socket to remote host:port or local socket path,
4670d93a896eSDag-Erling Smørgrav  * passing back the failure reason if appropriate.
4671d93a896eSDag-Erling Smørgrav  */
46724f52dfbbSDag-Erling Smørgrav static int
46734f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
46744f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, struct channel_connect *cctx,
4675d93a896eSDag-Erling Smørgrav     int *reason, const char **errmsg)
4676d4af9e69SDag-Erling Smørgrav {
4677d4af9e69SDag-Erling Smørgrav 	struct addrinfo hints;
4678d4af9e69SDag-Erling Smørgrav 	int gaierr;
4679d4af9e69SDag-Erling Smørgrav 	int sock = -1;
4680d4af9e69SDag-Erling Smørgrav 	char strport[NI_MAXSERV];
4681a0ee8cc6SDag-Erling Smørgrav 
4682a0ee8cc6SDag-Erling Smørgrav 	if (port == PORT_STREAMLOCAL) {
4683a0ee8cc6SDag-Erling Smørgrav 		struct sockaddr_un *sunaddr;
4684a0ee8cc6SDag-Erling Smørgrav 		struct addrinfo *ai;
4685a0ee8cc6SDag-Erling Smørgrav 
4686a0ee8cc6SDag-Erling Smørgrav 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
4687a0ee8cc6SDag-Erling Smørgrav 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
46884f52dfbbSDag-Erling Smørgrav 			return -1;
4689a0ee8cc6SDag-Erling Smørgrav 		}
4690a0ee8cc6SDag-Erling Smørgrav 
4691a0ee8cc6SDag-Erling Smørgrav 		/*
4692a0ee8cc6SDag-Erling Smørgrav 		 * Fake up a struct addrinfo for AF_UNIX connections.
4693a0ee8cc6SDag-Erling Smørgrav 		 * channel_connect_ctx_free() must check ai_family
4694a0ee8cc6SDag-Erling Smørgrav 		 * and use free() not freeaddirinfo() for AF_UNIX.
4695a0ee8cc6SDag-Erling Smørgrav 		 */
4696a0ee8cc6SDag-Erling Smørgrav 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
4697a0ee8cc6SDag-Erling Smørgrav 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
4698a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addr = (struct sockaddr *)(ai + 1);
4699a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addrlen = sizeof(*sunaddr);
4700a0ee8cc6SDag-Erling Smørgrav 		ai->ai_family = AF_UNIX;
47014f52dfbbSDag-Erling Smørgrav 		ai->ai_socktype = socktype;
4702a0ee8cc6SDag-Erling Smørgrav 		ai->ai_protocol = PF_UNSPEC;
4703a0ee8cc6SDag-Erling Smørgrav 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
4704a0ee8cc6SDag-Erling Smørgrav 		sunaddr->sun_family = AF_UNIX;
4705a0ee8cc6SDag-Erling Smørgrav 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
47064f52dfbbSDag-Erling Smørgrav 		cctx->aitop = ai;
4707a0ee8cc6SDag-Erling Smørgrav 	} else {
4708d4af9e69SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
47094f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
47104f52dfbbSDag-Erling Smørgrav 		hints.ai_socktype = socktype;
4711d4af9e69SDag-Erling Smørgrav 		snprintf(strport, sizeof strport, "%d", port);
47124f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
4713d93a896eSDag-Erling Smørgrav 		    != 0) {
4714d93a896eSDag-Erling Smørgrav 			if (errmsg != NULL)
4715d93a896eSDag-Erling Smørgrav 				*errmsg = ssh_gai_strerror(gaierr);
4716d93a896eSDag-Erling Smørgrav 			if (reason != NULL)
4717d93a896eSDag-Erling Smørgrav 				*reason = SSH2_OPEN_CONNECT_FAILED;
4718a0ee8cc6SDag-Erling Smørgrav 			error("connect_to %.100s: unknown host (%s)", name,
4719d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
47204f52dfbbSDag-Erling Smørgrav 			return -1;
4721d4af9e69SDag-Erling Smørgrav 		}
4722a0ee8cc6SDag-Erling Smørgrav 	}
4723d4af9e69SDag-Erling Smørgrav 
47244f52dfbbSDag-Erling Smørgrav 	cctx->host = xstrdup(name);
47254f52dfbbSDag-Erling Smørgrav 	cctx->port = port;
47264f52dfbbSDag-Erling Smørgrav 	cctx->ai = cctx->aitop;
4727d4af9e69SDag-Erling Smørgrav 
47284f52dfbbSDag-Erling Smørgrav 	if ((sock = connect_next(cctx)) == -1) {
4729d4af9e69SDag-Erling Smørgrav 		error("connect to %.100s port %d failed: %s",
4730a0ee8cc6SDag-Erling Smørgrav 		    name, port, strerror(errno));
47314f52dfbbSDag-Erling Smørgrav 		return -1;
4732d4af9e69SDag-Erling Smørgrav 	}
47334f52dfbbSDag-Erling Smørgrav 
47344f52dfbbSDag-Erling Smørgrav 	return sock;
4735d4af9e69SDag-Erling Smørgrav }
4736d4af9e69SDag-Erling Smørgrav 
4737d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */
4738d93a896eSDag-Erling Smørgrav static Channel *
47394f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port,
47404f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4741d93a896eSDag-Erling Smørgrav {
47424f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
47434f52dfbbSDag-Erling Smørgrav 	Channel *c;
47444f52dfbbSDag-Erling Smørgrav 	int sock;
47454f52dfbbSDag-Erling Smørgrav 
47464f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
47474f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
47484f52dfbbSDag-Erling Smørgrav 	    &cctx, NULL, NULL);
47494f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
47504f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
47514f52dfbbSDag-Erling Smørgrav 		return NULL;
47524f52dfbbSDag-Erling Smørgrav 	}
47534f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
47544f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
47554f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
47564f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
47574f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
47584f52dfbbSDag-Erling Smørgrav 
47594f52dfbbSDag-Erling Smørgrav 	return c;
4760d93a896eSDag-Erling Smørgrav }
4761d93a896eSDag-Erling Smørgrav 
4762ca86bcf2SDag-Erling Smørgrav /*
4763ca86bcf2SDag-Erling Smørgrav  * returns either the newly connected channel or the downstream channel
4764ca86bcf2SDag-Erling Smørgrav  * that needs to deal with this connection.
4765ca86bcf2SDag-Erling Smørgrav  */
4766d4af9e69SDag-Erling Smørgrav Channel *
47674f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
4768a0ee8cc6SDag-Erling Smørgrav     u_short listen_port, char *ctype, char *rname)
4769d4af9e69SDag-Erling Smørgrav {
47704f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4771190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
47724f52dfbbSDag-Erling Smørgrav 	u_int i;
4773190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4774d4af9e69SDag-Erling Smørgrav 
4775190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4776190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4777190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm,
4778190cef3dSDag-Erling Smørgrav 		    listen_host, listen_port, 1)) {
4779190cef3dSDag-Erling Smørgrav 			if (perm->downstream)
4780190cef3dSDag-Erling Smørgrav 				return perm->downstream;
4781190cef3dSDag-Erling Smørgrav 			if (perm->port_to_connect == 0)
47824f52dfbbSDag-Erling Smørgrav 				return rdynamic_connect_prepare(ssh,
47834f52dfbbSDag-Erling Smørgrav 				    ctype, rname);
47844f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4785190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
47864f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4787d4af9e69SDag-Erling Smørgrav 		}
4788d4af9e69SDag-Erling Smørgrav 	}
4789d4af9e69SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4790d4af9e69SDag-Erling Smørgrav 	    listen_port);
4791d4af9e69SDag-Erling Smørgrav 	return NULL;
4792d4af9e69SDag-Erling Smørgrav }
4793d4af9e69SDag-Erling Smørgrav 
4794a0ee8cc6SDag-Erling Smørgrav Channel *
47954f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path,
47964f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4797a0ee8cc6SDag-Erling Smørgrav {
47984f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4799190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48004f52dfbbSDag-Erling Smørgrav 	u_int i;
4801190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4802a0ee8cc6SDag-Erling Smørgrav 
4803190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4804190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4805190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path)) {
48064f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4807190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
48084f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4809a0ee8cc6SDag-Erling Smørgrav 		}
4810a0ee8cc6SDag-Erling Smørgrav 	}
4811a0ee8cc6SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown path %.100s",
4812a0ee8cc6SDag-Erling Smørgrav 	    path);
4813a0ee8cc6SDag-Erling Smørgrav 	return NULL;
4814a0ee8cc6SDag-Erling Smørgrav }
4815a0ee8cc6SDag-Erling Smørgrav 
4816ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */
4817d4af9e69SDag-Erling Smørgrav Channel *
48184f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
48194f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, int *reason, const char **errmsg)
4820ca3176e7SBrian Feldman {
48214f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4822190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48234f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
48244f52dfbbSDag-Erling Smørgrav 	Channel *c;
48254f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
48264f52dfbbSDag-Erling Smørgrav 	int sock;
4827190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4828ca3176e7SBrian Feldman 
4829190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4830ca3176e7SBrian Feldman 	if (!permit) {
4831190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4832190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4833190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4834ca3176e7SBrian Feldman 				permit = 1;
4835a0ee8cc6SDag-Erling Smørgrav 				break;
4836a0ee8cc6SDag-Erling Smørgrav 			}
4837ca3176e7SBrian Feldman 		}
48384f52dfbbSDag-Erling Smørgrav 	}
4839333ee039SDag-Erling Smørgrav 
4840190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4841333ee039SDag-Erling Smørgrav 		permit_adm = 0;
4842190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4843190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4844190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4845333ee039SDag-Erling Smørgrav 				permit_adm = 1;
4846a0ee8cc6SDag-Erling Smørgrav 				break;
4847a0ee8cc6SDag-Erling Smørgrav 			}
4848333ee039SDag-Erling Smørgrav 		}
48494f52dfbbSDag-Erling Smørgrav 	}
4850333ee039SDag-Erling Smørgrav 
4851333ee039SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
485219261079SEd Maste 		logit("Received request from %.100s port %d to connect to "
485319261079SEd Maste 		    "host %.100s port %d, but the request was denied.",
485419261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port);
4855d93a896eSDag-Erling Smørgrav 		if (reason != NULL)
4856d93a896eSDag-Erling Smørgrav 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4857d4af9e69SDag-Erling Smørgrav 		return NULL;
4858ca3176e7SBrian Feldman 	}
48594f52dfbbSDag-Erling Smørgrav 
48604f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
48614f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
48624f52dfbbSDag-Erling Smørgrav 	    &cctx, reason, errmsg);
48634f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
48644f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
48654f52dfbbSDag-Erling Smørgrav 		return NULL;
48664f52dfbbSDag-Erling Smørgrav 	}
48674f52dfbbSDag-Erling Smørgrav 
48684f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
48694f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
48704f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
48714f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
48724f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
48734f52dfbbSDag-Erling Smørgrav 
48744f52dfbbSDag-Erling Smørgrav 	return c;
4875ca3176e7SBrian Feldman }
4876ca3176e7SBrian Feldman 
4877a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */
4878a0ee8cc6SDag-Erling Smørgrav Channel *
48794f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path,
48804f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4881a0ee8cc6SDag-Erling Smørgrav {
48824f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4883190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48844f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4885190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4886a0ee8cc6SDag-Erling Smørgrav 
4887190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4888a0ee8cc6SDag-Erling Smørgrav 	if (!permit) {
4889190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4890190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4891190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4892a0ee8cc6SDag-Erling Smørgrav 				permit = 1;
4893a0ee8cc6SDag-Erling Smørgrav 				break;
4894a0ee8cc6SDag-Erling Smørgrav 			}
4895a0ee8cc6SDag-Erling Smørgrav 		}
48964f52dfbbSDag-Erling Smørgrav 	}
4897a0ee8cc6SDag-Erling Smørgrav 
4898190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4899a0ee8cc6SDag-Erling Smørgrav 		permit_adm = 0;
4900190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4901190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4902190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4903a0ee8cc6SDag-Erling Smørgrav 				permit_adm = 1;
4904a0ee8cc6SDag-Erling Smørgrav 				break;
4905a0ee8cc6SDag-Erling Smørgrav 			}
4906a0ee8cc6SDag-Erling Smørgrav 		}
49074f52dfbbSDag-Erling Smørgrav 	}
4908a0ee8cc6SDag-Erling Smørgrav 
4909a0ee8cc6SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4910a0ee8cc6SDag-Erling Smørgrav 		logit("Received request to connect to path %.100s, "
4911a0ee8cc6SDag-Erling Smørgrav 		    "but the request was denied.", path);
4912a0ee8cc6SDag-Erling Smørgrav 		return NULL;
4913a0ee8cc6SDag-Erling Smørgrav 	}
49144f52dfbbSDag-Erling Smørgrav 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
4915a0ee8cc6SDag-Erling Smørgrav }
4916a0ee8cc6SDag-Erling Smørgrav 
491721e764dfSDag-Erling Smørgrav void
49184f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh)
491921e764dfSDag-Erling Smørgrav {
49204f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
492121e764dfSDag-Erling Smørgrav 	struct winsize ws;
49224f52dfbbSDag-Erling Smørgrav 	int r;
49234f52dfbbSDag-Erling Smørgrav 	u_int i;
492421e764dfSDag-Erling Smørgrav 
49254f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
49264f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
49274f52dfbbSDag-Erling Smørgrav 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
492821e764dfSDag-Erling Smørgrav 			continue;
492919261079SEd Maste 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1)
493021e764dfSDag-Erling Smørgrav 			continue;
49314f52dfbbSDag-Erling Smørgrav 		channel_request_start(ssh, i, "window-change", 0);
49324f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
49334f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
49344f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
49354f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
49364f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0)
493719261079SEd Maste 			fatal_fr(r, "channel %u; send window-change", i);
493821e764dfSDag-Erling Smørgrav 	}
493921e764dfSDag-Erling Smørgrav }
494021e764dfSDag-Erling Smørgrav 
49414f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
49424f52dfbbSDag-Erling Smørgrav static Channel *
49434f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
49444f52dfbbSDag-Erling Smørgrav {
49454f52dfbbSDag-Erling Smørgrav 	Channel *c;
49464f52dfbbSDag-Erling Smørgrav 	int r;
49474f52dfbbSDag-Erling Smørgrav 
49484f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
49494f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
49504f52dfbbSDag-Erling Smørgrav 	c->host_port = 0;
49514f52dfbbSDag-Erling Smørgrav 	c->path = NULL;
49524f52dfbbSDag-Erling Smørgrav 
49534f52dfbbSDag-Erling Smørgrav 	/*
49544f52dfbbSDag-Erling Smørgrav 	 * We need to open the channel before we have a FD,
49554f52dfbbSDag-Erling Smørgrav 	 * so that we can get SOCKS header from peer.
49564f52dfbbSDag-Erling Smørgrav 	 */
49574f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
49584f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
49594f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
49604f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
496119261079SEd Maste 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0)
496219261079SEd Maste 		fatal_fr(r, "channel %i; confirm", c->self);
49634f52dfbbSDag-Erling Smørgrav 	return c;
49644f52dfbbSDag-Erling Smørgrav }
49654f52dfbbSDag-Erling Smørgrav 
49664f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */
49674f52dfbbSDag-Erling Smørgrav static int
49684f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c)
49694f52dfbbSDag-Erling Smørgrav {
497019261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
497119261079SEd Maste 	struct permission_set *pset = &sc->local_perms;
497219261079SEd Maste 	struct permission *perm;
49734f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
497419261079SEd Maste 	u_int i, permit_adm = 1;
49754f52dfbbSDag-Erling Smørgrav 	int sock;
49764f52dfbbSDag-Erling Smørgrav 
497719261079SEd Maste 	if (pset->num_permitted_admin > 0) {
497819261079SEd Maste 		permit_adm = 0;
497919261079SEd Maste 		for (i = 0; i < pset->num_permitted_admin; i++) {
498019261079SEd Maste 			perm = &pset->permitted_admin[i];
498119261079SEd Maste 			if (open_match(perm, c->path, c->host_port)) {
498219261079SEd Maste 				permit_adm = 1;
498319261079SEd Maste 				break;
498419261079SEd Maste 			}
498519261079SEd Maste 		}
498619261079SEd Maste 	}
498719261079SEd Maste 	if (!permit_adm) {
498819261079SEd Maste 		debug_f("requested forward not permitted");
498919261079SEd Maste 		return -1;
499019261079SEd Maste 	}
499119261079SEd Maste 
49924f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
49934f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
49944f52dfbbSDag-Erling Smørgrav 	    NULL, &cctx, NULL, NULL);
49954f52dfbbSDag-Erling Smørgrav 	if (sock == -1)
49964f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
49974f52dfbbSDag-Erling Smørgrav 	else {
49984f52dfbbSDag-Erling Smørgrav 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
49994f52dfbbSDag-Erling Smørgrav 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
50004f52dfbbSDag-Erling Smørgrav 		c->connect_ctx = cctx;
50014f52dfbbSDag-Erling Smørgrav 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
50024f52dfbbSDag-Erling Smørgrav 	}
50034f52dfbbSDag-Erling Smørgrav 	return sock;
50044f52dfbbSDag-Erling Smørgrav }
50054f52dfbbSDag-Erling Smørgrav 
5006af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */
5007511b41d2SMark Murray 
5008511b41d2SMark Murray /*
5009511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
5010a82e551fSDag-Erling Smørgrav  * Returns 0 and a suitable display number for the DISPLAY variable
5011a82e551fSDag-Erling Smørgrav  * stored in display_numberp , or -1 if an error occurs.
5012511b41d2SMark Murray  */
5013af12a3e7SDag-Erling Smørgrav int
50144f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
50154f52dfbbSDag-Erling Smørgrav     int x11_use_localhost, int single_connection,
50164f52dfbbSDag-Erling Smørgrav     u_int *display_numberp, int **chanids)
5017511b41d2SMark Murray {
5018af12a3e7SDag-Erling Smørgrav 	Channel *nc = NULL;
5019511b41d2SMark Murray 	int display_number, sock;
5020511b41d2SMark Murray 	u_short port;
5021511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
5022511b41d2SMark Murray 	char strport[NI_MAXSERV];
5023511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
5024511b41d2SMark Murray 
5025b74df5b2SDag-Erling Smørgrav 	if (chanids == NULL)
5026b74df5b2SDag-Erling Smørgrav 		return -1;
5027b74df5b2SDag-Erling Smørgrav 
5028511b41d2SMark Murray 	for (display_number = x11_display_offset;
5029511b41d2SMark Murray 	    display_number < MAX_DISPLAYS;
5030511b41d2SMark Murray 	    display_number++) {
5031511b41d2SMark Murray 		port = 6000 + display_number;
5032511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
50334f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
5034af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
5035511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
5036511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
50374f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(NULL, strport,
50384f52dfbbSDag-Erling Smørgrav 		    &hints, &aitop)) != 0) {
5039d4af9e69SDag-Erling Smørgrav 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
5040af12a3e7SDag-Erling Smørgrav 			return -1;
5041511b41d2SMark Murray 		}
5042511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
50434f52dfbbSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
50444f52dfbbSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
5045511b41d2SMark Murray 				continue;
5046221552e4SDag-Erling Smørgrav 			sock = socket(ai->ai_family, ai->ai_socktype,
5047221552e4SDag-Erling Smørgrav 			    ai->ai_protocol);
504819261079SEd Maste 			if (sock == -1) {
50498ad9b54aSDag-Erling Smørgrav 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
50508ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT
50518ad9b54aSDag-Erling Smørgrav 				    && (errno != EPFNOSUPPORT)
50528ad9b54aSDag-Erling Smørgrav #endif
50538ad9b54aSDag-Erling Smørgrav 				    ) {
5054511b41d2SMark Murray 					error("socket: %.100s", strerror(errno));
505521e764dfSDag-Erling Smørgrav 					freeaddrinfo(aitop);
5056af12a3e7SDag-Erling Smørgrav 					return -1;
5057989dd127SDag-Erling Smørgrav 				} else {
5058989dd127SDag-Erling Smørgrav 					debug("x11_create_display_inet: Socket family %d not supported",
5059989dd127SDag-Erling Smørgrav 						 ai->ai_family);
5060989dd127SDag-Erling Smørgrav 					continue;
5061511b41d2SMark Murray 				}
5062989dd127SDag-Erling Smørgrav 			}
5063b15c8340SDag-Erling Smørgrav 			if (ai->ai_family == AF_INET6)
5064b15c8340SDag-Erling Smørgrav 				sock_set_v6only(sock);
5065d4af9e69SDag-Erling Smørgrav 			if (x11_use_localhost)
506647dd1d1bSDag-Erling Smørgrav 				set_reuseaddr(sock);
506719261079SEd Maste 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
506819261079SEd Maste 				debug2_f("bind port %d: %.100s", port,
506919261079SEd Maste 				    strerror(errno));
5070511b41d2SMark Murray 				close(sock);
50714f52dfbbSDag-Erling Smørgrav 				for (n = 0; n < num_socks; n++)
5072511b41d2SMark Murray 					close(socks[n]);
5073511b41d2SMark Murray 				num_socks = 0;
5074511b41d2SMark Murray 				break;
5075511b41d2SMark Murray 			}
5076511b41d2SMark Murray 			socks[num_socks++] = sock;
5077511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
5078511b41d2SMark Murray 				break;
5079511b41d2SMark Murray 		}
5080ca3176e7SBrian Feldman 		freeaddrinfo(aitop);
5081511b41d2SMark Murray 		if (num_socks > 0)
5082511b41d2SMark Murray 			break;
5083511b41d2SMark Murray 	}
5084511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
5085511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
5086af12a3e7SDag-Erling Smørgrav 		return -1;
5087511b41d2SMark Murray 	}
5088511b41d2SMark Murray 	/* Start listening for connections on the socket. */
5089511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5090511b41d2SMark Murray 		sock = socks[n];
509119261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
5092511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
5093511b41d2SMark Murray 			close(sock);
5094af12a3e7SDag-Erling Smørgrav 			return -1;
5095511b41d2SMark Murray 		}
5096511b41d2SMark Murray 	}
5097511b41d2SMark Murray 
5098511b41d2SMark Murray 	/* Allocate a channel for each socket. */
5099333ee039SDag-Erling Smørgrav 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
5100511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5101511b41d2SMark Murray 		sock = socks[n];
5102f374ba41SEd Maste 		nc = channel_new(ssh, "x11-listener",
5103a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
5104a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
5105221552e4SDag-Erling Smørgrav 		    0, "X11 inet listener", 1);
5106af12a3e7SDag-Erling Smørgrav 		nc->single_connection = single_connection;
5107d4ecd108SDag-Erling Smørgrav 		(*chanids)[n] = nc->self;
5108511b41d2SMark Murray 	}
5109d4ecd108SDag-Erling Smørgrav 	(*chanids)[n] = -1;
5110511b41d2SMark Murray 
5111af12a3e7SDag-Erling Smørgrav 	/* Return the display number for the DISPLAY environment variable. */
5112a82e551fSDag-Erling Smørgrav 	*display_numberp = display_number;
51134f52dfbbSDag-Erling Smørgrav 	return 0;
5114511b41d2SMark Murray }
5115511b41d2SMark Murray 
5116af12a3e7SDag-Erling Smørgrav static int
5117cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname)
5118511b41d2SMark Murray {
5119511b41d2SMark Murray 	int sock;
5120511b41d2SMark Murray 	struct sockaddr_un addr;
5121511b41d2SMark Murray 
5122511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
5123535af610SEd Maste 	if (sock == -1) {
5124511b41d2SMark Murray 		error("socket: %.100s", strerror(errno));
5125535af610SEd Maste 		return -1;
5126535af610SEd Maste 	}
5127511b41d2SMark Murray 	memset(&addr, 0, sizeof(addr));
5128511b41d2SMark Murray 	addr.sun_family = AF_UNIX;
5129cce7d346SDag-Erling Smørgrav 	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
5130511b41d2SMark Murray 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
5131511b41d2SMark Murray 		return sock;
5132511b41d2SMark Murray 	close(sock);
5133511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
5134511b41d2SMark Murray 	return -1;
5135511b41d2SMark Murray }
5136511b41d2SMark Murray 
5137cce7d346SDag-Erling Smørgrav static int
5138cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr)
5139cce7d346SDag-Erling Smørgrav {
5140cce7d346SDag-Erling Smørgrav 	char buf[1024];
5141cce7d346SDag-Erling Smørgrav 	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
5142cce7d346SDag-Erling Smørgrav 	return connect_local_xsocket_path(buf);
5143cce7d346SDag-Erling Smørgrav }
5144cce7d346SDag-Erling Smørgrav 
5145d93a896eSDag-Erling Smørgrav #ifdef __APPLE__
5146d93a896eSDag-Erling Smørgrav static int
5147d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen)
5148d93a896eSDag-Erling Smørgrav {
5149d93a896eSDag-Erling Smørgrav 	struct stat sbuf;
5150d93a896eSDag-Erling Smørgrav 
5151d93a896eSDag-Erling Smørgrav 	if (strlcpy(path, display, pathlen) >= pathlen) {
5152d93a896eSDag-Erling Smørgrav 		error("%s: display path too long", __func__);
5153d93a896eSDag-Erling Smørgrav 		return 0;
5154d93a896eSDag-Erling Smørgrav 	}
5155d93a896eSDag-Erling Smørgrav 	if (display[0] != '/')
5156d93a896eSDag-Erling Smørgrav 		return 0;
5157d93a896eSDag-Erling Smørgrav 	if (stat(path, &sbuf) == 0) {
5158d93a896eSDag-Erling Smørgrav 		return 1;
5159d93a896eSDag-Erling Smørgrav 	} else {
5160d93a896eSDag-Erling Smørgrav 		char *dot = strrchr(path, '.');
5161d93a896eSDag-Erling Smørgrav 		if (dot != NULL) {
5162d93a896eSDag-Erling Smørgrav 			*dot = '\0';
5163d93a896eSDag-Erling Smørgrav 			if (stat(path, &sbuf) == 0) {
5164d93a896eSDag-Erling Smørgrav 				return 1;
5165d93a896eSDag-Erling Smørgrav 			}
5166d93a896eSDag-Erling Smørgrav 		}
5167d93a896eSDag-Erling Smørgrav 	}
5168d93a896eSDag-Erling Smørgrav 	return 0;
5169d93a896eSDag-Erling Smørgrav }
5170d93a896eSDag-Erling Smørgrav #endif
5171d93a896eSDag-Erling Smørgrav 
5172a04a10f8SKris Kennaway int
51734f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh)
5174511b41d2SMark Murray {
5175333ee039SDag-Erling Smørgrav 	u_int display_number;
5176511b41d2SMark Murray 	const char *display;
5177a04a10f8SKris Kennaway 	char buf[1024], *cp;
5178511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
5179511b41d2SMark Murray 	char strport[NI_MAXSERV];
5180333ee039SDag-Erling Smørgrav 	int gaierr, sock = 0;
5181511b41d2SMark Murray 
5182511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
5183511b41d2SMark Murray 	display = getenv("DISPLAY");
5184511b41d2SMark Murray 	if (!display) {
5185511b41d2SMark Murray 		error("DISPLAY not set.");
5186a04a10f8SKris Kennaway 		return -1;
5187511b41d2SMark Murray 	}
5188511b41d2SMark Murray 	/*
5189511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
5190511b41d2SMark Murray 	 * connection to the real X server.
5191511b41d2SMark Murray 	 */
5192511b41d2SMark Murray 
5193cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
5194d93a896eSDag-Erling Smørgrav 	/* Check if display is a path to a socket (as set by launchd). */
5195d93a896eSDag-Erling Smørgrav 	{
5196d93a896eSDag-Erling Smørgrav 		char path[PATH_MAX];
5197d93a896eSDag-Erling Smørgrav 
5198d93a896eSDag-Erling Smørgrav 		if (is_path_to_xsocket(display, path, sizeof(path))) {
5199d93a896eSDag-Erling Smørgrav 			debug("x11_connect_display: $DISPLAY is launchd");
5200d93a896eSDag-Erling Smørgrav 
5201d93a896eSDag-Erling Smørgrav 			/* Create a socket. */
5202d93a896eSDag-Erling Smørgrav 			sock = connect_local_xsocket_path(path);
5203cce7d346SDag-Erling Smørgrav 			if (sock < 0)
5204cce7d346SDag-Erling Smørgrav 				return -1;
5205cce7d346SDag-Erling Smørgrav 
5206cce7d346SDag-Erling Smørgrav 			/* OK, we now have a connection to the display. */
5207cce7d346SDag-Erling Smørgrav 			return sock;
5208cce7d346SDag-Erling Smørgrav 		}
5209d93a896eSDag-Erling Smørgrav 	}
5210cce7d346SDag-Erling Smørgrav #endif
5211511b41d2SMark Murray 	/*
5212511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
5213511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
5214511b41d2SMark Murray 	 */
5215511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
5216511b41d2SMark Murray 	    display[0] == ':') {
5217511b41d2SMark Murray 		/* Connect to the unix domain socket. */
52184f52dfbbSDag-Erling Smørgrav 		if (sscanf(strrchr(display, ':') + 1, "%u",
52194f52dfbbSDag-Erling Smørgrav 		    &display_number) != 1) {
52204f52dfbbSDag-Erling Smørgrav 			error("Could not parse display number from DISPLAY: "
52214f52dfbbSDag-Erling Smørgrav 			    "%.100s", display);
5222a04a10f8SKris Kennaway 			return -1;
5223511b41d2SMark Murray 		}
5224511b41d2SMark Murray 		/* Create a socket. */
5225511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
5226511b41d2SMark Murray 		if (sock < 0)
5227a04a10f8SKris Kennaway 			return -1;
5228511b41d2SMark Murray 
5229511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
5230a04a10f8SKris Kennaway 		return sock;
5231511b41d2SMark Murray 	}
5232511b41d2SMark Murray 	/*
5233511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
5234511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
5235511b41d2SMark Murray 	 */
5236af12a3e7SDag-Erling Smørgrav 	strlcpy(buf, display, sizeof(buf));
5237511b41d2SMark Murray 	cp = strchr(buf, ':');
5238511b41d2SMark Murray 	if (!cp) {
5239511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
5240a04a10f8SKris Kennaway 		return -1;
5241511b41d2SMark Murray 	}
5242511b41d2SMark Murray 	*cp = 0;
52434f52dfbbSDag-Erling Smørgrav 	/*
52444f52dfbbSDag-Erling Smørgrav 	 * buf now contains the host name.  But first we parse the
52454f52dfbbSDag-Erling Smørgrav 	 * display number.
52464f52dfbbSDag-Erling Smørgrav 	 */
5247333ee039SDag-Erling Smørgrav 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
5248511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
5249511b41d2SMark Murray 		    display);
5250a04a10f8SKris Kennaway 		return -1;
5251511b41d2SMark Murray 	}
5252511b41d2SMark Murray 
5253511b41d2SMark Murray 	/* Look up the host address */
5254511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
52554f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
5256511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
5257333ee039SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
5258511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
5259d4af9e69SDag-Erling Smørgrav 		error("%.100s: unknown host. (%s)", buf,
5260d4af9e69SDag-Erling Smørgrav 		ssh_gai_strerror(gaierr));
5261a04a10f8SKris Kennaway 		return -1;
5262511b41d2SMark Murray 	}
5263511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
5264511b41d2SMark Murray 		/* Create a socket. */
5265221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
526619261079SEd Maste 		if (sock == -1) {
5267221552e4SDag-Erling Smørgrav 			debug2("socket: %.100s", strerror(errno));
5268511b41d2SMark Murray 			continue;
5269511b41d2SMark Murray 		}
5270511b41d2SMark Murray 		/* Connect it to the display. */
527119261079SEd Maste 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
5272333ee039SDag-Erling Smørgrav 			debug2("connect %.100s port %u: %.100s", buf,
5273a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
5274511b41d2SMark Murray 			close(sock);
5275511b41d2SMark Murray 			continue;
5276511b41d2SMark Murray 		}
5277511b41d2SMark Murray 		/* Success */
5278511b41d2SMark Murray 		break;
5279a04a10f8SKris Kennaway 	}
5280511b41d2SMark Murray 	freeaddrinfo(aitop);
5281511b41d2SMark Murray 	if (!ai) {
52824f52dfbbSDag-Erling Smørgrav 		error("connect %.100s port %u: %.100s", buf,
52834f52dfbbSDag-Erling Smørgrav 		    6000 + display_number, strerror(errno));
5284a04a10f8SKris Kennaway 		return -1;
5285511b41d2SMark Murray 	}
5286af12a3e7SDag-Erling Smørgrav 	set_nodelay(sock);
5287a04a10f8SKris Kennaway 	return sock;
5288a04a10f8SKris Kennaway }
5289511b41d2SMark Murray 
5290a04a10f8SKris Kennaway /*
5291511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
5292511b41d2SMark Murray  * data, and enables authentication spoofing.
5293af12a3e7SDag-Erling Smørgrav  * This should be called in the client only.
5294511b41d2SMark Murray  */
5295511b41d2SMark Murray void
52964f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
52974f52dfbbSDag-Erling Smørgrav     const char *disp, const char *proto, const char *data, int want_reply)
5298511b41d2SMark Murray {
52994f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
5300ca3176e7SBrian Feldman 	u_int data_len = (u_int) strlen(data) / 2;
5301d4ecd108SDag-Erling Smørgrav 	u_int i, value;
5302511b41d2SMark Murray 	const char *cp;
53034f52dfbbSDag-Erling Smørgrav 	char *new_data;
53044f52dfbbSDag-Erling Smørgrav 	int r, screen_number;
5305511b41d2SMark Murray 
53064f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_display == NULL)
53074f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_display = xstrdup(disp);
53084f52dfbbSDag-Erling Smørgrav 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
5309d4ecd108SDag-Erling Smørgrav 		error("x11_request_forwarding_with_spoofing: different "
5310d4ecd108SDag-Erling Smørgrav 		    "$DISPLAY already forwarded");
5311d4ecd108SDag-Erling Smørgrav 		return;
5312d4ecd108SDag-Erling Smørgrav 	}
5313d4ecd108SDag-Erling Smørgrav 
5314d4ecd108SDag-Erling Smørgrav 	cp = strchr(disp, ':');
5315511b41d2SMark Murray 	if (cp)
5316511b41d2SMark Murray 		cp = strchr(cp, '.');
5317511b41d2SMark Murray 	if (cp)
5318333ee039SDag-Erling Smørgrav 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
5319511b41d2SMark Murray 	else
5320511b41d2SMark Murray 		screen_number = 0;
5321511b41d2SMark Murray 
53224f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_proto == NULL) {
5323511b41d2SMark Murray 		/* Save protocol name. */
53244f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_proto = xstrdup(proto);
5325ca86bcf2SDag-Erling Smørgrav 
5326ca86bcf2SDag-Erling Smørgrav 		/* Extract real authentication data. */
53274f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data = xmalloc(data_len);
5328511b41d2SMark Murray 		for (i = 0; i < data_len; i++) {
532919261079SEd Maste 			if (sscanf(data + 2 * i, "%2x", &value) != 1) {
5330d4ecd108SDag-Erling Smørgrav 				fatal("x11_request_forwarding: bad "
5331d4ecd108SDag-Erling Smørgrav 				    "authentication data: %.100s", data);
533219261079SEd Maste 			}
53334f52dfbbSDag-Erling Smørgrav 			sc->x11_saved_data[i] = value;
5334511b41d2SMark Murray 		}
53354f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data_len = data_len;
5336ca86bcf2SDag-Erling Smørgrav 
5337ca86bcf2SDag-Erling Smørgrav 		/* Generate fake data of the same length. */
53384f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data = xmalloc(data_len);
53394f52dfbbSDag-Erling Smørgrav 		arc4random_buf(sc->x11_fake_data, data_len);
53404f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data_len = data_len;
5341d4ecd108SDag-Erling Smørgrav 	}
5342511b41d2SMark Murray 
5343511b41d2SMark Murray 	/* Convert the fake data into hex. */
53444f52dfbbSDag-Erling Smørgrav 	new_data = tohex(sc->x11_fake_data, data_len);
5345511b41d2SMark Murray 
5346511b41d2SMark Murray 	/* Send the request packet. */
53474f52dfbbSDag-Erling Smørgrav 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
53484f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
53494f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
53504f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
53514f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
53524f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0 ||
53534f52dfbbSDag-Erling Smørgrav 	    (r = ssh_packet_write_wait(ssh)) != 0)
535419261079SEd Maste 		fatal_fr(r, "send x11-req");
5355e4a9863fSDag-Erling Smørgrav 	free(new_data);
5356511b41d2SMark Murray }
5357