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