xref: /freebsd/crypto/openssh/channels.c (revision 3d9fd9fcb432750f3716b28f6ccb0104cd9d351a)
1*3d9fd9fcSEd Maste /* $OpenBSD: channels.c,v 1.439 2024/07/25 22:40:08 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 
964f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */
974f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */
984f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS  1000
99511b41d2SMark Murray 
1001323ec57SEd Maste /* Per-channel callback for pre/post IO actions */
1011323ec57SEd Maste typedef void chan_fn(struct ssh *, Channel *c);
102190cef3dSDag-Erling Smørgrav 
103511b41d2SMark Murray /*
104511b41d2SMark Murray  * Data structure for storing which hosts are permitted for forward requests.
105511b41d2SMark Murray  * The local sides of any remote forwards are stored in this array to prevent
106511b41d2SMark Murray  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
107511b41d2SMark Murray  * network (which might be behind a firewall).
108511b41d2SMark Murray  */
109a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */
110a0ee8cc6SDag-Erling Smørgrav /*      Overload host_to_connect; we could just make this match Forward */
111a0ee8cc6SDag-Erling Smørgrav /*	XXX - can we use listen_host instead of listen_path? */
112190cef3dSDag-Erling Smørgrav struct permission {
113a04a10f8SKris Kennaway 	char *host_to_connect;		/* Connect to 'host'. */
114a0ee8cc6SDag-Erling Smørgrav 	int port_to_connect;		/* Connect to 'port'. */
115a0ee8cc6SDag-Erling Smørgrav 	char *listen_host;		/* Remote side should listen address. */
116a0ee8cc6SDag-Erling Smørgrav 	char *listen_path;		/* Remote side should listen path. */
117a0ee8cc6SDag-Erling Smørgrav 	int listen_port;		/* Remote side should listen port. */
118ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;		/* Downstream mux*/
119190cef3dSDag-Erling Smørgrav };
120511b41d2SMark Murray 
121190cef3dSDag-Erling Smørgrav /*
122190cef3dSDag-Erling Smørgrav  * Stores the forwarding permission state for a single direction (local or
123190cef3dSDag-Erling Smørgrav  * remote).
124190cef3dSDag-Erling Smørgrav  */
125190cef3dSDag-Erling Smørgrav struct permission_set {
126190cef3dSDag-Erling Smørgrav 	/*
127190cef3dSDag-Erling Smørgrav 	 * List of all local permitted host/port pairs to allow for the
128190cef3dSDag-Erling Smørgrav 	 * user.
129190cef3dSDag-Erling Smørgrav 	 */
130190cef3dSDag-Erling Smørgrav 	u_int num_permitted_user;
131190cef3dSDag-Erling Smørgrav 	struct permission *permitted_user;
132190cef3dSDag-Erling Smørgrav 
133190cef3dSDag-Erling Smørgrav 	/*
134190cef3dSDag-Erling Smørgrav 	 * List of all permitted host/port pairs to allow for the admin.
135190cef3dSDag-Erling Smørgrav 	 */
136190cef3dSDag-Erling Smørgrav 	u_int num_permitted_admin;
137190cef3dSDag-Erling Smørgrav 	struct permission *permitted_admin;
138190cef3dSDag-Erling Smørgrav 
139190cef3dSDag-Erling Smørgrav 	/*
140190cef3dSDag-Erling Smørgrav 	 * If this is true, all opens/listens are permitted.  This is the
141190cef3dSDag-Erling Smørgrav 	 * case on the server on which we have to trust the client anyway,
142190cef3dSDag-Erling Smørgrav 	 * and the user could do anything after logging in.
143190cef3dSDag-Erling Smørgrav 	 */
144190cef3dSDag-Erling Smørgrav 	int all_permitted;
145190cef3dSDag-Erling Smørgrav };
146af12a3e7SDag-Erling Smørgrav 
147f374ba41SEd Maste /* Used to record timeouts per channel type */
148f374ba41SEd Maste struct ssh_channel_timeout {
149f374ba41SEd Maste 	char *type_pattern;
150535af610SEd Maste 	int timeout_secs;
151f374ba41SEd Maste };
152f374ba41SEd Maste 
1534f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */
1544f52dfbbSDag-Erling Smørgrav struct ssh_channels {
1554f52dfbbSDag-Erling Smørgrav 	/*
1564f52dfbbSDag-Erling Smørgrav 	 * Pointer to an array containing all allocated channels.  The array
1574f52dfbbSDag-Erling Smørgrav 	 * is dynamically extended as needed.
1584f52dfbbSDag-Erling Smørgrav 	 */
1594f52dfbbSDag-Erling Smørgrav 	Channel **channels;
160076ad2f8SDag-Erling Smørgrav 
161511b41d2SMark Murray 	/*
1624f52dfbbSDag-Erling Smørgrav 	 * Size of the channel array.  All slots of the array must always be
1634f52dfbbSDag-Erling Smørgrav 	 * initialized (at least the type field); unused slots set to NULL
164511b41d2SMark Murray 	 */
1654f52dfbbSDag-Erling Smørgrav 	u_int channels_alloc;
166511b41d2SMark Murray 
1674f52dfbbSDag-Erling Smørgrav 	/*
1681323ec57SEd Maste 	 * 'channel_pre*' are called just before IO to add any bits
1691323ec57SEd Maste 	 * relevant to channels in the c->io_want bitmasks.
1704f52dfbbSDag-Erling Smørgrav 	 *
1714f52dfbbSDag-Erling Smørgrav 	 * 'channel_post*': perform any appropriate operations for
1721323ec57SEd Maste 	 * channels which have c->io_ready events pending.
1734f52dfbbSDag-Erling Smørgrav 	 */
1744f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_pre;
1754f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_post;
1764f52dfbbSDag-Erling Smørgrav 
1774f52dfbbSDag-Erling Smørgrav 	/* -- tcp forwarding */
178190cef3dSDag-Erling Smørgrav 	struct permission_set local_perms;
179190cef3dSDag-Erling Smørgrav 	struct permission_set remote_perms;
180af12a3e7SDag-Erling Smørgrav 
181af12a3e7SDag-Erling Smørgrav 	/* -- X11 forwarding */
182af12a3e7SDag-Erling Smørgrav 
183d4ecd108SDag-Erling Smørgrav 	/* Saved X11 local (client) display. */
1844f52dfbbSDag-Erling Smørgrav 	char *x11_saved_display;
185d4ecd108SDag-Erling Smørgrav 
186af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication protocol name. */
1874f52dfbbSDag-Erling Smørgrav 	char *x11_saved_proto;
188af12a3e7SDag-Erling Smørgrav 
189af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication data.  This is the real data. */
1904f52dfbbSDag-Erling Smørgrav 	char *x11_saved_data;
1914f52dfbbSDag-Erling Smørgrav 	u_int x11_saved_data_len;
192af12a3e7SDag-Erling Smørgrav 
193557f75e5SDag-Erling Smørgrav 	/* Deadline after which all X11 connections are refused */
1944d3fc8b0SEd Maste 	time_t x11_refuse_time;
195557f75e5SDag-Erling Smørgrav 
196af12a3e7SDag-Erling Smørgrav 	/*
1974f52dfbbSDag-Erling Smørgrav 	 * Fake X11 authentication data.  This is what the server will be
1984f52dfbbSDag-Erling Smørgrav 	 * sending us; we should replace any occurrences of this by the
1994f52dfbbSDag-Erling Smørgrav 	 * real data.
200af12a3e7SDag-Erling Smørgrav 	 */
2014f52dfbbSDag-Erling Smørgrav 	u_char *x11_fake_data;
2024f52dfbbSDag-Erling Smørgrav 	u_int x11_fake_data_len;
203af12a3e7SDag-Erling Smørgrav 
204ca3176e7SBrian Feldman 	/* AF_UNSPEC or AF_INET or AF_INET6 */
2054f52dfbbSDag-Erling Smørgrav 	int IPv4or6;
206f374ba41SEd Maste 
207f374ba41SEd Maste 	/* Channel timeouts by type */
208f374ba41SEd Maste 	struct ssh_channel_timeout *timeouts;
209f374ba41SEd Maste 	size_t ntimeouts;
210a91a2465SEd Maste 	/* Global timeout for all OPEN channels */
211a91a2465SEd Maste 	int global_deadline;
212a91a2465SEd Maste 	time_t lastused;
2134f52dfbbSDag-Erling Smørgrav };
214ca3176e7SBrian Feldman 
215af12a3e7SDag-Erling Smørgrav /* helper */
2164f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
217ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host);
218ca3176e7SBrian Feldman 
219d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */
220d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *);
221d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *);
2224f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
2234f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *);
2244f52dfbbSDag-Erling Smørgrav 
2254f52dfbbSDag-Erling Smørgrav /* Setup helper */
2264f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc);
227d4af9e69SDag-Erling Smørgrav 
228af12a3e7SDag-Erling Smørgrav /* -- channel core */
229a04a10f8SKris Kennaway 
2304f52dfbbSDag-Erling Smørgrav void
channel_init_channels(struct ssh * ssh)2314f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh)
2324f52dfbbSDag-Erling Smørgrav {
2334f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc;
2344f52dfbbSDag-Erling Smørgrav 
23519261079SEd Maste 	if ((sc = calloc(1, sizeof(*sc))) == NULL)
23619261079SEd Maste 		fatal_f("allocation failed");
2374f52dfbbSDag-Erling Smørgrav 	sc->channels_alloc = 10;
2384f52dfbbSDag-Erling Smørgrav 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2394f52dfbbSDag-Erling Smørgrav 	sc->IPv4or6 = AF_UNSPEC;
2404f52dfbbSDag-Erling Smørgrav 	channel_handler_init(sc);
2414f52dfbbSDag-Erling Smørgrav 
2424f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt = sc;
2434f52dfbbSDag-Erling Smørgrav }
2444f52dfbbSDag-Erling Smørgrav 
245a04a10f8SKris Kennaway Channel *
channel_by_id(struct ssh * ssh,int id)2464f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id)
247a04a10f8SKris Kennaway {
248a04a10f8SKris Kennaway 	Channel *c;
249af12a3e7SDag-Erling Smørgrav 
2504f52dfbbSDag-Erling Smørgrav 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
25119261079SEd Maste 		logit_f("%d: bad id", id);
252a04a10f8SKris Kennaway 		return NULL;
253a04a10f8SKris Kennaway 	}
2544f52dfbbSDag-Erling Smørgrav 	c = ssh->chanctxt->channels[id];
255af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
25619261079SEd Maste 		logit_f("%d: bad id: channel free", id);
257a04a10f8SKris Kennaway 		return NULL;
258a04a10f8SKris Kennaway 	}
259a04a10f8SKris Kennaway 	return c;
260a04a10f8SKris Kennaway }
261a04a10f8SKris Kennaway 
262ca86bcf2SDag-Erling Smørgrav Channel *
channel_by_remote_id(struct ssh * ssh,u_int remote_id)2634f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id)
264ca86bcf2SDag-Erling Smørgrav {
265ca86bcf2SDag-Erling Smørgrav 	Channel *c;
266ca86bcf2SDag-Erling Smørgrav 	u_int i;
267ca86bcf2SDag-Erling Smørgrav 
2684f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2694f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
2704f52dfbbSDag-Erling Smørgrav 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
271ca86bcf2SDag-Erling Smørgrav 			return c;
272ca86bcf2SDag-Erling Smørgrav 	}
273ca86bcf2SDag-Erling Smørgrav 	return NULL;
274ca86bcf2SDag-Erling Smørgrav }
275ca86bcf2SDag-Erling Smørgrav 
276a04a10f8SKris Kennaway /*
277b74df5b2SDag-Erling Smørgrav  * Returns the channel if it is allowed to receive protocol messages.
278b74df5b2SDag-Erling Smørgrav  * Private channels, like listening sockets, may not receive messages.
279b74df5b2SDag-Erling Smørgrav  */
280b74df5b2SDag-Erling Smørgrav Channel *
channel_lookup(struct ssh * ssh,int id)2814f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id)
282b74df5b2SDag-Erling Smørgrav {
283b74df5b2SDag-Erling Smørgrav 	Channel *c;
284b74df5b2SDag-Erling Smørgrav 
2854f52dfbbSDag-Erling Smørgrav 	if ((c = channel_by_id(ssh, id)) == NULL)
2864f52dfbbSDag-Erling Smørgrav 		return NULL;
287b74df5b2SDag-Erling Smørgrav 
288b74df5b2SDag-Erling Smørgrav 	switch (c->type) {
289b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_X11_OPEN:
290b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_LARVAL:
291b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_CONNECTING:
292b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_DYNAMIC:
2934f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_OPEN:
2944f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_FINISH:
295b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPENING:
296b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPEN:
297e4a9863fSDag-Erling Smørgrav 	case SSH_CHANNEL_ABANDONED:
298ca86bcf2SDag-Erling Smørgrav 	case SSH_CHANNEL_MUX_PROXY:
2994f52dfbbSDag-Erling Smørgrav 		return c;
300b74df5b2SDag-Erling Smørgrav 	}
301b74df5b2SDag-Erling Smørgrav 	logit("Non-public channel %d, type %d.", id, c->type);
3024f52dfbbSDag-Erling Smørgrav 	return NULL;
303b74df5b2SDag-Erling Smørgrav }
304b74df5b2SDag-Erling Smørgrav 
305b74df5b2SDag-Erling Smørgrav /*
306f374ba41SEd Maste  * Add a timeout for open channels whose c->ctype (or c->xctype if it is set)
307f374ba41SEd Maste  * match type_pattern.
308f374ba41SEd Maste  */
309f374ba41SEd Maste void
channel_add_timeout(struct ssh * ssh,const char * type_pattern,int timeout_secs)310f374ba41SEd Maste channel_add_timeout(struct ssh *ssh, const char *type_pattern,
311535af610SEd Maste     int timeout_secs)
312f374ba41SEd Maste {
313f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
314f374ba41SEd Maste 
315a91a2465SEd Maste 	if (strcmp(type_pattern, "global") == 0) {
316a91a2465SEd Maste 		debug2_f("global channel timeout %d seconds", timeout_secs);
317a91a2465SEd Maste 		sc->global_deadline = timeout_secs;
318a91a2465SEd Maste 		return;
319a91a2465SEd Maste 	}
320535af610SEd Maste 	debug2_f("channel type \"%s\" timeout %d seconds",
321f374ba41SEd Maste 	    type_pattern, timeout_secs);
322f374ba41SEd Maste 	sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts,
323f374ba41SEd Maste 	    sc->ntimeouts + 1, sizeof(*sc->timeouts));
324f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern);
325f374ba41SEd Maste 	sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs;
326f374ba41SEd Maste 	sc->ntimeouts++;
327f374ba41SEd Maste }
328f374ba41SEd Maste 
329f374ba41SEd Maste /* Clears all previously-added channel timeouts */
330f374ba41SEd Maste void
channel_clear_timeouts(struct ssh * ssh)331f374ba41SEd Maste channel_clear_timeouts(struct ssh *ssh)
332f374ba41SEd Maste {
333f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
334f374ba41SEd Maste 	size_t i;
335f374ba41SEd Maste 
336f374ba41SEd Maste 	debug3_f("clearing");
337f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++)
338f374ba41SEd Maste 		free(sc->timeouts[i].type_pattern);
339f374ba41SEd Maste 	free(sc->timeouts);
340f374ba41SEd Maste 	sc->timeouts = NULL;
341f374ba41SEd Maste 	sc->ntimeouts = 0;
342f374ba41SEd Maste }
343f374ba41SEd Maste 
344535af610SEd Maste static int
lookup_timeout(struct ssh * ssh,const char * type)345f374ba41SEd Maste lookup_timeout(struct ssh *ssh, const char *type)
346f374ba41SEd Maste {
347f374ba41SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
348f374ba41SEd Maste 	size_t i;
349f374ba41SEd Maste 
350f374ba41SEd Maste 	for (i = 0; i < sc->ntimeouts; i++) {
351f374ba41SEd Maste 		if (match_pattern(type, sc->timeouts[i].type_pattern))
352f374ba41SEd Maste 			return sc->timeouts[i].timeout_secs;
353f374ba41SEd Maste 	}
354f374ba41SEd Maste 
355f374ba41SEd Maste 	return 0;
356f374ba41SEd Maste }
357f374ba41SEd Maste 
358f374ba41SEd Maste /*
359f374ba41SEd Maste  * Sets "extended type" of a channel; used by session layer to add additional
360f374ba41SEd Maste  * information about channel types (e.g. shell, login, subsystem) that can then
361f374ba41SEd Maste  * be used to select timeouts.
362f374ba41SEd Maste  * Will reset c->inactive_deadline as a side-effect.
363f374ba41SEd Maste  */
364f374ba41SEd Maste void
channel_set_xtype(struct ssh * ssh,int id,const char * xctype)365f374ba41SEd Maste channel_set_xtype(struct ssh *ssh, int id, const char *xctype)
366f374ba41SEd Maste {
367f374ba41SEd Maste 	Channel *c;
368f374ba41SEd Maste 
369f374ba41SEd Maste 	if ((c = channel_by_id(ssh, id)) == NULL)
370f374ba41SEd Maste 		fatal_f("missing channel %d", id);
371f374ba41SEd Maste 	if (c->xctype != NULL)
372f374ba41SEd Maste 		free(c->xctype);
373f374ba41SEd Maste 	c->xctype = xstrdup(xctype);
374f374ba41SEd Maste 	/* Type has changed, so look up inactivity deadline again */
375f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->xctype);
376f374ba41SEd Maste 	debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype,
377f374ba41SEd Maste 	    c->inactive_deadline);
378f374ba41SEd Maste }
379f374ba41SEd Maste 
380f374ba41SEd Maste /*
381a91a2465SEd Maste  * update "last used" time on a channel.
382a91a2465SEd Maste  * NB. nothing else should update lastused except to clear it.
383a91a2465SEd Maste  */
384a91a2465SEd Maste static void
channel_set_used_time(struct ssh * ssh,Channel * c)385a91a2465SEd Maste channel_set_used_time(struct ssh *ssh, Channel *c)
386a91a2465SEd Maste {
387a91a2465SEd Maste 	ssh->chanctxt->lastused = monotime();
388a91a2465SEd Maste 	if (c != NULL)
389a91a2465SEd Maste 		c->lastused = ssh->chanctxt->lastused;
390a91a2465SEd Maste }
391a91a2465SEd Maste 
392a91a2465SEd Maste /*
393a91a2465SEd Maste  * Get the time at which a channel is due to time out for inactivity.
394a91a2465SEd Maste  * Returns 0 if the channel is not due to time out ever.
395a91a2465SEd Maste  */
396a91a2465SEd Maste static time_t
channel_get_expiry(struct ssh * ssh,Channel * c)397a91a2465SEd Maste channel_get_expiry(struct ssh *ssh, Channel *c)
398a91a2465SEd Maste {
399a91a2465SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
400a91a2465SEd Maste 	time_t expiry = 0, channel_expiry;
401a91a2465SEd Maste 
402a91a2465SEd Maste 	if (sc->lastused != 0 && sc->global_deadline != 0)
403a91a2465SEd Maste 		expiry = sc->lastused + sc->global_deadline;
404a91a2465SEd Maste 	if (c->lastused != 0 && c->inactive_deadline != 0) {
405a91a2465SEd Maste 		channel_expiry = c->lastused + c->inactive_deadline;
406a91a2465SEd Maste 		if (expiry == 0 || channel_expiry < expiry)
407a91a2465SEd Maste 			expiry = channel_expiry;
408a91a2465SEd Maste 	}
409a91a2465SEd Maste 	return expiry;
410a91a2465SEd Maste }
411a91a2465SEd Maste 
412a91a2465SEd Maste /*
413a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
414a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
415a04a10f8SKris Kennaway  */
416af12a3e7SDag-Erling Smørgrav static void
channel_register_fds(struct ssh * ssh,Channel * c,int rfd,int wfd,int efd,int extusage,int nonblock,int is_tty)4174f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
418d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty)
419a04a10f8SKris Kennaway {
42038a52bd3SEd Maste 	int val;
42138a52bd3SEd Maste 
422b15c8340SDag-Erling Smørgrav 	if (rfd != -1)
4234d3fc8b0SEd Maste 		(void)fcntl(rfd, F_SETFD, FD_CLOEXEC);
424b15c8340SDag-Erling Smørgrav 	if (wfd != -1 && wfd != rfd)
4254d3fc8b0SEd Maste 		(void)fcntl(wfd, F_SETFD, FD_CLOEXEC);
426b15c8340SDag-Erling Smørgrav 	if (efd != -1 && efd != rfd && efd != wfd)
4274d3fc8b0SEd Maste 		(void)fcntl(efd, F_SETFD, FD_CLOEXEC);
428a04a10f8SKris Kennaway 
429a04a10f8SKris Kennaway 	c->rfd = rfd;
430a04a10f8SKris Kennaway 	c->wfd = wfd;
431a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
432a04a10f8SKris Kennaway 	c->efd = efd;
433a04a10f8SKris Kennaway 	c->extended_usage = extusage;
4345b9b2fafSBrian Feldman 
435d4af9e69SDag-Erling Smørgrav 	if ((c->isatty = is_tty) != 0)
436221552e4SDag-Erling Smørgrav 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
437e4a9863fSDag-Erling Smørgrav #ifdef _AIX
438e4a9863fSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
439d4af9e69SDag-Erling Smørgrav 	c->wfd_isatty = is_tty || isatty(c->wfd);
440e4a9863fSDag-Erling Smørgrav #endif
441e0fbb1d2SBrian Feldman 
4425b9b2fafSBrian Feldman 	/* enable nonblocking mode */
44319261079SEd Maste 	c->restore_block = 0;
44419261079SEd Maste 	if (nonblock == CHANNEL_NONBLOCK_STDIO) {
44519261079SEd Maste 		/*
44619261079SEd Maste 		 * Special handling for stdio file descriptors: do not set
44719261079SEd Maste 		 * non-blocking mode if they are TTYs. Otherwise prepare to
44819261079SEd Maste 		 * restore their blocking state on exit to avoid interfering
44919261079SEd Maste 		 * with other programs that follow.
45019261079SEd Maste 		 */
45138a52bd3SEd Maste 		if (rfd != -1 && !isatty(rfd) &&
45238a52bd3SEd Maste 		    (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
453f374ba41SEd Maste 			c->restore_flags[0] = val;
45419261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_RFD;
45519261079SEd Maste 			set_nonblock(rfd);
45619261079SEd Maste 		}
45738a52bd3SEd Maste 		if (wfd != -1 && !isatty(wfd) &&
45838a52bd3SEd Maste 		    (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
459f374ba41SEd Maste 			c->restore_flags[1] = val;
46019261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_WFD;
46119261079SEd Maste 			set_nonblock(wfd);
46219261079SEd Maste 		}
46338a52bd3SEd Maste 		if (efd != -1 && !isatty(efd) &&
46438a52bd3SEd Maste 		    (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) {
465f374ba41SEd Maste 			c->restore_flags[2] = val;
46619261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_EFD;
46719261079SEd Maste 			set_nonblock(efd);
46819261079SEd Maste 		}
46919261079SEd Maste 	} else if (nonblock) {
470a04a10f8SKris Kennaway 		if (rfd != -1)
471a04a10f8SKris Kennaway 			set_nonblock(rfd);
472a04a10f8SKris Kennaway 		if (wfd != -1)
473a04a10f8SKris Kennaway 			set_nonblock(wfd);
474a04a10f8SKris Kennaway 		if (efd != -1)
475a04a10f8SKris Kennaway 			set_nonblock(efd);
476a04a10f8SKris Kennaway 	}
477a91a2465SEd Maste 	/* channel might be entering a larval state, so reset global timeout */
478a91a2465SEd Maste 	channel_set_used_time(ssh, NULL);
4795b9b2fafSBrian Feldman }
480a04a10f8SKris Kennaway 
481511b41d2SMark Murray /*
48238a52bd3SEd Maste  * Allocate a new channel object and set its type and socket.
483511b41d2SMark Murray  */
484af12a3e7SDag-Erling Smørgrav Channel *
channel_new(struct ssh * ssh,char * ctype,int type,int rfd,int wfd,int efd,u_int window,u_int maxpack,int extusage,const char * remote_name,int nonblock)4854f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
48638a52bd3SEd Maste     u_int window, u_int maxpack, int extusage, const char *remote_name,
48738a52bd3SEd Maste     int nonblock)
488511b41d2SMark Murray {
4894f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
49038a52bd3SEd Maste 	u_int i, found = 0;
491511b41d2SMark Murray 	Channel *c;
49219261079SEd Maste 	int r;
493511b41d2SMark Murray 
494511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
4954f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
4964f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL) {
497511b41d2SMark Murray 			/* Found a free slot. */
4984f52dfbbSDag-Erling Smørgrav 			found = i;
499511b41d2SMark Murray 			break;
500511b41d2SMark Murray 		}
5014f52dfbbSDag-Erling Smørgrav 	}
5024f52dfbbSDag-Erling Smørgrav 	if (i >= sc->channels_alloc) {
5034f52dfbbSDag-Erling Smørgrav 		/*
5044f52dfbbSDag-Erling Smørgrav 		 * There are no free slots. Take last+1 slot and expand
5054f52dfbbSDag-Erling Smørgrav 		 * the array.
5064f52dfbbSDag-Erling Smørgrav 		 */
5074f52dfbbSDag-Erling Smørgrav 		found = sc->channels_alloc;
5084f52dfbbSDag-Erling Smørgrav 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
50919261079SEd Maste 			fatal_f("internal error: channels_alloc %d too big",
51019261079SEd Maste 			    sc->channels_alloc);
5114f52dfbbSDag-Erling Smørgrav 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
5124f52dfbbSDag-Erling Smørgrav 		    sc->channels_alloc + 10, sizeof(*sc->channels));
5134f52dfbbSDag-Erling Smørgrav 		sc->channels_alloc += 10;
5144f52dfbbSDag-Erling Smørgrav 		debug2("channel: expanding %d", sc->channels_alloc);
515511b41d2SMark Murray 	}
516af12a3e7SDag-Erling Smørgrav 	/* Initialize and return new channel. */
5174f52dfbbSDag-Erling Smørgrav 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
5184f52dfbbSDag-Erling Smørgrav 	if ((c->input = sshbuf_new()) == NULL ||
5194f52dfbbSDag-Erling Smørgrav 	    (c->output = sshbuf_new()) == NULL ||
5204f52dfbbSDag-Erling Smørgrav 	    (c->extended = sshbuf_new()) == NULL)
52119261079SEd Maste 		fatal_f("sshbuf_new failed");
52219261079SEd Maste 	if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
52319261079SEd Maste 		fatal_fr(r, "sshbuf_set_max_size");
524af12a3e7SDag-Erling Smørgrav 	c->ostate = CHAN_OUTPUT_OPEN;
525af12a3e7SDag-Erling Smørgrav 	c->istate = CHAN_INPUT_OPEN;
5264f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
527511b41d2SMark Murray 	c->self = found;
528511b41d2SMark Murray 	c->type = type;
529a04a10f8SKris Kennaway 	c->ctype = ctype;
530a04a10f8SKris Kennaway 	c->local_window = window;
531a04a10f8SKris Kennaway 	c->local_window_max = window;
532a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
533221552e4SDag-Erling Smørgrav 	c->remote_name = xstrdup(remote_name);
534b15c8340SDag-Erling Smørgrav 	c->ctl_chan = -1;
535b15c8340SDag-Erling Smørgrav 	c->delayed = 1;		/* prevent call to channel_post handler */
536f374ba41SEd Maste 	c->inactive_deadline = lookup_timeout(ssh, c->ctype);
537d4af9e69SDag-Erling Smørgrav 	TAILQ_INIT(&c->status_confirms);
538f374ba41SEd Maste 	debug("channel %d: new %s [%s] (inactive timeout: %u)",
539f374ba41SEd Maste 	    found, c->ctype, remote_name, c->inactive_deadline);
540af12a3e7SDag-Erling Smørgrav 	return c;
541a04a10f8SKris Kennaway }
542511b41d2SMark Murray 
543af12a3e7SDag-Erling Smørgrav int
channel_close_fd(struct ssh * ssh,Channel * c,int * fdp)54419261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp)
545af12a3e7SDag-Erling Smørgrav {
54619261079SEd Maste 	int ret, fd = *fdp;
547af12a3e7SDag-Erling Smørgrav 
54819261079SEd Maste 	if (fd == -1)
54919261079SEd Maste 		return 0;
55019261079SEd Maste 
551f374ba41SEd Maste 	/* restore blocking */
552f374ba41SEd Maste 	if (*fdp == c->rfd &&
553f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_RFD) != 0)
554f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[0]);
555f374ba41SEd Maste 	else if (*fdp == c->wfd &&
556f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_WFD) != 0)
557f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[1]);
558f374ba41SEd Maste 	else if (*fdp == c->efd &&
559f374ba41SEd Maste 	    (c->restore_block & CHANNEL_RESTORE_EFD) != 0)
560f374ba41SEd Maste 		(void)fcntl(*fdp, F_SETFL, c->restore_flags[2]);
56119261079SEd Maste 
5621323ec57SEd Maste 	if (*fdp == c->rfd) {
5631323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_RFD;
5641323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_RFD;
5651323ec57SEd Maste 		c->rfd = -1;
56687c1498dSEd Maste 		c->pfds[0] = -1;
5671323ec57SEd Maste 	}
5681323ec57SEd Maste 	if (*fdp == c->wfd) {
5691323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_WFD;
5701323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_WFD;
5711323ec57SEd Maste 		c->wfd = -1;
57287c1498dSEd Maste 		c->pfds[1] = -1;
5731323ec57SEd Maste 	}
5741323ec57SEd Maste 	if (*fdp == c->efd) {
5751323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_EFD;
5761323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_EFD;
5771323ec57SEd Maste 		c->efd = -1;
57887c1498dSEd Maste 		c->pfds[2] = -1;
5791323ec57SEd Maste 	}
5801323ec57SEd Maste 	if (*fdp == c->sock) {
5811323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_SOCK;
5821323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_SOCK;
5831323ec57SEd Maste 		c->sock = -1;
58487c1498dSEd Maste 		c->pfds[3] = -1;
5851323ec57SEd Maste 	}
5861323ec57SEd Maste 
587af12a3e7SDag-Erling Smørgrav 	ret = close(fd);
5881323ec57SEd Maste 	*fdp = -1; /* probably redundant */
589af12a3e7SDag-Erling Smørgrav 	return ret;
590af12a3e7SDag-Erling Smørgrav }
591a04a10f8SKris Kennaway 
592a04a10f8SKris Kennaway /* Close all channel fd/socket. */
593af12a3e7SDag-Erling Smørgrav static void
channel_close_fds(struct ssh * ssh,Channel * c)5944f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c)
595511b41d2SMark Murray {
59647dd1d1bSDag-Erling Smørgrav 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
59747dd1d1bSDag-Erling Smørgrav 
59819261079SEd Maste 	channel_close_fd(ssh, c, &c->sock);
59947dd1d1bSDag-Erling Smørgrav 	if (rfd != sock)
60019261079SEd Maste 		channel_close_fd(ssh, c, &c->rfd);
60147dd1d1bSDag-Erling Smørgrav 	if (wfd != sock && wfd != rfd)
60219261079SEd Maste 		channel_close_fd(ssh, c, &c->wfd);
60347dd1d1bSDag-Erling Smørgrav 	if (efd != sock && efd != rfd && efd != wfd)
60419261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
6054f52dfbbSDag-Erling Smørgrav }
6064f52dfbbSDag-Erling Smørgrav 
6074f52dfbbSDag-Erling Smørgrav static void
fwd_perm_clear(struct permission * perm)608190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm)
6094f52dfbbSDag-Erling Smørgrav {
610190cef3dSDag-Erling Smørgrav 	free(perm->host_to_connect);
611190cef3dSDag-Erling Smørgrav 	free(perm->listen_host);
612190cef3dSDag-Erling Smørgrav 	free(perm->listen_path);
61319261079SEd Maste 	memset(perm, 0, sizeof(*perm));
6144f52dfbbSDag-Erling Smørgrav }
6154f52dfbbSDag-Erling Smørgrav 
616190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */
617190cef3dSDag-Erling Smørgrav static const char *
fwd_ident(int who,int where)618190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where)
619190cef3dSDag-Erling Smørgrav {
620190cef3dSDag-Erling Smørgrav 	if (who == FORWARD_ADM) {
621190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
622190cef3dSDag-Erling Smørgrav 			return "admin local";
623190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
624190cef3dSDag-Erling Smørgrav 			return "admin remote";
625190cef3dSDag-Erling Smørgrav 	} else if (who == FORWARD_USER) {
626190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
627190cef3dSDag-Erling Smørgrav 			return "user local";
628190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
629190cef3dSDag-Erling Smørgrav 			return "user remote";
630190cef3dSDag-Erling Smørgrav 	}
631190cef3dSDag-Erling Smørgrav 	fatal("Unknown forward permission list %d/%d", who, where);
632190cef3dSDag-Erling Smørgrav }
6334f52dfbbSDag-Erling Smørgrav 
634190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */
635190cef3dSDag-Erling Smørgrav static struct permission_set *
permission_set_get(struct ssh * ssh,int where)636190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where)
637190cef3dSDag-Erling Smørgrav {
638190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
639190cef3dSDag-Erling Smørgrav 
640190cef3dSDag-Erling Smørgrav 	switch (where) {
641190cef3dSDag-Erling Smørgrav 	case FORWARD_LOCAL:
642190cef3dSDag-Erling Smørgrav 		return &sc->local_perms;
643190cef3dSDag-Erling Smørgrav 		break;
644190cef3dSDag-Erling Smørgrav 	case FORWARD_REMOTE:
645190cef3dSDag-Erling Smørgrav 		return &sc->remote_perms;
646190cef3dSDag-Erling Smørgrav 		break;
647190cef3dSDag-Erling Smørgrav 	default:
64819261079SEd Maste 		fatal_f("invalid forwarding direction %d", where);
649190cef3dSDag-Erling Smørgrav 	}
650190cef3dSDag-Erling Smørgrav }
651190cef3dSDag-Erling Smørgrav 
65219261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */
653190cef3dSDag-Erling Smørgrav static void
permission_set_get_array(struct ssh * ssh,int who,int where,struct permission *** permpp,u_int ** npermpp)654190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where,
655190cef3dSDag-Erling Smørgrav     struct permission ***permpp, u_int **npermpp)
656190cef3dSDag-Erling Smørgrav {
657190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
658190cef3dSDag-Erling Smørgrav 
659190cef3dSDag-Erling Smørgrav 	switch (who) {
660190cef3dSDag-Erling Smørgrav 	case FORWARD_USER:
661190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_user;
662190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_user;
663190cef3dSDag-Erling Smørgrav 		break;
664190cef3dSDag-Erling Smørgrav 	case FORWARD_ADM:
665190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_admin;
666190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_admin;
667190cef3dSDag-Erling Smørgrav 		break;
668190cef3dSDag-Erling Smørgrav 	default:
66919261079SEd Maste 		fatal_f("invalid forwarding client %d", who);
670190cef3dSDag-Erling Smørgrav 	}
671190cef3dSDag-Erling Smørgrav }
672190cef3dSDag-Erling Smørgrav 
6731323ec57SEd Maste /* Adds an entry to the specified forwarding list */
6744f52dfbbSDag-Erling Smørgrav static int
permission_set_add(struct ssh * ssh,int who,int where,const char * host_to_connect,int port_to_connect,const char * listen_host,const char * listen_path,int listen_port,Channel * downstream)675190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where,
6764f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, int port_to_connect,
6774f52dfbbSDag-Erling Smørgrav     const char *listen_host, const char *listen_path, int listen_port,
6784f52dfbbSDag-Erling Smørgrav     Channel *downstream)
6794f52dfbbSDag-Erling Smørgrav {
680190cef3dSDag-Erling Smørgrav 	struct permission **permp;
681190cef3dSDag-Erling Smørgrav 	u_int n, *npermp;
6824f52dfbbSDag-Erling Smørgrav 
683190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
6844f52dfbbSDag-Erling Smørgrav 
685190cef3dSDag-Erling Smørgrav 	if (*npermp >= INT_MAX)
68619261079SEd Maste 		fatal_f("%s overflow", fwd_ident(who, where));
6874f52dfbbSDag-Erling Smørgrav 
688190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
689190cef3dSDag-Erling Smørgrav 	n = (*npermp)++;
6904f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
691190cef3dSDag-Erling Smørgrav 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
692190cef3dSDag-Erling Smørgrav 	(*permp)[n].port_to_connect = port_to_connect;
693190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
694190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
695190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_port = listen_port;
696190cef3dSDag-Erling Smørgrav 	(*permp)[n].downstream = downstream;
6974f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP
6984f52dfbbSDag-Erling Smørgrav 	return (int)n;
6994f52dfbbSDag-Erling Smørgrav }
7004f52dfbbSDag-Erling Smørgrav 
7014f52dfbbSDag-Erling Smørgrav static void
mux_remove_remote_forwardings(struct ssh * ssh,Channel * c)7024f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
7034f52dfbbSDag-Erling Smørgrav {
7044f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
705190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
706190cef3dSDag-Erling Smørgrav 	struct permission *perm;
7074f52dfbbSDag-Erling Smørgrav 	int r;
7084f52dfbbSDag-Erling Smørgrav 	u_int i;
7094f52dfbbSDag-Erling Smørgrav 
710190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
711190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
712190cef3dSDag-Erling Smørgrav 		if (perm->downstream != c)
7134f52dfbbSDag-Erling Smørgrav 			continue;
7144f52dfbbSDag-Erling Smørgrav 
7154f52dfbbSDag-Erling Smørgrav 		/* cancel on the server, since mux client is gone */
7164f52dfbbSDag-Erling Smørgrav 		debug("channel %d: cleanup remote forward for %s:%u",
717190cef3dSDag-Erling Smørgrav 		    c->self, perm->listen_host, perm->listen_port);
7184f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
7194f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
7204f52dfbbSDag-Erling Smørgrav 		    "cancel-tcpip-forward")) != 0 ||
7214f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
7224f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
723190cef3dSDag-Erling Smørgrav 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
724190cef3dSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
7254f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
72619261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
7274f52dfbbSDag-Erling Smørgrav 		}
728190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(perm); /* unregister */
7294f52dfbbSDag-Erling Smørgrav 	}
730a04a10f8SKris Kennaway }
731511b41d2SMark Murray 
732a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
733a04a10f8SKris Kennaway void
channel_free(struct ssh * ssh,Channel * c)7344f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c)
735a04a10f8SKris Kennaway {
7364f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
737af12a3e7SDag-Erling Smørgrav 	char *s;
73821e764dfSDag-Erling Smørgrav 	u_int i, n;
739ca86bcf2SDag-Erling Smørgrav 	Channel *other;
740d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
741ca3176e7SBrian Feldman 
7424f52dfbbSDag-Erling Smørgrav 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
7434f52dfbbSDag-Erling Smørgrav 		if ((other = sc->channels[i]) == NULL)
7444f52dfbbSDag-Erling Smørgrav 			continue;
745af12a3e7SDag-Erling Smørgrav 		n++;
746ca86bcf2SDag-Erling Smørgrav 		/* detach from mux client and prepare for closing */
747ca86bcf2SDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
748ca86bcf2SDag-Erling Smørgrav 		    other->type == SSH_CHANNEL_MUX_PROXY &&
749ca86bcf2SDag-Erling Smørgrav 		    other->mux_ctx == c) {
750ca86bcf2SDag-Erling Smørgrav 			other->mux_ctx = NULL;
751ca86bcf2SDag-Erling Smørgrav 			other->type = SSH_CHANNEL_OPEN;
752ca86bcf2SDag-Erling Smørgrav 			other->istate = CHAN_INPUT_CLOSED;
753ca86bcf2SDag-Erling Smørgrav 			other->ostate = CHAN_OUTPUT_CLOSED;
754ca86bcf2SDag-Erling Smørgrav 		}
755ca86bcf2SDag-Erling Smørgrav 	}
75621e764dfSDag-Erling Smørgrav 	debug("channel %d: free: %s, nchannels %u", c->self,
757af12a3e7SDag-Erling Smørgrav 	    c->remote_name ? c->remote_name : "???", n);
758af12a3e7SDag-Erling Smørgrav 
759e9e8876aSEd Maste 	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
7604f52dfbbSDag-Erling Smørgrav 		mux_remove_remote_forwardings(ssh, c);
761e9e8876aSEd Maste 		free(c->mux_ctx);
762e9e8876aSEd Maste 		c->mux_ctx = NULL;
763e9e8876aSEd Maste 	} else if (c->type == SSH_CHANNEL_MUX_LISTENER) {
76419261079SEd Maste 		free(c->mux_ctx);
76519261079SEd Maste 		c->mux_ctx = NULL;
76619261079SEd Maste 	}
767ca86bcf2SDag-Erling Smørgrav 
768190cef3dSDag-Erling Smørgrav 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
7694f52dfbbSDag-Erling Smørgrav 		s = channel_open_message(ssh);
770221552e4SDag-Erling Smørgrav 		debug3("channel %d: status: %s", c->self, s);
771e4a9863fSDag-Erling Smørgrav 		free(s);
772190cef3dSDag-Erling Smørgrav 	}
773ca3176e7SBrian Feldman 
7744f52dfbbSDag-Erling Smørgrav 	channel_close_fds(ssh, c);
7754f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->input);
7764f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->output);
7774f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->extended);
7784f52dfbbSDag-Erling Smørgrav 	c->input = c->output = c->extended = NULL;
779e4a9863fSDag-Erling Smørgrav 	free(c->remote_name);
780a04a10f8SKris Kennaway 	c->remote_name = NULL;
781e4a9863fSDag-Erling Smørgrav 	free(c->path);
782cce7d346SDag-Erling Smørgrav 	c->path = NULL;
783e4a9863fSDag-Erling Smørgrav 	free(c->listening_addr);
784462c32cbSDag-Erling Smørgrav 	c->listening_addr = NULL;
785f374ba41SEd Maste 	free(c->xctype);
786f374ba41SEd Maste 	c->xctype = NULL;
787d4af9e69SDag-Erling Smørgrav 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
788d4af9e69SDag-Erling Smørgrav 		if (cc->abandon_cb != NULL)
7894f52dfbbSDag-Erling Smørgrav 			cc->abandon_cb(ssh, c, cc->ctx);
790d4af9e69SDag-Erling Smørgrav 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
79119261079SEd Maste 		freezero(cc, sizeof(*cc));
792d4af9e69SDag-Erling Smørgrav 	}
793d4af9e69SDag-Erling Smørgrav 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
7944f52dfbbSDag-Erling Smørgrav 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
7954f52dfbbSDag-Erling Smørgrav 	sc->channels[c->self] = NULL;
79619261079SEd Maste 	freezero(c, sizeof(*c));
797af12a3e7SDag-Erling Smørgrav }
798af12a3e7SDag-Erling Smørgrav 
799af12a3e7SDag-Erling Smørgrav void
channel_free_all(struct ssh * ssh)8004f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh)
801af12a3e7SDag-Erling Smørgrav {
80221e764dfSDag-Erling Smørgrav 	u_int i;
80319261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
804af12a3e7SDag-Erling Smørgrav 
80519261079SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
80619261079SEd Maste 		if (sc->channels[i] != NULL)
80719261079SEd Maste 			channel_free(ssh, sc->channels[i]);
80819261079SEd Maste 
80919261079SEd Maste 	free(sc->channels);
81019261079SEd Maste 	sc->channels = NULL;
81119261079SEd Maste 	sc->channels_alloc = 0;
81219261079SEd Maste 
81319261079SEd Maste 	free(sc->x11_saved_display);
81419261079SEd Maste 	sc->x11_saved_display = NULL;
81519261079SEd Maste 
81619261079SEd Maste 	free(sc->x11_saved_proto);
81719261079SEd Maste 	sc->x11_saved_proto = NULL;
81819261079SEd Maste 
81919261079SEd Maste 	free(sc->x11_saved_data);
82019261079SEd Maste 	sc->x11_saved_data = NULL;
82119261079SEd Maste 	sc->x11_saved_data_len = 0;
82219261079SEd Maste 
82319261079SEd Maste 	free(sc->x11_fake_data);
82419261079SEd Maste 	sc->x11_fake_data = NULL;
82519261079SEd Maste 	sc->x11_fake_data_len = 0;
826af12a3e7SDag-Erling Smørgrav }
827af12a3e7SDag-Erling Smørgrav 
828af12a3e7SDag-Erling Smørgrav /*
829af12a3e7SDag-Erling Smørgrav  * Closes the sockets/fds of all channels.  This is used to close extra file
830af12a3e7SDag-Erling Smørgrav  * descriptors after a fork.
831af12a3e7SDag-Erling Smørgrav  */
832af12a3e7SDag-Erling Smørgrav void
channel_close_all(struct ssh * ssh)8334f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh)
834af12a3e7SDag-Erling Smørgrav {
83521e764dfSDag-Erling Smørgrav 	u_int i;
836af12a3e7SDag-Erling Smørgrav 
8374f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
8384f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
8394f52dfbbSDag-Erling Smørgrav 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
840af12a3e7SDag-Erling Smørgrav }
841af12a3e7SDag-Erling Smørgrav 
842af12a3e7SDag-Erling Smørgrav /*
843af12a3e7SDag-Erling Smørgrav  * Stop listening to channels.
844af12a3e7SDag-Erling Smørgrav  */
845af12a3e7SDag-Erling Smørgrav void
channel_stop_listening(struct ssh * ssh)8464f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh)
847af12a3e7SDag-Erling Smørgrav {
84821e764dfSDag-Erling Smørgrav 	u_int i;
849af12a3e7SDag-Erling Smørgrav 	Channel *c;
850af12a3e7SDag-Erling Smørgrav 
8514f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8524f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
853af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
854af12a3e7SDag-Erling Smørgrav 			switch (c->type) {
855af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_AUTH_SOCKET:
856af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_PORT_LISTENER:
857af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_RPORT_LISTENER:
858af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_X11_LISTENER:
859a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_UNIX_LISTENER:
860a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_RUNIX_LISTENER:
86119261079SEd Maste 				channel_close_fd(ssh, c, &c->sock);
8624f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
863af12a3e7SDag-Erling Smørgrav 				break;
864af12a3e7SDag-Erling Smørgrav 			}
865af12a3e7SDag-Erling Smørgrav 		}
866af12a3e7SDag-Erling Smørgrav 	}
867af12a3e7SDag-Erling Smørgrav }
868af12a3e7SDag-Erling Smørgrav 
869af12a3e7SDag-Erling Smørgrav /*
870af12a3e7SDag-Erling Smørgrav  * Returns true if no channel has too much buffered data, and false if one or
871af12a3e7SDag-Erling Smørgrav  * more channel is overfull.
872af12a3e7SDag-Erling Smørgrav  */
873af12a3e7SDag-Erling Smørgrav int
channel_not_very_much_buffered_data(struct ssh * ssh)8744f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh)
875af12a3e7SDag-Erling Smørgrav {
876af12a3e7SDag-Erling Smørgrav 	u_int i;
8774f52dfbbSDag-Erling Smørgrav 	u_int maxsize = ssh_packet_get_maxsize(ssh);
878af12a3e7SDag-Erling Smørgrav 	Channel *c;
879af12a3e7SDag-Erling Smørgrav 
8804f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8814f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
8824f52dfbbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
8834f52dfbbSDag-Erling Smørgrav 			continue;
8844f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > maxsize) {
8854f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: big output buffer %zu > %u",
8864f52dfbbSDag-Erling Smørgrav 			    c->self, sshbuf_len(c->output), maxsize);
887af12a3e7SDag-Erling Smørgrav 			return 0;
888af12a3e7SDag-Erling Smørgrav 		}
889af12a3e7SDag-Erling Smørgrav 	}
890af12a3e7SDag-Erling Smørgrav 	return 1;
891af12a3e7SDag-Erling Smørgrav }
892af12a3e7SDag-Erling Smørgrav 
893af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */
894af12a3e7SDag-Erling Smørgrav int
channel_still_open(struct ssh * ssh)8954f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh)
896af12a3e7SDag-Erling Smørgrav {
89721e764dfSDag-Erling Smørgrav 	u_int i;
898af12a3e7SDag-Erling Smørgrav 	Channel *c;
899af12a3e7SDag-Erling Smørgrav 
9004f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9014f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
902af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
903af12a3e7SDag-Erling Smørgrav 			continue;
904af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
905af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
906af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
907af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
908b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
909af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
910af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
911af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9124f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
913af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
914af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
915e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
916a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
917a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
918af12a3e7SDag-Erling Smørgrav 			continue;
919af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
920af12a3e7SDag-Erling Smørgrav 			continue;
921af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
922af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
9234f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
924af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
925b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
926ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
927af12a3e7SDag-Erling Smørgrav 			return 1;
928af12a3e7SDag-Erling Smørgrav 		default:
92919261079SEd Maste 			fatal_f("bad channel type %d", c->type);
930af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
931af12a3e7SDag-Erling Smørgrav 		}
932af12a3e7SDag-Erling Smørgrav 	}
933af12a3e7SDag-Erling Smørgrav 	return 0;
934af12a3e7SDag-Erling Smørgrav }
935af12a3e7SDag-Erling Smørgrav 
936069ac184SEd Maste /* Returns true if a channel with a TTY is open. */
937069ac184SEd Maste int
channel_tty_open(struct ssh * ssh)938069ac184SEd Maste channel_tty_open(struct ssh *ssh)
939069ac184SEd Maste {
940069ac184SEd Maste 	u_int i;
941069ac184SEd Maste 	Channel *c;
942069ac184SEd Maste 
943069ac184SEd Maste 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
944069ac184SEd Maste 		c = ssh->chanctxt->channels[i];
945069ac184SEd Maste 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
946069ac184SEd Maste 			continue;
947069ac184SEd Maste 		if (c->client_tty)
948069ac184SEd Maste 			return 1;
949069ac184SEd Maste 	}
950069ac184SEd Maste 	return 0;
951069ac184SEd Maste }
952069ac184SEd Maste 
953af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */
954af12a3e7SDag-Erling Smørgrav int
channel_find_open(struct ssh * ssh)9554f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh)
956af12a3e7SDag-Erling Smørgrav {
95721e764dfSDag-Erling Smørgrav 	u_int i;
958af12a3e7SDag-Erling Smørgrav 	Channel *c;
959af12a3e7SDag-Erling Smørgrav 
9604f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9614f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
9624f52dfbbSDag-Erling Smørgrav 		if (c == NULL || !c->have_remote_id)
963af12a3e7SDag-Erling Smørgrav 			continue;
964af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
965af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
966af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9674f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
9684f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
969af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
970af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
971af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
972b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
973b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
974ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
975af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
976af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
977af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
978e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
979a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
980a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
981af12a3e7SDag-Erling Smørgrav 			continue;
982af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
983af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
984af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
985af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
986af12a3e7SDag-Erling Smørgrav 			return i;
987af12a3e7SDag-Erling Smørgrav 		default:
98819261079SEd Maste 			fatal_f("bad channel type %d", c->type);
989af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
990af12a3e7SDag-Erling Smørgrav 		}
991af12a3e7SDag-Erling Smørgrav 	}
992af12a3e7SDag-Erling Smørgrav 	return -1;
993af12a3e7SDag-Erling Smørgrav }
994af12a3e7SDag-Erling Smørgrav 
9952f513db7SEd Maste /* Returns the state of the channel's extended usage flag */
9962f513db7SEd Maste const char *
channel_format_extended_usage(const Channel * c)9972f513db7SEd Maste channel_format_extended_usage(const Channel *c)
9982f513db7SEd Maste {
9992f513db7SEd Maste 	if (c->efd == -1)
10002f513db7SEd Maste 		return "closed";
10012f513db7SEd Maste 
10022f513db7SEd Maste 	switch (c->extended_usage) {
10032f513db7SEd Maste 	case CHAN_EXTENDED_WRITE:
10042f513db7SEd Maste 		return "write";
10052f513db7SEd Maste 	case CHAN_EXTENDED_READ:
10062f513db7SEd Maste 		return "read";
10072f513db7SEd Maste 	case CHAN_EXTENDED_IGNORE:
10082f513db7SEd Maste 		return "ignore";
10092f513db7SEd Maste 	default:
10102f513db7SEd Maste 		return "UNKNOWN";
10112f513db7SEd Maste 	}
10122f513db7SEd Maste }
10132f513db7SEd Maste 
10142f513db7SEd Maste static char *
channel_format_status(const Channel * c)10152f513db7SEd Maste channel_format_status(const Channel *c)
10162f513db7SEd Maste {
10172f513db7SEd Maste 	char *ret = NULL;
10182f513db7SEd Maste 
1019*3d9fd9fcSEd Maste 	xasprintf(&ret, "t%d [%s] %s%u %s%u i%u/%zu o%u/%zu e[%s]/%zu "
1020*3d9fd9fcSEd Maste 	    "fd %d/%d/%d sock %d cc %d %s%u io 0x%02x/0x%02x",
1021f374ba41SEd Maste 	    c->type, c->xctype != NULL ? c->xctype : c->ctype,
10222f513db7SEd Maste 	    c->have_remote_id ? "r" : "nr", c->remote_id,
1023*3d9fd9fcSEd Maste 	    c->mux_ctx != NULL ? "m" : "nm", c->mux_downstream_id,
10242f513db7SEd Maste 	    c->istate, sshbuf_len(c->input),
10252f513db7SEd Maste 	    c->ostate, sshbuf_len(c->output),
10262f513db7SEd Maste 	    channel_format_extended_usage(c), sshbuf_len(c->extended),
10271323ec57SEd Maste 	    c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan,
1028*3d9fd9fcSEd Maste 	    c->have_ctl_child_id ? "c" : "nc", c->ctl_child_id,
10291323ec57SEd Maste 	    c->io_want, c->io_ready);
10302f513db7SEd Maste 	return ret;
10312f513db7SEd Maste }
10322f513db7SEd Maste 
1033af12a3e7SDag-Erling Smørgrav /*
1034af12a3e7SDag-Erling Smørgrav  * Returns a message describing the currently open forwarded connections,
1035af12a3e7SDag-Erling Smørgrav  * suitable for sending to the client.  The message contains crlf pairs for
1036af12a3e7SDag-Erling Smørgrav  * newlines.
1037af12a3e7SDag-Erling Smørgrav  */
1038af12a3e7SDag-Erling Smørgrav char *
channel_open_message(struct ssh * ssh)10394f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh)
1040af12a3e7SDag-Erling Smørgrav {
10414f52dfbbSDag-Erling Smørgrav 	struct sshbuf *buf;
1042af12a3e7SDag-Erling Smørgrav 	Channel *c;
104321e764dfSDag-Erling Smørgrav 	u_int i;
10444f52dfbbSDag-Erling Smørgrav 	int r;
10452f513db7SEd Maste 	char *cp, *ret;
1046af12a3e7SDag-Erling Smørgrav 
10474f52dfbbSDag-Erling Smørgrav 	if ((buf = sshbuf_new()) == NULL)
104819261079SEd Maste 		fatal_f("sshbuf_new");
10494f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_putf(buf,
10504f52dfbbSDag-Erling Smørgrav 	    "The following connections are open:\r\n")) != 0)
105119261079SEd Maste 		fatal_fr(r, "sshbuf_putf");
10524f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
10534f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
1054af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
1055af12a3e7SDag-Erling Smørgrav 			continue;
1056af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
1057af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
1058af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
1059af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
1060af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
1061af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
1062af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
1063e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
1064b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
1065a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
1066a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
1067af12a3e7SDag-Erling Smørgrav 			continue;
1068af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
1069af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
1070af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
1071af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
10724f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
10734f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
1074af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
1075af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
1076ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
1077ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
10782f513db7SEd Maste 			cp = channel_format_status(c);
10792f513db7SEd Maste 			if ((r = sshbuf_putf(buf, "  #%d %.300s (%s)\r\n",
10802f513db7SEd Maste 			    c->self, c->remote_name, cp)) != 0) {
10812f513db7SEd Maste 				free(cp);
108219261079SEd Maste 				fatal_fr(r, "sshbuf_putf");
10832f513db7SEd Maste 			}
10842f513db7SEd Maste 			free(cp);
1085af12a3e7SDag-Erling Smørgrav 			continue;
1086af12a3e7SDag-Erling Smørgrav 		default:
108719261079SEd Maste 			fatal_f("bad channel type %d", c->type);
1088af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
1089af12a3e7SDag-Erling Smørgrav 		}
1090af12a3e7SDag-Erling Smørgrav 	}
10914f52dfbbSDag-Erling Smørgrav 	if ((ret = sshbuf_dup_string(buf)) == NULL)
109219261079SEd Maste 		fatal_f("sshbuf_dup_string");
10934f52dfbbSDag-Erling Smørgrav 	sshbuf_free(buf);
10944f52dfbbSDag-Erling Smørgrav 	return ret;
10954f52dfbbSDag-Erling Smørgrav }
10964f52dfbbSDag-Erling Smørgrav 
10974f52dfbbSDag-Erling Smørgrav static void
open_preamble(struct ssh * ssh,const char * where,Channel * c,const char * type)10984f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
10994f52dfbbSDag-Erling Smørgrav {
11004f52dfbbSDag-Erling Smørgrav 	int r;
11014f52dfbbSDag-Erling Smørgrav 
11024f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
11034f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
11044f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
11054f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
11064f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
110719261079SEd Maste 		fatal_r(r, "%s: channel %i: open", where, c->self);
11084f52dfbbSDag-Erling Smørgrav 	}
1109af12a3e7SDag-Erling Smørgrav }
1110af12a3e7SDag-Erling Smørgrav 
1111af12a3e7SDag-Erling Smørgrav void
channel_send_open(struct ssh * ssh,int id)11124f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id)
1113af12a3e7SDag-Erling Smørgrav {
11144f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
11154f52dfbbSDag-Erling Smørgrav 	int r;
1116f388f5efSDag-Erling Smørgrav 
1117af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
1118221552e4SDag-Erling Smørgrav 		logit("channel_send_open: %d: bad id", id);
1119af12a3e7SDag-Erling Smørgrav 		return;
1120af12a3e7SDag-Erling Smørgrav 	}
1121e73e9afaSDag-Erling Smørgrav 	debug2("channel %d: send open", id);
11224f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, c->ctype);
11234f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
112419261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1125af12a3e7SDag-Erling Smørgrav }
1126af12a3e7SDag-Erling Smørgrav 
1127af12a3e7SDag-Erling Smørgrav void
channel_request_start(struct ssh * ssh,int id,char * service,int wantconfirm)11284f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
1129af12a3e7SDag-Erling Smørgrav {
11304f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
11314f52dfbbSDag-Erling Smørgrav 	int r;
1132f388f5efSDag-Erling Smørgrav 
1133af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
113419261079SEd Maste 		logit_f("%d: unknown channel id", id);
1135af12a3e7SDag-Erling Smørgrav 		return;
1136af12a3e7SDag-Erling Smørgrav 	}
11374f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
113819261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
11394f52dfbbSDag-Erling Smørgrav 
114021e764dfSDag-Erling Smørgrav 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
11414f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
11424f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
11434f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
11444f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
114519261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
11464f52dfbbSDag-Erling Smørgrav 	}
1147af12a3e7SDag-Erling Smørgrav }
1148333ee039SDag-Erling Smørgrav 
1149af12a3e7SDag-Erling Smørgrav void
channel_register_status_confirm(struct ssh * ssh,int id,channel_confirm_cb * cb,channel_confirm_abandon_cb * abandon_cb,void * ctx)11504f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id,
11514f52dfbbSDag-Erling Smørgrav     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
1152d4af9e69SDag-Erling Smørgrav {
1153d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
1154d4af9e69SDag-Erling Smørgrav 	Channel *c;
1155d4af9e69SDag-Erling Smørgrav 
11564f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL)
115719261079SEd Maste 		fatal_f("%d: bad id", id);
1158d4af9e69SDag-Erling Smørgrav 
11590a37d4a3SXin LI 	cc = xcalloc(1, sizeof(*cc));
1160d4af9e69SDag-Erling Smørgrav 	cc->cb = cb;
1161d4af9e69SDag-Erling Smørgrav 	cc->abandon_cb = abandon_cb;
1162d4af9e69SDag-Erling Smørgrav 	cc->ctx = ctx;
1163d4af9e69SDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
1164d4af9e69SDag-Erling Smørgrav }
1165d4af9e69SDag-Erling Smørgrav 
1166d4af9e69SDag-Erling Smørgrav void
channel_register_open_confirm(struct ssh * ssh,int id,channel_open_fn * fn,void * ctx)11674f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id,
11684f52dfbbSDag-Erling Smørgrav     channel_open_fn *fn, void *ctx)
1169af12a3e7SDag-Erling Smørgrav {
11704f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1171f388f5efSDag-Erling Smørgrav 
1172af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
117319261079SEd Maste 		logit_f("%d: bad id", id);
1174af12a3e7SDag-Erling Smørgrav 		return;
1175af12a3e7SDag-Erling Smørgrav 	}
1176d4af9e69SDag-Erling Smørgrav 	c->open_confirm = fn;
1177d4af9e69SDag-Erling Smørgrav 	c->open_confirm_ctx = ctx;
1178af12a3e7SDag-Erling Smørgrav }
1179333ee039SDag-Erling Smørgrav 
1180af12a3e7SDag-Erling Smørgrav void
channel_register_cleanup(struct ssh * ssh,int id,channel_callback_fn * fn,int do_close)11814f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id,
11824f52dfbbSDag-Erling Smørgrav     channel_callback_fn *fn, int do_close)
1183af12a3e7SDag-Erling Smørgrav {
11844f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1185f388f5efSDag-Erling Smørgrav 
1186af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
118719261079SEd Maste 		logit_f("%d: bad id", id);
1188af12a3e7SDag-Erling Smørgrav 		return;
1189af12a3e7SDag-Erling Smørgrav 	}
1190af12a3e7SDag-Erling Smørgrav 	c->detach_user = fn;
1191b74df5b2SDag-Erling Smørgrav 	c->detach_close = do_close;
1192af12a3e7SDag-Erling Smørgrav }
1193333ee039SDag-Erling Smørgrav 
1194af12a3e7SDag-Erling Smørgrav void
channel_cancel_cleanup(struct ssh * ssh,int id)11954f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id)
1196af12a3e7SDag-Erling Smørgrav {
11974f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1198f388f5efSDag-Erling Smørgrav 
1199af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
120019261079SEd Maste 		logit_f("%d: bad id", id);
1201af12a3e7SDag-Erling Smørgrav 		return;
1202af12a3e7SDag-Erling Smørgrav 	}
1203af12a3e7SDag-Erling Smørgrav 	c->detach_user = NULL;
1204b74df5b2SDag-Erling Smørgrav 	c->detach_close = 0;
1205af12a3e7SDag-Erling Smørgrav }
1206333ee039SDag-Erling Smørgrav 
1207af12a3e7SDag-Erling Smørgrav void
channel_register_filter(struct ssh * ssh,int id,channel_infilter_fn * ifn,channel_outfilter_fn * ofn,channel_filter_cleanup_fn * cfn,void * ctx)12084f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
1209d4af9e69SDag-Erling Smørgrav     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
1210af12a3e7SDag-Erling Smørgrav {
12114f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1212f388f5efSDag-Erling Smørgrav 
1213af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
121419261079SEd Maste 		logit_f("%d: bad id", id);
1215af12a3e7SDag-Erling Smørgrav 		return;
1216af12a3e7SDag-Erling Smørgrav 	}
1217b74df5b2SDag-Erling Smørgrav 	c->input_filter = ifn;
1218b74df5b2SDag-Erling Smørgrav 	c->output_filter = ofn;
1219d4af9e69SDag-Erling Smørgrav 	c->filter_ctx = ctx;
1220d4af9e69SDag-Erling Smørgrav 	c->filter_cleanup = cfn;
1221af12a3e7SDag-Erling Smørgrav }
1222af12a3e7SDag-Erling Smørgrav 
1223af12a3e7SDag-Erling Smørgrav void
channel_set_fds(struct ssh * ssh,int id,int rfd,int wfd,int efd,int extusage,int nonblock,int is_tty,u_int window_max)12244f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
1225d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty, u_int window_max)
1226af12a3e7SDag-Erling Smørgrav {
12274f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
12284f52dfbbSDag-Erling Smørgrav 	int r;
1229f388f5efSDag-Erling Smørgrav 
1230af12a3e7SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1231af12a3e7SDag-Erling Smørgrav 		fatal("channel_activate for non-larval channel %d.", id);
12324f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
123319261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
12344f52dfbbSDag-Erling Smørgrav 
12354f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1236af12a3e7SDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1237a91a2465SEd Maste 	channel_set_used_time(ssh, c);
1238af12a3e7SDag-Erling Smørgrav 	c->local_window = c->local_window_max = window_max;
12394f52dfbbSDag-Erling Smørgrav 
12404f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
12414f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
12424f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
12434f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
124419261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1245511b41d2SMark Murray }
1246511b41d2SMark Murray 
1247af12a3e7SDag-Erling Smørgrav static void
channel_pre_listener(struct ssh * ssh,Channel * c)12481323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c)
1249511b41d2SMark Murray {
12501323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_R;
1251a04a10f8SKris Kennaway }
1252a04a10f8SKris Kennaway 
1253af12a3e7SDag-Erling Smørgrav static void
channel_pre_connecting(struct ssh * ssh,Channel * c)12541323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c)
1255ca3176e7SBrian Feldman {
1256ca3176e7SBrian Feldman 	debug3("channel %d: waiting for connection", c->self);
12571323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_W;
1258ca3176e7SBrian Feldman }
1259ca3176e7SBrian Feldman 
1260af12a3e7SDag-Erling Smørgrav static void
channel_pre_open(struct ssh * ssh,Channel * c)12611323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c)
1262a04a10f8SKris Kennaway {
12631323ec57SEd Maste 	c->io_want = 0;
1264a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
12654f52dfbbSDag-Erling Smørgrav 	    c->remote_window > 0 &&
12664f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->input) < c->remote_window &&
12674f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
12681323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1269a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1270a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
12714f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0) {
12721323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1273a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
127480628bacSDag-Erling Smørgrav 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
12754f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
12764f52dfbbSDag-Erling Smørgrav 				    "obuf_empty delayed efd %d/(%zu)", c->self,
12774f52dfbbSDag-Erling Smørgrav 				    c->efd, sshbuf_len(c->extended));
127880628bacSDag-Erling Smørgrav 			else
12794f52dfbbSDag-Erling Smørgrav 				chan_obuf_empty(ssh, c);
1280a04a10f8SKris Kennaway 		}
1281a04a10f8SKris Kennaway 	}
1282a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
12834f52dfbbSDag-Erling Smørgrav 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
12844f52dfbbSDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1285a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
12864f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) > 0)
12871323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_W;
1288e2f6069cSDag-Erling Smørgrav 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
1289e2f6069cSDag-Erling Smørgrav 		    (c->extended_usage == CHAN_EXTENDED_READ ||
1290e2f6069cSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
12914f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) < c->remote_window)
12921323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_R;
1293a04a10f8SKris Kennaway 	}
129421e764dfSDag-Erling Smørgrav 	/* XXX: What about efd? races? */
1295a04a10f8SKris Kennaway }
1296a04a10f8SKris Kennaway 
1297a04a10f8SKris Kennaway /*
1298a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
1299a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
1300a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
1301a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
1302a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
1303a04a10f8SKris Kennaway  * XXX All this happens at the client side.
1304af12a3e7SDag-Erling Smørgrav  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1305a04a10f8SKris Kennaway  */
1306af12a3e7SDag-Erling Smørgrav static int
x11_open_helper(struct ssh * ssh,struct sshbuf * b)13074f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1308a04a10f8SKris Kennaway {
13094f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
1310ca3176e7SBrian Feldman 	u_char *ucp;
1311ca3176e7SBrian Feldman 	u_int proto_len, data_len;
1312511b41d2SMark Murray 
1313557f75e5SDag-Erling Smørgrav 	/* Is this being called after the refusal deadline? */
13144f52dfbbSDag-Erling Smørgrav 	if (sc->x11_refuse_time != 0 &&
13154d3fc8b0SEd Maste 	    monotime() >= sc->x11_refuse_time) {
1316557f75e5SDag-Erling Smørgrav 		verbose("Rejected X11 connection after ForwardX11Timeout "
1317557f75e5SDag-Erling Smørgrav 		    "expired");
1318557f75e5SDag-Erling Smørgrav 		return -1;
1319557f75e5SDag-Erling Smørgrav 	}
1320557f75e5SDag-Erling Smørgrav 
1321511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
13224f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) < 12)
1323a04a10f8SKris Kennaway 		return 0;
1324511b41d2SMark Murray 
1325511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
13264f52dfbbSDag-Erling Smørgrav 	ucp = sshbuf_mutable_ptr(b);
1327511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1328511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
1329511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
1330511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1331511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
1332511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
1333511b41d2SMark Murray 	} else {
1334221552e4SDag-Erling Smørgrav 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1335511b41d2SMark Murray 		    ucp[0]);
1336a04a10f8SKris Kennaway 		return -1;
1337511b41d2SMark Murray 	}
1338511b41d2SMark Murray 
1339511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
13404f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) <
1341511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1342a04a10f8SKris Kennaway 		return 0;
1343511b41d2SMark Murray 
1344511b41d2SMark Murray 	/* Check if authentication protocol matches. */
13454f52dfbbSDag-Erling Smørgrav 	if (proto_len != strlen(sc->x11_saved_proto) ||
13464f52dfbbSDag-Erling Smørgrav 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1347221552e4SDag-Erling Smørgrav 		debug2("X11 connection uses different authentication protocol.");
1348a04a10f8SKris Kennaway 		return -1;
1349511b41d2SMark Murray 	}
1350511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
13514f52dfbbSDag-Erling Smørgrav 	if (data_len != sc->x11_fake_data_len ||
1352e2f6069cSDag-Erling Smørgrav 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
13534f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1354221552e4SDag-Erling Smørgrav 		debug2("X11 auth data does not match fake data.");
1355a04a10f8SKris Kennaway 		return -1;
1356511b41d2SMark Murray 	}
1357511b41d2SMark Murray 	/* Check fake data length */
13584f52dfbbSDag-Erling Smørgrav 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1359511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
13604f52dfbbSDag-Erling Smørgrav 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1361a04a10f8SKris Kennaway 		return -1;
1362511b41d2SMark Murray 	}
1363511b41d2SMark Murray 	/*
1364511b41d2SMark Murray 	 * Received authentication protocol and data match
1365511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
1366511b41d2SMark Murray 	 * data.
1367511b41d2SMark Murray 	 */
1368511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
13694f52dfbbSDag-Erling Smørgrav 	    sc->x11_saved_data, sc->x11_saved_data_len);
1370a04a10f8SKris Kennaway 	return 1;
1371a04a10f8SKris Kennaway }
1372511b41d2SMark Murray 
1373f374ba41SEd Maste void
channel_force_close(struct ssh * ssh,Channel * c,int abandon)1374f374ba41SEd Maste channel_force_close(struct ssh *ssh, Channel *c, int abandon)
1375f374ba41SEd Maste {
1376f374ba41SEd Maste 	debug3_f("channel %d: forcibly closing", c->self);
1377f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_OPEN)
1378f374ba41SEd Maste 		chan_read_failed(ssh, c);
1379f374ba41SEd Maste 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1380f374ba41SEd Maste 		sshbuf_reset(c->input);
1381f374ba41SEd Maste 		chan_ibuf_empty(ssh, c);
1382f374ba41SEd Maste 	}
1383f374ba41SEd Maste 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1384f374ba41SEd Maste 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1385f374ba41SEd Maste 		sshbuf_reset(c->output);
1386f374ba41SEd Maste 		chan_write_failed(ssh, c);
1387f374ba41SEd Maste 	}
1388f374ba41SEd Maste 	if (c->detach_user)
1389f374ba41SEd Maste 		c->detach_user(ssh, c->self, 1, NULL);
1390f374ba41SEd Maste 	if (c->efd != -1)
1391f374ba41SEd Maste 		channel_close_fd(ssh, c, &c->efd);
1392f374ba41SEd Maste 	if (abandon)
1393f374ba41SEd Maste 		c->type = SSH_CHANNEL_ABANDONED;
1394f374ba41SEd Maste 	/* exempt from inactivity timeouts */
1395f374ba41SEd Maste 	c->inactive_deadline = 0;
1396f374ba41SEd Maste 	c->lastused = 0;
1397f374ba41SEd Maste }
1398f374ba41SEd Maste 
1399af12a3e7SDag-Erling Smørgrav static void
channel_pre_x11_open(struct ssh * ssh,Channel * c)14001323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c)
1401a04a10f8SKris Kennaway {
14024f52dfbbSDag-Erling Smørgrav 	int ret = x11_open_helper(ssh, c->output);
1403af12a3e7SDag-Erling Smørgrav 
1404af12a3e7SDag-Erling Smørgrav 	/* c->force_drain = 1; */
1405af12a3e7SDag-Erling Smørgrav 
1406a04a10f8SKris Kennaway 	if (ret == 1) {
1407a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
1408a91a2465SEd Maste 		channel_set_used_time(ssh, c);
14091323ec57SEd Maste 		channel_pre_open(ssh, c);
1410a04a10f8SKris Kennaway 	} else if (ret == -1) {
1411f374ba41SEd Maste 		logit("X11 connection rejected because of wrong "
1412f374ba41SEd Maste 		    "authentication.");
14134f52dfbbSDag-Erling Smørgrav 		debug2("X11 rejected %d i%d/o%d",
14144f52dfbbSDag-Erling Smørgrav 		    c->self, c->istate, c->ostate);
1415f374ba41SEd Maste 		channel_force_close(ssh, c, 0);
1416a04a10f8SKris Kennaway 	}
1417a04a10f8SKris Kennaway }
1418a04a10f8SKris Kennaway 
1419b15c8340SDag-Erling Smørgrav static void
channel_pre_mux_client(struct ssh * ssh,Channel * c)14201323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c)
1421b15c8340SDag-Erling Smørgrav {
14221323ec57SEd Maste 	c->io_want = 0;
1423e2f6069cSDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
14244f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
14251323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1426b15c8340SDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1427b15c8340SDag-Erling Smørgrav 		/* clear buffer immediately (discard any partial packet) */
14284f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
14294f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
1430b15c8340SDag-Erling Smørgrav 		/* Start output drain. XXX just kill chan? */
14314f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
1432b15c8340SDag-Erling Smørgrav 	}
1433b15c8340SDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1434b15c8340SDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
14354f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0)
14361323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1437b15c8340SDag-Erling Smørgrav 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
14384f52dfbbSDag-Erling Smørgrav 			chan_obuf_empty(ssh, c);
1439b15c8340SDag-Erling Smørgrav 	}
1440b15c8340SDag-Erling Smørgrav }
1441b15c8340SDag-Erling Smørgrav 
1442ca3176e7SBrian Feldman /* try to decode a socks4 header */
1443af12a3e7SDag-Erling Smørgrav static int
channel_decode_socks4(Channel * c,struct sshbuf * input,struct sshbuf * output)14444f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1445ca3176e7SBrian Feldman {
14464f52dfbbSDag-Erling Smørgrav 	const u_char *p;
14474f52dfbbSDag-Erling Smørgrav 	char *host;
1448cce7d346SDag-Erling Smørgrav 	u_int len, have, i, found, need;
1449ca3176e7SBrian Feldman 	char username[256];
1450ca3176e7SBrian Feldman 	struct {
1451ca3176e7SBrian Feldman 		u_int8_t version;
1452ca3176e7SBrian Feldman 		u_int8_t command;
1453ca3176e7SBrian Feldman 		u_int16_t dest_port;
1454ca3176e7SBrian Feldman 		struct in_addr dest_addr;
1455ca3176e7SBrian Feldman 	} s4_req, s4_rsp;
14564f52dfbbSDag-Erling Smørgrav 	int r;
1457ca3176e7SBrian Feldman 
1458ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4", c->self);
1459ca3176e7SBrian Feldman 
14604f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1461ca3176e7SBrian Feldman 	len = sizeof(s4_req);
1462ca3176e7SBrian Feldman 	if (have < len)
1463ca3176e7SBrian Feldman 		return 0;
14644f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1465cce7d346SDag-Erling Smørgrav 
1466cce7d346SDag-Erling Smørgrav 	need = 1;
1467cce7d346SDag-Erling Smørgrav 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1468cce7d346SDag-Erling Smørgrav 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1469cce7d346SDag-Erling Smørgrav 		debug2("channel %d: socks4a request", c->self);
1470cce7d346SDag-Erling Smørgrav 		/* ... and needs an extra string (the hostname) */
1471cce7d346SDag-Erling Smørgrav 		need = 2;
1472cce7d346SDag-Erling Smørgrav 	}
1473cce7d346SDag-Erling Smørgrav 	/* Check for terminating NUL on the string(s) */
1474ca3176e7SBrian Feldman 	for (found = 0, i = len; i < have; i++) {
1475ca3176e7SBrian Feldman 		if (p[i] == '\0') {
1476cce7d346SDag-Erling Smørgrav 			found++;
1477cce7d346SDag-Erling Smørgrav 			if (found == need)
1478ca3176e7SBrian Feldman 				break;
1479ca3176e7SBrian Feldman 		}
1480ca3176e7SBrian Feldman 		if (i > 1024) {
1481ca3176e7SBrian Feldman 			/* the peer is probably sending garbage */
1482ca3176e7SBrian Feldman 			debug("channel %d: decode socks4: too long",
1483ca3176e7SBrian Feldman 			    c->self);
1484ca3176e7SBrian Feldman 			return -1;
1485ca3176e7SBrian Feldman 		}
1486ca3176e7SBrian Feldman 	}
1487cce7d346SDag-Erling Smørgrav 	if (found < need)
1488ca3176e7SBrian Feldman 		return 0;
14894f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
14904f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
14914f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
14924f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
149319261079SEd Maste 		debug_r(r, "channels %d: decode socks4", c->self);
14944f52dfbbSDag-Erling Smørgrav 		return -1;
14954f52dfbbSDag-Erling Smørgrav 	}
14964f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
14974f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
14984f52dfbbSDag-Erling Smørgrav 	if (memchr(p, '\0', have) == NULL) {
149919261079SEd Maste 		error("channel %d: decode socks4: unterminated user", c->self);
15004f52dfbbSDag-Erling Smørgrav 		return -1;
15014f52dfbbSDag-Erling Smørgrav 	}
1502ca3176e7SBrian Feldman 	len = strlen(p);
1503ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1504cce7d346SDag-Erling Smørgrav 	len++; /* trailing '\0' */
1505ca3176e7SBrian Feldman 	strlcpy(username, p, sizeof(username));
150619261079SEd Maste 	if ((r = sshbuf_consume(input, len)) != 0)
150719261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
1508e4a9863fSDag-Erling Smørgrav 	free(c->path);
1509cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1510cce7d346SDag-Erling Smørgrav 	if (need == 1) {			/* SOCKS4: one string */
1511ca3176e7SBrian Feldman 		host = inet_ntoa(s4_req.dest_addr);
1512cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
1513cce7d346SDag-Erling Smørgrav 	} else {				/* SOCKS4A: two strings */
15144f52dfbbSDag-Erling Smørgrav 		have = sshbuf_len(input);
15154f52dfbbSDag-Erling Smørgrav 		p = sshbuf_ptr(input);
15164f52dfbbSDag-Erling Smørgrav 		if (memchr(p, '\0', have) == NULL) {
15174f52dfbbSDag-Erling Smørgrav 			error("channel %d: decode socks4a: host not nul "
15184f52dfbbSDag-Erling Smørgrav 			    "terminated", c->self);
15194f52dfbbSDag-Erling Smørgrav 			return -1;
15204f52dfbbSDag-Erling Smørgrav 		}
1521cce7d346SDag-Erling Smørgrav 		len = strlen(p);
1522cce7d346SDag-Erling Smørgrav 		debug2("channel %d: decode socks4a: host %s/%d",
1523cce7d346SDag-Erling Smørgrav 		    c->self, p, len);
1524cce7d346SDag-Erling Smørgrav 		len++;				/* trailing '\0' */
1525cce7d346SDag-Erling Smørgrav 		if (len > NI_MAXHOST) {
1526cce7d346SDag-Erling Smørgrav 			error("channel %d: hostname \"%.100s\" too long",
1527cce7d346SDag-Erling Smørgrav 			    c->self, p);
1528cce7d346SDag-Erling Smørgrav 			return -1;
1529cce7d346SDag-Erling Smørgrav 		}
1530cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(p);
153119261079SEd Maste 		if ((r = sshbuf_consume(input, len)) != 0)
153219261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
1533cce7d346SDag-Erling Smørgrav 	}
1534ca3176e7SBrian Feldman 	c->host_port = ntohs(s4_req.dest_port);
1535ca3176e7SBrian Feldman 
1536221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1537cce7d346SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s4_req.command);
1538ca3176e7SBrian Feldman 
1539ca3176e7SBrian Feldman 	if (s4_req.command != 1) {
1540cce7d346SDag-Erling Smørgrav 		debug("channel %d: cannot handle: %s cn %d",
1541cce7d346SDag-Erling Smørgrav 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1542ca3176e7SBrian Feldman 		return -1;
1543ca3176e7SBrian Feldman 	}
1544ca3176e7SBrian Feldman 	s4_rsp.version = 0;			/* vn: 0 for reply */
1545ca3176e7SBrian Feldman 	s4_rsp.command = 90;			/* cd: req granted */
1546ca3176e7SBrian Feldman 	s4_rsp.dest_port = 0;			/* ignored */
1547ca3176e7SBrian Feldman 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
154819261079SEd Maste 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0)
154919261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1550ca3176e7SBrian Feldman 	return 1;
1551ca3176e7SBrian Feldman }
1552ca3176e7SBrian Feldman 
1553221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */
1554221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE	0x1000
1555221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH	0x00
1556221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4		0x01
1557221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN	0x03
1558221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6		0x04
1559221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT	0x01
1560221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS	0x00
1561221552e4SDag-Erling Smørgrav 
1562221552e4SDag-Erling Smørgrav static int
channel_decode_socks5(Channel * c,struct sshbuf * input,struct sshbuf * output)15634f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1564221552e4SDag-Erling Smørgrav {
15654f52dfbbSDag-Erling Smørgrav 	/* XXX use get/put_u8 instead of trusting struct padding */
1566221552e4SDag-Erling Smørgrav 	struct {
1567221552e4SDag-Erling Smørgrav 		u_int8_t version;
1568221552e4SDag-Erling Smørgrav 		u_int8_t command;
1569221552e4SDag-Erling Smørgrav 		u_int8_t reserved;
1570221552e4SDag-Erling Smørgrav 		u_int8_t atyp;
1571221552e4SDag-Erling Smørgrav 	} s5_req, s5_rsp;
1572221552e4SDag-Erling Smørgrav 	u_int16_t dest_port;
1573e4a9863fSDag-Erling Smørgrav 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
15744f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1575333ee039SDag-Erling Smørgrav 	u_int have, need, i, found, nmethods, addrlen, af;
15764f52dfbbSDag-Erling Smørgrav 	int r;
1577221552e4SDag-Erling Smørgrav 
1578221552e4SDag-Erling Smørgrav 	debug2("channel %d: decode socks5", c->self);
15794f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1580221552e4SDag-Erling Smørgrav 	if (p[0] != 0x05)
1581221552e4SDag-Erling Smørgrav 		return -1;
15824f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1583221552e4SDag-Erling Smørgrav 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1584221552e4SDag-Erling Smørgrav 		/* format: ver | nmethods | methods */
1585221552e4SDag-Erling Smørgrav 		if (have < 2)
1586221552e4SDag-Erling Smørgrav 			return 0;
1587221552e4SDag-Erling Smørgrav 		nmethods = p[1];
1588221552e4SDag-Erling Smørgrav 		if (have < nmethods + 2)
1589221552e4SDag-Erling Smørgrav 			return 0;
1590221552e4SDag-Erling Smørgrav 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1591221552e4SDag-Erling Smørgrav 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1592221552e4SDag-Erling Smørgrav 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1593221552e4SDag-Erling Smørgrav 				found = 1;
1594221552e4SDag-Erling Smørgrav 				break;
1595221552e4SDag-Erling Smørgrav 			}
1596221552e4SDag-Erling Smørgrav 		}
1597221552e4SDag-Erling Smørgrav 		if (!found) {
1598221552e4SDag-Erling Smørgrav 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1599221552e4SDag-Erling Smørgrav 			    c->self);
1600221552e4SDag-Erling Smørgrav 			return -1;
1601221552e4SDag-Erling Smørgrav 		}
160219261079SEd Maste 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0)
160319261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
16044f52dfbbSDag-Erling Smørgrav 		/* version, method */
16054f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
160619261079SEd Maste 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0)
160719261079SEd Maste 			fatal_fr(r, "channel %d: append reply", c->self);
1608221552e4SDag-Erling Smørgrav 		c->flags |= SSH_SOCKS5_AUTHDONE;
1609221552e4SDag-Erling Smørgrav 		debug2("channel %d: socks5 auth done", c->self);
1610221552e4SDag-Erling Smørgrav 		return 0;				/* need more */
1611221552e4SDag-Erling Smørgrav 	}
1612221552e4SDag-Erling Smørgrav 	debug2("channel %d: socks5 post auth", c->self);
1613221552e4SDag-Erling Smørgrav 	if (have < sizeof(s5_req)+1)
1614221552e4SDag-Erling Smørgrav 		return 0;			/* need more */
1615333ee039SDag-Erling Smørgrav 	memcpy(&s5_req, p, sizeof(s5_req));
1616221552e4SDag-Erling Smørgrav 	if (s5_req.version != 0x05 ||
1617221552e4SDag-Erling Smørgrav 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1618221552e4SDag-Erling Smørgrav 	    s5_req.reserved != 0x00) {
1619221552e4SDag-Erling Smørgrav 		debug2("channel %d: only socks5 connect supported", c->self);
1620221552e4SDag-Erling Smørgrav 		return -1;
1621221552e4SDag-Erling Smørgrav 	}
1622221552e4SDag-Erling Smørgrav 	switch (s5_req.atyp){
1623221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV4:
1624221552e4SDag-Erling Smørgrav 		addrlen = 4;
1625221552e4SDag-Erling Smørgrav 		af = AF_INET;
1626221552e4SDag-Erling Smørgrav 		break;
1627221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_DOMAIN:
1628221552e4SDag-Erling Smørgrav 		addrlen = p[sizeof(s5_req)];
1629221552e4SDag-Erling Smørgrav 		af = -1;
1630221552e4SDag-Erling Smørgrav 		break;
1631221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV6:
1632221552e4SDag-Erling Smørgrav 		addrlen = 16;
1633221552e4SDag-Erling Smørgrav 		af = AF_INET6;
1634221552e4SDag-Erling Smørgrav 		break;
1635221552e4SDag-Erling Smørgrav 	default:
1636221552e4SDag-Erling Smørgrav 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1637221552e4SDag-Erling Smørgrav 		return -1;
1638221552e4SDag-Erling Smørgrav 	}
1639333ee039SDag-Erling Smørgrav 	need = sizeof(s5_req) + addrlen + 2;
1640333ee039SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1641333ee039SDag-Erling Smørgrav 		need++;
1642333ee039SDag-Erling Smørgrav 	if (have < need)
1643221552e4SDag-Erling Smørgrav 		return 0;
164419261079SEd Maste 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0)
164519261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
16464f52dfbbSDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
16474f52dfbbSDag-Erling Smørgrav 		/* host string length */
164819261079SEd Maste 		if ((r = sshbuf_consume(input, 1)) != 0)
164919261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
16504f52dfbbSDag-Erling Smørgrav 	}
16514f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
16524f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
165319261079SEd Maste 		debug_r(r, "channel %d: parse addr/port", c->self);
16544f52dfbbSDag-Erling Smørgrav 		return -1;
16554f52dfbbSDag-Erling Smørgrav 	}
1656221552e4SDag-Erling Smørgrav 	dest_addr[addrlen] = '\0';
1657e4a9863fSDag-Erling Smørgrav 	free(c->path);
1658cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1659cce7d346SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1660cce7d346SDag-Erling Smørgrav 		if (addrlen >= NI_MAXHOST) {
1661cce7d346SDag-Erling Smørgrav 			error("channel %d: dynamic request: socks5 hostname "
1662cce7d346SDag-Erling Smørgrav 			    "\"%.100s\" too long", c->self, dest_addr);
1663221552e4SDag-Erling Smørgrav 			return -1;
1664cce7d346SDag-Erling Smørgrav 		}
1665cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(dest_addr);
1666cce7d346SDag-Erling Smørgrav 	} else {
1667cce7d346SDag-Erling Smørgrav 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1668cce7d346SDag-Erling Smørgrav 			return -1;
1669cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(ntop);
1670cce7d346SDag-Erling Smørgrav 	}
1671221552e4SDag-Erling Smørgrav 	c->host_port = ntohs(dest_port);
1672221552e4SDag-Erling Smørgrav 
1673221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1674221552e4SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s5_req.command);
1675221552e4SDag-Erling Smørgrav 
1676221552e4SDag-Erling Smørgrav 	s5_rsp.version = 0x05;
1677221552e4SDag-Erling Smørgrav 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1678221552e4SDag-Erling Smørgrav 	s5_rsp.reserved = 0;			/* ignored */
1679221552e4SDag-Erling Smørgrav 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1680221552e4SDag-Erling Smørgrav 	dest_port = 0;				/* ignored */
1681221552e4SDag-Erling Smørgrav 
16824f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
16834f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
16844f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
168519261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1686221552e4SDag-Erling Smørgrav 	return 1;
1687221552e4SDag-Erling Smørgrav }
1688221552e4SDag-Erling Smørgrav 
1689b15c8340SDag-Erling Smørgrav Channel *
channel_connect_stdio_fwd(struct ssh * ssh,const char * host_to_connect,int port_to_connect,int in,int out,int nonblock)16904f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh,
1691535af610SEd Maste     const char *host_to_connect, int port_to_connect,
169219261079SEd Maste     int in, int out, int nonblock)
1693b15c8340SDag-Erling Smørgrav {
1694b15c8340SDag-Erling Smørgrav 	Channel *c;
1695b15c8340SDag-Erling Smørgrav 
169619261079SEd Maste 	debug_f("%s:%d", host_to_connect, port_to_connect);
1697b15c8340SDag-Erling Smørgrav 
16984f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
1699b15c8340SDag-Erling Smørgrav 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
170019261079SEd Maste 	    0, "stdio-forward", nonblock);
1701b15c8340SDag-Erling Smørgrav 
1702b15c8340SDag-Erling Smørgrav 	c->path = xstrdup(host_to_connect);
1703b15c8340SDag-Erling Smørgrav 	c->host_port = port_to_connect;
1704b15c8340SDag-Erling Smørgrav 	c->listening_port = 0;
1705b15c8340SDag-Erling Smørgrav 	c->force_drain = 1;
1706b15c8340SDag-Erling Smørgrav 
17074f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
1708535af610SEd Maste 	port_open_helper(ssh, c, port_to_connect == PORT_STREAMLOCAL ?
1709535af610SEd Maste 	    "direct-streamlocal@openssh.com" : "direct-tcpip");
1710b15c8340SDag-Erling Smørgrav 
1711b15c8340SDag-Erling Smørgrav 	return c;
1712b15c8340SDag-Erling Smørgrav }
1713b15c8340SDag-Erling Smørgrav 
1714ca3176e7SBrian Feldman /* dynamic port forwarding */
1715af12a3e7SDag-Erling Smørgrav static void
channel_pre_dynamic(struct ssh * ssh,Channel * c)17161323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c)
1717ca3176e7SBrian Feldman {
17184f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1719d4ecd108SDag-Erling Smørgrav 	u_int have;
1720d4ecd108SDag-Erling Smørgrav 	int ret;
1721ca3176e7SBrian Feldman 
17221323ec57SEd Maste 	c->io_want = 0;
17234f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->input);
1724ca3176e7SBrian Feldman 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
17254f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->input, stderr); */
1726ca3176e7SBrian Feldman 	/* check if the fixed size part of the packet is in buffer. */
1727221552e4SDag-Erling Smørgrav 	if (have < 3) {
1728ca3176e7SBrian Feldman 		/* need more */
17291323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1730ca3176e7SBrian Feldman 		return;
1731ca3176e7SBrian Feldman 	}
1732ca3176e7SBrian Feldman 	/* try to guess the protocol */
17334f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->input);
17344f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u8? */
1735ca3176e7SBrian Feldman 	switch (p[0]) {
1736ca3176e7SBrian Feldman 	case 0x04:
17374f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->input, c->output);
1738ca3176e7SBrian Feldman 		break;
1739221552e4SDag-Erling Smørgrav 	case 0x05:
17404f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->input, c->output);
1741221552e4SDag-Erling Smørgrav 		break;
1742ca3176e7SBrian Feldman 	default:
1743ca3176e7SBrian Feldman 		ret = -1;
1744ca3176e7SBrian Feldman 		break;
1745ca3176e7SBrian Feldman 	}
1746ca3176e7SBrian Feldman 	if (ret < 0) {
17474f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1748ca3176e7SBrian Feldman 	} else if (ret == 0) {
1749ca3176e7SBrian Feldman 		debug2("channel %d: pre_dynamic: need more", c->self);
1750ca3176e7SBrian Feldman 		/* need more */
17511323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
17524f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output))
17531323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1754ca3176e7SBrian Feldman 	} else {
1755ca3176e7SBrian Feldman 		/* switch to the next state */
1756ca3176e7SBrian Feldman 		c->type = SSH_CHANNEL_OPENING;
17574f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, c, "direct-tcpip");
17584f52dfbbSDag-Erling Smørgrav 	}
17594f52dfbbSDag-Erling Smørgrav }
17604f52dfbbSDag-Erling Smørgrav 
17614f52dfbbSDag-Erling Smørgrav /* simulate read-error */
17624f52dfbbSDag-Erling Smørgrav static void
rdynamic_close(struct ssh * ssh,Channel * c)17634f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c)
17644f52dfbbSDag-Erling Smørgrav {
17654f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1766f374ba41SEd Maste 	channel_force_close(ssh, c, 0);
17674f52dfbbSDag-Erling Smørgrav }
17684f52dfbbSDag-Erling Smørgrav 
17694f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */
17704f52dfbbSDag-Erling Smørgrav static void
channel_before_prepare_io_rdynamic(struct ssh * ssh,Channel * c)17711323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c)
17724f52dfbbSDag-Erling Smørgrav {
17734f52dfbbSDag-Erling Smørgrav 	const u_char *p;
17744f52dfbbSDag-Erling Smørgrav 	u_int have, len;
17754f52dfbbSDag-Erling Smørgrav 	int r, ret;
17764f52dfbbSDag-Erling Smørgrav 
17774f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->output);
17784f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
17794f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->output, stderr); */
17804f52dfbbSDag-Erling Smørgrav 	/* EOF received */
17814f52dfbbSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
178219261079SEd Maste 		if ((r = sshbuf_consume(c->output, have)) != 0)
178319261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
17844f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
17854f52dfbbSDag-Erling Smørgrav 		return;
17864f52dfbbSDag-Erling Smørgrav 	}
17874f52dfbbSDag-Erling Smørgrav 	/* check if the fixed size part of the packet is in buffer. */
17884f52dfbbSDag-Erling Smørgrav 	if (have < 3)
17894f52dfbbSDag-Erling Smørgrav 		return;
17904f52dfbbSDag-Erling Smørgrav 	/* try to guess the protocol */
17914f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->output);
17924f52dfbbSDag-Erling Smørgrav 	switch (p[0]) {
17934f52dfbbSDag-Erling Smørgrav 	case 0x04:
17944f52dfbbSDag-Erling Smørgrav 		/* switch input/output for reverse forwarding */
17954f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->output, c->input);
17964f52dfbbSDag-Erling Smørgrav 		break;
17974f52dfbbSDag-Erling Smørgrav 	case 0x05:
17984f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->output, c->input);
17994f52dfbbSDag-Erling Smørgrav 		break;
18004f52dfbbSDag-Erling Smørgrav 	default:
18014f52dfbbSDag-Erling Smørgrav 		ret = -1;
18024f52dfbbSDag-Erling Smørgrav 		break;
18034f52dfbbSDag-Erling Smørgrav 	}
18044f52dfbbSDag-Erling Smørgrav 	if (ret < 0) {
18054f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
18064f52dfbbSDag-Erling Smørgrav 	} else if (ret == 0) {
18074f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: pre_rdynamic: need more", c->self);
18084f52dfbbSDag-Erling Smørgrav 		/* send socks request to peer */
18094f52dfbbSDag-Erling Smørgrav 		len = sshbuf_len(c->input);
18104f52dfbbSDag-Erling Smørgrav 		if (len > 0 && len < c->remote_window) {
18114f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
18124f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
18134f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
18144f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0) {
181519261079SEd Maste 				fatal_fr(r, "channel %i: rdynamic", c->self);
18164f52dfbbSDag-Erling Smørgrav 			}
181719261079SEd Maste 			if ((r = sshbuf_consume(c->input, len)) != 0)
181819261079SEd Maste 				fatal_fr(r, "channel %d: consume", c->self);
18194f52dfbbSDag-Erling Smørgrav 			c->remote_window -= len;
18204f52dfbbSDag-Erling Smørgrav 		}
18214f52dfbbSDag-Erling Smørgrav 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
18224f52dfbbSDag-Erling Smørgrav 		/* the connect failed */
18234f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
1824ca3176e7SBrian Feldman 	}
1825ca3176e7SBrian Feldman }
1826ca3176e7SBrian Feldman 
1827a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
1828af12a3e7SDag-Erling Smørgrav static void
channel_post_x11_listener(struct ssh * ssh,Channel * c)18291323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c)
1830511b41d2SMark Murray {
1831af12a3e7SDag-Erling Smørgrav 	Channel *nc;
1832d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
18334f52dfbbSDag-Erling Smørgrav 	int r, newsock, oerrno, remote_port;
1834511b41d2SMark Murray 	socklen_t addrlen;
1835ca3176e7SBrian Feldman 	char buf[16384], *remote_ipaddr;
1836511b41d2SMark Murray 
18371323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
18384f52dfbbSDag-Erling Smørgrav 		return;
18394f52dfbbSDag-Erling Smørgrav 
1840511b41d2SMark Murray 	debug("X11 connection requested.");
1841511b41d2SMark Murray 	addrlen = sizeof(addr);
1842d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1843af12a3e7SDag-Erling Smørgrav 	if (c->single_connection) {
1844e4a9863fSDag-Erling Smørgrav 		oerrno = errno;
1845221552e4SDag-Erling Smørgrav 		debug2("single_connection: closing X11 listener.");
184619261079SEd Maste 		channel_close_fd(ssh, c, &c->sock);
18474f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1848e4a9863fSDag-Erling Smørgrav 		errno = oerrno;
1849af12a3e7SDag-Erling Smørgrav 	}
185019261079SEd Maste 	if (newsock == -1) {
1851e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1852e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1853511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1854462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1855e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1856a04a10f8SKris Kennaway 		return;
1857511b41d2SMark Murray 	}
1858af12a3e7SDag-Erling Smørgrav 	set_nodelay(newsock);
1859ca3176e7SBrian Feldman 	remote_ipaddr = get_peer_ipaddr(newsock);
1860a04a10f8SKris Kennaway 	remote_port = get_peer_port(newsock);
1861511b41d2SMark Murray 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1862ca3176e7SBrian Feldman 	    remote_ipaddr, remote_port);
1863a04a10f8SKris Kennaway 
1864f374ba41SEd Maste 	nc = channel_new(ssh, "x11-connection",
1865a04a10f8SKris Kennaway 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1866221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
18674f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "x11");
186847dd1d1bSDag-Erling Smørgrav 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
186947dd1d1bSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
187019261079SEd Maste 		fatal_fr(r, "channel %i: reply", c->self);
1871511b41d2SMark Murray 	}
18724f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
187319261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1874e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1875a04a10f8SKris Kennaway }
1876511b41d2SMark Murray 
1877af12a3e7SDag-Erling Smørgrav static void
port_open_helper(struct ssh * ssh,Channel * c,char * rtype)18784f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
1879ca3176e7SBrian Feldman {
1880f7167e0eSDag-Erling Smørgrav 	char *local_ipaddr = get_local_ipaddr(c->sock);
1881076ad2f8SDag-Erling Smørgrav 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1882ca3176e7SBrian Feldman 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1883d4ecd108SDag-Erling Smørgrav 	int remote_port = get_peer_port(c->sock);
18844f52dfbbSDag-Erling Smørgrav 	int r;
1885ca3176e7SBrian Feldman 
1886b15c8340SDag-Erling Smørgrav 	if (remote_port == -1) {
1887b15c8340SDag-Erling Smørgrav 		/* Fake addr/port to appease peers that validate it (Tectia) */
1888e4a9863fSDag-Erling Smørgrav 		free(remote_ipaddr);
1889b15c8340SDag-Erling Smørgrav 		remote_ipaddr = xstrdup("127.0.0.1");
1890b15c8340SDag-Erling Smørgrav 		remote_port = 65535;
1891b15c8340SDag-Erling Smørgrav 	}
1892b15c8340SDag-Erling Smørgrav 
18934f52dfbbSDag-Erling Smørgrav 	free(c->remote_name);
18944f52dfbbSDag-Erling Smørgrav 	xasprintf(&c->remote_name,
1895ca3176e7SBrian Feldman 	    "%s: listening port %d for %.100s port %d, "
1896f7167e0eSDag-Erling Smørgrav 	    "connect from %.200s port %d to %.100s port %d",
1897ca3176e7SBrian Feldman 	    rtype, c->listening_port, c->path, c->host_port,
1898f7167e0eSDag-Erling Smørgrav 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1899ca3176e7SBrian Feldman 
19004f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, rtype);
1901a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "direct-tcpip") == 0) {
1902ca3176e7SBrian Feldman 		/* target host, port */
19034f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
190419261079SEd Maste 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0)
190519261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1906a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
1907a0ee8cc6SDag-Erling Smørgrav 		/* target path */
190819261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
190919261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1910a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1911a0ee8cc6SDag-Erling Smørgrav 		/* listen path */
191219261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
191319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1914ca3176e7SBrian Feldman 	} else {
1915ca3176e7SBrian Feldman 		/* listen address, port */
19164f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
191719261079SEd Maste 		    (r = sshpkt_put_u32(ssh, local_port)) != 0)
191819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1919ca3176e7SBrian Feldman 	}
1920a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1921a0ee8cc6SDag-Erling Smørgrav 		/* reserved for future owner/mode info */
192219261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, "")) != 0)
192319261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1924a0ee8cc6SDag-Erling Smørgrav 	} else {
1925ca3176e7SBrian Feldman 		/* originator host and port */
19264f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
192719261079SEd Maste 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0)
192819261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1929ca3176e7SBrian Feldman 	}
19304f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
193119261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1932e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1933f7167e0eSDag-Erling Smørgrav 	free(local_ipaddr);
1934ca3176e7SBrian Feldman }
1935ca3176e7SBrian Feldman 
1936557f75e5SDag-Erling Smørgrav void
channel_set_x11_refuse_time(struct ssh * ssh,time_t refuse_time)19374d3fc8b0SEd Maste channel_set_x11_refuse_time(struct ssh *ssh, time_t refuse_time)
1938557f75e5SDag-Erling Smørgrav {
19394f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->x11_refuse_time = refuse_time;
1940557f75e5SDag-Erling Smørgrav }
1941557f75e5SDag-Erling Smørgrav 
1942511b41d2SMark Murray /*
1943a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
1944511b41d2SMark Murray  */
1945af12a3e7SDag-Erling Smørgrav static void
channel_post_port_listener(struct ssh * ssh,Channel * c)19461323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c)
1947a04a10f8SKris Kennaway {
1948ca3176e7SBrian Feldman 	Channel *nc;
1949d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1950af12a3e7SDag-Erling Smørgrav 	int newsock, nextstate;
1951a04a10f8SKris Kennaway 	socklen_t addrlen;
1952ca3176e7SBrian Feldman 	char *rtype;
1953a04a10f8SKris Kennaway 
19541323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
19554f52dfbbSDag-Erling Smørgrav 		return;
19564f52dfbbSDag-Erling Smørgrav 
19574f52dfbbSDag-Erling Smørgrav 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1958a04a10f8SKris Kennaway 	    c->listening_port, c->path, c->host_port);
1959ca3176e7SBrian Feldman 
1960af12a3e7SDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1961af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1962af12a3e7SDag-Erling Smørgrav 		rtype = "forwarded-tcpip";
1963a0ee8cc6SDag-Erling Smørgrav 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
1964a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1965a0ee8cc6SDag-Erling Smørgrav 		rtype = "forwarded-streamlocal@openssh.com";
1966a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == PORT_STREAMLOCAL) {
1967a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1968a0ee8cc6SDag-Erling Smørgrav 		rtype = "direct-streamlocal@openssh.com";
1969a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == 0) {
1970af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_DYNAMIC;
1971af12a3e7SDag-Erling Smørgrav 		rtype = "dynamic-tcpip";
1972af12a3e7SDag-Erling Smørgrav 	} else {
1973af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1974af12a3e7SDag-Erling Smørgrav 		rtype = "direct-tcpip";
1975af12a3e7SDag-Erling Smørgrav 	}
1976ca3176e7SBrian Feldman 
1977511b41d2SMark Murray 	addrlen = sizeof(addr);
1978d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
197919261079SEd Maste 	if (newsock == -1) {
1980e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1981e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1982511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1983462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1984e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1985a04a10f8SKris Kennaway 		return;
1986511b41d2SMark Murray 	}
1987a0ee8cc6SDag-Erling Smørgrav 	if (c->host_port != PORT_STREAMLOCAL)
1988af12a3e7SDag-Erling Smørgrav 		set_nodelay(newsock);
19894f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
1990221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
1991ca3176e7SBrian Feldman 	nc->listening_port = c->listening_port;
1992ca3176e7SBrian Feldman 	nc->host_port = c->host_port;
1993cce7d346SDag-Erling Smørgrav 	if (c->path != NULL)
1994cce7d346SDag-Erling Smørgrav 		nc->path = xstrdup(c->path);
1995ca3176e7SBrian Feldman 
1996b15c8340SDag-Erling Smørgrav 	if (nextstate != SSH_CHANNEL_DYNAMIC)
19974f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, nc, rtype);
1998a04a10f8SKris Kennaway }
1999511b41d2SMark Murray 
2000511b41d2SMark Murray /*
2001a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
2002a04a10f8SKris Kennaway  * clients.
2003511b41d2SMark Murray  */
2004af12a3e7SDag-Erling Smørgrav static void
channel_post_auth_listener(struct ssh * ssh,Channel * c)20051323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c)
2006a04a10f8SKris Kennaway {
2007af12a3e7SDag-Erling Smørgrav 	Channel *nc;
20084f52dfbbSDag-Erling Smørgrav 	int r, newsock;
2009d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2010a04a10f8SKris Kennaway 	socklen_t addrlen;
2011a04a10f8SKris Kennaway 
20121323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
20134f52dfbbSDag-Erling Smørgrav 		return;
20144f52dfbbSDag-Erling Smørgrav 
2015511b41d2SMark Murray 	addrlen = sizeof(addr);
2016d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
201719261079SEd Maste 	if (newsock == -1) {
20184f52dfbbSDag-Erling Smørgrav 		error("accept from auth socket: %.100s", strerror(errno));
2019462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2020e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2021a04a10f8SKris Kennaway 		return;
2022511b41d2SMark Murray 	}
2023f374ba41SEd Maste 	nc = channel_new(ssh, "agent-connection",
2024ca3176e7SBrian Feldman 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
2025ca3176e7SBrian Feldman 	    c->local_window_max, c->local_maxpacket,
2026221552e4SDag-Erling Smørgrav 	    0, "accepted auth socket", 1);
20274f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
20284f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
202919261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
2030a04a10f8SKris Kennaway }
2031511b41d2SMark Murray 
2032af12a3e7SDag-Erling Smørgrav static void
channel_post_connecting(struct ssh * ssh,Channel * c)20331323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c)
2034ca3176e7SBrian Feldman {
20354f52dfbbSDag-Erling Smørgrav 	int err = 0, sock, isopen, r;
2036af12a3e7SDag-Erling Smørgrav 	socklen_t sz = sizeof(err);
2037af12a3e7SDag-Erling Smørgrav 
20381323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
20394f52dfbbSDag-Erling Smørgrav 		return;
20404f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
204119261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
20424f52dfbbSDag-Erling Smørgrav 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
20434f52dfbbSDag-Erling Smørgrav 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
20444d3fc8b0SEd Maste 
204519261079SEd Maste 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) {
2046af12a3e7SDag-Erling Smørgrav 		err = errno;
2047af12a3e7SDag-Erling Smørgrav 		error("getsockopt SO_ERROR failed");
2048af12a3e7SDag-Erling Smørgrav 	}
20494d3fc8b0SEd Maste 
2050ca3176e7SBrian Feldman 	if (err == 0) {
20514d3fc8b0SEd Maste 		/* Non-blocking connection completed */
2052d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connected to %s port %d",
2053d4af9e69SDag-Erling Smørgrav 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
2054d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
2055af12a3e7SDag-Erling Smørgrav 		c->type = SSH_CHANNEL_OPEN;
2056a91a2465SEd Maste 		channel_set_used_time(ssh, c);
20574f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20584f52dfbbSDag-Erling Smørgrav 			/* no message necessary */
2059af12a3e7SDag-Erling Smørgrav 		} else {
20604f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20614f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
20624f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
20634f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
20644f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
206519261079SEd Maste 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
206619261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
206719261079SEd Maste 				fatal_fr(r, "channel %i open confirm", c->self);
2068af12a3e7SDag-Erling Smørgrav 		}
2069d4af9e69SDag-Erling Smørgrav 		return;
2070d4af9e69SDag-Erling Smørgrav 	}
20714d3fc8b0SEd Maste 	if (err == EINTR || err == EAGAIN || err == EINPROGRESS)
20724d3fc8b0SEd Maste 		return;
20734d3fc8b0SEd Maste 
20744d3fc8b0SEd Maste 	/* Non-blocking connection failed */
20754d3fc8b0SEd Maste 	debug("channel %d: connection failed: %s", c->self, strerror(err));
20764d3fc8b0SEd Maste 
20774d3fc8b0SEd Maste 	/* Try next address, if any */
20784d3fc8b0SEd Maste 	if ((sock = connect_next(&c->connect_ctx)) == -1) {
20794d3fc8b0SEd Maste 		/* Exhausted all addresses for this destination */
2080d4af9e69SDag-Erling Smørgrav 		error("connect_to %.100s port %d: failed.",
2081d4af9e69SDag-Erling Smørgrav 		    c->connect_ctx.host, c->connect_ctx.port);
2082d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
20834f52dfbbSDag-Erling Smørgrav 		if (isopen) {
20844f52dfbbSDag-Erling Smørgrav 			rdynamic_close(ssh, c);
2085af12a3e7SDag-Erling Smørgrav 		} else {
20864f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
20874f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
20884f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
208947dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh,
209047dd1d1bSDag-Erling Smørgrav 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
209147dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
209219261079SEd Maste 			    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
209319261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
209419261079SEd Maste 				fatal_fr(r, "channel %i: failure", c->self);
20954f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2096ca3176e7SBrian Feldman 		}
2097ca3176e7SBrian Feldman 	}
20984d3fc8b0SEd Maste 
20994d3fc8b0SEd Maste 	/* New non-blocking connection in progress */
21004d3fc8b0SEd Maste 	close(c->sock);
21014d3fc8b0SEd Maste 	c->sock = c->rfd = c->wfd = sock;
2102ca3176e7SBrian Feldman }
2103ca3176e7SBrian Feldman 
2104af12a3e7SDag-Erling Smørgrav static int
channel_handle_rfd(struct ssh * ssh,Channel * c)21051323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c)
2106a04a10f8SKris Kennaway {
2107aa49c926SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
21084f52dfbbSDag-Erling Smørgrav 	ssize_t len;
21094f52dfbbSDag-Erling Smørgrav 	int r, force;
2110f374ba41SEd Maste 	size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ;
21111323ec57SEd Maste 	int pty_zeroread = 0;
21121323ec57SEd Maste 
21131323ec57SEd Maste #ifdef PTY_ZEROREAD
21141323ec57SEd Maste 	/* Bug on AIX: read(1) can return 0 for a non-closed fd */
21151323ec57SEd Maste 	pty_zeroread = c->isatty;
21161323ec57SEd Maste #endif
2117511b41d2SMark Murray 
2118d4af9e69SDag-Erling Smørgrav 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
21194f52dfbbSDag-Erling Smørgrav 
21201323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0)
21214f52dfbbSDag-Erling Smørgrav 		return 1;
21221323ec57SEd Maste 	if ((avail = sshbuf_avail(c->input)) == 0)
21231323ec57SEd Maste 		return 1; /* Shouldn't happen */
21241323ec57SEd Maste 
21251323ec57SEd Maste 	/*
21261323ec57SEd Maste 	 * For "simple" channels (i.e. not datagram or filtered), we can
21271323ec57SEd Maste 	 * read directly to the channel buffer.
21281323ec57SEd Maste 	 */
21291323ec57SEd Maste 	if (!pty_zeroread && c->input_filter == NULL && !c->datagram) {
21301323ec57SEd Maste 		/* Only OPEN channels have valid rwin */
21311323ec57SEd Maste 		if (c->type == SSH_CHANNEL_OPEN) {
21321323ec57SEd Maste 			if ((have = sshbuf_len(c->input)) >= c->remote_window)
21331323ec57SEd Maste 				return 1; /* shouldn't happen */
21341323ec57SEd Maste 			if (maxlen > c->remote_window - have)
21351323ec57SEd Maste 				maxlen = c->remote_window - have;
21361323ec57SEd Maste 		}
21371323ec57SEd Maste 		if (maxlen > avail)
21381323ec57SEd Maste 			maxlen = avail;
2139f374ba41SEd Maste 		if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) {
21401323ec57SEd Maste 			if (errno == EINTR || (!force &&
21411323ec57SEd Maste 			    (errno == EAGAIN || errno == EWOULDBLOCK)))
21421323ec57SEd Maste 				return 1;
21431323ec57SEd Maste 			debug2("channel %d: read failed rfd %d maxlen %zu: %s",
21441323ec57SEd Maste 			    c->self, c->rfd, maxlen, ssh_err(r));
21451323ec57SEd Maste 			goto rfail;
21461323ec57SEd Maste 		}
2147f374ba41SEd Maste 		if (nr != 0)
2148a91a2465SEd Maste 			channel_set_used_time(ssh, c);
21491323ec57SEd Maste 		return 1;
21501323ec57SEd Maste 	}
21514f52dfbbSDag-Erling Smørgrav 
2152333ee039SDag-Erling Smørgrav 	errno = 0;
2153a04a10f8SKris Kennaway 	len = read(c->rfd, buf, sizeof(buf));
21541323ec57SEd Maste 	/* fixup AIX zero-length read with errno set to look more like errors */
21551323ec57SEd Maste 	if (pty_zeroread && len == 0 && errno != 0)
21561323ec57SEd Maste 		len = -1;
215719261079SEd Maste 	if (len == -1 && (errno == EINTR ||
2158d4af9e69SDag-Erling Smørgrav 	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
2159a04a10f8SKris Kennaway 		return 1;
21601323ec57SEd Maste 	if (len < 0 || (!pty_zeroread && len == 0)) {
21611323ec57SEd Maste 		debug2("channel %d: read<=0 rfd %d len %zd: %s",
21621323ec57SEd Maste 		    c->self, c->rfd, len,
21631323ec57SEd Maste 		    len == 0 ? "closed" : strerror(errno));
21641323ec57SEd Maste  rfail:
2165ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2166221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
21674f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2168ca3176e7SBrian Feldman 			return -1;
2169a04a10f8SKris Kennaway 		} else {
21704f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2171a04a10f8SKris Kennaway 		}
2172a04a10f8SKris Kennaway 		return -1;
2173a04a10f8SKris Kennaway 	}
2174a91a2465SEd Maste 	channel_set_used_time(ssh, c);
2175b66f2d16SKris Kennaway 	if (c->input_filter != NULL) {
21764f52dfbbSDag-Erling Smørgrav 		if (c->input_filter(ssh, c, buf, len) == -1) {
2177221552e4SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
21784f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2179b66f2d16SKris Kennaway 		}
2180b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
21814f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
218219261079SEd Maste 			fatal_fr(r, "channel %i: put datagram", c->self);
218319261079SEd Maste 	} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
218419261079SEd Maste 		fatal_fr(r, "channel %i: put data", c->self);
21851323ec57SEd Maste 
2186a04a10f8SKris Kennaway 	return 1;
2187a04a10f8SKris Kennaway }
2188333ee039SDag-Erling Smørgrav 
2189af12a3e7SDag-Erling Smørgrav static int
channel_handle_wfd(struct ssh * ssh,Channel * c)21901323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c)
2191a04a10f8SKris Kennaway {
2192ca3176e7SBrian Feldman 	struct termios tio;
21934f52dfbbSDag-Erling Smørgrav 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
21944f52dfbbSDag-Erling Smørgrav 	size_t dlen, olen = 0;
21954f52dfbbSDag-Erling Smørgrav 	int r, len;
21964f52dfbbSDag-Erling Smørgrav 
21971323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
21981323ec57SEd Maste 		return 1;
21991323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
22004f52dfbbSDag-Erling Smørgrav 		return 1;
2201a04a10f8SKris Kennaway 
2202a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
22034f52dfbbSDag-Erling Smørgrav 	olen = sshbuf_len(c->output);
2204b74df5b2SDag-Erling Smørgrav 	if (c->output_filter != NULL) {
22054f52dfbbSDag-Erling Smørgrav 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
2206b74df5b2SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
2207b74df5b2SDag-Erling Smørgrav 			if (c->type != SSH_CHANNEL_OPEN)
22084f52dfbbSDag-Erling Smørgrav 				chan_mark_dead(ssh, c);
2209b74df5b2SDag-Erling Smørgrav 			else
22104f52dfbbSDag-Erling Smørgrav 				chan_write_failed(ssh, c);
2211b74df5b2SDag-Erling Smørgrav 			return -1;
2212b74df5b2SDag-Erling Smørgrav 		}
2213b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
22144f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
221519261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
22164f52dfbbSDag-Erling Smørgrav 		buf = data;
2217b74df5b2SDag-Erling Smørgrav 	} else {
22184f52dfbbSDag-Erling Smørgrav 		buf = data = sshbuf_mutable_ptr(c->output);
22194f52dfbbSDag-Erling Smørgrav 		dlen = sshbuf_len(c->output);
2220b74df5b2SDag-Erling Smørgrav 	}
2221b74df5b2SDag-Erling Smørgrav 
2222b74df5b2SDag-Erling Smørgrav 	if (c->datagram) {
2223b74df5b2SDag-Erling Smørgrav 		/* ignore truncated writes, datagrams might get lost */
2224b74df5b2SDag-Erling Smørgrav 		len = write(c->wfd, buf, dlen);
2225e4a9863fSDag-Erling Smørgrav 		free(data);
222619261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2227d4af9e69SDag-Erling Smørgrav 		    errno == EWOULDBLOCK))
2228b74df5b2SDag-Erling Smørgrav 			return 1;
22294f52dfbbSDag-Erling Smørgrav 		if (len <= 0)
22304f52dfbbSDag-Erling Smørgrav 			goto write_fail;
2231e2f6069cSDag-Erling Smørgrav 		goto out;
2232b74df5b2SDag-Erling Smørgrav 	}
22334f52dfbbSDag-Erling Smørgrav 
2234f388f5efSDag-Erling Smørgrav #ifdef _AIX
2235f388f5efSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
22364f52dfbbSDag-Erling Smørgrav 	if (c->wfd_isatty)
22371323ec57SEd Maste 		dlen = MINIMUM(dlen, 8*1024);
2238f388f5efSDag-Erling Smørgrav #endif
2239b74df5b2SDag-Erling Smørgrav 
2240b74df5b2SDag-Erling Smørgrav 	len = write(c->wfd, buf, dlen);
224119261079SEd Maste 	if (len == -1 &&
2242d4af9e69SDag-Erling Smørgrav 	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
2243a04a10f8SKris Kennaway 		return 1;
2244511b41d2SMark Murray 	if (len <= 0) {
22454f52dfbbSDag-Erling Smørgrav  write_fail:
2246ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2247221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
22484f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2249ca3176e7SBrian Feldman 			return -1;
2250511b41d2SMark Murray 		} else {
22514f52dfbbSDag-Erling Smørgrav 			chan_write_failed(ssh, c);
2252511b41d2SMark Murray 		}
2253a04a10f8SKris Kennaway 		return -1;
2254511b41d2SMark Murray 	}
2255a91a2465SEd Maste 	channel_set_used_time(ssh, c);
22567aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON
22574f52dfbbSDag-Erling Smørgrav 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2258e0fbb1d2SBrian Feldman 		if (tcgetattr(c->wfd, &tio) == 0 &&
2259e0fbb1d2SBrian Feldman 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2260e0fbb1d2SBrian Feldman 			/*
2261e0fbb1d2SBrian Feldman 			 * Simulate echo to reduce the impact of
2262ca3176e7SBrian Feldman 			 * traffic analysis. We need to match the
2263ca3176e7SBrian Feldman 			 * size of a SSH2_MSG_CHANNEL_DATA message
2264b74df5b2SDag-Erling Smørgrav 			 * (4 byte channel id + buf)
2265e0fbb1d2SBrian Feldman 			 */
22664f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
22674f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0)
226819261079SEd Maste 				fatal_fr(r, "channel %i: ignore", c->self);
2269e0fbb1d2SBrian Feldman 		}
2270e0fbb1d2SBrian Feldman 	}
22714f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */
227219261079SEd Maste 	if ((r = sshbuf_consume(c->output, len)) != 0)
227319261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
2274e2f6069cSDag-Erling Smørgrav  out:
22754f52dfbbSDag-Erling Smørgrav 	c->local_consumed += olen - sshbuf_len(c->output);
22764f52dfbbSDag-Erling Smørgrav 
2277a04a10f8SKris Kennaway 	return 1;
2278511b41d2SMark Murray }
2279333ee039SDag-Erling Smørgrav 
2280af12a3e7SDag-Erling Smørgrav static int
channel_handle_efd_write(struct ssh * ssh,Channel * c)22811323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c)
2282a04a10f8SKris Kennaway {
22834f52dfbbSDag-Erling Smørgrav 	int r;
22844f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2285511b41d2SMark Murray 
22861323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0)
22871323ec57SEd Maste 		return 1;
22881323ec57SEd Maste 	if (sshbuf_len(c->extended) == 0)
22894f52dfbbSDag-Erling Smørgrav 		return 1;
22904f52dfbbSDag-Erling Smørgrav 
22914f52dfbbSDag-Erling Smørgrav 	len = write(c->efd, sshbuf_ptr(c->extended),
22924f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->extended));
22934f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
229419261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2295d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK))
2296ca3176e7SBrian Feldman 		return 1;
2297ca3176e7SBrian Feldman 	if (len <= 0) {
22984f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
229919261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2300ca3176e7SBrian Feldman 	} else {
230119261079SEd Maste 		if ((r = sshbuf_consume(c->extended, len)) != 0)
230219261079SEd Maste 			fatal_fr(r, "channel %i: consume", c->self);
2303a04a10f8SKris Kennaway 		c->local_consumed += len;
2304a91a2465SEd Maste 		channel_set_used_time(ssh, c);
2305a04a10f8SKris Kennaway 	}
23064f52dfbbSDag-Erling Smørgrav 	return 1;
23074f52dfbbSDag-Erling Smørgrav }
23084f52dfbbSDag-Erling Smørgrav 
23094f52dfbbSDag-Erling Smørgrav static int
channel_handle_efd_read(struct ssh * ssh,Channel * c)23101323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c)
23114f52dfbbSDag-Erling Smørgrav {
23124f52dfbbSDag-Erling Smørgrav 	char buf[CHAN_RBUF];
23134f52dfbbSDag-Erling Smørgrav 	ssize_t len;
231419261079SEd Maste 	int r, force;
23154f52dfbbSDag-Erling Smørgrav 
231619261079SEd Maste 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
231719261079SEd Maste 
23181323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
23194f52dfbbSDag-Erling Smørgrav 		return 1;
23204f52dfbbSDag-Erling Smørgrav 
2321a04a10f8SKris Kennaway 	len = read(c->efd, buf, sizeof(buf));
23224f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
232319261079SEd Maste 	if (len == -1 && (errno == EINTR || ((errno == EAGAIN ||
232419261079SEd Maste 	    errno == EWOULDBLOCK) && !force)))
2325ca3176e7SBrian Feldman 		return 1;
2326ca3176e7SBrian Feldman 	if (len <= 0) {
232719261079SEd Maste 		debug2("channel %d: closing read-efd %d", c->self, c->efd);
232819261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2329f374ba41SEd Maste 		return 1;
2330f374ba41SEd Maste 	}
2331a91a2465SEd Maste 	channel_set_used_time(ssh, c);
2332f374ba41SEd Maste 	if (c->extended_usage == CHAN_EXTENDED_IGNORE)
233319261079SEd Maste 		debug3("channel %d: discard efd", c->self);
233419261079SEd Maste 	else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
233519261079SEd Maste 		fatal_fr(r, "channel %i: append", c->self);
2336a04a10f8SKris Kennaway 	return 1;
2337a04a10f8SKris Kennaway }
2338333ee039SDag-Erling Smørgrav 
233921e764dfSDag-Erling Smørgrav static int
channel_handle_efd(struct ssh * ssh,Channel * c)23401323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c)
2341a04a10f8SKris Kennaway {
23424f52dfbbSDag-Erling Smørgrav 	if (c->efd == -1)
23434f52dfbbSDag-Erling Smørgrav 		return 1;
23444f52dfbbSDag-Erling Smørgrav 
23454f52dfbbSDag-Erling Smørgrav 	/** XXX handle drain efd, too */
23464f52dfbbSDag-Erling Smørgrav 
23474f52dfbbSDag-Erling Smørgrav 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
23481323ec57SEd Maste 		return channel_handle_efd_write(ssh, c);
23494f52dfbbSDag-Erling Smørgrav 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
23504f52dfbbSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
23511323ec57SEd Maste 		return channel_handle_efd_read(ssh, c);
23524f52dfbbSDag-Erling Smørgrav 
23534f52dfbbSDag-Erling Smørgrav 	return 1;
23544f52dfbbSDag-Erling Smørgrav }
23554f52dfbbSDag-Erling Smørgrav 
23564f52dfbbSDag-Erling Smørgrav static int
channel_check_window(struct ssh * ssh,Channel * c)23574f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c)
23584f52dfbbSDag-Erling Smørgrav {
23594f52dfbbSDag-Erling Smørgrav 	int r;
23604f52dfbbSDag-Erling Smørgrav 
2361ca3176e7SBrian Feldman 	if (c->type == SSH_CHANNEL_OPEN &&
2362ca3176e7SBrian Feldman 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2363d4af9e69SDag-Erling Smørgrav 	    ((c->local_window_max - c->local_window >
2364d4af9e69SDag-Erling Smørgrav 	    c->local_maxpacket*3) ||
2365d4af9e69SDag-Erling Smørgrav 	    c->local_window < c->local_window_max/2) &&
2366a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
23674f52dfbbSDag-Erling Smørgrav 		if (!c->have_remote_id)
236819261079SEd Maste 			fatal_f("channel %d: no remote id", c->self);
23694f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh,
23704f52dfbbSDag-Erling Smørgrav 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
23714f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
23724f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
23734f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
237419261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
23754f52dfbbSDag-Erling Smørgrav 		}
237619261079SEd Maste 		debug2("channel %d: window %d sent adjust %d", c->self,
237719261079SEd Maste 		    c->local_window, c->local_consumed);
237860c59fadSDag-Erling Smørgrav 		c->local_window += c->local_consumed;
2379a04a10f8SKris Kennaway 		c->local_consumed = 0;
2380a04a10f8SKris Kennaway 	}
2381a04a10f8SKris Kennaway 	return 1;
2382a04a10f8SKris Kennaway }
2383a04a10f8SKris Kennaway 
2384af12a3e7SDag-Erling Smørgrav static void
channel_post_open(struct ssh * ssh,Channel * c)23851323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c)
2386a04a10f8SKris Kennaway {
23871323ec57SEd Maste 	channel_handle_rfd(ssh, c);
23881323ec57SEd Maste 	channel_handle_wfd(ssh, c);
23891323ec57SEd Maste 	channel_handle_efd(ssh, c);
23904f52dfbbSDag-Erling Smørgrav 	channel_check_window(ssh, c);
2391a04a10f8SKris Kennaway }
2392a04a10f8SKris Kennaway 
2393b15c8340SDag-Erling Smørgrav static u_int
read_mux(struct ssh * ssh,Channel * c,u_int need)23944f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need)
2395b15c8340SDag-Erling Smørgrav {
2396b15c8340SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
23974f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2398b15c8340SDag-Erling Smørgrav 	u_int rlen;
23994f52dfbbSDag-Erling Smørgrav 	int r;
2400b15c8340SDag-Erling Smørgrav 
24014f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(c->input) < need) {
24024f52dfbbSDag-Erling Smørgrav 		rlen = need - sshbuf_len(c->input);
2403ca86bcf2SDag-Erling Smørgrav 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
240419261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
24054f52dfbbSDag-Erling Smørgrav 			return sshbuf_len(c->input);
2406b15c8340SDag-Erling Smørgrav 		if (len <= 0) {
24074f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
2408b15c8340SDag-Erling Smørgrav 			    c->self, c->rfd, len);
24094f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2410b15c8340SDag-Erling Smørgrav 			return 0;
241119261079SEd Maste 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
241219261079SEd Maste 			fatal_fr(r, "channel %i: append", c->self);
24134f52dfbbSDag-Erling Smørgrav 	}
24144f52dfbbSDag-Erling Smørgrav 	return sshbuf_len(c->input);
2415b15c8340SDag-Erling Smørgrav }
2416b15c8340SDag-Erling Smørgrav 
2417b15c8340SDag-Erling Smørgrav static void
channel_post_mux_client_read(struct ssh * ssh,Channel * c)24181323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c)
2419b15c8340SDag-Erling Smørgrav {
2420b15c8340SDag-Erling Smørgrav 	u_int need;
2421b15c8340SDag-Erling Smørgrav 
24221323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
24234f52dfbbSDag-Erling Smørgrav 		return;
24244f52dfbbSDag-Erling Smørgrav 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
24254f52dfbbSDag-Erling Smørgrav 		return;
24264f52dfbbSDag-Erling Smørgrav 	if (c->mux_pause)
24274f52dfbbSDag-Erling Smørgrav 		return;
2428b15c8340SDag-Erling Smørgrav 
2429b15c8340SDag-Erling Smørgrav 	/*
2430b15c8340SDag-Erling Smørgrav 	 * Don't not read past the precise end of packets to
2431b15c8340SDag-Erling Smørgrav 	 * avoid disrupting fd passing.
2432b15c8340SDag-Erling Smørgrav 	 */
24334f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, 4) < 4) /* read header */
2434b15c8340SDag-Erling Smørgrav 		return;
24354f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u32 */
24364f52dfbbSDag-Erling Smørgrav 	need = PEEK_U32(sshbuf_ptr(c->input));
2437b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
2438b15c8340SDag-Erling Smørgrav 	if (need > CHANNEL_MUX_MAX_PACKET) {
2439b15c8340SDag-Erling Smørgrav 		debug2("channel %d: packet too big %u > %u",
2440b15c8340SDag-Erling Smørgrav 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
24414f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
2442b15c8340SDag-Erling Smørgrav 		return;
2443b15c8340SDag-Erling Smørgrav 	}
24444f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
2445b15c8340SDag-Erling Smørgrav 		return;
24464f52dfbbSDag-Erling Smørgrav 	if (c->mux_rcb(ssh, c) != 0) {
2447b15c8340SDag-Erling Smørgrav 		debug("channel %d: mux_rcb failed", c->self);
24484f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
2449b15c8340SDag-Erling Smørgrav 		return;
2450b15c8340SDag-Erling Smørgrav 	}
2451b15c8340SDag-Erling Smørgrav }
2452b15c8340SDag-Erling Smørgrav 
2453b15c8340SDag-Erling Smørgrav static void
channel_post_mux_client_write(struct ssh * ssh,Channel * c)24541323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c)
24554f52dfbbSDag-Erling Smørgrav {
24564f52dfbbSDag-Erling Smørgrav 	ssize_t len;
24574f52dfbbSDag-Erling Smørgrav 	int r;
24584f52dfbbSDag-Erling Smørgrav 
24591323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
24601323ec57SEd Maste 		return;
24611323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
24624f52dfbbSDag-Erling Smørgrav 		return;
24634f52dfbbSDag-Erling Smørgrav 
24644f52dfbbSDag-Erling Smørgrav 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
246519261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
24664f52dfbbSDag-Erling Smørgrav 		return;
24674f52dfbbSDag-Erling Smørgrav 	if (len <= 0) {
24684f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
24694f52dfbbSDag-Erling Smørgrav 		return;
24704f52dfbbSDag-Erling Smørgrav 	}
24714f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0)
247219261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
24734f52dfbbSDag-Erling Smørgrav }
24744f52dfbbSDag-Erling Smørgrav 
24754f52dfbbSDag-Erling Smørgrav static void
channel_post_mux_client(struct ssh * ssh,Channel * c)24761323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c)
24774f52dfbbSDag-Erling Smørgrav {
24781323ec57SEd Maste 	channel_post_mux_client_read(ssh, c);
24791323ec57SEd Maste 	channel_post_mux_client_write(ssh, c);
24804f52dfbbSDag-Erling Smørgrav }
24814f52dfbbSDag-Erling Smørgrav 
24824f52dfbbSDag-Erling Smørgrav static void
channel_post_mux_listener(struct ssh * ssh,Channel * c)24831323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c)
2484b15c8340SDag-Erling Smørgrav {
2485b15c8340SDag-Erling Smørgrav 	Channel *nc;
2486b15c8340SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2487b15c8340SDag-Erling Smørgrav 	socklen_t addrlen;
2488b15c8340SDag-Erling Smørgrav 	int newsock;
2489b15c8340SDag-Erling Smørgrav 	uid_t euid;
2490b15c8340SDag-Erling Smørgrav 	gid_t egid;
2491b15c8340SDag-Erling Smørgrav 
24921323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
2493b15c8340SDag-Erling Smørgrav 		return;
2494b15c8340SDag-Erling Smørgrav 
2495b15c8340SDag-Erling Smørgrav 	debug("multiplexing control connection");
2496b15c8340SDag-Erling Smørgrav 
2497b15c8340SDag-Erling Smørgrav 	/*
2498b15c8340SDag-Erling Smørgrav 	 * Accept connection on control socket
2499b15c8340SDag-Erling Smørgrav 	 */
2500b15c8340SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
2501b15c8340SDag-Erling Smørgrav 	addrlen = sizeof(addr);
2502b15c8340SDag-Erling Smørgrav 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
2503b15c8340SDag-Erling Smørgrav 	    &addrlen)) == -1) {
250419261079SEd Maste 		error_f("accept: %s", strerror(errno));
2505462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2506e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2507b15c8340SDag-Erling Smørgrav 		return;
2508b15c8340SDag-Erling Smørgrav 	}
2509b15c8340SDag-Erling Smørgrav 
251019261079SEd Maste 	if (getpeereid(newsock, &euid, &egid) == -1) {
251119261079SEd Maste 		error_f("getpeereid failed: %s", strerror(errno));
2512b15c8340SDag-Erling Smørgrav 		close(newsock);
2513b15c8340SDag-Erling Smørgrav 		return;
2514b15c8340SDag-Erling Smørgrav 	}
2515b15c8340SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
2516b15c8340SDag-Erling Smørgrav 		error("multiplex uid mismatch: peer euid %u != uid %u",
2517b15c8340SDag-Erling Smørgrav 		    (u_int)euid, (u_int)getuid());
2518b15c8340SDag-Erling Smørgrav 		close(newsock);
2519b15c8340SDag-Erling Smørgrav 		return;
2520b15c8340SDag-Erling Smørgrav 	}
2521f374ba41SEd Maste 	nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT,
2522b15c8340SDag-Erling Smørgrav 	    newsock, newsock, -1, c->local_window_max,
2523b15c8340SDag-Erling Smørgrav 	    c->local_maxpacket, 0, "mux-control", 1);
2524b15c8340SDag-Erling Smørgrav 	nc->mux_rcb = c->mux_rcb;
252519261079SEd Maste 	debug3_f("new mux channel %d fd %d", nc->self, nc->sock);
2526b15c8340SDag-Erling Smørgrav 	/* establish state */
25274f52dfbbSDag-Erling Smørgrav 	nc->mux_rcb(ssh, nc);
2528b15c8340SDag-Erling Smørgrav 	/* mux state transitions must not elicit protocol messages */
2529b15c8340SDag-Erling Smørgrav 	nc->flags |= CHAN_LOCAL;
2530b15c8340SDag-Erling Smørgrav }
2531b15c8340SDag-Erling Smørgrav 
2532af12a3e7SDag-Erling Smørgrav static void
channel_handler_init(struct ssh_channels * sc)25334f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc)
2534a04a10f8SKris Kennaway {
25354f52dfbbSDag-Erling Smørgrav 	chan_fn **pre, **post;
2536f388f5efSDag-Erling Smørgrav 
25374f52dfbbSDag-Erling Smørgrav 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
25384f52dfbbSDag-Erling Smørgrav 	    (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
253919261079SEd Maste 		fatal_f("allocation failed");
2540511b41d2SMark Murray 
25414f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
25424f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
25434f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
25444f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
25454f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
25464f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
25474f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
25484f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
25494f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
25504f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
25514f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
25524f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
25534f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2554a04a10f8SKris Kennaway 
25554f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
25564f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
25574f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
25584f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
25594f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
25604f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
25614f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
25624f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
25634f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
25644f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
25654f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
25664f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2567a04a10f8SKris Kennaway 
25684f52dfbbSDag-Erling Smørgrav 	sc->channel_pre = pre;
25694f52dfbbSDag-Erling Smørgrav 	sc->channel_post = post;
2570a04a10f8SKris Kennaway }
2571a04a10f8SKris Kennaway 
2572af12a3e7SDag-Erling Smørgrav /* gc dead channels */
2573af12a3e7SDag-Erling Smørgrav static void
channel_garbage_collect(struct ssh * ssh,Channel * c)25744f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c)
2575af12a3e7SDag-Erling Smørgrav {
2576af12a3e7SDag-Erling Smørgrav 	if (c == NULL)
2577af12a3e7SDag-Erling Smørgrav 		return;
2578af12a3e7SDag-Erling Smørgrav 	if (c->detach_user != NULL) {
25794f52dfbbSDag-Erling Smørgrav 		if (!chan_is_dead(ssh, c, c->detach_close))
2580af12a3e7SDag-Erling Smørgrav 			return;
25812f513db7SEd Maste 
2582221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: notify user", c->self);
2583f374ba41SEd Maste 		c->detach_user(ssh, c->self, 0, NULL);
2584af12a3e7SDag-Erling Smørgrav 		/* if we still have a callback */
2585af12a3e7SDag-Erling Smørgrav 		if (c->detach_user != NULL)
2586af12a3e7SDag-Erling Smørgrav 			return;
2587221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: user detached", c->self);
2588af12a3e7SDag-Erling Smørgrav 	}
25894f52dfbbSDag-Erling Smørgrav 	if (!chan_is_dead(ssh, c, 1))
2590af12a3e7SDag-Erling Smørgrav 		return;
2591221552e4SDag-Erling Smørgrav 	debug2("channel %d: garbage collecting", c->self);
25924f52dfbbSDag-Erling Smørgrav 	channel_free(ssh, c);
2593af12a3e7SDag-Erling Smørgrav }
2594af12a3e7SDag-Erling Smørgrav 
25954f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST };
25964f52dfbbSDag-Erling Smørgrav 
2597af12a3e7SDag-Erling Smørgrav static void
channel_handler(struct ssh * ssh,int table,struct timespec * timeout)2598f374ba41SEd Maste channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
2599a04a10f8SKris Kennaway {
26004f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
26014f52dfbbSDag-Erling Smørgrav 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
2602b15c8340SDag-Erling Smørgrav 	u_int i, oalloc;
2603a04a10f8SKris Kennaway 	Channel *c;
2604462c32cbSDag-Erling Smørgrav 	time_t now;
2605a04a10f8SKris Kennaway 
2606e4a9863fSDag-Erling Smørgrav 	now = monotime();
26074f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26084f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2609af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2610511b41d2SMark Murray 			continue;
261138a52bd3SEd Maste 		/* Try to keep IO going while rekeying */
261238a52bd3SEd Maste 		if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN)
261338a52bd3SEd Maste 			continue;
2614b15c8340SDag-Erling Smørgrav 		if (c->delayed) {
26154f52dfbbSDag-Erling Smørgrav 			if (table == CHAN_PRE)
2616b15c8340SDag-Erling Smørgrav 				c->delayed = 0;
2617b15c8340SDag-Erling Smørgrav 			else
2618b15c8340SDag-Erling Smørgrav 				continue;
2619b15c8340SDag-Erling Smørgrav 		}
2620462c32cbSDag-Erling Smørgrav 		if (ftab[c->type] != NULL) {
2621a91a2465SEd Maste 			if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN &&
2622a91a2465SEd Maste 			    channel_get_expiry(ssh, c) != 0 &&
2623a91a2465SEd Maste 			    now >= channel_get_expiry(ssh, c)) {
2624f374ba41SEd Maste 				/* channel closed for inactivity */
2625f374ba41SEd Maste 				verbose("channel %d: closing after %u seconds "
2626f374ba41SEd Maste 				    "of inactivity", c->self,
2627f374ba41SEd Maste 				    c->inactive_deadline);
2628f374ba41SEd Maste 				channel_force_close(ssh, c, 1);
2629f374ba41SEd Maste 			} else if (c->notbefore <= now) {
2630f374ba41SEd Maste 				/* Run handlers that are not paused. */
26311323ec57SEd Maste 				(*ftab[c->type])(ssh, c);
2632f374ba41SEd Maste 				/* inactivity timeouts must interrupt poll() */
2633f374ba41SEd Maste 				if (timeout != NULL &&
2634f374ba41SEd Maste 				    c->type == SSH_CHANNEL_OPEN &&
2635a91a2465SEd Maste 				    channel_get_expiry(ssh, c) != 0) {
2636f374ba41SEd Maste 					ptimeout_deadline_monotime(timeout,
2637a91a2465SEd Maste 					    channel_get_expiry(ssh, c));
2638f374ba41SEd Maste 				}
2639f374ba41SEd Maste 			} else if (timeout != NULL) {
2640462c32cbSDag-Erling Smørgrav 				/*
2641f374ba41SEd Maste 				 * Arrange for poll() wakeup when channel pause
2642f374ba41SEd Maste 				 * timer expires.
2643462c32cbSDag-Erling Smørgrav 				 */
2644f374ba41SEd Maste 				ptimeout_deadline_monotime(timeout,
2645f374ba41SEd Maste 				    c->notbefore);
2646462c32cbSDag-Erling Smørgrav 			}
2647462c32cbSDag-Erling Smørgrav 		}
26484f52dfbbSDag-Erling Smørgrav 		channel_garbage_collect(ssh, c);
2649511b41d2SMark Murray 	}
2650511b41d2SMark Murray }
2651a04a10f8SKris Kennaway 
2652af12a3e7SDag-Erling Smørgrav /*
26531323ec57SEd Maste  * Create sockets before preparing IO.
26544f52dfbbSDag-Erling Smørgrav  * This is necessary for things that need to happen after reading
26551323ec57SEd Maste  * the network-input but need to be completed before IO event setup, e.g.
26561323ec57SEd Maste  * because they may create new channels.
26574f52dfbbSDag-Erling Smørgrav  */
26584f52dfbbSDag-Erling Smørgrav static void
channel_before_prepare_io(struct ssh * ssh)26591323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh)
26604f52dfbbSDag-Erling Smørgrav {
26614f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
26624f52dfbbSDag-Erling Smørgrav 	Channel *c;
26634f52dfbbSDag-Erling Smørgrav 	u_int i, oalloc;
26644f52dfbbSDag-Erling Smørgrav 
26654f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
26664f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
26674f52dfbbSDag-Erling Smørgrav 		if (c == NULL)
26684f52dfbbSDag-Erling Smørgrav 			continue;
26694f52dfbbSDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
26701323ec57SEd Maste 			channel_before_prepare_io_rdynamic(ssh, c);
26714f52dfbbSDag-Erling Smørgrav 	}
26724f52dfbbSDag-Erling Smørgrav }
26734f52dfbbSDag-Erling Smørgrav 
26741323ec57SEd Maste static void
dump_channel_poll(const char * func,const char * what,Channel * c,u_int pollfd_offset,struct pollfd * pfd)26751323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c,
26761323ec57SEd Maste     u_int pollfd_offset, struct pollfd *pfd)
2677a04a10f8SKris Kennaway {
26781323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
267987c1498dSEd Maste 	debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] "
268087c1498dSEd Maste 	    "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d "
268187c1498dSEd Maste 	    "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what,
268287c1498dSEd Maste 	    c->rfd, c->wfd, c->efd, c->sock,
268387c1498dSEd Maste 	    c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3],
268487c1498dSEd Maste 	    c->io_want, c->io_ready,
268587c1498dSEd Maste 	    pollfd_offset, pfd->fd, pfd->events, pfd->revents);
26861323ec57SEd Maste #endif
2687ca3176e7SBrian Feldman }
2688ca3176e7SBrian Feldman 
26891323ec57SEd Maste /* Prepare pollfd entries for a single channel */
26901323ec57SEd Maste static void
channel_prepare_pollfd(Channel * c,u_int * next_pollfd,struct pollfd * pfd,u_int npfd)26911323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd,
26921323ec57SEd Maste     struct pollfd *pfd, u_int npfd)
26931323ec57SEd Maste {
269487c1498dSEd Maste 	u_int ev, p = *next_pollfd;
26951323ec57SEd Maste 
26961323ec57SEd Maste 	if (c == NULL)
26971323ec57SEd Maste 		return;
26981323ec57SEd Maste 	if (p + 4 > npfd) {
26991323ec57SEd Maste 		/* Shouldn't happen */
27001323ec57SEd Maste 		fatal_f("channel %d: bad pfd offset %u (max %u)",
27011323ec57SEd Maste 		    c->self, p, npfd);
27021323ec57SEd Maste 	}
270387c1498dSEd Maste 	c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1;
27041323ec57SEd Maste 	/*
27051323ec57SEd Maste 	 * prepare c->rfd
27061323ec57SEd Maste 	 *
27071323ec57SEd Maste 	 * This is a special case, since c->rfd might be the same as
27081323ec57SEd Maste 	 * c->wfd, c->efd and/or c->sock. Handle those here if they want
27091323ec57SEd Maste 	 * IO too.
27101323ec57SEd Maste 	 */
27111323ec57SEd Maste 	if (c->rfd != -1) {
271287c1498dSEd Maste 		ev = 0;
27131323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_RFD) != 0)
271487c1498dSEd Maste 			ev |= POLLIN;
27151323ec57SEd Maste 		/* rfd == wfd */
271687c1498dSEd Maste 		if (c->wfd == c->rfd) {
271787c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_WFD) != 0)
271887c1498dSEd Maste 				ev |= POLLOUT;
271987c1498dSEd Maste 		}
27201323ec57SEd Maste 		/* rfd == efd */
272187c1498dSEd Maste 		if (c->efd == c->rfd) {
272287c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
272387c1498dSEd Maste 				ev |= POLLIN;
272487c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
272587c1498dSEd Maste 				ev |= POLLOUT;
272687c1498dSEd Maste 		}
27271323ec57SEd Maste 		/* rfd == sock */
272887c1498dSEd Maste 		if (c->sock == c->rfd) {
272987c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
273087c1498dSEd Maste 				ev |= POLLIN;
273187c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
273287c1498dSEd Maste 				ev |= POLLOUT;
273387c1498dSEd Maste 		}
273487c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
273587c1498dSEd Maste 		if (ev != 0) {
273687c1498dSEd Maste 			c->pfds[0] = p;
273787c1498dSEd Maste 			pfd[p].fd = c->rfd;
273887c1498dSEd Maste 			pfd[p].events = ev;
27391323ec57SEd Maste 			dump_channel_poll(__func__, "rfd", c, p, &pfd[p]);
27401323ec57SEd Maste 			p++;
27411323ec57SEd Maste 		}
274287c1498dSEd Maste 	}
274387c1498dSEd Maste 	/* prepare c->wfd if wanting IO and not already handled above */
27441323ec57SEd Maste 	if (c->wfd != -1 && c->rfd != c->wfd) {
274587c1498dSEd Maste 		ev = 0;
274687c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_WFD))
274787c1498dSEd Maste 			ev |= POLLOUT;
274887c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
274987c1498dSEd Maste 		if (ev != 0) {
275087c1498dSEd Maste 			c->pfds[1] = p;
27511323ec57SEd Maste 			pfd[p].fd = c->wfd;
275287c1498dSEd Maste 			pfd[p].events = ev;
27531323ec57SEd Maste 			dump_channel_poll(__func__, "wfd", c, p, &pfd[p]);
27541323ec57SEd Maste 			p++;
27551323ec57SEd Maste 		}
275687c1498dSEd Maste 	}
275787c1498dSEd Maste 	/* prepare c->efd if wanting IO and not already handled above */
27581323ec57SEd Maste 	if (c->efd != -1 && c->rfd != c->efd) {
275987c1498dSEd Maste 		ev = 0;
27601323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
276187c1498dSEd Maste 			ev |= POLLIN;
27621323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
276387c1498dSEd Maste 			ev |= POLLOUT;
276487c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
276587c1498dSEd Maste 		if (ev != 0) {
276687c1498dSEd Maste 			c->pfds[2] = p;
276787c1498dSEd Maste 			pfd[p].fd = c->efd;
276887c1498dSEd Maste 			pfd[p].events = ev;
27691323ec57SEd Maste 			dump_channel_poll(__func__, "efd", c, p, &pfd[p]);
27701323ec57SEd Maste 			p++;
27711323ec57SEd Maste 		}
277287c1498dSEd Maste 	}
277387c1498dSEd Maste 	/* prepare c->sock if wanting IO and not already handled above */
27741323ec57SEd Maste 	if (c->sock != -1 && c->rfd != c->sock) {
277587c1498dSEd Maste 		ev = 0;
277687c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
277787c1498dSEd Maste 			ev |= POLLIN;
277887c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
277987c1498dSEd Maste 			ev |= POLLOUT;
278087c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
278187c1498dSEd Maste 		if (ev != 0) {
278287c1498dSEd Maste 			c->pfds[3] = p;
27831323ec57SEd Maste 			pfd[p].fd = c->sock;
27841323ec57SEd Maste 			pfd[p].events = 0;
27851323ec57SEd Maste 			dump_channel_poll(__func__, "sock", c, p, &pfd[p]);
27861323ec57SEd Maste 			p++;
27871323ec57SEd Maste 		}
278887c1498dSEd Maste 	}
27891323ec57SEd Maste 	*next_pollfd = p;
27901323ec57SEd Maste }
27911323ec57SEd Maste 
27921323ec57SEd Maste /* * Allocate/prepare poll structure */
27931323ec57SEd Maste void
channel_prepare_poll(struct ssh * ssh,struct pollfd ** pfdp,u_int * npfd_allocp,u_int * npfd_activep,u_int npfd_reserved,struct timespec * timeout)27941323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
2795f374ba41SEd Maste     u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout)
27961323ec57SEd Maste {
27971323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
27981323ec57SEd Maste 	u_int i, oalloc, p, npfd = npfd_reserved;
27991323ec57SEd Maste 
28001323ec57SEd Maste 	channel_before_prepare_io(ssh); /* might create a new channel */
28019fce8d41SEd Maste 	/* clear out I/O flags from last poll */
28029fce8d41SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
28039fce8d41SEd Maste 		if (sc->channels[i] == NULL)
28049fce8d41SEd Maste 			continue;
28059fce8d41SEd Maste 		sc->channels[i]->io_want = sc->channels[i]->io_ready = 0;
28069fce8d41SEd Maste 	}
28071323ec57SEd Maste 	/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */
28081323ec57SEd Maste 	if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved)
28091323ec57SEd Maste 		fatal_f("too many channels"); /* shouldn't happen */
28101323ec57SEd Maste 	npfd += sc->channels_alloc * 4;
28111323ec57SEd Maste 	if (npfd > *npfd_allocp) {
28121323ec57SEd Maste 		*pfdp = xrecallocarray(*pfdp, *npfd_allocp,
28131323ec57SEd Maste 		    npfd, sizeof(**pfdp));
28141323ec57SEd Maste 		*npfd_allocp = npfd;
28151323ec57SEd Maste 	}
28161323ec57SEd Maste 	*npfd_activep = npfd_reserved;
28171323ec57SEd Maste 	oalloc = sc->channels_alloc;
28181323ec57SEd Maste 
2819f374ba41SEd Maste 	channel_handler(ssh, CHAN_PRE, timeout);
28201323ec57SEd Maste 
28211323ec57SEd Maste 	if (oalloc != sc->channels_alloc) {
28221323ec57SEd Maste 		/* shouldn't happen */
28231323ec57SEd Maste 		fatal_f("channels_alloc changed during CHAN_PRE "
28241323ec57SEd Maste 		    "(was %u, now %u)", oalloc, sc->channels_alloc);
28251323ec57SEd Maste 	}
28261323ec57SEd Maste 
28271323ec57SEd Maste 	/* Prepare pollfd */
28281323ec57SEd Maste 	p = npfd_reserved;
28291323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
28301323ec57SEd Maste 		channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
28311323ec57SEd Maste 	*npfd_activep = p;
28321323ec57SEd Maste }
28331323ec57SEd Maste 
28341323ec57SEd Maste static void
fd_ready(Channel * c,int p,struct pollfd * pfds,u_int npfd,int fd,const char * what,u_int revents_mask,u_int ready)283587c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd,
28361323ec57SEd Maste     const char *what, u_int revents_mask, u_int ready)
28371323ec57SEd Maste {
28381323ec57SEd Maste 	struct pollfd *pfd = &pfds[p];
28391323ec57SEd Maste 
28401323ec57SEd Maste 	if (fd == -1)
28411323ec57SEd Maste 		return;
284287c1498dSEd Maste 	if (p == -1 || (u_int)p >= npfd)
284387c1498dSEd Maste 		fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd);
28441323ec57SEd Maste 	dump_channel_poll(__func__, what, c, p, pfd);
28451323ec57SEd Maste 	if (pfd->fd != fd) {
28461323ec57SEd Maste 		fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
28471323ec57SEd Maste 		    "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
28481323ec57SEd Maste 		    c->rfd, c->wfd, c->efd, c->sock);
28491323ec57SEd Maste 	}
28501323ec57SEd Maste 	if ((pfd->revents & POLLNVAL) != 0) {
28511323ec57SEd Maste 		fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d",
28521323ec57SEd Maste 		    c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock);
28531323ec57SEd Maste 	}
28541323ec57SEd Maste 	if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0)
28551323ec57SEd Maste 		c->io_ready |= ready & c->io_want;
2856a04a10f8SKris Kennaway }
2857a04a10f8SKris Kennaway 
2858af12a3e7SDag-Erling Smørgrav /*
28591323ec57SEd Maste  * After poll, perform any appropriate operations for channels which have
2860af12a3e7SDag-Erling Smørgrav  * events pending.
2861af12a3e7SDag-Erling Smørgrav  */
2862a04a10f8SKris Kennaway void
channel_after_poll(struct ssh * ssh,struct pollfd * pfd,u_int npfd)28631323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
2864a04a10f8SKris Kennaway {
28651323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
286687c1498dSEd Maste 	u_int i;
286787c1498dSEd Maste 	int p;
28681323ec57SEd Maste 	Channel *c;
28691323ec57SEd Maste 
28701323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
287187c1498dSEd Maste 	for (p = 0; p < (int)npfd; p++) {
28721323ec57SEd Maste 		if (pfd[p].revents == 0)
28731323ec57SEd Maste 			continue;
28741323ec57SEd Maste 		debug_f("pfd[%u].fd %d rev 0x%04x",
28751323ec57SEd Maste 		    p, pfd[p].fd, pfd[p].revents);
28761323ec57SEd Maste 	}
28771323ec57SEd Maste #endif
28781323ec57SEd Maste 
28791323ec57SEd Maste 	/* Convert pollfd into c->io_ready */
28801323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
28811323ec57SEd Maste 		c = sc->channels[i];
288287c1498dSEd Maste 		if (c == NULL)
28831323ec57SEd Maste 			continue;
28841323ec57SEd Maste 		/* if rfd is shared with efd/sock then wfd should be too */
28851323ec57SEd Maste 		if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd &&
28861323ec57SEd Maste 		    (c->rfd == c->efd || c->rfd == c->sock)) {
28871323ec57SEd Maste 			/* Shouldn't happen */
28881323ec57SEd Maste 			fatal_f("channel %d: unexpected fds r%d w%d e%d s%d",
28891323ec57SEd Maste 			    c->self, c->rfd, c->wfd, c->efd, c->sock);
28901323ec57SEd Maste 		}
28911323ec57SEd Maste 		c->io_ready = 0;
28921323ec57SEd Maste 		/* rfd, potentially shared with wfd, efd and sock */
289387c1498dSEd Maste 		if (c->rfd != -1 && (p = c->pfds[0]) != -1) {
289487c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->rfd,
289587c1498dSEd Maste 			    "rfd", POLLIN, SSH_CHAN_IO_RFD);
28961323ec57SEd Maste 			if (c->rfd == c->wfd) {
289787c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->wfd,
289887c1498dSEd Maste 				    "wfd/r", POLLOUT, SSH_CHAN_IO_WFD);
28991323ec57SEd Maste 			}
29001323ec57SEd Maste 			if (c->rfd == c->efd) {
290187c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
290287c1498dSEd Maste 				    "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R);
290387c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
290487c1498dSEd Maste 				    "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W);
29051323ec57SEd Maste 			}
29061323ec57SEd Maste 			if (c->rfd == c->sock) {
290787c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
290887c1498dSEd Maste 				    "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R);
290987c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
291087c1498dSEd Maste 				    "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W);
29111323ec57SEd Maste 			}
291287c1498dSEd Maste 			dump_channel_poll(__func__, "rfd", c, p, pfd);
29131323ec57SEd Maste 		}
29141323ec57SEd Maste 		/* wfd */
291587c1498dSEd Maste 		if (c->wfd != -1 && c->wfd != c->rfd &&
291687c1498dSEd Maste 		    (p = c->pfds[1]) != -1) {
291787c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->wfd,
291887c1498dSEd Maste 			    "wfd", POLLOUT, SSH_CHAN_IO_WFD);
291987c1498dSEd Maste 			dump_channel_poll(__func__, "wfd", c, p, pfd);
29201323ec57SEd Maste 		}
29211323ec57SEd Maste 		/* efd */
292287c1498dSEd Maste 		if (c->efd != -1 && c->efd != c->rfd &&
292387c1498dSEd Maste 		    (p = c->pfds[2]) != -1) {
292487c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
292587c1498dSEd Maste 			    "efdr", POLLIN, SSH_CHAN_IO_EFD_R);
292687c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
292787c1498dSEd Maste 			    "efdw", POLLOUT, SSH_CHAN_IO_EFD_W);
292887c1498dSEd Maste 			dump_channel_poll(__func__, "efd", c, p, pfd);
29291323ec57SEd Maste 		}
29301323ec57SEd Maste 		/* sock */
293187c1498dSEd Maste 		if (c->sock != -1 && c->sock != c->rfd &&
293287c1498dSEd Maste 		    (p = c->pfds[3]) != -1) {
293387c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
293487c1498dSEd Maste 			    "sockr", POLLIN, SSH_CHAN_IO_SOCK_R);
293587c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
293687c1498dSEd Maste 			    "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W);
293787c1498dSEd Maste 			dump_channel_poll(__func__, "sock", c, p, pfd);
29381323ec57SEd Maste 		}
29391323ec57SEd Maste 	}
29401323ec57SEd Maste 	channel_handler(ssh, CHAN_POST, NULL);
2941511b41d2SMark Murray }
2942511b41d2SMark Murray 
29434f52dfbbSDag-Erling Smørgrav /*
29444f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open or draining c->input.
2945edf85781SEd Maste  * Returns non-zero if a packet was enqueued.
29464f52dfbbSDag-Erling Smørgrav  */
2947edf85781SEd Maste static int
channel_output_poll_input_open(struct ssh * ssh,Channel * c)29484f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c)
29494f52dfbbSDag-Erling Smørgrav {
29504f52dfbbSDag-Erling Smørgrav 	size_t len, plen;
29514f52dfbbSDag-Erling Smørgrav 	const u_char *pkt;
29524f52dfbbSDag-Erling Smørgrav 	int r;
29534f52dfbbSDag-Erling Smørgrav 
29544f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->input)) == 0) {
29554f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
29564f52dfbbSDag-Erling Smørgrav 			/*
29574f52dfbbSDag-Erling Smørgrav 			 * input-buffer is empty and read-socket shutdown:
29584f52dfbbSDag-Erling Smørgrav 			 * tell peer, that we will not send more data:
29594f52dfbbSDag-Erling Smørgrav 			 * send IEOF.
29604f52dfbbSDag-Erling Smørgrav 			 * hack for extended data: delay EOF if EFD still
29614f52dfbbSDag-Erling Smørgrav 			 * in use.
29624f52dfbbSDag-Erling Smørgrav 			 */
29634f52dfbbSDag-Erling Smørgrav 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
29644f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
29654f52dfbbSDag-Erling Smørgrav 				    "ibuf_empty delayed efd %d/(%zu)",
29664f52dfbbSDag-Erling Smørgrav 				    c->self, c->efd, sshbuf_len(c->extended));
29674f52dfbbSDag-Erling Smørgrav 			else
29684f52dfbbSDag-Erling Smørgrav 				chan_ibuf_empty(ssh, c);
29694f52dfbbSDag-Erling Smørgrav 		}
2970edf85781SEd Maste 		return 0;
29714f52dfbbSDag-Erling Smørgrav 	}
29724f52dfbbSDag-Erling Smørgrav 
29734f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
297419261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
29754f52dfbbSDag-Erling Smørgrav 
29764f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
29774f52dfbbSDag-Erling Smørgrav 		/* Check datagram will fit; drop if not */
29784f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
297919261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
29804f52dfbbSDag-Erling Smørgrav 		/*
29814f52dfbbSDag-Erling Smørgrav 		 * XXX this does tail-drop on the datagram queue which is
29824f52dfbbSDag-Erling Smørgrav 		 * usually suboptimal compared to head-drop. Better to have
29834f52dfbbSDag-Erling Smørgrav 		 * backpressure at read time? (i.e. read + discard)
29844f52dfbbSDag-Erling Smørgrav 		 */
29854f52dfbbSDag-Erling Smørgrav 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
29864f52dfbbSDag-Erling Smørgrav 			debug("channel %d: datagram too big", c->self);
2987edf85781SEd Maste 			return 0;
29884f52dfbbSDag-Erling Smørgrav 		}
29894f52dfbbSDag-Erling Smørgrav 		/* Enqueue it */
29904f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
29914f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
29924f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
299319261079SEd Maste 		    (r = sshpkt_send(ssh)) != 0)
299419261079SEd Maste 			fatal_fr(r, "channel %i: send datagram", c->self);
29954f52dfbbSDag-Erling Smørgrav 		c->remote_window -= plen;
2996edf85781SEd Maste 		return 1;
29974f52dfbbSDag-Erling Smørgrav 	}
29984f52dfbbSDag-Erling Smørgrav 
29994f52dfbbSDag-Erling Smørgrav 	/* Enqueue packet for buffered data. */
30004f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
30014f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
30024f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
30034f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
30044f52dfbbSDag-Erling Smørgrav 	if (len == 0)
3005edf85781SEd Maste 		return 0;
30064f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
30074f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30084f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
300919261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
301019261079SEd Maste 		fatal_fr(r, "channel %i: send data", c->self);
30114f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->input, len)) != 0)
301219261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
30134f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
3014edf85781SEd Maste 	return 1;
30154f52dfbbSDag-Erling Smørgrav }
30164f52dfbbSDag-Erling Smørgrav 
30174f52dfbbSDag-Erling Smørgrav /*
30184f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open c->extended in read mode.
3019edf85781SEd Maste  * Returns non-zero if a packet was enqueued.
30204f52dfbbSDag-Erling Smørgrav  */
3021edf85781SEd Maste static int
channel_output_poll_extended_read(struct ssh * ssh,Channel * c)30224f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
30234f52dfbbSDag-Erling Smørgrav {
30244f52dfbbSDag-Erling Smørgrav 	size_t len;
30254f52dfbbSDag-Erling Smørgrav 	int r;
30264f52dfbbSDag-Erling Smørgrav 
30274f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->extended)) == 0)
3028edf85781SEd Maste 		return 0;
30294f52dfbbSDag-Erling Smørgrav 
30304f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
30314f52dfbbSDag-Erling Smørgrav 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
30324f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
30334f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
30344f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
30354f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
30364f52dfbbSDag-Erling Smørgrav 	if (len == 0)
3037edf85781SEd Maste 		return 0;
30384f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
303919261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
30404f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
30414f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
30424f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
30434f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
304419261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
304519261079SEd Maste 		fatal_fr(r, "channel %i: data", c->self);
30464f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->extended, len)) != 0)
304719261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
30484f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
30494f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: sent ext data %zu", c->self, len);
3050edf85781SEd Maste 	return 1;
30514f52dfbbSDag-Erling Smørgrav }
3052af12a3e7SDag-Erling Smørgrav 
3053edf85781SEd Maste /*
3054edf85781SEd Maste  * If there is data to send to the connection, enqueue some of it now.
3055edf85781SEd Maste  * Returns non-zero if data was enqueued.
3056edf85781SEd Maste  */
3057edf85781SEd Maste int
channel_output_poll(struct ssh * ssh)30584f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh)
3059511b41d2SMark Murray {
30604f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3061a04a10f8SKris Kennaway 	Channel *c;
30624f52dfbbSDag-Erling Smørgrav 	u_int i;
3063edf85781SEd Maste 	int ret = 0;
3064511b41d2SMark Murray 
30654f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
30664f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
3067af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
3068af12a3e7SDag-Erling Smørgrav 			continue;
3069511b41d2SMark Murray 
3070af12a3e7SDag-Erling Smørgrav 		/*
3071af12a3e7SDag-Erling Smørgrav 		 * We are only interested in channels that can have buffered
3072af12a3e7SDag-Erling Smørgrav 		 * incoming data.
3073af12a3e7SDag-Erling Smørgrav 		 */
3074a04a10f8SKris Kennaway 		if (c->type != SSH_CHANNEL_OPEN)
3075511b41d2SMark Murray 			continue;
30764f52dfbbSDag-Erling Smørgrav 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
3077ca3176e7SBrian Feldman 			/* XXX is this true? */
30784f52dfbbSDag-Erling Smørgrav 			debug3("channel %d: will not send data after close",
30794f52dfbbSDag-Erling Smørgrav 			    c->self);
3080511b41d2SMark Murray 			continue;
3081511b41d2SMark Murray 		}
3082511b41d2SMark Murray 
3083511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
30844f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_OPEN ||
30854f52dfbbSDag-Erling Smørgrav 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
3086edf85781SEd Maste 			ret |= channel_output_poll_input_open(ssh, c);
3087a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
30884f52dfbbSDag-Erling Smørgrav 		if (!(c->flags & CHAN_EOF_SENT) &&
30894f52dfbbSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_READ)
3090edf85781SEd Maste 			ret |= channel_output_poll_extended_read(ssh, c);
3091511b41d2SMark Murray 	}
3092edf85781SEd Maste 	return ret;
3093511b41d2SMark Murray }
3094511b41d2SMark Murray 
3095ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support  */
3096ca86bcf2SDag-Erling Smørgrav 
3097ca86bcf2SDag-Erling Smørgrav /*
3098ca86bcf2SDag-Erling Smørgrav  * When multiplexing channel messages for mux clients we have to deal
3099ca86bcf2SDag-Erling Smørgrav  * with downstream messages from the mux client and upstream messages
3100ca86bcf2SDag-Erling Smørgrav  * from the ssh server:
3101ca86bcf2SDag-Erling Smørgrav  * 1) Handling downstream messages is straightforward and happens
3102ca86bcf2SDag-Erling Smørgrav  *    in channel_proxy_downstream():
3103ca86bcf2SDag-Erling Smørgrav  *    - We forward all messages (mostly) unmodified to the server.
3104ca86bcf2SDag-Erling Smørgrav  *    - However, in order to route messages from upstream to the correct
3105ca86bcf2SDag-Erling Smørgrav  *      downstream client, we have to replace the channel IDs used by the
3106ca86bcf2SDag-Erling Smørgrav  *      mux clients with a unique channel ID because the mux clients might
3107ca86bcf2SDag-Erling Smørgrav  *      use conflicting channel IDs.
3108ca86bcf2SDag-Erling Smørgrav  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
3109ca86bcf2SDag-Erling Smørgrav  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
3110ca86bcf2SDag-Erling Smørgrav  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
3111ca86bcf2SDag-Erling Smørgrav  *      with the newly allocated channel ID.
3112ca86bcf2SDag-Erling Smørgrav  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
3113190cef3dSDag-Erling Smørgrav  *    channels and processed by channel_proxy_upstream(). The local channel ID
3114ca86bcf2SDag-Erling Smørgrav  *    is then translated back to the original mux client ID.
3115ca86bcf2SDag-Erling Smørgrav  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
3116ca86bcf2SDag-Erling Smørgrav  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
3117ca86bcf2SDag-Erling Smørgrav  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
3118ca86bcf2SDag-Erling Smørgrav  *    downstream mux client are removed.
3119ca86bcf2SDag-Erling Smørgrav  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
3120ca86bcf2SDag-Erling Smørgrav  *    requires more work, because they are not addressed to a specific
3121ca86bcf2SDag-Erling Smørgrav  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
3122ca86bcf2SDag-Erling Smørgrav  *    out whether the request is addressed to the local client or a
3123ca86bcf2SDag-Erling Smørgrav  *    specific downstream client based on the listen-address/port.
3124190cef3dSDag-Erling Smørgrav  * 6) Agent and X11-Forwarding have a similar problem and are currently
3125ca86bcf2SDag-Erling Smørgrav  *    not supported as the matching session/channel cannot be identified
3126ca86bcf2SDag-Erling Smørgrav  *    easily.
3127ca86bcf2SDag-Erling Smørgrav  */
3128ca86bcf2SDag-Erling Smørgrav 
3129ca86bcf2SDag-Erling Smørgrav /*
3130ca86bcf2SDag-Erling Smørgrav  * receive packets from downstream mux clients:
3131ca86bcf2SDag-Erling Smørgrav  * channel callback fired on read from mux client, creates
3132ca86bcf2SDag-Erling Smørgrav  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
3133ca86bcf2SDag-Erling Smørgrav  * on channel creation.
3134ca86bcf2SDag-Erling Smørgrav  */
3135ca86bcf2SDag-Erling Smørgrav int
channel_proxy_downstream(struct ssh * ssh,Channel * downstream)31364f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
3137ca86bcf2SDag-Erling Smørgrav {
3138ca86bcf2SDag-Erling Smørgrav 	Channel *c = NULL;
3139ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *original = NULL, *modified = NULL;
3140ca86bcf2SDag-Erling Smørgrav 	const u_char *cp;
3141ca86bcf2SDag-Erling Smørgrav 	char *ctype = NULL, *listen_host = NULL;
3142ca86bcf2SDag-Erling Smørgrav 	u_char type;
3143ca86bcf2SDag-Erling Smørgrav 	size_t have;
31444f52dfbbSDag-Erling Smørgrav 	int ret = -1, r;
3145ca86bcf2SDag-Erling Smørgrav 	u_int id, remote_id, listen_port;
3146ca86bcf2SDag-Erling Smørgrav 
31474f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(downstream->input, stderr); */
31484f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
3149ca86bcf2SDag-Erling Smørgrav 	    != 0) {
315019261079SEd Maste 		error_fr(r, "parse");
3151ca86bcf2SDag-Erling Smørgrav 		return -1;
3152ca86bcf2SDag-Erling Smørgrav 	}
3153ca86bcf2SDag-Erling Smørgrav 	if (have < 2) {
315419261079SEd Maste 		error_f("short message");
3155ca86bcf2SDag-Erling Smørgrav 		return -1;
3156ca86bcf2SDag-Erling Smørgrav 	}
3157ca86bcf2SDag-Erling Smørgrav 	type = cp[1];
3158ca86bcf2SDag-Erling Smørgrav 	/* skip padlen + type */
3159ca86bcf2SDag-Erling Smørgrav 	cp += 2;
3160ca86bcf2SDag-Erling Smørgrav 	have -= 2;
3161ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
316219261079SEd Maste 		debug3_f("channel %u: down->up: type %u",
3163ca86bcf2SDag-Erling Smørgrav 		    downstream->self, type);
3164ca86bcf2SDag-Erling Smørgrav 
3165ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3166ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN:
3167ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3168ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
316919261079SEd Maste 			error_f("alloc");
3170ca86bcf2SDag-Erling Smørgrav 			goto out;
3171ca86bcf2SDag-Erling Smørgrav 		}
3172ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
3173ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
317419261079SEd Maste 			error_fr(r, "parse");
3175ca86bcf2SDag-Erling Smørgrav 			goto out;
3176ca86bcf2SDag-Erling Smørgrav 		}
3177f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3178ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, ctype, 1);
3179ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3180ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;	/* original downstream id */
3181ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
3182ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3183ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
318419261079SEd Maste 			error_fr(r, "compose");
31854f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3186ca86bcf2SDag-Erling Smørgrav 			goto out;
3187ca86bcf2SDag-Erling Smørgrav 		}
3188ca86bcf2SDag-Erling Smørgrav 		break;
3189ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3190ca86bcf2SDag-Erling Smørgrav 		/*
3191ca86bcf2SDag-Erling Smørgrav 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
3192ca86bcf2SDag-Erling Smørgrav 		 * need to parse 'remote_id' instead of 'ctype'.
3193ca86bcf2SDag-Erling Smørgrav 		 */
3194ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
3195ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
319619261079SEd Maste 			error_f("alloc");
3197ca86bcf2SDag-Erling Smørgrav 			goto out;
3198ca86bcf2SDag-Erling Smørgrav 		}
3199ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
3200ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
320119261079SEd Maste 			error_fr(r, "parse");
3202ca86bcf2SDag-Erling Smørgrav 			goto out;
3203ca86bcf2SDag-Erling Smørgrav 		}
3204f374ba41SEd Maste 		c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY,
3205ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
3206ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3207ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;
3208ca86bcf2SDag-Erling Smørgrav 		c->remote_id = remote_id;
32094f52dfbbSDag-Erling Smørgrav 		c->have_remote_id = 1;
3210ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
3211ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3212ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
321319261079SEd Maste 			error_fr(r, "compose");
32144f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3215ca86bcf2SDag-Erling Smørgrav 			goto out;
3216ca86bcf2SDag-Erling Smørgrav 		}
3217ca86bcf2SDag-Erling Smørgrav 		break;
3218ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_GLOBAL_REQUEST:
3219ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL) {
322019261079SEd Maste 			error_f("alloc");
3221ca86bcf2SDag-Erling Smørgrav 			goto out;
3222ca86bcf2SDag-Erling Smørgrav 		}
3223ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
322419261079SEd Maste 			error_fr(r, "parse");
3225ca86bcf2SDag-Erling Smørgrav 			goto out;
3226ca86bcf2SDag-Erling Smørgrav 		}
3227ca86bcf2SDag-Erling Smørgrav 		if (strcmp(ctype, "tcpip-forward") != 0) {
322819261079SEd Maste 			error_f("unsupported request %s", ctype);
3229ca86bcf2SDag-Erling Smørgrav 			goto out;
3230ca86bcf2SDag-Erling Smørgrav 		}
3231ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
3232ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
3233ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
323419261079SEd Maste 			error_fr(r, "parse");
3235ca86bcf2SDag-Erling Smørgrav 			goto out;
3236ca86bcf2SDag-Erling Smørgrav 		}
3237ca86bcf2SDag-Erling Smørgrav 		if (listen_port > 65535) {
323819261079SEd Maste 			error_f("tcpip-forward for %s: bad port %u",
323919261079SEd Maste 			    listen_host, listen_port);
3240ca86bcf2SDag-Erling Smørgrav 			goto out;
3241ca86bcf2SDag-Erling Smørgrav 		}
3242ca86bcf2SDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
3243a91a2465SEd Maste 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>",
3244a91a2465SEd Maste 		    -1, listen_host, NULL, (int)listen_port, downstream);
3245ca86bcf2SDag-Erling Smørgrav 		break;
3246ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3247ca86bcf2SDag-Erling Smørgrav 		if (have < 4)
3248ca86bcf2SDag-Erling Smørgrav 			break;
3249ca86bcf2SDag-Erling Smørgrav 		remote_id = PEEK_U32(cp);
32504f52dfbbSDag-Erling Smørgrav 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
3251ca86bcf2SDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD)
32524f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
3253ca86bcf2SDag-Erling Smørgrav 			else
3254ca86bcf2SDag-Erling Smørgrav 				c->flags |= CHAN_CLOSE_SENT;
3255ca86bcf2SDag-Erling Smørgrav 		}
3256ca86bcf2SDag-Erling Smørgrav 		break;
3257ca86bcf2SDag-Erling Smørgrav 	}
3258ca86bcf2SDag-Erling Smørgrav 	if (modified) {
3259ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3260ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
3261ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
326219261079SEd Maste 			error_fr(r, "send");
3263ca86bcf2SDag-Erling Smørgrav 			goto out;
3264ca86bcf2SDag-Erling Smørgrav 		}
3265ca86bcf2SDag-Erling Smørgrav 	} else {
3266ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3267ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
3268ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
326919261079SEd Maste 			error_fr(r, "send");
3270ca86bcf2SDag-Erling Smørgrav 			goto out;
3271ca86bcf2SDag-Erling Smørgrav 		}
3272ca86bcf2SDag-Erling Smørgrav 	}
3273ca86bcf2SDag-Erling Smørgrav 	ret = 0;
3274ca86bcf2SDag-Erling Smørgrav  out:
3275ca86bcf2SDag-Erling Smørgrav 	free(ctype);
3276ca86bcf2SDag-Erling Smørgrav 	free(listen_host);
3277ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(original);
3278ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(modified);
3279ca86bcf2SDag-Erling Smørgrav 	return ret;
3280ca86bcf2SDag-Erling Smørgrav }
3281ca86bcf2SDag-Erling Smørgrav 
3282ca86bcf2SDag-Erling Smørgrav /*
3283ca86bcf2SDag-Erling Smørgrav  * receive packets from upstream server and de-multiplex packets
3284ca86bcf2SDag-Erling Smørgrav  * to correct downstream:
3285ca86bcf2SDag-Erling Smørgrav  * implemented as a helper for channel input handlers,
3286ca86bcf2SDag-Erling Smørgrav  * replaces local (proxy) channel ID with downstream channel ID.
3287ca86bcf2SDag-Erling Smørgrav  */
3288ca86bcf2SDag-Erling Smørgrav int
channel_proxy_upstream(Channel * c,int type,u_int32_t seq,struct ssh * ssh)32894f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
3290ca86bcf2SDag-Erling Smørgrav {
3291ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *b = NULL;
3292ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;
3293ca86bcf2SDag-Erling Smørgrav 	const u_char *cp = NULL;
3294ca86bcf2SDag-Erling Smørgrav 	size_t len;
3295ca86bcf2SDag-Erling Smørgrav 	int r;
3296ca86bcf2SDag-Erling Smørgrav 
3297ca86bcf2SDag-Erling Smørgrav 	/*
3298ca86bcf2SDag-Erling Smørgrav 	 * When receiving packets from the peer we need to check whether we
3299ca86bcf2SDag-Erling Smørgrav 	 * need to forward the packets to the mux client. In this case we
3300190cef3dSDag-Erling Smørgrav 	 * restore the original channel id and keep track of CLOSE messages,
3301ca86bcf2SDag-Erling Smørgrav 	 * so we can cleanup the channel.
3302ca86bcf2SDag-Erling Smørgrav 	 */
3303ca86bcf2SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
3304ca86bcf2SDag-Erling Smørgrav 		return 0;
3305ca86bcf2SDag-Erling Smørgrav 	if ((downstream = c->mux_ctx) == NULL)
3306ca86bcf2SDag-Erling Smørgrav 		return 0;
3307ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3308ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3309ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_DATA:
3310ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EOF:
3311ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
3312ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3313ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
3314ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3315ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_SUCCESS:
3316ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_FAILURE:
3317ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_REQUEST:
3318ca86bcf2SDag-Erling Smørgrav 		break;
3319ca86bcf2SDag-Erling Smørgrav 	default:
332019261079SEd Maste 		debug2_f("channel %u: unsupported type %u", c->self, type);
3321ca86bcf2SDag-Erling Smørgrav 		return 0;
3322ca86bcf2SDag-Erling Smørgrav 	}
3323ca86bcf2SDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
332419261079SEd Maste 		error_f("alloc reply");
3325ca86bcf2SDag-Erling Smørgrav 		goto out;
3326ca86bcf2SDag-Erling Smørgrav 	}
3327ca86bcf2SDag-Erling Smørgrav 	/* get remaining payload (after id) */
3328ca86bcf2SDag-Erling Smørgrav 	cp = sshpkt_ptr(ssh, &len);
3329ca86bcf2SDag-Erling Smørgrav 	if (cp == NULL) {
333019261079SEd Maste 		error_f("no packet");
3331ca86bcf2SDag-Erling Smørgrav 		goto out;
3332ca86bcf2SDag-Erling Smørgrav 	}
3333ca86bcf2SDag-Erling Smørgrav 	/* translate id and send to muxclient */
3334ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
3335ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u8(b, type)) != 0 ||
3336ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
3337ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put(b, cp, len)) != 0 ||
33384f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
333919261079SEd Maste 		error_fr(r, "compose muxclient");
3340ca86bcf2SDag-Erling Smørgrav 		goto out;
3341ca86bcf2SDag-Erling Smørgrav 	}
3342ca86bcf2SDag-Erling Smørgrav 	/* sshbuf_dump(b, stderr); */
3343ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
334419261079SEd Maste 		debug3_f("channel %u: up->down: type %u", c->self, type);
3345ca86bcf2SDag-Erling Smørgrav  out:
3346ca86bcf2SDag-Erling Smørgrav 	/* update state */
3347ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3348ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3349ca86bcf2SDag-Erling Smørgrav 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
33504f52dfbbSDag-Erling Smørgrav 		if (cp && len > 4) {
3351ca86bcf2SDag-Erling Smørgrav 			c->remote_id = PEEK_U32(cp);
33524f52dfbbSDag-Erling Smørgrav 			c->have_remote_id = 1;
33534f52dfbbSDag-Erling Smørgrav 		}
3354ca86bcf2SDag-Erling Smørgrav 		break;
3355ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3356ca86bcf2SDag-Erling Smørgrav 		if (c->flags & CHAN_CLOSE_SENT)
33574f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3358ca86bcf2SDag-Erling Smørgrav 		else
3359ca86bcf2SDag-Erling Smørgrav 			c->flags |= CHAN_CLOSE_RCVD;
3360ca86bcf2SDag-Erling Smørgrav 		break;
3361ca86bcf2SDag-Erling Smørgrav 	}
3362ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(b);
3363ca86bcf2SDag-Erling Smørgrav 	return 1;
3364ca86bcf2SDag-Erling Smørgrav }
3365af12a3e7SDag-Erling Smørgrav 
3366af12a3e7SDag-Erling Smørgrav /* -- protocol input */
3367511b41d2SMark Murray 
33684f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */
33694f52dfbbSDag-Erling Smørgrav static int
channel_parse_id(struct ssh * ssh,const char * where,const char * what)33704f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what)
3371511b41d2SMark Murray {
33724f52dfbbSDag-Erling Smørgrav 	u_int32_t id;
33734f52dfbbSDag-Erling Smørgrav 	int r;
33744f52dfbbSDag-Erling Smørgrav 
33754f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
337619261079SEd Maste 		error_r(r, "%s: parse id", where);
33774f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
33784f52dfbbSDag-Erling Smørgrav 	}
33794f52dfbbSDag-Erling Smørgrav 	if (id > INT_MAX) {
338019261079SEd Maste 		error_r(r, "%s: bad channel id %u", where, id);
33814f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
33824f52dfbbSDag-Erling Smørgrav 	}
33834f52dfbbSDag-Erling Smørgrav 	return (int)id;
33844f52dfbbSDag-Erling Smørgrav }
33854f52dfbbSDag-Erling Smørgrav 
33864f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */
33874f52dfbbSDag-Erling Smørgrav static Channel *
channel_from_packet_id(struct ssh * ssh,const char * where,const char * what)33884f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
33894f52dfbbSDag-Erling Smørgrav {
33904f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, where, what);
3391a04a10f8SKris Kennaway 	Channel *c;
3392511b41d2SMark Murray 
33934f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
33944f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh,
33954f52dfbbSDag-Erling Smørgrav 		    "%s packet referred to nonexistent channel %d", what, id);
33964f52dfbbSDag-Erling Smørgrav 	}
33974f52dfbbSDag-Erling Smørgrav 	return c;
33984f52dfbbSDag-Erling Smørgrav }
33994f52dfbbSDag-Erling Smørgrav 
34004f52dfbbSDag-Erling Smørgrav int
channel_input_data(int type,u_int32_t seq,struct ssh * ssh)34014f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
34024f52dfbbSDag-Erling Smørgrav {
34034f52dfbbSDag-Erling Smørgrav 	const u_char *data;
34044f52dfbbSDag-Erling Smørgrav 	size_t data_len, win_len;
34054f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
34064f52dfbbSDag-Erling Smørgrav 	int r;
34074f52dfbbSDag-Erling Smørgrav 
34084f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3409ca86bcf2SDag-Erling Smørgrav 		return 0;
3410511b41d2SMark Murray 
3411511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
3412a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
34134f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
34144f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
3415a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
3416bc5531deSDag-Erling Smørgrav 		return 0;
3417511b41d2SMark Murray 
3418511b41d2SMark Murray 	/* Get the data. */
341919261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
342019261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0)
342119261079SEd Maste 		fatal_fr(r, "channel %i: get data", c->self);
34224f52dfbbSDag-Erling Smørgrav 
3423e2f6069cSDag-Erling Smørgrav 	win_len = data_len;
3424e2f6069cSDag-Erling Smørgrav 	if (c->datagram)
3425e2f6069cSDag-Erling Smørgrav 		win_len += 4;  /* string length header */
3426a04a10f8SKris Kennaway 
3427476cd3b2SDag-Erling Smørgrav 	/*
34284f52dfbbSDag-Erling Smørgrav 	 * The sending side reduces its window as it sends data, so we
34294f52dfbbSDag-Erling Smørgrav 	 * must 'fake' consumption of the data in order to ensure that window
34304f52dfbbSDag-Erling Smørgrav 	 * updates are sent back. Otherwise the connection might deadlock.
3431476cd3b2SDag-Erling Smørgrav 	 */
34324f52dfbbSDag-Erling Smørgrav 	if (c->ostate != CHAN_OUTPUT_OPEN) {
3433e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3434e2f6069cSDag-Erling Smørgrav 		c->local_consumed += win_len;
3435bc5531deSDag-Erling Smørgrav 		return 0;
3436476cd3b2SDag-Erling Smørgrav 	}
3437476cd3b2SDag-Erling Smørgrav 
3438e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_maxpacket) {
34394f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd big packet %zu, maxpack %u",
3440e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_maxpacket);
34414f52dfbbSDag-Erling Smørgrav 		return 0;
3442a04a10f8SKris Kennaway 	}
3443e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_window) {
3444069ac184SEd Maste 		c->local_window_exceeded += win_len - c->local_window;
3445069ac184SEd Maste 		logit("channel %d: rcvd too much data %zu, win %u/%u "
3446069ac184SEd Maste 		    "(excess %u)", c->self, win_len, c->local_window,
3447069ac184SEd Maste 		    c->local_window_max, c->local_window_exceeded);
3448069ac184SEd Maste 		c->local_window = 0;
3449069ac184SEd Maste 		/* Allow 10% grace before bringing the hammer down */
3450069ac184SEd Maste 		if (c->local_window_exceeded > (c->local_window_max / 10)) {
3451069ac184SEd Maste 			ssh_packet_disconnect(ssh, "channel %d: peer ignored "
3452069ac184SEd Maste 			    "channel window", c->self);
3453a04a10f8SKris Kennaway 		}
3454069ac184SEd Maste 	} else {
3455e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3456069ac184SEd Maste 		c->local_window_exceeded = 0;
3457069ac184SEd Maste 	}
34584f52dfbbSDag-Erling Smørgrav 
34594f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
34604f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
346119261079SEd Maste 			fatal_fr(r, "channel %i: append datagram", c->self);
34624f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
346319261079SEd Maste 		fatal_fr(r, "channel %i: append data", c->self);
34644f52dfbbSDag-Erling Smørgrav 
3465bc5531deSDag-Erling Smørgrav 	return 0;
3466511b41d2SMark Murray }
3467af12a3e7SDag-Erling Smørgrav 
3468bc5531deSDag-Erling Smørgrav int
channel_input_extended_data(int type,u_int32_t seq,struct ssh * ssh)34694f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3470a04a10f8SKris Kennaway {
34714f52dfbbSDag-Erling Smørgrav 	const u_char *data;
34724f52dfbbSDag-Erling Smørgrav 	size_t data_len;
34734f52dfbbSDag-Erling Smørgrav 	u_int32_t tcode;
34744f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
34754f52dfbbSDag-Erling Smørgrav 	int r;
3476a04a10f8SKris Kennaway 
34774f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3478ca86bcf2SDag-Erling Smørgrav 		return 0;
3479a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
34804f52dfbbSDag-Erling Smørgrav 		logit("channel %d: ext data for non open", c->self);
3481bc5531deSDag-Erling Smørgrav 		return 0;
3482a04a10f8SKris Kennaway 	}
348380628bacSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
348419261079SEd Maste 		if (ssh->compat & SSH_BUG_EXTEOF)
34854f52dfbbSDag-Erling Smørgrav 			debug("channel %d: accepting ext data after eof",
34864f52dfbbSDag-Erling Smørgrav 			    c->self);
348780628bacSDag-Erling Smørgrav 		else
34884f52dfbbSDag-Erling Smørgrav 			ssh_packet_disconnect(ssh, "Received extended_data "
34894f52dfbbSDag-Erling Smørgrav 			    "after EOF on channel %d.", c->self);
349080628bacSDag-Erling Smørgrav 	}
34914f52dfbbSDag-Erling Smørgrav 
34924f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
349319261079SEd Maste 		error_fr(r, "parse tcode");
34944f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
34954f52dfbbSDag-Erling Smørgrav 	}
3496a04a10f8SKris Kennaway 	if (c->efd == -1 ||
3497a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3498a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3499221552e4SDag-Erling Smørgrav 		logit("channel %d: bad ext data", c->self);
3500bc5531deSDag-Erling Smørgrav 		return 0;
3501a04a10f8SKris Kennaway 	}
350219261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
350319261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
350419261079SEd Maste 		error_fr(r, "parse data");
35054f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
35064f52dfbbSDag-Erling Smørgrav 	}
35074f52dfbbSDag-Erling Smørgrav 
3508a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
35094f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3510a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
3511bc5531deSDag-Erling Smørgrav 		return 0;
3512a04a10f8SKris Kennaway 	}
35134f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
35144f52dfbbSDag-Erling Smørgrav 	/* XXX sshpkt_getb? */
35154f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
351619261079SEd Maste 		error_fr(r, "append");
3517a04a10f8SKris Kennaway 	c->local_window -= data_len;
3518bc5531deSDag-Erling Smørgrav 	return 0;
3519a04a10f8SKris Kennaway }
3520a04a10f8SKris Kennaway 
3521bc5531deSDag-Erling Smørgrav int
channel_input_ieof(int type,u_int32_t seq,struct ssh * ssh)35224f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3523a04a10f8SKris Kennaway {
35244f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
352519261079SEd Maste 	int r;
3526a04a10f8SKris Kennaway 
352719261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
352819261079SEd Maste 		error_fr(r, "parse data");
352919261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid ieof message");
353019261079SEd Maste 	}
35314f52dfbbSDag-Erling Smørgrav 
35324f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3533ca86bcf2SDag-Erling Smørgrav 		return 0;
35344f52dfbbSDag-Erling Smørgrav 	chan_rcvd_ieof(ssh, c);
3535af12a3e7SDag-Erling Smørgrav 
3536af12a3e7SDag-Erling Smørgrav 	/* XXX force input close */
3537af12a3e7SDag-Erling Smørgrav 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3538af12a3e7SDag-Erling Smørgrav 		debug("channel %d: FORCE input drain", c->self);
3539af12a3e7SDag-Erling Smørgrav 		c->istate = CHAN_INPUT_WAIT_DRAIN;
35404f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->input) == 0)
35414f52dfbbSDag-Erling Smørgrav 			chan_ibuf_empty(ssh, c);
3542af12a3e7SDag-Erling Smørgrav 	}
3543bc5531deSDag-Erling Smørgrav 	return 0;
3544a04a10f8SKris Kennaway }
3545511b41d2SMark Murray 
3546bc5531deSDag-Erling Smørgrav int
channel_input_oclose(int type,u_int32_t seq,struct ssh * ssh)35474f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3548511b41d2SMark Murray {
35494f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
355019261079SEd Maste 	int r;
3551511b41d2SMark Murray 
35524f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3553ca86bcf2SDag-Erling Smørgrav 		return 0;
355419261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
355519261079SEd Maste 		error_fr(r, "parse data");
355619261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid oclose message");
355719261079SEd Maste 	}
35584f52dfbbSDag-Erling Smørgrav 	chan_rcvd_oclose(ssh, c);
3559bc5531deSDag-Erling Smørgrav 	return 0;
3560511b41d2SMark Murray }
3561511b41d2SMark Murray 
3562bc5531deSDag-Erling Smørgrav int
channel_input_open_confirmation(int type,u_int32_t seq,struct ssh * ssh)35634f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3564a04a10f8SKris Kennaway {
35654f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
35664f52dfbbSDag-Erling Smørgrav 	u_int32_t remote_window, remote_maxpacket;
35674f52dfbbSDag-Erling Smørgrav 	int r;
3568af12a3e7SDag-Erling Smørgrav 
35694f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3570ca86bcf2SDag-Erling Smørgrav 		return 0;
3571ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
357219261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open confirmation for "
35734f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
35744f52dfbbSDag-Erling Smørgrav 	/*
35754f52dfbbSDag-Erling Smørgrav 	 * Record the remote channel number and mark that the channel
35764f52dfbbSDag-Erling Smørgrav 	 * is now open.
35774f52dfbbSDag-Erling Smørgrav 	 */
35784f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
35794f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
358019261079SEd Maste 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 ||
358119261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
358219261079SEd Maste 		error_fr(r, "window/maxpacket");
358319261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open confirmation message");
35844f52dfbbSDag-Erling Smørgrav 	}
3585a04a10f8SKris Kennaway 
35864f52dfbbSDag-Erling Smørgrav 	c->have_remote_id = 1;
35874f52dfbbSDag-Erling Smørgrav 	c->remote_window = remote_window;
35884f52dfbbSDag-Erling Smørgrav 	c->remote_maxpacket = remote_maxpacket;
35894f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
3590d4af9e69SDag-Erling Smørgrav 	if (c->open_confirm) {
359119261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
35924f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
359319261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3594a04a10f8SKris Kennaway 	}
3595a91a2465SEd Maste 	channel_set_used_time(ssh, c);
3596221552e4SDag-Erling Smørgrav 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3597a04a10f8SKris Kennaway 	    c->remote_window, c->remote_maxpacket);
3598bc5531deSDag-Erling Smørgrav 	return 0;
3599af12a3e7SDag-Erling Smørgrav }
3600af12a3e7SDag-Erling Smørgrav 
3601af12a3e7SDag-Erling Smørgrav static char *
reason2txt(int reason)3602af12a3e7SDag-Erling Smørgrav reason2txt(int reason)
3603af12a3e7SDag-Erling Smørgrav {
3604af12a3e7SDag-Erling Smørgrav 	switch (reason) {
3605af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3606af12a3e7SDag-Erling Smørgrav 		return "administratively prohibited";
3607af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_CONNECT_FAILED:
3608af12a3e7SDag-Erling Smørgrav 		return "connect failed";
3609af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3610af12a3e7SDag-Erling Smørgrav 		return "unknown channel type";
3611af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3612af12a3e7SDag-Erling Smørgrav 		return "resource shortage";
3613af12a3e7SDag-Erling Smørgrav 	}
3614af12a3e7SDag-Erling Smørgrav 	return "unknown reason";
3615a04a10f8SKris Kennaway }
3616a04a10f8SKris Kennaway 
3617bc5531deSDag-Erling Smørgrav int
channel_input_open_failure(int type,u_int32_t seq,struct ssh * ssh)36184f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3619a04a10f8SKris Kennaway {
36204f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
36214f52dfbbSDag-Erling Smørgrav 	u_int32_t reason;
36224f52dfbbSDag-Erling Smørgrav 	char *msg = NULL;
36234f52dfbbSDag-Erling Smørgrav 	int r;
3624a04a10f8SKris Kennaway 
36254f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3626ca86bcf2SDag-Erling Smørgrav 		return 0;
3627ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
362819261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open failure for "
36294f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
36304f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
363119261079SEd Maste 		error_fr(r, "parse reason");
363219261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
3633ca3176e7SBrian Feldman 	}
36344f52dfbbSDag-Erling Smørgrav 	/* skip language */
36354f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
363619261079SEd Maste 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 ||
363719261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
363819261079SEd Maste 		error_fr(r, "parse msg/lang");
363919261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
36404f52dfbbSDag-Erling Smørgrav 	}
36414f52dfbbSDag-Erling Smørgrav 	logit("channel %d: open failed: %s%s%s", c->self,
3642af12a3e7SDag-Erling Smørgrav 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
3643e4a9863fSDag-Erling Smørgrav 	free(msg);
3644e2f6069cSDag-Erling Smørgrav 	if (c->open_confirm) {
364519261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
36464f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
364719261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3648e2f6069cSDag-Erling Smørgrav 	}
3649cce7d346SDag-Erling Smørgrav 	/* Schedule the channel for cleanup/deletion. */
36504f52dfbbSDag-Erling Smørgrav 	chan_mark_dead(ssh, c);
3651bc5531deSDag-Erling Smørgrav 	return 0;
3652a04a10f8SKris Kennaway }
3653a04a10f8SKris Kennaway 
3654bc5531deSDag-Erling Smørgrav int
channel_input_window_adjust(int type,u_int32_t seq,struct ssh * ssh)36554f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3656a04a10f8SKris Kennaway {
36574f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "window adjust");
3658a04a10f8SKris Kennaway 	Channel *c;
36594f52dfbbSDag-Erling Smørgrav 	u_int32_t adjust;
36604f52dfbbSDag-Erling Smørgrav 	u_int new_rwin;
36614f52dfbbSDag-Erling Smørgrav 	int r;
3662a04a10f8SKris Kennaway 
36634f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
3664b74df5b2SDag-Erling Smørgrav 		logit("Received window adjust for non-open channel %d.", id);
3665bc5531deSDag-Erling Smørgrav 		return 0;
3666511b41d2SMark Murray 	}
36674f52dfbbSDag-Erling Smørgrav 
36684f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3669ca86bcf2SDag-Erling Smørgrav 		return 0;
367019261079SEd Maste 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 ||
367119261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
367219261079SEd Maste 		error_fr(r, "parse adjust");
367319261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid window adjust message");
36744f52dfbbSDag-Erling Smørgrav 	}
36754f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
36764f52dfbbSDag-Erling Smørgrav 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
3677557f75e5SDag-Erling Smørgrav 		fatal("channel %d: adjust %u overflows remote window %u",
36784f52dfbbSDag-Erling Smørgrav 		    c->self, adjust, c->remote_window);
36794f52dfbbSDag-Erling Smørgrav 	}
36804f52dfbbSDag-Erling Smørgrav 	c->remote_window = new_rwin;
3681bc5531deSDag-Erling Smørgrav 	return 0;
3682511b41d2SMark Murray }
3683511b41d2SMark Murray 
3684bc5531deSDag-Erling Smørgrav int
channel_input_status_confirm(int type,u_int32_t seq,struct ssh * ssh)36854f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3686af12a3e7SDag-Erling Smørgrav {
36874f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "status confirm");
3688d4af9e69SDag-Erling Smørgrav 	Channel *c;
3689d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
3690d4af9e69SDag-Erling Smørgrav 
3691d4af9e69SDag-Erling Smørgrav 	/* Reset keepalive timeout */
369219261079SEd Maste 	ssh_packet_set_alive_timeouts(ssh, 0);
3693d4af9e69SDag-Erling Smørgrav 
369419261079SEd Maste 	debug2_f("type %d id %d", type, id);
3695d4af9e69SDag-Erling Smørgrav 
36964f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
369719261079SEd Maste 		logit_f("%d: unknown", id);
3698bc5531deSDag-Erling Smørgrav 		return 0;
3699d4af9e69SDag-Erling Smørgrav 	}
37004f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3701ca86bcf2SDag-Erling Smørgrav 		return 0;
370219261079SEd Maste         if (sshpkt_get_end(ssh) != 0)
370319261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid status confirm message");
3704d4af9e69SDag-Erling Smørgrav 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3705bc5531deSDag-Erling Smørgrav 		return 0;
37064f52dfbbSDag-Erling Smørgrav 	cc->cb(ssh, type, c, cc->ctx);
3707d4af9e69SDag-Erling Smørgrav 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
370819261079SEd Maste 	freezero(cc, sizeof(*cc));
3709bc5531deSDag-Erling Smørgrav 	return 0;
3710d4af9e69SDag-Erling Smørgrav }
3711af12a3e7SDag-Erling Smørgrav 
3712af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
3713511b41d2SMark Murray 
3714511b41d2SMark Murray void
channel_set_af(struct ssh * ssh,int af)37154f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af)
3716511b41d2SMark Murray {
37174f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->IPv4or6 = af;
3718511b41d2SMark Murray }
3719511b41d2SMark Murray 
372089986192SBrooks Davis 
3721462c32cbSDag-Erling Smørgrav /*
3722462c32cbSDag-Erling Smørgrav  * Determine whether or not a port forward listens to loopback, the
3723462c32cbSDag-Erling Smørgrav  * specified address or wildcard. On the client, a specified bind
3724462c32cbSDag-Erling Smørgrav  * address will always override gateway_ports. On the server, a
3725462c32cbSDag-Erling Smørgrav  * gateway_ports of 1 (``yes'') will override the client's specification
3726462c32cbSDag-Erling Smørgrav  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3727462c32cbSDag-Erling Smørgrav  * will bind to whatever address the client asked for.
3728462c32cbSDag-Erling Smørgrav  *
3729462c32cbSDag-Erling Smørgrav  * Special-case listen_addrs are:
3730462c32cbSDag-Erling Smørgrav  *
3731462c32cbSDag-Erling Smørgrav  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3732462c32cbSDag-Erling Smørgrav  * "" (empty string), "*"  -> wildcard v4/v6
3733462c32cbSDag-Erling Smørgrav  * "localhost"             -> loopback v4/v6
3734a0ee8cc6SDag-Erling Smørgrav  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3735462c32cbSDag-Erling Smørgrav  */
3736462c32cbSDag-Erling Smørgrav static const char *
channel_fwd_bind_addr(struct ssh * ssh,const char * listen_addr,int * wildcardp,int is_client,struct ForwardOptions * fwd_opts)373719261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp,
3738a0ee8cc6SDag-Erling Smørgrav     int is_client, struct ForwardOptions *fwd_opts)
3739462c32cbSDag-Erling Smørgrav {
3740462c32cbSDag-Erling Smørgrav 	const char *addr = NULL;
3741462c32cbSDag-Erling Smørgrav 	int wildcard = 0;
3742462c32cbSDag-Erling Smørgrav 
3743462c32cbSDag-Erling Smørgrav 	if (listen_addr == NULL) {
3744462c32cbSDag-Erling Smørgrav 		/* No address specified: default to gateway_ports setting */
3745a0ee8cc6SDag-Erling Smørgrav 		if (fwd_opts->gateway_ports)
3746462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3747a0ee8cc6SDag-Erling Smørgrav 	} else if (fwd_opts->gateway_ports || is_client) {
374819261079SEd Maste 		if (((ssh->compat & SSH_OLD_FORWARD_ADDR) &&
3749462c32cbSDag-Erling Smørgrav 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3750462c32cbSDag-Erling Smørgrav 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
3751a0ee8cc6SDag-Erling Smørgrav 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3752462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3753f7167e0eSDag-Erling Smørgrav 			/*
3754f7167e0eSDag-Erling Smørgrav 			 * Notify client if they requested a specific listen
3755f7167e0eSDag-Erling Smørgrav 			 * address and it was overridden.
3756f7167e0eSDag-Erling Smørgrav 			 */
3757f7167e0eSDag-Erling Smørgrav 			if (*listen_addr != '\0' &&
3758f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
3759f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "*") != 0) {
376019261079SEd Maste 				ssh_packet_send_debug(ssh,
376119261079SEd Maste 				    "Forwarding listen address "
3762f7167e0eSDag-Erling Smørgrav 				    "\"%s\" overridden by server "
3763f7167e0eSDag-Erling Smørgrav 				    "GatewayPorts", listen_addr);
3764f7167e0eSDag-Erling Smørgrav 			}
3765a0ee8cc6SDag-Erling Smørgrav 		} else if (strcmp(listen_addr, "localhost") != 0 ||
3766a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
3767a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "::1") == 0) {
376819261079SEd Maste 			/*
376919261079SEd Maste 			 * Accept explicit localhost address when
377019261079SEd Maste 			 * GatewayPorts=yes. The "localhost" hostname is
377119261079SEd Maste 			 * deliberately skipped here so it will listen on all
377219261079SEd Maste 			 * available local address families.
377319261079SEd Maste 			 */
3774a0ee8cc6SDag-Erling Smørgrav 			addr = listen_addr;
3775f7167e0eSDag-Erling Smørgrav 		}
3776a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
3777a0ee8cc6SDag-Erling Smørgrav 	    strcmp(listen_addr, "::1") == 0) {
3778a0ee8cc6SDag-Erling Smørgrav 		/*
3779a0ee8cc6SDag-Erling Smørgrav 		 * If a specific IPv4/IPv6 localhost address has been
3780a0ee8cc6SDag-Erling Smørgrav 		 * requested then accept it even if gateway_ports is in
3781a0ee8cc6SDag-Erling Smørgrav 		 * effect. This allows the client to prefer IPv4 or IPv6.
3782a0ee8cc6SDag-Erling Smørgrav 		 */
3783462c32cbSDag-Erling Smørgrav 		addr = listen_addr;
3784462c32cbSDag-Erling Smørgrav 	}
3785462c32cbSDag-Erling Smørgrav 	if (wildcardp != NULL)
3786462c32cbSDag-Erling Smørgrav 		*wildcardp = wildcard;
3787462c32cbSDag-Erling Smørgrav 	return addr;
3788462c32cbSDag-Erling Smørgrav }
3789462c32cbSDag-Erling Smørgrav 
3790af12a3e7SDag-Erling Smørgrav static int
channel_setup_fwd_listener_tcpip(struct ssh * ssh,int type,struct Forward * fwd,int * allocated_listen_port,struct ForwardOptions * fwd_opts)37914f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
37924f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int *allocated_listen_port,
37934f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3794511b41d2SMark Murray {
3795af12a3e7SDag-Erling Smørgrav 	Channel *c;
3796b74df5b2SDag-Erling Smørgrav 	int sock, r, success = 0, wildcard = 0, is_client;
3797511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
3798aa49c926SDag-Erling Smørgrav 	const char *host, *addr;
3799af12a3e7SDag-Erling Smørgrav 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3800cce7d346SDag-Erling Smørgrav 	in_port_t *lport_p;
3801511b41d2SMark Murray 
3802aa49c926SDag-Erling Smørgrav 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3803511b41d2SMark Murray 
3804557f75e5SDag-Erling Smørgrav 	if (is_client && fwd->connect_path != NULL) {
3805557f75e5SDag-Erling Smørgrav 		host = fwd->connect_path;
3806557f75e5SDag-Erling Smørgrav 	} else {
3807557f75e5SDag-Erling Smørgrav 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
3808557f75e5SDag-Erling Smørgrav 		    fwd->listen_host : fwd->connect_host;
3809af12a3e7SDag-Erling Smørgrav 		if (host == NULL) {
3810af12a3e7SDag-Erling Smørgrav 			error("No forward host name.");
3811d4ecd108SDag-Erling Smørgrav 			return 0;
3812ca3176e7SBrian Feldman 		}
3813cce7d346SDag-Erling Smørgrav 		if (strlen(host) >= NI_MAXHOST) {
3814ca3176e7SBrian Feldman 			error("Forward host name too long.");
3815d4ecd108SDag-Erling Smørgrav 			return 0;
3816ca3176e7SBrian Feldman 		}
3817557f75e5SDag-Erling Smørgrav 	}
3818ca3176e7SBrian Feldman 
3819462c32cbSDag-Erling Smørgrav 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
382019261079SEd Maste 	addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard,
3821a0ee8cc6SDag-Erling Smørgrav 	    is_client, fwd_opts);
382219261079SEd Maste 	debug3_f("type %d wildcard %d addr %s", type, wildcard,
382319261079SEd Maste 	    (addr == NULL) ? "NULL" : addr);
3824aa49c926SDag-Erling Smørgrav 
3825aa49c926SDag-Erling Smørgrav 	/*
3826511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
3827511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3828511b41d2SMark Murray 	 */
3829511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
38304f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
3831aa49c926SDag-Erling Smørgrav 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3832511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
3833a0ee8cc6SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3834aa49c926SDag-Erling Smørgrav 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3835aa49c926SDag-Erling Smørgrav 		if (addr == NULL) {
3836aa49c926SDag-Erling Smørgrav 			/* This really shouldn't happen */
383719261079SEd Maste 			ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s",
3838d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3839aa49c926SDag-Erling Smørgrav 		} else {
384019261079SEd Maste 			error_f("getaddrinfo(%.64s): %s", addr,
3841d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3842aa49c926SDag-Erling Smørgrav 		}
3843d4ecd108SDag-Erling Smørgrav 		return 0;
3844aa49c926SDag-Erling Smørgrav 	}
3845cce7d346SDag-Erling Smørgrav 	if (allocated_listen_port != NULL)
3846cce7d346SDag-Erling Smørgrav 		*allocated_listen_port = 0;
3847511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
3848cce7d346SDag-Erling Smørgrav 		switch (ai->ai_family) {
3849cce7d346SDag-Erling Smørgrav 		case AF_INET:
3850cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3851cce7d346SDag-Erling Smørgrav 			    sin_port;
3852cce7d346SDag-Erling Smørgrav 			break;
3853cce7d346SDag-Erling Smørgrav 		case AF_INET6:
3854cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3855cce7d346SDag-Erling Smørgrav 			    sin6_port;
3856cce7d346SDag-Erling Smørgrav 			break;
3857cce7d346SDag-Erling Smørgrav 		default:
3858511b41d2SMark Murray 			continue;
3859cce7d346SDag-Erling Smørgrav 		}
3860cce7d346SDag-Erling Smørgrav 		/*
3861cce7d346SDag-Erling Smørgrav 		 * If allocating a port for -R forwards, then use the
3862cce7d346SDag-Erling Smørgrav 		 * same port for all address families.
3863cce7d346SDag-Erling Smørgrav 		 */
38644f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
38654f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
38664f52dfbbSDag-Erling Smørgrav 		    *allocated_listen_port > 0)
3867cce7d346SDag-Erling Smørgrav 			*lport_p = htons(*allocated_listen_port);
3868cce7d346SDag-Erling Smørgrav 
3869511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
38704f52dfbbSDag-Erling Smørgrav 		    strport, sizeof(strport),
38714f52dfbbSDag-Erling Smørgrav 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
387219261079SEd Maste 			error_f("getnameinfo failed");
3873511b41d2SMark Murray 			continue;
3874511b41d2SMark Murray 		}
3875511b41d2SMark Murray 		/* Create a port to listen for the host. */
3876221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
387719261079SEd Maste 		if (sock == -1) {
3878511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
387947dd1d1bSDag-Erling Smørgrav 			verbose("socket [%s]:%s: %.100s", ntop, strport,
388047dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3881511b41d2SMark Murray 			continue;
3882511b41d2SMark Murray 		}
3883b74df5b2SDag-Erling Smørgrav 
388447dd1d1bSDag-Erling Smørgrav 		set_reuseaddr(sock);
3885b15c8340SDag-Erling Smørgrav 		if (ai->ai_family == AF_INET6)
3886b15c8340SDag-Erling Smørgrav 			sock_set_v6only(sock);
3887f388f5efSDag-Erling Smørgrav 
3888cce7d346SDag-Erling Smørgrav 		debug("Local forwarding listening on %s port %s.",
3889cce7d346SDag-Erling Smørgrav 		    ntop, strport);
3890511b41d2SMark Murray 
3891511b41d2SMark Murray 		/* Bind the socket to the address. */
389219261079SEd Maste 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
38934f52dfbbSDag-Erling Smørgrav 			/*
38944f52dfbbSDag-Erling Smørgrav 			 * address can be in if use ipv6 address is
38954f52dfbbSDag-Erling Smørgrav 			 * already bound
38964f52dfbbSDag-Erling Smørgrav 			 */
3897989dd127SDag-Erling Smørgrav 			if (!ai->ai_next)
389847dd1d1bSDag-Erling Smørgrav 				error("bind [%s]:%s: %.100s",
389947dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3900989dd127SDag-Erling Smørgrav 			else
390147dd1d1bSDag-Erling Smørgrav 				verbose("bind [%s]:%s: %.100s",
390247dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3903989dd127SDag-Erling Smørgrav 
3904511b41d2SMark Murray 			close(sock);
3905511b41d2SMark Murray 			continue;
3906511b41d2SMark Murray 		}
3907511b41d2SMark Murray 		/* Start listening for connections on the socket. */
390819261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
390947dd1d1bSDag-Erling Smørgrav 			error("listen [%s]:%s: %.100s", ntop, strport,
391047dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3911511b41d2SMark Murray 			close(sock);
3912511b41d2SMark Murray 			continue;
3913511b41d2SMark Murray 		}
3914cce7d346SDag-Erling Smørgrav 
3915cce7d346SDag-Erling Smørgrav 		/*
3916a0ee8cc6SDag-Erling Smørgrav 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3917cce7d346SDag-Erling Smørgrav 		 * record what we got.
3918cce7d346SDag-Erling Smørgrav 		 */
39194f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
39204f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 &&
3921cce7d346SDag-Erling Smørgrav 		    allocated_listen_port != NULL &&
3922cce7d346SDag-Erling Smørgrav 		    *allocated_listen_port == 0) {
3923076ad2f8SDag-Erling Smørgrav 			*allocated_listen_port = get_local_port(sock);
3924cce7d346SDag-Erling Smørgrav 			debug("Allocated listen port %d",
3925cce7d346SDag-Erling Smørgrav 			    *allocated_listen_port);
3926cce7d346SDag-Erling Smørgrav 		}
3927cce7d346SDag-Erling Smørgrav 
392860c59fadSDag-Erling Smørgrav 		/* Allocate a channel number for the socket. */
3929f374ba41SEd Maste 		c = channel_new(ssh, "port-listener", type, sock, sock, -1,
3930a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3931221552e4SDag-Erling Smørgrav 		    0, "port listener", 1);
3932cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
3933a0ee8cc6SDag-Erling Smørgrav 		c->host_port = fwd->connect_port;
3934462c32cbSDag-Erling Smørgrav 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
3935a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
393619261079SEd Maste 		    !(ssh->compat & SSH_BUG_DYNAMIC_RPORT))
3937462c32cbSDag-Erling Smørgrav 			c->listening_port = *allocated_listen_port;
3938462c32cbSDag-Erling Smørgrav 		else
3939a0ee8cc6SDag-Erling Smørgrav 			c->listening_port = fwd->listen_port;
3940511b41d2SMark Murray 		success = 1;
3941511b41d2SMark Murray 	}
3942511b41d2SMark Murray 	if (success == 0)
394319261079SEd Maste 		error_f("cannot listen to port: %d", fwd->listen_port);
3944511b41d2SMark Murray 	freeaddrinfo(aitop);
3945ca3176e7SBrian Feldman 	return success;
3946511b41d2SMark Murray }
3947511b41d2SMark Murray 
3948a0ee8cc6SDag-Erling Smørgrav static int
channel_setup_fwd_listener_streamlocal(struct ssh * ssh,int type,struct Forward * fwd,struct ForwardOptions * fwd_opts)39494f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
39504f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
3951a0ee8cc6SDag-Erling Smørgrav {
3952a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
3953a0ee8cc6SDag-Erling Smørgrav 	const char *path;
3954a0ee8cc6SDag-Erling Smørgrav 	Channel *c;
3955a0ee8cc6SDag-Erling Smørgrav 	int port, sock;
3956a0ee8cc6SDag-Erling Smørgrav 	mode_t omask;
3957a0ee8cc6SDag-Erling Smørgrav 
3958a0ee8cc6SDag-Erling Smørgrav 	switch (type) {
3959a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_UNIX_LISTENER:
3960a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
3961a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
3962a0ee8cc6SDag-Erling Smørgrav 				error("Local connecting path too long: %s",
3963a0ee8cc6SDag-Erling Smørgrav 				    fwd->connect_path);
3964a0ee8cc6SDag-Erling Smørgrav 				return 0;
3965a0ee8cc6SDag-Erling Smørgrav 			}
3966a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_path;
3967a0ee8cc6SDag-Erling Smørgrav 			port = PORT_STREAMLOCAL;
3968a0ee8cc6SDag-Erling Smørgrav 		} else {
3969a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_host == NULL) {
3970a0ee8cc6SDag-Erling Smørgrav 				error("No forward host name.");
3971a0ee8cc6SDag-Erling Smørgrav 				return 0;
3972a0ee8cc6SDag-Erling Smørgrav 			}
3973a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
3974a0ee8cc6SDag-Erling Smørgrav 				error("Forward host name too long.");
3975a0ee8cc6SDag-Erling Smørgrav 				return 0;
3976a0ee8cc6SDag-Erling Smørgrav 			}
3977a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_host;
3978a0ee8cc6SDag-Erling Smørgrav 			port = fwd->connect_port;
3979a0ee8cc6SDag-Erling Smørgrav 		}
3980a0ee8cc6SDag-Erling Smørgrav 		break;
3981a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_RUNIX_LISTENER:
3982a0ee8cc6SDag-Erling Smørgrav 		path = fwd->listen_path;
3983a0ee8cc6SDag-Erling Smørgrav 		port = PORT_STREAMLOCAL;
3984a0ee8cc6SDag-Erling Smørgrav 		break;
3985a0ee8cc6SDag-Erling Smørgrav 	default:
398619261079SEd Maste 		error_f("unexpected channel type %d", type);
3987a0ee8cc6SDag-Erling Smørgrav 		return 0;
3988a0ee8cc6SDag-Erling Smørgrav 	}
3989a0ee8cc6SDag-Erling Smørgrav 
3990a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path == NULL) {
3991a0ee8cc6SDag-Erling Smørgrav 		error("No forward path name.");
3992a0ee8cc6SDag-Erling Smørgrav 		return 0;
3993a0ee8cc6SDag-Erling Smørgrav 	}
3994a0ee8cc6SDag-Erling Smørgrav 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
3995a0ee8cc6SDag-Erling Smørgrav 		error("Local listening path too long: %s", fwd->listen_path);
3996a0ee8cc6SDag-Erling Smørgrav 		return 0;
3997a0ee8cc6SDag-Erling Smørgrav 	}
3998a0ee8cc6SDag-Erling Smørgrav 
399919261079SEd Maste 	debug3_f("type %d path %s", type, fwd->listen_path);
4000a0ee8cc6SDag-Erling Smørgrav 
4001a0ee8cc6SDag-Erling Smørgrav 	/* Start a Unix domain listener. */
4002a0ee8cc6SDag-Erling Smørgrav 	omask = umask(fwd_opts->streamlocal_bind_mask);
4003a0ee8cc6SDag-Erling Smørgrav 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
4004a0ee8cc6SDag-Erling Smørgrav 	    fwd_opts->streamlocal_bind_unlink);
4005a0ee8cc6SDag-Erling Smørgrav 	umask(omask);
4006a0ee8cc6SDag-Erling Smørgrav 	if (sock < 0)
4007a0ee8cc6SDag-Erling Smørgrav 		return 0;
4008a0ee8cc6SDag-Erling Smørgrav 
4009a0ee8cc6SDag-Erling Smørgrav 	debug("Local forwarding listening on path %s.", fwd->listen_path);
4010a0ee8cc6SDag-Erling Smørgrav 
4011a0ee8cc6SDag-Erling Smørgrav 	/* Allocate a channel number for the socket. */
4012f374ba41SEd Maste 	c = channel_new(ssh, "unix-listener", type, sock, sock, -1,
4013a0ee8cc6SDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
4014a0ee8cc6SDag-Erling Smørgrav 	    0, "unix listener", 1);
4015a0ee8cc6SDag-Erling Smørgrav 	c->path = xstrdup(path);
4016a0ee8cc6SDag-Erling Smørgrav 	c->host_port = port;
4017a0ee8cc6SDag-Erling Smørgrav 	c->listening_port = PORT_STREAMLOCAL;
4018a0ee8cc6SDag-Erling Smørgrav 	c->listening_addr = xstrdup(fwd->listen_path);
4019a0ee8cc6SDag-Erling Smørgrav 	return 1;
4020a0ee8cc6SDag-Erling Smørgrav }
4021a0ee8cc6SDag-Erling Smørgrav 
4022a0ee8cc6SDag-Erling Smørgrav static int
channel_cancel_rport_listener_tcpip(struct ssh * ssh,const char * host,u_short port)40234f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh,
40244f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
402521e764dfSDag-Erling Smørgrav {
402621e764dfSDag-Erling Smørgrav 	u_int i;
402721e764dfSDag-Erling Smørgrav 	int found = 0;
402821e764dfSDag-Erling Smørgrav 
40294f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40304f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4031462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
4032462c32cbSDag-Erling Smørgrav 			continue;
4033462c32cbSDag-Erling Smørgrav 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
403419261079SEd Maste 			debug2_f("close channel %d", i);
40354f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4036462c32cbSDag-Erling Smørgrav 			found = 1;
4037462c32cbSDag-Erling Smørgrav 		}
4038462c32cbSDag-Erling Smørgrav 	}
403921e764dfSDag-Erling Smørgrav 
40404f52dfbbSDag-Erling Smørgrav 	return found;
4041462c32cbSDag-Erling Smørgrav }
4042462c32cbSDag-Erling Smørgrav 
4043a0ee8cc6SDag-Erling Smørgrav static int
channel_cancel_rport_listener_streamlocal(struct ssh * ssh,const char * path)40444f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
4045462c32cbSDag-Erling Smørgrav {
4046462c32cbSDag-Erling Smørgrav 	u_int i;
4047462c32cbSDag-Erling Smørgrav 	int found = 0;
4048a0ee8cc6SDag-Erling Smørgrav 
40494f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40504f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4051a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
4052a0ee8cc6SDag-Erling Smørgrav 			continue;
4053a0ee8cc6SDag-Erling Smørgrav 		if (c->path == NULL)
4054a0ee8cc6SDag-Erling Smørgrav 			continue;
4055a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->path, path) == 0) {
405619261079SEd Maste 			debug2_f("close channel %d", i);
40574f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4058a0ee8cc6SDag-Erling Smørgrav 			found = 1;
4059a0ee8cc6SDag-Erling Smørgrav 		}
4060a0ee8cc6SDag-Erling Smørgrav 	}
4061a0ee8cc6SDag-Erling Smørgrav 
40624f52dfbbSDag-Erling Smørgrav 	return found;
4063a0ee8cc6SDag-Erling Smørgrav }
4064a0ee8cc6SDag-Erling Smørgrav 
4065a0ee8cc6SDag-Erling Smørgrav int
channel_cancel_rport_listener(struct ssh * ssh,struct Forward * fwd)40664f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
4067a0ee8cc6SDag-Erling Smørgrav {
40684f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40694f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_streamlocal(ssh,
40704f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
40714f52dfbbSDag-Erling Smørgrav 	} else {
40724f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_tcpip(ssh,
40734f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port);
40744f52dfbbSDag-Erling Smørgrav 	}
4075a0ee8cc6SDag-Erling Smørgrav }
4076a0ee8cc6SDag-Erling Smørgrav 
4077a0ee8cc6SDag-Erling Smørgrav static int
channel_cancel_lport_listener_tcpip(struct ssh * ssh,const char * lhost,u_short lport,int cport,struct ForwardOptions * fwd_opts)40784f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh,
40794f52dfbbSDag-Erling Smørgrav     const char *lhost, u_short lport, int cport,
40804f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
4081a0ee8cc6SDag-Erling Smørgrav {
4082a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4083a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
408419261079SEd Maste 	const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts);
4085462c32cbSDag-Erling Smørgrav 
40864f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
40874f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4088462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
4089462c32cbSDag-Erling Smørgrav 			continue;
4090462c32cbSDag-Erling Smørgrav 		if (c->listening_port != lport)
4091462c32cbSDag-Erling Smørgrav 			continue;
4092462c32cbSDag-Erling Smørgrav 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
4093462c32cbSDag-Erling Smørgrav 			/* skip dynamic forwardings */
4094462c32cbSDag-Erling Smørgrav 			if (c->host_port == 0)
4095462c32cbSDag-Erling Smørgrav 				continue;
4096462c32cbSDag-Erling Smørgrav 		} else {
4097462c32cbSDag-Erling Smørgrav 			if (c->host_port != cport)
4098462c32cbSDag-Erling Smørgrav 				continue;
4099462c32cbSDag-Erling Smørgrav 		}
4100462c32cbSDag-Erling Smørgrav 		if ((c->listening_addr == NULL && addr != NULL) ||
4101462c32cbSDag-Erling Smørgrav 		    (c->listening_addr != NULL && addr == NULL))
4102462c32cbSDag-Erling Smørgrav 			continue;
4103462c32cbSDag-Erling Smørgrav 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
410419261079SEd Maste 			debug2_f("close channel %d", i);
41054f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
410621e764dfSDag-Erling Smørgrav 			found = 1;
410721e764dfSDag-Erling Smørgrav 		}
410821e764dfSDag-Erling Smørgrav 	}
410921e764dfSDag-Erling Smørgrav 
41104f52dfbbSDag-Erling Smørgrav 	return found;
411121e764dfSDag-Erling Smørgrav }
411221e764dfSDag-Erling Smørgrav 
4113a0ee8cc6SDag-Erling Smørgrav static int
channel_cancel_lport_listener_streamlocal(struct ssh * ssh,const char * path)41144f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
4115a0ee8cc6SDag-Erling Smørgrav {
4116a0ee8cc6SDag-Erling Smørgrav 	u_int i;
4117a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
4118a0ee8cc6SDag-Erling Smørgrav 
4119a0ee8cc6SDag-Erling Smørgrav 	if (path == NULL) {
412019261079SEd Maste 		error_f("no path specified.");
4121a0ee8cc6SDag-Erling Smørgrav 		return 0;
4122a0ee8cc6SDag-Erling Smørgrav 	}
4123a0ee8cc6SDag-Erling Smørgrav 
41244f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
41254f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
4126a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
4127a0ee8cc6SDag-Erling Smørgrav 			continue;
4128a0ee8cc6SDag-Erling Smørgrav 		if (c->listening_addr == NULL)
4129a0ee8cc6SDag-Erling Smørgrav 			continue;
4130a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->listening_addr, path) == 0) {
413119261079SEd Maste 			debug2_f("close channel %d", i);
41324f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
4133a0ee8cc6SDag-Erling Smørgrav 			found = 1;
4134a0ee8cc6SDag-Erling Smørgrav 		}
4135a0ee8cc6SDag-Erling Smørgrav 	}
4136a0ee8cc6SDag-Erling Smørgrav 
41374f52dfbbSDag-Erling Smørgrav 	return found;
4138a0ee8cc6SDag-Erling Smørgrav }
4139a0ee8cc6SDag-Erling Smørgrav 
4140a0ee8cc6SDag-Erling Smørgrav int
channel_cancel_lport_listener(struct ssh * ssh,struct Forward * fwd,int cport,struct ForwardOptions * fwd_opts)41414f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh,
41424f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
4143af12a3e7SDag-Erling Smørgrav {
4144a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
41454f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_streamlocal(ssh,
41464f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
41474f52dfbbSDag-Erling Smørgrav 	} else {
41484f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_tcpip(ssh,
41494f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
41504f52dfbbSDag-Erling Smørgrav 	}
41514f52dfbbSDag-Erling Smørgrav }
41524f52dfbbSDag-Erling Smørgrav 
41534f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */
41544f52dfbbSDag-Erling Smørgrav int
channel_setup_local_fwd_listener(struct ssh * ssh,struct Forward * fwd,struct ForwardOptions * fwd_opts)41554f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh,
41564f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
41574f52dfbbSDag-Erling Smørgrav {
41584f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
41594f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4160a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
4161a0ee8cc6SDag-Erling Smørgrav 	} else {
41624f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
41634f52dfbbSDag-Erling Smørgrav 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
4164a0ee8cc6SDag-Erling Smørgrav 	}
4165af12a3e7SDag-Erling Smørgrav }
4166af12a3e7SDag-Erling Smørgrav 
4167190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */
4168190cef3dSDag-Erling Smørgrav static int
remote_open_match(struct permission * allowed_open,struct Forward * fwd)4169190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd)
4170190cef3dSDag-Erling Smørgrav {
4171190cef3dSDag-Erling Smørgrav 	int ret;
4172190cef3dSDag-Erling Smørgrav 	char *lhost;
4173190cef3dSDag-Erling Smørgrav 
4174190cef3dSDag-Erling Smørgrav 	/* XXX add ACLs for streamlocal */
4175190cef3dSDag-Erling Smørgrav 	if (fwd->listen_path != NULL)
4176190cef3dSDag-Erling Smørgrav 		return 1;
4177190cef3dSDag-Erling Smørgrav 
4178190cef3dSDag-Erling Smørgrav 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
4179190cef3dSDag-Erling Smørgrav 		return 0;
4180190cef3dSDag-Erling Smørgrav 
4181190cef3dSDag-Erling Smørgrav 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
4182190cef3dSDag-Erling Smørgrav 	    allowed_open->listen_port != fwd->listen_port)
4183190cef3dSDag-Erling Smørgrav 		return 0;
4184190cef3dSDag-Erling Smørgrav 
4185190cef3dSDag-Erling Smørgrav 	/* Match hostnames case-insensitively */
4186190cef3dSDag-Erling Smørgrav 	lhost = xstrdup(fwd->listen_host);
4187190cef3dSDag-Erling Smørgrav 	lowercase(lhost);
4188190cef3dSDag-Erling Smørgrav 	ret = match_pattern(lhost, allowed_open->listen_host);
4189190cef3dSDag-Erling Smørgrav 	free(lhost);
4190190cef3dSDag-Erling Smørgrav 
4191190cef3dSDag-Erling Smørgrav 	return ret;
4192190cef3dSDag-Erling Smørgrav }
4193190cef3dSDag-Erling Smørgrav 
4194190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */
4195190cef3dSDag-Erling Smørgrav static int
check_rfwd_permission(struct ssh * ssh,struct Forward * fwd)4196190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
4197190cef3dSDag-Erling Smørgrav {
4198190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4199190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->remote_perms;
4200190cef3dSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4201190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4202190cef3dSDag-Erling Smørgrav 
4203190cef3dSDag-Erling Smørgrav 	/* XXX apply GatewayPorts override before checking? */
4204190cef3dSDag-Erling Smørgrav 
4205190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4206190cef3dSDag-Erling Smørgrav 	if (!permit) {
4207190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4208190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4209190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4210190cef3dSDag-Erling Smørgrav 				permit = 1;
4211190cef3dSDag-Erling Smørgrav 				break;
4212190cef3dSDag-Erling Smørgrav 			}
4213190cef3dSDag-Erling Smørgrav 		}
4214190cef3dSDag-Erling Smørgrav 	}
4215190cef3dSDag-Erling Smørgrav 
4216190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4217190cef3dSDag-Erling Smørgrav 		permit_adm = 0;
4218190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4219190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4220190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4221190cef3dSDag-Erling Smørgrav 				permit_adm = 1;
4222190cef3dSDag-Erling Smørgrav 				break;
4223190cef3dSDag-Erling Smørgrav 			}
4224190cef3dSDag-Erling Smørgrav 		}
4225190cef3dSDag-Erling Smørgrav 	}
4226190cef3dSDag-Erling Smørgrav 
4227190cef3dSDag-Erling Smørgrav 	return permit && permit_adm;
4228190cef3dSDag-Erling Smørgrav }
4229190cef3dSDag-Erling Smørgrav 
4230af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */
4231af12a3e7SDag-Erling Smørgrav int
channel_setup_remote_fwd_listener(struct ssh * ssh,struct Forward * fwd,int * allocated_listen_port,struct ForwardOptions * fwd_opts)42324f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
4233a0ee8cc6SDag-Erling Smørgrav     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
4234af12a3e7SDag-Erling Smørgrav {
4235190cef3dSDag-Erling Smørgrav 	if (!check_rfwd_permission(ssh, fwd)) {
423619261079SEd Maste 		ssh_packet_send_debug(ssh, "port forwarding refused");
423719261079SEd Maste 		if (fwd->listen_path != NULL)
423819261079SEd Maste 			/* XXX always allowed, see remote_open_match() */
423919261079SEd Maste 			logit("Received request from %.100s port %d to "
424019261079SEd Maste 			    "remote forward to path \"%.100s\", "
424119261079SEd Maste 			    "but the request was denied.",
424219261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
424319261079SEd Maste 			    fwd->listen_path);
424419261079SEd Maste 		else if(fwd->listen_host != NULL)
424519261079SEd Maste 			logit("Received request from %.100s port %d to "
424619261079SEd Maste 			    "remote forward to host %.100s port %d, "
424719261079SEd Maste 			    "but the request was denied.",
424819261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
424919261079SEd Maste 			    fwd->listen_host, fwd->listen_port );
425019261079SEd Maste 		else
425119261079SEd Maste 			logit("Received request from %.100s port %d to remote "
425219261079SEd Maste 			    "forward, but the request was denied.",
425319261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
4254190cef3dSDag-Erling Smørgrav 		return 0;
4255190cef3dSDag-Erling Smørgrav 	}
4256a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
42574f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4258a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
4259a0ee8cc6SDag-Erling Smørgrav 	} else {
42604f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
4261a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
4262a0ee8cc6SDag-Erling Smørgrav 		    fwd_opts);
4263a0ee8cc6SDag-Erling Smørgrav 	}
4264af12a3e7SDag-Erling Smørgrav }
4265af12a3e7SDag-Erling Smørgrav 
4266511b41d2SMark Murray /*
4267462c32cbSDag-Erling Smørgrav  * Translate the requested rfwd listen host to something usable for
4268462c32cbSDag-Erling Smørgrav  * this server.
4269462c32cbSDag-Erling Smørgrav  */
4270462c32cbSDag-Erling Smørgrav static const char *
channel_rfwd_bind_host(const char * listen_host)4271462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host)
4272462c32cbSDag-Erling Smørgrav {
4273462c32cbSDag-Erling Smørgrav 	if (listen_host == NULL) {
4274462c32cbSDag-Erling Smørgrav 		return "localhost";
4275462c32cbSDag-Erling Smørgrav 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
4276462c32cbSDag-Erling Smørgrav 		return "";
4277462c32cbSDag-Erling Smørgrav 	} else
4278462c32cbSDag-Erling Smørgrav 		return listen_host;
4279462c32cbSDag-Erling Smørgrav }
4280462c32cbSDag-Erling Smørgrav 
4281462c32cbSDag-Erling Smørgrav /*
4282511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
4283511b41d2SMark Murray  * the secure channel to host:port from local side.
4284462c32cbSDag-Erling Smørgrav  * Returns handle (index) for updating the dynamic listen port with
4285190cef3dSDag-Erling Smørgrav  * channel_update_permission().
4286511b41d2SMark Murray  */
4287333ee039SDag-Erling Smørgrav int
channel_request_remote_forwarding(struct ssh * ssh,struct Forward * fwd)42884f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
4289511b41d2SMark Murray {
42904f52dfbbSDag-Erling Smørgrav 	int r, success = 0, idx = -1;
4291f374ba41SEd Maste 	const char *host_to_connect, *listen_host, *listen_path;
42924f52dfbbSDag-Erling Smørgrav 	int port_to_connect, listen_port;
4293ca3176e7SBrian Feldman 
4294511b41d2SMark Murray 	/* Send the forward request to the remote side. */
4295a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
42964f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
42974f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
42984f52dfbbSDag-Erling Smørgrav 		    "streamlocal-forward@openssh.com")) != 0 ||
42994f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43004f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
43014f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
43024f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
430319261079SEd Maste 			fatal_fr(r, "request streamlocal");
4304a0ee8cc6SDag-Erling Smørgrav 	} else {
43054f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
43064f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
43074f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
43084f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
43094f52dfbbSDag-Erling Smørgrav 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
43104f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
43114f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
43124f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
431319261079SEd Maste 			fatal_fr(r, "request tcpip-forward");
4314a0ee8cc6SDag-Erling Smørgrav 	}
4315ca3176e7SBrian Feldman 	/* Assume that server accepts the request */
4316ca3176e7SBrian Feldman 	success = 1;
4317ca3176e7SBrian Feldman 	if (success) {
4318e2f6069cSDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
43194f52dfbbSDag-Erling Smørgrav 		host_to_connect = listen_host = listen_path = NULL;
43204f52dfbbSDag-Erling Smørgrav 		port_to_connect = listen_port = 0;
4321a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
4322f374ba41SEd Maste 			host_to_connect = fwd->connect_path;
43234f52dfbbSDag-Erling Smørgrav 			port_to_connect = PORT_STREAMLOCAL;
4324a0ee8cc6SDag-Erling Smørgrav 		} else {
4325f374ba41SEd Maste 			host_to_connect = fwd->connect_host;
43264f52dfbbSDag-Erling Smørgrav 			port_to_connect = fwd->connect_port;
4327a0ee8cc6SDag-Erling Smørgrav 		}
4328a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_path != NULL) {
4329f374ba41SEd Maste 			listen_path = fwd->listen_path;
43304f52dfbbSDag-Erling Smørgrav 			listen_port = PORT_STREAMLOCAL;
4331a0ee8cc6SDag-Erling Smørgrav 		} else {
4332f374ba41SEd Maste 			listen_host = fwd->listen_host;
43334f52dfbbSDag-Erling Smørgrav 			listen_port = fwd->listen_port;
4334a0ee8cc6SDag-Erling Smørgrav 		}
4335190cef3dSDag-Erling Smørgrav 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
43364f52dfbbSDag-Erling Smørgrav 		    host_to_connect, port_to_connect,
43374f52dfbbSDag-Erling Smørgrav 		    listen_host, listen_path, listen_port, NULL);
4338511b41d2SMark Murray 	}
43394f52dfbbSDag-Erling Smørgrav 	return idx;
4340a04a10f8SKris Kennaway }
4341511b41d2SMark Murray 
4342a0ee8cc6SDag-Erling Smørgrav static int
open_match(struct permission * allowed_open,const char * requestedhost,int requestedport)4343190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost,
4344a0ee8cc6SDag-Erling Smørgrav     int requestedport)
4345a0ee8cc6SDag-Erling Smørgrav {
4346a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4347a0ee8cc6SDag-Erling Smørgrav 		return 0;
4348a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
4349a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->port_to_connect != requestedport)
4350a0ee8cc6SDag-Erling Smørgrav 		return 0;
4351076ad2f8SDag-Erling Smørgrav 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
4352076ad2f8SDag-Erling Smørgrav 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
4353a0ee8cc6SDag-Erling Smørgrav 		return 0;
4354a0ee8cc6SDag-Erling Smørgrav 	return 1;
4355a0ee8cc6SDag-Erling Smørgrav }
4356a0ee8cc6SDag-Erling Smørgrav 
4357a0ee8cc6SDag-Erling Smørgrav /*
4358a0ee8cc6SDag-Erling Smørgrav  * Note that in the listen host/port case
4359a0ee8cc6SDag-Erling Smørgrav  * we don't support FWD_PERMIT_ANY_PORT and
4360a0ee8cc6SDag-Erling Smørgrav  * need to translate between the configured-host (listen_host)
4361a0ee8cc6SDag-Erling Smørgrav  * and what we've sent to the remote server (channel_rfwd_bind_host)
4362a0ee8cc6SDag-Erling Smørgrav  */
4363a0ee8cc6SDag-Erling Smørgrav static int
open_listen_match_tcpip(struct permission * allowed_open,const char * requestedhost,u_short requestedport,int translate)4364190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open,
4365a0ee8cc6SDag-Erling Smørgrav     const char *requestedhost, u_short requestedport, int translate)
4366a0ee8cc6SDag-Erling Smørgrav {
4367a0ee8cc6SDag-Erling Smørgrav 	const char *allowed_host;
4368a0ee8cc6SDag-Erling Smørgrav 
4369a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4370a0ee8cc6SDag-Erling Smørgrav 		return 0;
4371a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != requestedport)
4372a0ee8cc6SDag-Erling Smørgrav 		return 0;
4373a0ee8cc6SDag-Erling Smørgrav 	if (!translate && allowed_open->listen_host == NULL &&
4374a0ee8cc6SDag-Erling Smørgrav 	    requestedhost == NULL)
4375a0ee8cc6SDag-Erling Smørgrav 		return 1;
4376a0ee8cc6SDag-Erling Smørgrav 	allowed_host = translate ?
4377a0ee8cc6SDag-Erling Smørgrav 	    channel_rfwd_bind_host(allowed_open->listen_host) :
4378a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->listen_host;
4379190cef3dSDag-Erling Smørgrav 	if (allowed_host == NULL || requestedhost == NULL ||
4380a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_host, requestedhost) != 0)
4381a0ee8cc6SDag-Erling Smørgrav 		return 0;
4382a0ee8cc6SDag-Erling Smørgrav 	return 1;
4383a0ee8cc6SDag-Erling Smørgrav }
4384a0ee8cc6SDag-Erling Smørgrav 
4385a0ee8cc6SDag-Erling Smørgrav static int
open_listen_match_streamlocal(struct permission * allowed_open,const char * requestedpath)4386190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open,
4387a0ee8cc6SDag-Erling Smørgrav     const char *requestedpath)
4388a0ee8cc6SDag-Erling Smørgrav {
4389a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4390a0ee8cc6SDag-Erling Smørgrav 		return 0;
4391a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
4392a0ee8cc6SDag-Erling Smørgrav 		return 0;
4393a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_path == NULL ||
4394a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
4395a0ee8cc6SDag-Erling Smørgrav 		return 0;
4396a0ee8cc6SDag-Erling Smørgrav 	return 1;
4397a0ee8cc6SDag-Erling Smørgrav }
4398a0ee8cc6SDag-Erling Smørgrav 
4399511b41d2SMark Murray /*
440021e764dfSDag-Erling Smørgrav  * Request cancellation of remote forwarding of connection host:port from
440121e764dfSDag-Erling Smørgrav  * local side.
440221e764dfSDag-Erling Smørgrav  */
4403a0ee8cc6SDag-Erling Smørgrav static int
channel_request_rforward_cancel_tcpip(struct ssh * ssh,const char * host,u_short port)44044f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh,
44054f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
440621e764dfSDag-Erling Smørgrav {
44074f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4408190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
44094f52dfbbSDag-Erling Smørgrav 	int r;
44104f52dfbbSDag-Erling Smørgrav 	u_int i;
441119261079SEd Maste 	struct permission *perm = NULL;
441221e764dfSDag-Erling Smørgrav 
4413190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4414190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4415190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm, host, port, 0))
441621e764dfSDag-Erling Smørgrav 			break;
4417190cef3dSDag-Erling Smørgrav 		perm = NULL;
441821e764dfSDag-Erling Smørgrav 	}
4419190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
442019261079SEd Maste 		debug_f("requested forward not found");
4421462c32cbSDag-Erling Smørgrav 		return -1;
442221e764dfSDag-Erling Smørgrav 	}
44234f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44244f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
44254f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44264f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
44274f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
44284f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
442919261079SEd Maste 		fatal_fr(r, "send cancel");
443021e764dfSDag-Erling Smørgrav 
4431190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4432462c32cbSDag-Erling Smørgrav 
4433462c32cbSDag-Erling Smørgrav 	return 0;
443421e764dfSDag-Erling Smørgrav }
443521e764dfSDag-Erling Smørgrav 
443621e764dfSDag-Erling Smørgrav /*
4437a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of Unix domain socket
4438a0ee8cc6SDag-Erling Smørgrav  * path from local side.
4439a0ee8cc6SDag-Erling Smørgrav  */
4440a0ee8cc6SDag-Erling Smørgrav static int
channel_request_rforward_cancel_streamlocal(struct ssh * ssh,const char * path)44414f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
4442a0ee8cc6SDag-Erling Smørgrav {
44434f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4444190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
44454f52dfbbSDag-Erling Smørgrav 	int r;
44464f52dfbbSDag-Erling Smørgrav 	u_int i;
444719261079SEd Maste 	struct permission *perm = NULL;
4448a0ee8cc6SDag-Erling Smørgrav 
4449190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4450190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4451190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path))
4452a0ee8cc6SDag-Erling Smørgrav 			break;
4453190cef3dSDag-Erling Smørgrav 		perm = NULL;
4454a0ee8cc6SDag-Erling Smørgrav 	}
4455190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
445619261079SEd Maste 		debug_f("requested forward not found");
4457a0ee8cc6SDag-Erling Smørgrav 		return -1;
4458a0ee8cc6SDag-Erling Smørgrav 	}
44594f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
44604f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh,
44614f52dfbbSDag-Erling Smørgrav 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
44624f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
44634f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
44644f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
446519261079SEd Maste 		fatal_fr(r, "send cancel");
4466a0ee8cc6SDag-Erling Smørgrav 
4467190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4468a0ee8cc6SDag-Erling Smørgrav 
4469a0ee8cc6SDag-Erling Smørgrav 	return 0;
4470a0ee8cc6SDag-Erling Smørgrav }
4471a0ee8cc6SDag-Erling Smørgrav 
4472a0ee8cc6SDag-Erling Smørgrav /*
4473a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of a connection from local side.
4474a0ee8cc6SDag-Erling Smørgrav  */
4475a0ee8cc6SDag-Erling Smørgrav int
channel_request_rforward_cancel(struct ssh * ssh,struct Forward * fwd)44764f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
4477a0ee8cc6SDag-Erling Smørgrav {
4478a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
44794f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_streamlocal(ssh,
44804f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
4481a0ee8cc6SDag-Erling Smørgrav 	} else {
44824f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_tcpip(ssh,
44834f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host,
44844f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
4485a0ee8cc6SDag-Erling Smørgrav 	}
4486a0ee8cc6SDag-Erling Smørgrav }
4487a0ee8cc6SDag-Erling Smørgrav 
4488a0ee8cc6SDag-Erling Smørgrav /*
4489190cef3dSDag-Erling Smørgrav  * Permits opening to any host/port if permitted_user[] is empty.  This is
4490ca3176e7SBrian Feldman  * usually called by the server, because the user could connect to any port
4491ca3176e7SBrian Feldman  * anyway, and the server has no way to know but to trust the client anyway.
4492ca3176e7SBrian Feldman  */
4493ca3176e7SBrian Feldman void
channel_permit_all(struct ssh * ssh,int where)4494190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where)
4495ca3176e7SBrian Feldman {
4496190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
4497190cef3dSDag-Erling Smørgrav 
4498190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_user == 0)
4499190cef3dSDag-Erling Smørgrav 		pset->all_permitted = 1;
4500ca3176e7SBrian Feldman }
4501ca3176e7SBrian Feldman 
4502190cef3dSDag-Erling Smørgrav /*
4503190cef3dSDag-Erling Smørgrav  * Permit the specified host/port for forwarding.
4504190cef3dSDag-Erling Smørgrav  */
4505ca3176e7SBrian Feldman void
channel_add_permission(struct ssh * ssh,int who,int where,char * host,int port)4506190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where,
4507190cef3dSDag-Erling Smørgrav     char *host, int port)
4508ca3176e7SBrian Feldman {
4509190cef3dSDag-Erling Smørgrav 	int local = where == FORWARD_LOCAL;
4510190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
45114f52dfbbSDag-Erling Smørgrav 
4512190cef3dSDag-Erling Smørgrav 	debug("allow %s forwarding to host %s port %d",
4513190cef3dSDag-Erling Smørgrav 	    fwd_ident(who, where), host, port);
4514190cef3dSDag-Erling Smørgrav 	/*
4515190cef3dSDag-Erling Smørgrav 	 * Remote forwards set listen_host/port, local forwards set
4516190cef3dSDag-Erling Smørgrav 	 * host/port_to_connect.
4517190cef3dSDag-Erling Smørgrav 	 */
4518190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, who, where,
4519190cef3dSDag-Erling Smørgrav 	    local ? host : 0, local ? port : 0,
4520190cef3dSDag-Erling Smørgrav 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
4521190cef3dSDag-Erling Smørgrav 	pset->all_permitted = 0;
4522190cef3dSDag-Erling Smørgrav }
4523190cef3dSDag-Erling Smørgrav 
4524190cef3dSDag-Erling Smørgrav /*
4525190cef3dSDag-Erling Smørgrav  * Administratively disable forwarding.
4526190cef3dSDag-Erling Smørgrav  */
4527190cef3dSDag-Erling Smørgrav void
channel_disable_admin(struct ssh * ssh,int where)4528190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where)
4529190cef3dSDag-Erling Smørgrav {
4530190cef3dSDag-Erling Smørgrav 	channel_clear_permission(ssh, FORWARD_ADM, where);
4531190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, FORWARD_ADM, where,
4532190cef3dSDag-Erling Smørgrav 	    NULL, 0, NULL, NULL, 0, NULL);
4533190cef3dSDag-Erling Smørgrav }
4534190cef3dSDag-Erling Smørgrav 
4535190cef3dSDag-Erling Smørgrav /*
4536190cef3dSDag-Erling Smørgrav  * Clear a list of permitted opens.
4537190cef3dSDag-Erling Smørgrav  */
4538190cef3dSDag-Erling Smørgrav void
channel_clear_permission(struct ssh * ssh,int who,int where)4539190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where)
4540190cef3dSDag-Erling Smørgrav {
4541190cef3dSDag-Erling Smørgrav 	struct permission **permp;
4542190cef3dSDag-Erling Smørgrav 	u_int *npermp;
4543190cef3dSDag-Erling Smørgrav 
4544190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
4545190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
4546190cef3dSDag-Erling Smørgrav 	*npermp = 0;
4547ca3176e7SBrian Feldman }
4548ca3176e7SBrian Feldman 
4549462c32cbSDag-Erling Smørgrav /*
4550462c32cbSDag-Erling Smørgrav  * Update the listen port for a dynamic remote forward, after
4551462c32cbSDag-Erling Smørgrav  * the actual 'newport' has been allocated. If 'newport' < 0 is
4552462c32cbSDag-Erling Smørgrav  * passed then they entry will be invalidated.
4553462c32cbSDag-Erling Smørgrav  */
4554462c32cbSDag-Erling Smørgrav void
channel_update_permission(struct ssh * ssh,int idx,int newport)4555190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport)
4556462c32cbSDag-Erling Smørgrav {
4557190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &ssh->chanctxt->local_perms;
45584f52dfbbSDag-Erling Smørgrav 
4559190cef3dSDag-Erling Smørgrav 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
456019261079SEd Maste 		debug_f("index out of range: %d num_permitted_user %d",
456119261079SEd Maste 		    idx, pset->num_permitted_user);
4562462c32cbSDag-Erling Smørgrav 		return;
4563462c32cbSDag-Erling Smørgrav 	}
4564462c32cbSDag-Erling Smørgrav 	debug("%s allowed port %d for forwarding to host %s port %d",
4565462c32cbSDag-Erling Smørgrav 	    newport > 0 ? "Updating" : "Removing",
4566462c32cbSDag-Erling Smørgrav 	    newport,
4567190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].host_to_connect,
4568190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].port_to_connect);
45694f52dfbbSDag-Erling Smørgrav 	if (newport <= 0)
4570190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(&pset->permitted_user[idx]);
45714f52dfbbSDag-Erling Smørgrav 	else {
4572190cef3dSDag-Erling Smørgrav 		pset->permitted_user[idx].listen_port =
457319261079SEd Maste 		    (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4574462c32cbSDag-Erling Smørgrav 	}
4575462c32cbSDag-Erling Smørgrav }
4576462c32cbSDag-Erling Smørgrav 
4577d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */
4578d4af9e69SDag-Erling Smørgrav static int
connect_next(struct channel_connect * cctx)4579d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx)
4580d4af9e69SDag-Erling Smørgrav {
4581d4af9e69SDag-Erling Smørgrav 	int sock, saved_errno;
4582a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un *sunaddr;
45834f52dfbbSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
45844f52dfbbSDag-Erling Smørgrav 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4585d4af9e69SDag-Erling Smørgrav 
4586d4af9e69SDag-Erling Smørgrav 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
4587a0ee8cc6SDag-Erling Smørgrav 		switch (cctx->ai->ai_family) {
4588a0ee8cc6SDag-Erling Smørgrav 		case AF_UNIX:
4589a0ee8cc6SDag-Erling Smørgrav 			/* unix:pathname instead of host:port */
4590a0ee8cc6SDag-Erling Smørgrav 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
4591a0ee8cc6SDag-Erling Smørgrav 			strlcpy(ntop, "unix", sizeof(ntop));
4592a0ee8cc6SDag-Erling Smørgrav 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
4593a0ee8cc6SDag-Erling Smørgrav 			break;
4594a0ee8cc6SDag-Erling Smørgrav 		case AF_INET:
4595a0ee8cc6SDag-Erling Smørgrav 		case AF_INET6:
4596d4af9e69SDag-Erling Smørgrav 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4597d4af9e69SDag-Erling Smørgrav 			    ntop, sizeof(ntop), strport, sizeof(strport),
4598d4af9e69SDag-Erling Smørgrav 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
459938a52bd3SEd Maste 				error_f("getnameinfo failed");
4600511b41d2SMark Murray 				continue;
4601511b41d2SMark Murray 			}
4602a0ee8cc6SDag-Erling Smørgrav 			break;
4603a0ee8cc6SDag-Erling Smørgrav 		default:
4604a0ee8cc6SDag-Erling Smørgrav 			continue;
4605a0ee8cc6SDag-Erling Smørgrav 		}
460638a52bd3SEd Maste 		debug_f("start for host %.100s ([%.100s]:%s)",
460738a52bd3SEd Maste 		    cctx->host, ntop, strport);
4608d4af9e69SDag-Erling Smørgrav 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4609d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_protocol)) == -1) {
4610d4af9e69SDag-Erling Smørgrav 			if (cctx->ai->ai_next == NULL)
4611511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
4612e73e9afaSDag-Erling Smørgrav 			else
4613e73e9afaSDag-Erling Smørgrav 				verbose("socket: %.100s", strerror(errno));
4614511b41d2SMark Murray 			continue;
4615511b41d2SMark Murray 		}
461621e764dfSDag-Erling Smørgrav 		if (set_nonblock(sock) == -1)
461719261079SEd Maste 			fatal_f("set_nonblock(%d)", sock);
4618d4af9e69SDag-Erling Smørgrav 		if (connect(sock, cctx->ai->ai_addr,
4619d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
462038a52bd3SEd Maste 			debug_f("host %.100s ([%.100s]:%s): %.100s",
462138a52bd3SEd Maste 			    cctx->host, ntop, strport, strerror(errno));
4622d4af9e69SDag-Erling Smørgrav 			saved_errno = errno;
4623511b41d2SMark Murray 			close(sock);
4624d4af9e69SDag-Erling Smørgrav 			errno = saved_errno;
4625511b41d2SMark Murray 			continue;	/* fail -- try next */
4626511b41d2SMark Murray 		}
4627a0ee8cc6SDag-Erling Smørgrav 		if (cctx->ai->ai_family != AF_UNIX)
4628a0ee8cc6SDag-Erling Smørgrav 			set_nodelay(sock);
462938a52bd3SEd Maste 		debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d",
463038a52bd3SEd Maste 		    cctx->host, ntop, strport, sock);
4631d4af9e69SDag-Erling Smørgrav 		cctx->ai = cctx->ai->ai_next;
4632a04a10f8SKris Kennaway 		return sock;
4633a04a10f8SKris Kennaway 	}
4634ca3176e7SBrian Feldman 	return -1;
4635ca3176e7SBrian Feldman }
4636ca3176e7SBrian Feldman 
4637d4af9e69SDag-Erling Smørgrav static void
channel_connect_ctx_free(struct channel_connect * cctx)4638d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx)
4639d4af9e69SDag-Erling Smørgrav {
4640e4a9863fSDag-Erling Smørgrav 	free(cctx->host);
4641a0ee8cc6SDag-Erling Smørgrav 	if (cctx->aitop) {
4642a0ee8cc6SDag-Erling Smørgrav 		if (cctx->aitop->ai_family == AF_UNIX)
4643a0ee8cc6SDag-Erling Smørgrav 			free(cctx->aitop);
4644a0ee8cc6SDag-Erling Smørgrav 		else
4645d4af9e69SDag-Erling Smørgrav 			freeaddrinfo(cctx->aitop);
4646a0ee8cc6SDag-Erling Smørgrav 	}
4647b83788ffSDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
4648d4af9e69SDag-Erling Smørgrav }
4649d4af9e69SDag-Erling Smørgrav 
4650d93a896eSDag-Erling Smørgrav /*
46514f52dfbbSDag-Erling Smørgrav  * Return connecting socket to remote host:port or local socket path,
4652d93a896eSDag-Erling Smørgrav  * passing back the failure reason if appropriate.
4653d93a896eSDag-Erling Smørgrav  */
46544f52dfbbSDag-Erling Smørgrav static int
connect_to_helper(struct ssh * ssh,const char * name,int port,int socktype,char * ctype,char * rname,struct channel_connect * cctx,int * reason,const char ** errmsg)46554f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
46564f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, struct channel_connect *cctx,
4657d93a896eSDag-Erling Smørgrav     int *reason, const char **errmsg)
4658d4af9e69SDag-Erling Smørgrav {
4659d4af9e69SDag-Erling Smørgrav 	struct addrinfo hints;
4660d4af9e69SDag-Erling Smørgrav 	int gaierr;
4661d4af9e69SDag-Erling Smørgrav 	int sock = -1;
4662d4af9e69SDag-Erling Smørgrav 	char strport[NI_MAXSERV];
4663a0ee8cc6SDag-Erling Smørgrav 
4664a0ee8cc6SDag-Erling Smørgrav 	if (port == PORT_STREAMLOCAL) {
4665a0ee8cc6SDag-Erling Smørgrav 		struct sockaddr_un *sunaddr;
4666a0ee8cc6SDag-Erling Smørgrav 		struct addrinfo *ai;
4667a0ee8cc6SDag-Erling Smørgrav 
4668a0ee8cc6SDag-Erling Smørgrav 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
4669a0ee8cc6SDag-Erling Smørgrav 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
46704f52dfbbSDag-Erling Smørgrav 			return -1;
4671a0ee8cc6SDag-Erling Smørgrav 		}
4672a0ee8cc6SDag-Erling Smørgrav 
4673a0ee8cc6SDag-Erling Smørgrav 		/*
4674a0ee8cc6SDag-Erling Smørgrav 		 * Fake up a struct addrinfo for AF_UNIX connections.
4675a0ee8cc6SDag-Erling Smørgrav 		 * channel_connect_ctx_free() must check ai_family
4676a0ee8cc6SDag-Erling Smørgrav 		 * and use free() not freeaddirinfo() for AF_UNIX.
4677a0ee8cc6SDag-Erling Smørgrav 		 */
4678a0ee8cc6SDag-Erling Smørgrav 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
4679a0ee8cc6SDag-Erling Smørgrav 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
4680a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addr = (struct sockaddr *)(ai + 1);
4681a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addrlen = sizeof(*sunaddr);
4682a0ee8cc6SDag-Erling Smørgrav 		ai->ai_family = AF_UNIX;
46834f52dfbbSDag-Erling Smørgrav 		ai->ai_socktype = socktype;
4684a0ee8cc6SDag-Erling Smørgrav 		ai->ai_protocol = PF_UNSPEC;
4685a0ee8cc6SDag-Erling Smørgrav 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
4686a0ee8cc6SDag-Erling Smørgrav 		sunaddr->sun_family = AF_UNIX;
4687a0ee8cc6SDag-Erling Smørgrav 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
46884f52dfbbSDag-Erling Smørgrav 		cctx->aitop = ai;
4689a0ee8cc6SDag-Erling Smørgrav 	} else {
4690d4af9e69SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
46914f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
46924f52dfbbSDag-Erling Smørgrav 		hints.ai_socktype = socktype;
4693d4af9e69SDag-Erling Smørgrav 		snprintf(strport, sizeof strport, "%d", port);
46944f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
4695d93a896eSDag-Erling Smørgrav 		    != 0) {
4696d93a896eSDag-Erling Smørgrav 			if (errmsg != NULL)
4697d93a896eSDag-Erling Smørgrav 				*errmsg = ssh_gai_strerror(gaierr);
4698d93a896eSDag-Erling Smørgrav 			if (reason != NULL)
4699d93a896eSDag-Erling Smørgrav 				*reason = SSH2_OPEN_CONNECT_FAILED;
4700a0ee8cc6SDag-Erling Smørgrav 			error("connect_to %.100s: unknown host (%s)", name,
4701d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
47024f52dfbbSDag-Erling Smørgrav 			return -1;
4703d4af9e69SDag-Erling Smørgrav 		}
4704a0ee8cc6SDag-Erling Smørgrav 	}
4705d4af9e69SDag-Erling Smørgrav 
47064f52dfbbSDag-Erling Smørgrav 	cctx->host = xstrdup(name);
47074f52dfbbSDag-Erling Smørgrav 	cctx->port = port;
47084f52dfbbSDag-Erling Smørgrav 	cctx->ai = cctx->aitop;
4709d4af9e69SDag-Erling Smørgrav 
47104f52dfbbSDag-Erling Smørgrav 	if ((sock = connect_next(cctx)) == -1) {
4711d4af9e69SDag-Erling Smørgrav 		error("connect to %.100s port %d failed: %s",
4712a0ee8cc6SDag-Erling Smørgrav 		    name, port, strerror(errno));
47134f52dfbbSDag-Erling Smørgrav 		return -1;
4714d4af9e69SDag-Erling Smørgrav 	}
47154f52dfbbSDag-Erling Smørgrav 
47164f52dfbbSDag-Erling Smørgrav 	return sock;
4717d4af9e69SDag-Erling Smørgrav }
4718d4af9e69SDag-Erling Smørgrav 
4719d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */
4720d93a896eSDag-Erling Smørgrav static Channel *
connect_to(struct ssh * ssh,const char * host,int port,char * ctype,char * rname)47214f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port,
47224f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4723d93a896eSDag-Erling Smørgrav {
47244f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
47254f52dfbbSDag-Erling Smørgrav 	Channel *c;
47264f52dfbbSDag-Erling Smørgrav 	int sock;
47274f52dfbbSDag-Erling Smørgrav 
47284f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
47294f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
47304f52dfbbSDag-Erling Smørgrav 	    &cctx, NULL, NULL);
47314f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
47324f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
47334f52dfbbSDag-Erling Smørgrav 		return NULL;
47344f52dfbbSDag-Erling Smørgrav 	}
47354f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
47364f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
47374f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
47384f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
47394f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
47404f52dfbbSDag-Erling Smørgrav 
47414f52dfbbSDag-Erling Smørgrav 	return c;
4742d93a896eSDag-Erling Smørgrav }
4743d93a896eSDag-Erling Smørgrav 
4744ca86bcf2SDag-Erling Smørgrav /*
4745ca86bcf2SDag-Erling Smørgrav  * returns either the newly connected channel or the downstream channel
4746ca86bcf2SDag-Erling Smørgrav  * that needs to deal with this connection.
4747ca86bcf2SDag-Erling Smørgrav  */
4748d4af9e69SDag-Erling Smørgrav Channel *
channel_connect_by_listen_address(struct ssh * ssh,const char * listen_host,u_short listen_port,char * ctype,char * rname)47494f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
4750a0ee8cc6SDag-Erling Smørgrav     u_short listen_port, char *ctype, char *rname)
4751d4af9e69SDag-Erling Smørgrav {
47524f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4753190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
47544f52dfbbSDag-Erling Smørgrav 	u_int i;
4755190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4756d4af9e69SDag-Erling Smørgrav 
4757190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4758190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4759190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm,
4760190cef3dSDag-Erling Smørgrav 		    listen_host, listen_port, 1)) {
4761190cef3dSDag-Erling Smørgrav 			if (perm->downstream)
4762190cef3dSDag-Erling Smørgrav 				return perm->downstream;
4763190cef3dSDag-Erling Smørgrav 			if (perm->port_to_connect == 0)
47644f52dfbbSDag-Erling Smørgrav 				return rdynamic_connect_prepare(ssh,
47654f52dfbbSDag-Erling Smørgrav 				    ctype, rname);
47664f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4767190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
47684f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4769d4af9e69SDag-Erling Smørgrav 		}
4770d4af9e69SDag-Erling Smørgrav 	}
4771d4af9e69SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4772d4af9e69SDag-Erling Smørgrav 	    listen_port);
4773d4af9e69SDag-Erling Smørgrav 	return NULL;
4774d4af9e69SDag-Erling Smørgrav }
4775d4af9e69SDag-Erling Smørgrav 
4776a0ee8cc6SDag-Erling Smørgrav Channel *
channel_connect_by_listen_path(struct ssh * ssh,const char * path,char * ctype,char * rname)47774f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path,
47784f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4779a0ee8cc6SDag-Erling Smørgrav {
47804f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4781190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
47824f52dfbbSDag-Erling Smørgrav 	u_int i;
4783190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4784a0ee8cc6SDag-Erling Smørgrav 
4785190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4786190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4787190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path)) {
47884f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4789190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
47904f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4791a0ee8cc6SDag-Erling Smørgrav 		}
4792a0ee8cc6SDag-Erling Smørgrav 	}
4793a0ee8cc6SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown path %.100s",
4794a0ee8cc6SDag-Erling Smørgrav 	    path);
4795a0ee8cc6SDag-Erling Smørgrav 	return NULL;
4796a0ee8cc6SDag-Erling Smørgrav }
4797a0ee8cc6SDag-Erling Smørgrav 
4798ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */
4799d4af9e69SDag-Erling Smørgrav Channel *
channel_connect_to_port(struct ssh * ssh,const char * host,u_short port,char * ctype,char * rname,int * reason,const char ** errmsg)48004f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
48014f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, int *reason, const char **errmsg)
4802ca3176e7SBrian Feldman {
48034f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4804190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48054f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
48064f52dfbbSDag-Erling Smørgrav 	Channel *c;
48074f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
48084f52dfbbSDag-Erling Smørgrav 	int sock;
4809190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4810ca3176e7SBrian Feldman 
4811190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4812ca3176e7SBrian Feldman 	if (!permit) {
4813190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4814190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4815190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4816ca3176e7SBrian Feldman 				permit = 1;
4817a0ee8cc6SDag-Erling Smørgrav 				break;
4818a0ee8cc6SDag-Erling Smørgrav 			}
4819ca3176e7SBrian Feldman 		}
48204f52dfbbSDag-Erling Smørgrav 	}
4821333ee039SDag-Erling Smørgrav 
4822190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4823333ee039SDag-Erling Smørgrav 		permit_adm = 0;
4824190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4825190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4826190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4827333ee039SDag-Erling Smørgrav 				permit_adm = 1;
4828a0ee8cc6SDag-Erling Smørgrav 				break;
4829a0ee8cc6SDag-Erling Smørgrav 			}
4830333ee039SDag-Erling Smørgrav 		}
48314f52dfbbSDag-Erling Smørgrav 	}
4832333ee039SDag-Erling Smørgrav 
4833333ee039SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
483419261079SEd Maste 		logit("Received request from %.100s port %d to connect to "
483519261079SEd Maste 		    "host %.100s port %d, but the request was denied.",
483619261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port);
4837d93a896eSDag-Erling Smørgrav 		if (reason != NULL)
4838d93a896eSDag-Erling Smørgrav 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4839d4af9e69SDag-Erling Smørgrav 		return NULL;
4840ca3176e7SBrian Feldman 	}
48414f52dfbbSDag-Erling Smørgrav 
48424f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
48434f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
48444f52dfbbSDag-Erling Smørgrav 	    &cctx, reason, errmsg);
48454f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
48464f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
48474f52dfbbSDag-Erling Smørgrav 		return NULL;
48484f52dfbbSDag-Erling Smørgrav 	}
48494f52dfbbSDag-Erling Smørgrav 
48504f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
48514f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
48524f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
48534f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
48544f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
48554f52dfbbSDag-Erling Smørgrav 
48564f52dfbbSDag-Erling Smørgrav 	return c;
4857ca3176e7SBrian Feldman }
4858ca3176e7SBrian Feldman 
4859a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */
4860a0ee8cc6SDag-Erling Smørgrav Channel *
channel_connect_to_path(struct ssh * ssh,const char * path,char * ctype,char * rname)48614f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path,
48624f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4863a0ee8cc6SDag-Erling Smørgrav {
48644f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4865190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
48664f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4867190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4868a0ee8cc6SDag-Erling Smørgrav 
4869190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4870a0ee8cc6SDag-Erling Smørgrav 	if (!permit) {
4871190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4872190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4873190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4874a0ee8cc6SDag-Erling Smørgrav 				permit = 1;
4875a0ee8cc6SDag-Erling Smørgrav 				break;
4876a0ee8cc6SDag-Erling Smørgrav 			}
4877a0ee8cc6SDag-Erling Smørgrav 		}
48784f52dfbbSDag-Erling Smørgrav 	}
4879a0ee8cc6SDag-Erling Smørgrav 
4880190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4881a0ee8cc6SDag-Erling Smørgrav 		permit_adm = 0;
4882190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4883190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4884190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4885a0ee8cc6SDag-Erling Smørgrav 				permit_adm = 1;
4886a0ee8cc6SDag-Erling Smørgrav 				break;
4887a0ee8cc6SDag-Erling Smørgrav 			}
4888a0ee8cc6SDag-Erling Smørgrav 		}
48894f52dfbbSDag-Erling Smørgrav 	}
4890a0ee8cc6SDag-Erling Smørgrav 
4891a0ee8cc6SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4892a0ee8cc6SDag-Erling Smørgrav 		logit("Received request to connect to path %.100s, "
4893a0ee8cc6SDag-Erling Smørgrav 		    "but the request was denied.", path);
4894a0ee8cc6SDag-Erling Smørgrav 		return NULL;
4895a0ee8cc6SDag-Erling Smørgrav 	}
48964f52dfbbSDag-Erling Smørgrav 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
4897a0ee8cc6SDag-Erling Smørgrav }
4898a0ee8cc6SDag-Erling Smørgrav 
489921e764dfSDag-Erling Smørgrav void
channel_send_window_changes(struct ssh * ssh)49004f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh)
490121e764dfSDag-Erling Smørgrav {
49024f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
490321e764dfSDag-Erling Smørgrav 	struct winsize ws;
49044f52dfbbSDag-Erling Smørgrav 	int r;
49054f52dfbbSDag-Erling Smørgrav 	u_int i;
490621e764dfSDag-Erling Smørgrav 
49074f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
49084f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
49094f52dfbbSDag-Erling Smørgrav 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
491021e764dfSDag-Erling Smørgrav 			continue;
491119261079SEd Maste 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1)
491221e764dfSDag-Erling Smørgrav 			continue;
49134f52dfbbSDag-Erling Smørgrav 		channel_request_start(ssh, i, "window-change", 0);
49144f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
49154f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
49164f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
49174f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
49184f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0)
491919261079SEd Maste 			fatal_fr(r, "channel %u; send window-change", i);
492021e764dfSDag-Erling Smørgrav 	}
492121e764dfSDag-Erling Smørgrav }
492221e764dfSDag-Erling Smørgrav 
49234f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
49244f52dfbbSDag-Erling Smørgrav static Channel *
rdynamic_connect_prepare(struct ssh * ssh,char * ctype,char * rname)49254f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
49264f52dfbbSDag-Erling Smørgrav {
49274f52dfbbSDag-Erling Smørgrav 	Channel *c;
49284f52dfbbSDag-Erling Smørgrav 	int r;
49294f52dfbbSDag-Erling Smørgrav 
49304f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
49314f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
49324f52dfbbSDag-Erling Smørgrav 	c->host_port = 0;
49334f52dfbbSDag-Erling Smørgrav 	c->path = NULL;
49344f52dfbbSDag-Erling Smørgrav 
49354f52dfbbSDag-Erling Smørgrav 	/*
49364f52dfbbSDag-Erling Smørgrav 	 * We need to open the channel before we have a FD,
49374f52dfbbSDag-Erling Smørgrav 	 * so that we can get SOCKS header from peer.
49384f52dfbbSDag-Erling Smørgrav 	 */
49394f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
49404f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
49414f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
49424f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
494319261079SEd Maste 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0)
494419261079SEd Maste 		fatal_fr(r, "channel %i; confirm", c->self);
49454f52dfbbSDag-Erling Smørgrav 	return c;
49464f52dfbbSDag-Erling Smørgrav }
49474f52dfbbSDag-Erling Smørgrav 
49484f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */
49494f52dfbbSDag-Erling Smørgrav static int
rdynamic_connect_finish(struct ssh * ssh,Channel * c)49504f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c)
49514f52dfbbSDag-Erling Smørgrav {
495219261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
495319261079SEd Maste 	struct permission_set *pset = &sc->local_perms;
495419261079SEd Maste 	struct permission *perm;
49554f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
495619261079SEd Maste 	u_int i, permit_adm = 1;
49574f52dfbbSDag-Erling Smørgrav 	int sock;
49584f52dfbbSDag-Erling Smørgrav 
495919261079SEd Maste 	if (pset->num_permitted_admin > 0) {
496019261079SEd Maste 		permit_adm = 0;
496119261079SEd Maste 		for (i = 0; i < pset->num_permitted_admin; i++) {
496219261079SEd Maste 			perm = &pset->permitted_admin[i];
496319261079SEd Maste 			if (open_match(perm, c->path, c->host_port)) {
496419261079SEd Maste 				permit_adm = 1;
496519261079SEd Maste 				break;
496619261079SEd Maste 			}
496719261079SEd Maste 		}
496819261079SEd Maste 	}
496919261079SEd Maste 	if (!permit_adm) {
497019261079SEd Maste 		debug_f("requested forward not permitted");
497119261079SEd Maste 		return -1;
497219261079SEd Maste 	}
497319261079SEd Maste 
49744f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
49754f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
49764f52dfbbSDag-Erling Smørgrav 	    NULL, &cctx, NULL, NULL);
49774f52dfbbSDag-Erling Smørgrav 	if (sock == -1)
49784f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
49794f52dfbbSDag-Erling Smørgrav 	else {
49804f52dfbbSDag-Erling Smørgrav 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
49814f52dfbbSDag-Erling Smørgrav 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
49824f52dfbbSDag-Erling Smørgrav 		c->connect_ctx = cctx;
49834f52dfbbSDag-Erling Smørgrav 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
49844f52dfbbSDag-Erling Smørgrav 	}
49854f52dfbbSDag-Erling Smørgrav 	return sock;
49864f52dfbbSDag-Erling Smørgrav }
49874f52dfbbSDag-Erling Smørgrav 
4988af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */
4989511b41d2SMark Murray 
4990511b41d2SMark Murray /*
4991511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
4992a82e551fSDag-Erling Smørgrav  * Returns 0 and a suitable display number for the DISPLAY variable
4993a82e551fSDag-Erling Smørgrav  * stored in display_numberp , or -1 if an error occurs.
4994511b41d2SMark Murray  */
4995af12a3e7SDag-Erling Smørgrav int
x11_create_display_inet(struct ssh * ssh,int x11_display_offset,int x11_use_localhost,int single_connection,u_int * display_numberp,int ** chanids)49964f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
49974f52dfbbSDag-Erling Smørgrav     int x11_use_localhost, int single_connection,
49984f52dfbbSDag-Erling Smørgrav     u_int *display_numberp, int **chanids)
4999511b41d2SMark Murray {
5000af12a3e7SDag-Erling Smørgrav 	Channel *nc = NULL;
5001511b41d2SMark Murray 	int display_number, sock;
5002511b41d2SMark Murray 	u_short port;
5003511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
5004511b41d2SMark Murray 	char strport[NI_MAXSERV];
5005511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
5006511b41d2SMark Murray 
5007b74df5b2SDag-Erling Smørgrav 	if (chanids == NULL)
5008b74df5b2SDag-Erling Smørgrav 		return -1;
5009b74df5b2SDag-Erling Smørgrav 
5010511b41d2SMark Murray 	for (display_number = x11_display_offset;
5011511b41d2SMark Murray 	    display_number < MAX_DISPLAYS;
5012511b41d2SMark Murray 	    display_number++) {
5013511b41d2SMark Murray 		port = 6000 + display_number;
5014511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
50154f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
5016af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
5017511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
5018511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
50194f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(NULL, strport,
50204f52dfbbSDag-Erling Smørgrav 		    &hints, &aitop)) != 0) {
5021d4af9e69SDag-Erling Smørgrav 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
5022af12a3e7SDag-Erling Smørgrav 			return -1;
5023511b41d2SMark Murray 		}
5024511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
50254f52dfbbSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
50264f52dfbbSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
5027511b41d2SMark Murray 				continue;
5028221552e4SDag-Erling Smørgrav 			sock = socket(ai->ai_family, ai->ai_socktype,
5029221552e4SDag-Erling Smørgrav 			    ai->ai_protocol);
503019261079SEd Maste 			if (sock == -1) {
50318ad9b54aSDag-Erling Smørgrav 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
50328ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT
50338ad9b54aSDag-Erling Smørgrav 				    && (errno != EPFNOSUPPORT)
50348ad9b54aSDag-Erling Smørgrav #endif
50358ad9b54aSDag-Erling Smørgrav 				    ) {
5036511b41d2SMark Murray 					error("socket: %.100s", strerror(errno));
503721e764dfSDag-Erling Smørgrav 					freeaddrinfo(aitop);
5038af12a3e7SDag-Erling Smørgrav 					return -1;
5039989dd127SDag-Erling Smørgrav 				} else {
5040989dd127SDag-Erling Smørgrav 					debug("x11_create_display_inet: Socket family %d not supported",
5041989dd127SDag-Erling Smørgrav 						 ai->ai_family);
5042989dd127SDag-Erling Smørgrav 					continue;
5043511b41d2SMark Murray 				}
5044989dd127SDag-Erling Smørgrav 			}
5045b15c8340SDag-Erling Smørgrav 			if (ai->ai_family == AF_INET6)
5046b15c8340SDag-Erling Smørgrav 				sock_set_v6only(sock);
5047d4af9e69SDag-Erling Smørgrav 			if (x11_use_localhost)
504847dd1d1bSDag-Erling Smørgrav 				set_reuseaddr(sock);
504919261079SEd Maste 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
505019261079SEd Maste 				debug2_f("bind port %d: %.100s", port,
505119261079SEd Maste 				    strerror(errno));
5052511b41d2SMark Murray 				close(sock);
50534f52dfbbSDag-Erling Smørgrav 				for (n = 0; n < num_socks; n++)
5054511b41d2SMark Murray 					close(socks[n]);
5055511b41d2SMark Murray 				num_socks = 0;
5056511b41d2SMark Murray 				break;
5057511b41d2SMark Murray 			}
5058511b41d2SMark Murray 			socks[num_socks++] = sock;
5059511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
5060511b41d2SMark Murray 				break;
5061511b41d2SMark Murray 		}
5062ca3176e7SBrian Feldman 		freeaddrinfo(aitop);
5063511b41d2SMark Murray 		if (num_socks > 0)
5064511b41d2SMark Murray 			break;
5065511b41d2SMark Murray 	}
5066511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
5067511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
5068af12a3e7SDag-Erling Smørgrav 		return -1;
5069511b41d2SMark Murray 	}
5070511b41d2SMark Murray 	/* Start listening for connections on the socket. */
5071511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5072511b41d2SMark Murray 		sock = socks[n];
507319261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
5074511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
5075511b41d2SMark Murray 			close(sock);
5076af12a3e7SDag-Erling Smørgrav 			return -1;
5077511b41d2SMark Murray 		}
5078511b41d2SMark Murray 	}
5079511b41d2SMark Murray 
5080511b41d2SMark Murray 	/* Allocate a channel for each socket. */
5081333ee039SDag-Erling Smørgrav 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
5082511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
5083511b41d2SMark Murray 		sock = socks[n];
5084f374ba41SEd Maste 		nc = channel_new(ssh, "x11-listener",
5085a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
5086a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
5087221552e4SDag-Erling Smørgrav 		    0, "X11 inet listener", 1);
5088af12a3e7SDag-Erling Smørgrav 		nc->single_connection = single_connection;
5089d4ecd108SDag-Erling Smørgrav 		(*chanids)[n] = nc->self;
5090511b41d2SMark Murray 	}
5091d4ecd108SDag-Erling Smørgrav 	(*chanids)[n] = -1;
5092511b41d2SMark Murray 
5093af12a3e7SDag-Erling Smørgrav 	/* Return the display number for the DISPLAY environment variable. */
5094a82e551fSDag-Erling Smørgrav 	*display_numberp = display_number;
50954f52dfbbSDag-Erling Smørgrav 	return 0;
5096511b41d2SMark Murray }
5097511b41d2SMark Murray 
5098af12a3e7SDag-Erling Smørgrav static int
connect_local_xsocket_path(const char * pathname)5099cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname)
5100511b41d2SMark Murray {
5101511b41d2SMark Murray 	int sock;
5102511b41d2SMark Murray 	struct sockaddr_un addr;
5103511b41d2SMark Murray 
5104511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
5105535af610SEd Maste 	if (sock == -1) {
5106511b41d2SMark Murray 		error("socket: %.100s", strerror(errno));
5107535af610SEd Maste 		return -1;
5108535af610SEd Maste 	}
5109511b41d2SMark Murray 	memset(&addr, 0, sizeof(addr));
5110511b41d2SMark Murray 	addr.sun_family = AF_UNIX;
5111cce7d346SDag-Erling Smørgrav 	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
5112511b41d2SMark Murray 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
5113511b41d2SMark Murray 		return sock;
5114511b41d2SMark Murray 	close(sock);
5115511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
5116511b41d2SMark Murray 	return -1;
5117511b41d2SMark Murray }
5118511b41d2SMark Murray 
5119cce7d346SDag-Erling Smørgrav static int
connect_local_xsocket(u_int dnr)5120cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr)
5121cce7d346SDag-Erling Smørgrav {
5122cce7d346SDag-Erling Smørgrav 	char buf[1024];
5123cce7d346SDag-Erling Smørgrav 	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
5124cce7d346SDag-Erling Smørgrav 	return connect_local_xsocket_path(buf);
5125cce7d346SDag-Erling Smørgrav }
5126cce7d346SDag-Erling Smørgrav 
5127d93a896eSDag-Erling Smørgrav #ifdef __APPLE__
5128d93a896eSDag-Erling Smørgrav static int
is_path_to_xsocket(const char * display,char * path,size_t pathlen)5129d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen)
5130d93a896eSDag-Erling Smørgrav {
5131d93a896eSDag-Erling Smørgrav 	struct stat sbuf;
5132d93a896eSDag-Erling Smørgrav 
5133d93a896eSDag-Erling Smørgrav 	if (strlcpy(path, display, pathlen) >= pathlen) {
5134d93a896eSDag-Erling Smørgrav 		error("%s: display path too long", __func__);
5135d93a896eSDag-Erling Smørgrav 		return 0;
5136d93a896eSDag-Erling Smørgrav 	}
5137d93a896eSDag-Erling Smørgrav 	if (display[0] != '/')
5138d93a896eSDag-Erling Smørgrav 		return 0;
5139d93a896eSDag-Erling Smørgrav 	if (stat(path, &sbuf) == 0) {
5140d93a896eSDag-Erling Smørgrav 		return 1;
5141d93a896eSDag-Erling Smørgrav 	} else {
5142d93a896eSDag-Erling Smørgrav 		char *dot = strrchr(path, '.');
5143d93a896eSDag-Erling Smørgrav 		if (dot != NULL) {
5144d93a896eSDag-Erling Smørgrav 			*dot = '\0';
5145d93a896eSDag-Erling Smørgrav 			if (stat(path, &sbuf) == 0) {
5146d93a896eSDag-Erling Smørgrav 				return 1;
5147d93a896eSDag-Erling Smørgrav 			}
5148d93a896eSDag-Erling Smørgrav 		}
5149d93a896eSDag-Erling Smørgrav 	}
5150d93a896eSDag-Erling Smørgrav 	return 0;
5151d93a896eSDag-Erling Smørgrav }
5152d93a896eSDag-Erling Smørgrav #endif
5153d93a896eSDag-Erling Smørgrav 
5154a04a10f8SKris Kennaway int
x11_connect_display(struct ssh * ssh)51554f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh)
5156511b41d2SMark Murray {
5157333ee039SDag-Erling Smørgrav 	u_int display_number;
5158511b41d2SMark Murray 	const char *display;
5159a04a10f8SKris Kennaway 	char buf[1024], *cp;
5160511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
5161511b41d2SMark Murray 	char strport[NI_MAXSERV];
5162333ee039SDag-Erling Smørgrav 	int gaierr, sock = 0;
5163511b41d2SMark Murray 
5164511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
5165511b41d2SMark Murray 	display = getenv("DISPLAY");
5166511b41d2SMark Murray 	if (!display) {
5167511b41d2SMark Murray 		error("DISPLAY not set.");
5168a04a10f8SKris Kennaway 		return -1;
5169511b41d2SMark Murray 	}
5170511b41d2SMark Murray 	/*
5171511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
5172511b41d2SMark Murray 	 * connection to the real X server.
5173511b41d2SMark Murray 	 */
5174511b41d2SMark Murray 
5175cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
5176d93a896eSDag-Erling Smørgrav 	/* Check if display is a path to a socket (as set by launchd). */
5177d93a896eSDag-Erling Smørgrav 	{
5178d93a896eSDag-Erling Smørgrav 		char path[PATH_MAX];
5179d93a896eSDag-Erling Smørgrav 
5180d93a896eSDag-Erling Smørgrav 		if (is_path_to_xsocket(display, path, sizeof(path))) {
5181d93a896eSDag-Erling Smørgrav 			debug("x11_connect_display: $DISPLAY is launchd");
5182d93a896eSDag-Erling Smørgrav 
5183d93a896eSDag-Erling Smørgrav 			/* Create a socket. */
5184d93a896eSDag-Erling Smørgrav 			sock = connect_local_xsocket_path(path);
5185cce7d346SDag-Erling Smørgrav 			if (sock < 0)
5186cce7d346SDag-Erling Smørgrav 				return -1;
5187cce7d346SDag-Erling Smørgrav 
5188cce7d346SDag-Erling Smørgrav 			/* OK, we now have a connection to the display. */
5189cce7d346SDag-Erling Smørgrav 			return sock;
5190cce7d346SDag-Erling Smørgrav 		}
5191d93a896eSDag-Erling Smørgrav 	}
5192cce7d346SDag-Erling Smørgrav #endif
5193511b41d2SMark Murray 	/*
5194511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
5195511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
5196511b41d2SMark Murray 	 */
5197511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
5198511b41d2SMark Murray 	    display[0] == ':') {
5199511b41d2SMark Murray 		/* Connect to the unix domain socket. */
52004f52dfbbSDag-Erling Smørgrav 		if (sscanf(strrchr(display, ':') + 1, "%u",
52014f52dfbbSDag-Erling Smørgrav 		    &display_number) != 1) {
52024f52dfbbSDag-Erling Smørgrav 			error("Could not parse display number from DISPLAY: "
52034f52dfbbSDag-Erling Smørgrav 			    "%.100s", display);
5204a04a10f8SKris Kennaway 			return -1;
5205511b41d2SMark Murray 		}
5206511b41d2SMark Murray 		/* Create a socket. */
5207511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
5208511b41d2SMark Murray 		if (sock < 0)
5209a04a10f8SKris Kennaway 			return -1;
5210511b41d2SMark Murray 
5211511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
5212a04a10f8SKris Kennaway 		return sock;
5213511b41d2SMark Murray 	}
5214511b41d2SMark Murray 	/*
5215511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
5216511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
5217511b41d2SMark Murray 	 */
5218af12a3e7SDag-Erling Smørgrav 	strlcpy(buf, display, sizeof(buf));
5219511b41d2SMark Murray 	cp = strchr(buf, ':');
5220511b41d2SMark Murray 	if (!cp) {
5221511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
5222a04a10f8SKris Kennaway 		return -1;
5223511b41d2SMark Murray 	}
5224511b41d2SMark Murray 	*cp = 0;
52254f52dfbbSDag-Erling Smørgrav 	/*
52264f52dfbbSDag-Erling Smørgrav 	 * buf now contains the host name.  But first we parse the
52274f52dfbbSDag-Erling Smørgrav 	 * display number.
52284f52dfbbSDag-Erling Smørgrav 	 */
5229333ee039SDag-Erling Smørgrav 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
5230511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
5231511b41d2SMark Murray 		    display);
5232a04a10f8SKris Kennaway 		return -1;
5233511b41d2SMark Murray 	}
5234511b41d2SMark Murray 
5235511b41d2SMark Murray 	/* Look up the host address */
5236511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
52374f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
5238511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
5239333ee039SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
5240511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
5241d4af9e69SDag-Erling Smørgrav 		error("%.100s: unknown host. (%s)", buf,
5242d4af9e69SDag-Erling Smørgrav 		ssh_gai_strerror(gaierr));
5243a04a10f8SKris Kennaway 		return -1;
5244511b41d2SMark Murray 	}
5245511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
5246511b41d2SMark Murray 		/* Create a socket. */
5247221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
524819261079SEd Maste 		if (sock == -1) {
5249221552e4SDag-Erling Smørgrav 			debug2("socket: %.100s", strerror(errno));
5250511b41d2SMark Murray 			continue;
5251511b41d2SMark Murray 		}
5252511b41d2SMark Murray 		/* Connect it to the display. */
525319261079SEd Maste 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
5254333ee039SDag-Erling Smørgrav 			debug2("connect %.100s port %u: %.100s", buf,
5255a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
5256511b41d2SMark Murray 			close(sock);
5257511b41d2SMark Murray 			continue;
5258511b41d2SMark Murray 		}
5259511b41d2SMark Murray 		/* Success */
5260511b41d2SMark Murray 		break;
5261a04a10f8SKris Kennaway 	}
5262511b41d2SMark Murray 	freeaddrinfo(aitop);
5263511b41d2SMark Murray 	if (!ai) {
52644f52dfbbSDag-Erling Smørgrav 		error("connect %.100s port %u: %.100s", buf,
52654f52dfbbSDag-Erling Smørgrav 		    6000 + display_number, strerror(errno));
5266a04a10f8SKris Kennaway 		return -1;
5267511b41d2SMark Murray 	}
5268af12a3e7SDag-Erling Smørgrav 	set_nodelay(sock);
5269a04a10f8SKris Kennaway 	return sock;
5270a04a10f8SKris Kennaway }
5271511b41d2SMark Murray 
5272a04a10f8SKris Kennaway /*
5273511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
5274511b41d2SMark Murray  * data, and enables authentication spoofing.
5275af12a3e7SDag-Erling Smørgrav  * This should be called in the client only.
5276511b41d2SMark Murray  */
5277511b41d2SMark Murray void
x11_request_forwarding_with_spoofing(struct ssh * ssh,int client_session_id,const char * disp,const char * proto,const char * data,int want_reply)52784f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
52794f52dfbbSDag-Erling Smørgrav     const char *disp, const char *proto, const char *data, int want_reply)
5280511b41d2SMark Murray {
52814f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
5282ca3176e7SBrian Feldman 	u_int data_len = (u_int) strlen(data) / 2;
5283d4ecd108SDag-Erling Smørgrav 	u_int i, value;
5284511b41d2SMark Murray 	const char *cp;
52854f52dfbbSDag-Erling Smørgrav 	char *new_data;
52864f52dfbbSDag-Erling Smørgrav 	int r, screen_number;
5287511b41d2SMark Murray 
52884f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_display == NULL)
52894f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_display = xstrdup(disp);
52904f52dfbbSDag-Erling Smørgrav 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
5291d4ecd108SDag-Erling Smørgrav 		error("x11_request_forwarding_with_spoofing: different "
5292d4ecd108SDag-Erling Smørgrav 		    "$DISPLAY already forwarded");
5293d4ecd108SDag-Erling Smørgrav 		return;
5294d4ecd108SDag-Erling Smørgrav 	}
5295d4ecd108SDag-Erling Smørgrav 
5296d4ecd108SDag-Erling Smørgrav 	cp = strchr(disp, ':');
5297511b41d2SMark Murray 	if (cp)
5298511b41d2SMark Murray 		cp = strchr(cp, '.');
5299511b41d2SMark Murray 	if (cp)
5300333ee039SDag-Erling Smørgrav 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
5301511b41d2SMark Murray 	else
5302511b41d2SMark Murray 		screen_number = 0;
5303511b41d2SMark Murray 
53044f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_proto == NULL) {
5305511b41d2SMark Murray 		/* Save protocol name. */
53064f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_proto = xstrdup(proto);
5307ca86bcf2SDag-Erling Smørgrav 
5308ca86bcf2SDag-Erling Smørgrav 		/* Extract real authentication data. */
53094f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data = xmalloc(data_len);
5310511b41d2SMark Murray 		for (i = 0; i < data_len; i++) {
531119261079SEd Maste 			if (sscanf(data + 2 * i, "%2x", &value) != 1) {
5312d4ecd108SDag-Erling Smørgrav 				fatal("x11_request_forwarding: bad "
5313d4ecd108SDag-Erling Smørgrav 				    "authentication data: %.100s", data);
531419261079SEd Maste 			}
53154f52dfbbSDag-Erling Smørgrav 			sc->x11_saved_data[i] = value;
5316511b41d2SMark Murray 		}
53174f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data_len = data_len;
5318ca86bcf2SDag-Erling Smørgrav 
5319ca86bcf2SDag-Erling Smørgrav 		/* Generate fake data of the same length. */
53204f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data = xmalloc(data_len);
53214f52dfbbSDag-Erling Smørgrav 		arc4random_buf(sc->x11_fake_data, data_len);
53224f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data_len = data_len;
5323d4ecd108SDag-Erling Smørgrav 	}
5324511b41d2SMark Murray 
5325511b41d2SMark Murray 	/* Convert the fake data into hex. */
53264f52dfbbSDag-Erling Smørgrav 	new_data = tohex(sc->x11_fake_data, data_len);
5327511b41d2SMark Murray 
5328511b41d2SMark Murray 	/* Send the request packet. */
53294f52dfbbSDag-Erling Smørgrav 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
53304f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
53314f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
53324f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
53334f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
53344f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0 ||
53354f52dfbbSDag-Erling Smørgrav 	    (r = ssh_packet_write_wait(ssh)) != 0)
533619261079SEd Maste 		fatal_fr(r, "send x11-req");
5337e4a9863fSDag-Erling Smørgrav 	free(new_data);
5338511b41d2SMark Murray }
5339