xref: /freebsd/crypto/openssh/channels.c (revision e0fbb1d2de5da5201d80ec05cf2aee2d90e3f1b0)
1511b41d2SMark Murray /*
2511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4511b41d2SMark Murray  *                    All rights reserved
5511b41d2SMark Murray  * This file contains functions for generic socket connection forwarding.
6511b41d2SMark Murray  * There is also code for initiating connection forwarding for X11 connections,
7511b41d2SMark Murray  * arbitrary tcp/ip connections, and the authentication agent connection.
8511b41d2SMark Murray  *
9b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
10b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
11b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
12b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
13b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
14b66f2d16SKris Kennaway  *
15b66f2d16SKris Kennaway  *
16a04a10f8SKris Kennaway  * SSH2 support added by Markus Friedl.
17b66f2d16SKris Kennaway  * Copyright (c) 1999,2000 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"
43e0fbb1d2SBrian Feldman RCSID("$FreeBSD$");
445b9b2fafSBrian Feldman RCSID("$OpenBSD: channels.c,v 1.72 2000/10/27 07:48:22 markus Exp $");
45511b41d2SMark Murray 
46511b41d2SMark Murray #include "ssh.h"
47511b41d2SMark Murray #include "packet.h"
48511b41d2SMark Murray #include "xmalloc.h"
49511b41d2SMark Murray #include "buffer.h"
50511b41d2SMark Murray #include "uidswap.h"
51511b41d2SMark Murray #include "readconf.h"
52511b41d2SMark Murray #include "servconf.h"
53511b41d2SMark Murray 
54511b41d2SMark Murray #include "channels.h"
55511b41d2SMark Murray #include "nchan.h"
56511b41d2SMark Murray #include "compat.h"
57511b41d2SMark Murray 
58a04a10f8SKris Kennaway #include "ssh2.h"
59a04a10f8SKris Kennaway 
60b66f2d16SKris Kennaway #include <openssl/rsa.h>
61b66f2d16SKris Kennaway #include <openssl/dsa.h>
62b66f2d16SKris Kennaway #include "key.h"
63b66f2d16SKris Kennaway #include "authfd.h"
64b66f2d16SKris Kennaway 
65511b41d2SMark Murray /* Maximum number of fake X11 displays to try. */
66511b41d2SMark Murray #define MAX_DISPLAYS  1000
67511b41d2SMark Murray 
68511b41d2SMark Murray /* Max len of agent socket */
69511b41d2SMark Murray #define MAX_SOCKET_NAME 100
70511b41d2SMark Murray 
71511b41d2SMark Murray /*
72511b41d2SMark Murray  * Pointer to an array containing all allocated channels.  The array is
73511b41d2SMark Murray  * dynamically extended as needed.
74511b41d2SMark Murray  */
75511b41d2SMark Murray static Channel *channels = NULL;
76511b41d2SMark Murray 
77511b41d2SMark Murray /*
78511b41d2SMark Murray  * Size of the channel array.  All slots of the array must always be
79511b41d2SMark Murray  * initialized (at least the type field); unused slots are marked with type
80511b41d2SMark Murray  * SSH_CHANNEL_FREE.
81511b41d2SMark Murray  */
82511b41d2SMark Murray static int channels_alloc = 0;
83511b41d2SMark Murray 
84511b41d2SMark Murray /*
85511b41d2SMark Murray  * Maximum file descriptor value used in any of the channels.  This is
86511b41d2SMark Murray  * updated in channel_allocate.
87511b41d2SMark Murray  */
88511b41d2SMark Murray static int channel_max_fd_value = 0;
89511b41d2SMark Murray 
90511b41d2SMark Murray /* Name and directory of socket for authentication agent forwarding. */
91511b41d2SMark Murray static char *channel_forwarded_auth_socket_name = NULL;
92511b41d2SMark Murray static char *channel_forwarded_auth_socket_dir = NULL;
93511b41d2SMark Murray 
94511b41d2SMark Murray /* Saved X11 authentication protocol name. */
95511b41d2SMark Murray char *x11_saved_proto = NULL;
96511b41d2SMark Murray 
97511b41d2SMark Murray /* Saved X11 authentication data.  This is the real data. */
98511b41d2SMark Murray char *x11_saved_data = NULL;
99511b41d2SMark Murray unsigned int x11_saved_data_len = 0;
100511b41d2SMark Murray 
101511b41d2SMark Murray /*
102511b41d2SMark Murray  * Fake X11 authentication data.  This is what the server will be sending us;
103511b41d2SMark Murray  * we should replace any occurrences of this by the real data.
104511b41d2SMark Murray  */
105511b41d2SMark Murray char *x11_fake_data = NULL;
106511b41d2SMark Murray unsigned int x11_fake_data_len;
107511b41d2SMark Murray 
108511b41d2SMark Murray /*
109511b41d2SMark Murray  * Data structure for storing which hosts are permitted for forward requests.
110511b41d2SMark Murray  * The local sides of any remote forwards are stored in this array to prevent
111511b41d2SMark Murray  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
112511b41d2SMark Murray  * network (which might be behind a firewall).
113511b41d2SMark Murray  */
114511b41d2SMark Murray typedef struct {
115a04a10f8SKris Kennaway 	char *host_to_connect;		/* Connect to 'host'. */
116a04a10f8SKris Kennaway 	u_short port_to_connect;	/* Connect to 'port'. */
117a04a10f8SKris Kennaway 	u_short listen_port;		/* Remote side should listen port number. */
118511b41d2SMark Murray } ForwardPermission;
119511b41d2SMark Murray 
120511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */
121511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
122511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */
123511b41d2SMark Murray static int num_permitted_opens = 0;
124511b41d2SMark Murray /*
125511b41d2SMark Murray  * If this is true, all opens are permitted.  This is the case on the server
126511b41d2SMark Murray  * on which we have to trust the client anyway, and the user could do
127511b41d2SMark Murray  * anything after logging in anyway.
128511b41d2SMark Murray  */
129511b41d2SMark Murray static int all_opens_permitted = 0;
130511b41d2SMark Murray 
131511b41d2SMark Murray /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */
132511b41d2SMark Murray static int have_hostname_in_open = 0;
133511b41d2SMark Murray 
134511b41d2SMark Murray /* Sets specific protocol options. */
135511b41d2SMark Murray 
136511b41d2SMark Murray void
137511b41d2SMark Murray channel_set_options(int hostname_in_open)
138511b41d2SMark Murray {
139511b41d2SMark Murray 	have_hostname_in_open = hostname_in_open;
140511b41d2SMark Murray }
141511b41d2SMark Murray 
142511b41d2SMark Murray /*
143511b41d2SMark Murray  * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
144511b41d2SMark Murray  * called by the server, because the user could connect to any port anyway,
145511b41d2SMark Murray  * and the server has no way to know but to trust the client anyway.
146511b41d2SMark Murray  */
147511b41d2SMark Murray 
148511b41d2SMark Murray void
149511b41d2SMark Murray channel_permit_all_opens()
150511b41d2SMark Murray {
151511b41d2SMark Murray 	all_opens_permitted = 1;
152511b41d2SMark Murray }
153511b41d2SMark Murray 
154a04a10f8SKris Kennaway /* lookup channel by id */
155a04a10f8SKris Kennaway 
156a04a10f8SKris Kennaway Channel *
157a04a10f8SKris Kennaway channel_lookup(int id)
158a04a10f8SKris Kennaway {
159a04a10f8SKris Kennaway 	Channel *c;
160b66f2d16SKris Kennaway 	if (id < 0 || id > channels_alloc) {
161a04a10f8SKris Kennaway 		log("channel_lookup: %d: bad id", id);
162a04a10f8SKris Kennaway 		return NULL;
163a04a10f8SKris Kennaway 	}
164a04a10f8SKris Kennaway 	c = &channels[id];
165a04a10f8SKris Kennaway 	if (c->type == SSH_CHANNEL_FREE) {
166a04a10f8SKris Kennaway 		log("channel_lookup: %d: bad id: channel free", id);
167a04a10f8SKris Kennaway 		return NULL;
168a04a10f8SKris Kennaway 	}
169a04a10f8SKris Kennaway 	return c;
170a04a10f8SKris Kennaway }
171a04a10f8SKris Kennaway 
172a04a10f8SKris Kennaway /*
173a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
174a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
175a04a10f8SKris Kennaway  */
176a04a10f8SKris Kennaway 
177a04a10f8SKris Kennaway void
1785b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd,
1795b9b2fafSBrian Feldman     int extusage, int nonblock)
180a04a10f8SKris Kennaway {
181a04a10f8SKris Kennaway 	/* Update the maximum file descriptor value. */
182a04a10f8SKris Kennaway 	if (rfd > channel_max_fd_value)
183a04a10f8SKris Kennaway 		channel_max_fd_value = rfd;
184a04a10f8SKris Kennaway 	if (wfd > channel_max_fd_value)
185a04a10f8SKris Kennaway 		channel_max_fd_value = wfd;
186a04a10f8SKris Kennaway 	if (efd > channel_max_fd_value)
187a04a10f8SKris Kennaway 		channel_max_fd_value = efd;
188a04a10f8SKris Kennaway 	/* XXX set close-on-exec -markus */
189a04a10f8SKris Kennaway 
190a04a10f8SKris Kennaway 	c->rfd = rfd;
191a04a10f8SKris Kennaway 	c->wfd = wfd;
192a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
193a04a10f8SKris Kennaway 	c->efd = efd;
194a04a10f8SKris Kennaway 	c->extended_usage = extusage;
1955b9b2fafSBrian Feldman 
196e0fbb1d2SBrian Feldman 	/* XXX ugly hack: nonblock is only set by the server */
197e0fbb1d2SBrian Feldman 	if (nonblock && isatty(c->rfd)) {
198e0fbb1d2SBrian Feldman 		debug("channel: %d: rfd %d isatty", c->self, c->rfd);
199e0fbb1d2SBrian Feldman 		c->isatty = 1;
200e0fbb1d2SBrian Feldman 		if (!isatty(c->wfd)) {
201e0fbb1d2SBrian Feldman 			error("channel: %d: wfd %d is not a tty?",
202e0fbb1d2SBrian Feldman 			    c->self, c->wfd);
203e0fbb1d2SBrian Feldman 		}
204e0fbb1d2SBrian Feldman 	} else {
205e0fbb1d2SBrian Feldman 		c->isatty = 0;
206e0fbb1d2SBrian Feldman 	}
207e0fbb1d2SBrian Feldman 
2085b9b2fafSBrian Feldman 	/* enable nonblocking mode */
2095b9b2fafSBrian Feldman 	if (nonblock) {
210a04a10f8SKris Kennaway 		if (rfd != -1)
211a04a10f8SKris Kennaway 			set_nonblock(rfd);
212a04a10f8SKris Kennaway 		if (wfd != -1)
213a04a10f8SKris Kennaway 			set_nonblock(wfd);
214a04a10f8SKris Kennaway 		if (efd != -1)
215a04a10f8SKris Kennaway 			set_nonblock(efd);
216a04a10f8SKris Kennaway 	}
2175b9b2fafSBrian Feldman }
218a04a10f8SKris Kennaway 
219511b41d2SMark Murray /*
220511b41d2SMark Murray  * Allocate a new channel object and set its type and socket. This will cause
221511b41d2SMark Murray  * remote_name to be freed.
222511b41d2SMark Murray  */
223511b41d2SMark Murray 
224511b41d2SMark Murray int
225a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd,
2265b9b2fafSBrian Feldman     int window, int maxpack, int extusage, char *remote_name, int nonblock)
227511b41d2SMark Murray {
228511b41d2SMark Murray 	int i, found;
229511b41d2SMark Murray 	Channel *c;
230511b41d2SMark Murray 
231511b41d2SMark Murray 	/* Do initial allocation if this is the first call. */
232511b41d2SMark Murray 	if (channels_alloc == 0) {
233a04a10f8SKris Kennaway 		chan_init();
234511b41d2SMark Murray 		channels_alloc = 10;
235511b41d2SMark Murray 		channels = xmalloc(channels_alloc * sizeof(Channel));
236511b41d2SMark Murray 		for (i = 0; i < channels_alloc; i++)
237511b41d2SMark Murray 			channels[i].type = SSH_CHANNEL_FREE;
238511b41d2SMark Murray 		/*
239511b41d2SMark Murray 		 * Kludge: arrange a call to channel_stop_listening if we
240511b41d2SMark Murray 		 * terminate with fatal().
241511b41d2SMark Murray 		 */
242511b41d2SMark Murray 		fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL);
243511b41d2SMark Murray 	}
244511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
245511b41d2SMark Murray 	for (found = -1, i = 0; i < channels_alloc; i++)
246511b41d2SMark Murray 		if (channels[i].type == SSH_CHANNEL_FREE) {
247511b41d2SMark Murray 			/* Found a free slot. */
248511b41d2SMark Murray 			found = i;
249511b41d2SMark Murray 			break;
250511b41d2SMark Murray 		}
251511b41d2SMark Murray 	if (found == -1) {
252511b41d2SMark Murray 		/* There are no free slots.  Take last+1 slot and expand the array.  */
253511b41d2SMark Murray 		found = channels_alloc;
254511b41d2SMark Murray 		channels_alloc += 10;
2555b9b2fafSBrian Feldman 		debug2("channel: expanding %d", channels_alloc);
256511b41d2SMark Murray 		channels = xrealloc(channels, channels_alloc * sizeof(Channel));
257511b41d2SMark Murray 		for (i = found; i < channels_alloc; i++)
258511b41d2SMark Murray 			channels[i].type = SSH_CHANNEL_FREE;
259511b41d2SMark Murray 	}
260511b41d2SMark Murray 	/* Initialize and return new channel number. */
261511b41d2SMark Murray 	c = &channels[found];
262511b41d2SMark Murray 	buffer_init(&c->input);
263511b41d2SMark Murray 	buffer_init(&c->output);
264a04a10f8SKris Kennaway 	buffer_init(&c->extended);
265511b41d2SMark Murray 	chan_init_iostates(c);
2665b9b2fafSBrian Feldman 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
267511b41d2SMark Murray 	c->self = found;
268511b41d2SMark Murray 	c->type = type;
269a04a10f8SKris Kennaway 	c->ctype = ctype;
270a04a10f8SKris Kennaway 	c->local_window = window;
271a04a10f8SKris Kennaway 	c->local_window_max = window;
272a04a10f8SKris Kennaway 	c->local_consumed = 0;
273a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
274511b41d2SMark Murray 	c->remote_id = -1;
275511b41d2SMark Murray 	c->remote_name = remote_name;
276a04a10f8SKris Kennaway 	c->remote_window = 0;
277a04a10f8SKris Kennaway 	c->remote_maxpacket = 0;
278a04a10f8SKris Kennaway 	c->cb_fn = NULL;
279a04a10f8SKris Kennaway 	c->cb_arg = NULL;
280a04a10f8SKris Kennaway 	c->cb_event = 0;
281a04a10f8SKris Kennaway 	c->dettach_user = NULL;
282b66f2d16SKris Kennaway 	c->input_filter = NULL;
283511b41d2SMark Murray 	debug("channel %d: new [%s]", found, remote_name);
284511b41d2SMark Murray 	return found;
285511b41d2SMark Murray }
286a04a10f8SKris Kennaway /* old interface XXX */
287a04a10f8SKris Kennaway int
288a04a10f8SKris Kennaway channel_allocate(int type, int sock, char *remote_name)
289a04a10f8SKris Kennaway {
2905b9b2fafSBrian Feldman 	return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name, 1);
291a04a10f8SKris Kennaway }
292511b41d2SMark Murray 
293a04a10f8SKris Kennaway 
294a04a10f8SKris Kennaway /* Close all channel fd/socket. */
295511b41d2SMark Murray 
296511b41d2SMark Murray void
297a04a10f8SKris Kennaway channel_close_fds(Channel *c)
298511b41d2SMark Murray {
299a04a10f8SKris Kennaway 	if (c->sock != -1) {
300a04a10f8SKris Kennaway 		close(c->sock);
301a04a10f8SKris Kennaway 		c->sock = -1;
302a04a10f8SKris Kennaway 	}
303a04a10f8SKris Kennaway 	if (c->rfd != -1) {
304a04a10f8SKris Kennaway 		close(c->rfd);
305a04a10f8SKris Kennaway 		c->rfd = -1;
306a04a10f8SKris Kennaway 	}
307a04a10f8SKris Kennaway 	if (c->wfd != -1) {
308a04a10f8SKris Kennaway 		close(c->wfd);
309a04a10f8SKris Kennaway 		c->wfd = -1;
310a04a10f8SKris Kennaway 	}
311a04a10f8SKris Kennaway 	if (c->efd != -1) {
312a04a10f8SKris Kennaway 		close(c->efd);
313a04a10f8SKris Kennaway 		c->efd = -1;
314a04a10f8SKris Kennaway 	}
315a04a10f8SKris Kennaway }
316511b41d2SMark Murray 
317a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
318a04a10f8SKris Kennaway 
319a04a10f8SKris Kennaway void
320a04a10f8SKris Kennaway channel_free(int id)
321a04a10f8SKris Kennaway {
322a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
323a04a10f8SKris Kennaway 	if (c == NULL)
324a04a10f8SKris Kennaway 		packet_disconnect("channel free: bad local channel %d", id);
325a04a10f8SKris Kennaway 	debug("channel_free: channel %d: status: %s", id, channel_open_message());
326a04a10f8SKris Kennaway 	if (c->dettach_user != NULL) {
327a04a10f8SKris Kennaway 		debug("channel_free: channel %d: dettaching channel user", id);
328a04a10f8SKris Kennaway 		c->dettach_user(c->self, NULL);
329a04a10f8SKris Kennaway 	}
330a04a10f8SKris Kennaway 	if (c->sock != -1)
331a04a10f8SKris Kennaway 		shutdown(c->sock, SHUT_RDWR);
332a04a10f8SKris Kennaway 	channel_close_fds(c);
333a04a10f8SKris Kennaway 	buffer_free(&c->input);
334a04a10f8SKris Kennaway 	buffer_free(&c->output);
335a04a10f8SKris Kennaway 	buffer_free(&c->extended);
336a04a10f8SKris Kennaway 	c->type = SSH_CHANNEL_FREE;
337a04a10f8SKris Kennaway 	if (c->remote_name) {
338a04a10f8SKris Kennaway 		xfree(c->remote_name);
339a04a10f8SKris Kennaway 		c->remote_name = NULL;
340511b41d2SMark Murray 	}
341511b41d2SMark Murray }
342511b41d2SMark Murray 
343511b41d2SMark Murray /*
344a04a10f8SKris Kennaway  * 'channel_pre*' are called just before select() to add any bits relevant to
345a04a10f8SKris Kennaway  * channels in the select bitmasks.
346511b41d2SMark Murray  */
347a04a10f8SKris Kennaway /*
348a04a10f8SKris Kennaway  * 'channel_post*': perform any appropriate operations for channels which
349a04a10f8SKris Kennaway  * have events pending.
350a04a10f8SKris Kennaway  */
351a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
352a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
353a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
354511b41d2SMark Murray 
355511b41d2SMark Murray void
356a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
357511b41d2SMark Murray {
358a04a10f8SKris Kennaway 	FD_SET(c->sock, readset);
359a04a10f8SKris Kennaway }
360a04a10f8SKris Kennaway 
361a04a10f8SKris Kennaway void
362a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
363a04a10f8SKris Kennaway {
364a04a10f8SKris Kennaway 	if (buffer_len(&c->input) < packet_get_maxsize())
365a04a10f8SKris Kennaway 		FD_SET(c->sock, readset);
366a04a10f8SKris Kennaway 	if (buffer_len(&c->output) > 0)
367a04a10f8SKris Kennaway 		FD_SET(c->sock, writeset);
368a04a10f8SKris Kennaway }
369a04a10f8SKris Kennaway 
370a04a10f8SKris Kennaway void
371a04a10f8SKris Kennaway channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset)
372a04a10f8SKris Kennaway {
373a04a10f8SKris Kennaway 	/* test whether sockets are 'alive' for read/write */
374a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN)
375a04a10f8SKris Kennaway 		if (buffer_len(&c->input) < packet_get_maxsize())
376a04a10f8SKris Kennaway 			FD_SET(c->sock, readset);
377a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
378a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
379a04a10f8SKris Kennaway 		if (buffer_len(&c->output) > 0) {
380a04a10f8SKris Kennaway 			FD_SET(c->sock, writeset);
381a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
382a04a10f8SKris Kennaway 			chan_obuf_empty(c);
383a04a10f8SKris Kennaway 		}
384a04a10f8SKris Kennaway 	}
385a04a10f8SKris Kennaway }
386a04a10f8SKris Kennaway 
387a04a10f8SKris Kennaway void
388a04a10f8SKris Kennaway channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset)
389a04a10f8SKris Kennaway {
390a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
391a04a10f8SKris Kennaway 	    c->remote_window > 0 &&
392a04a10f8SKris Kennaway 	    buffer_len(&c->input) < c->remote_window)
393a04a10f8SKris Kennaway 		FD_SET(c->rfd, readset);
394a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
395a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
396a04a10f8SKris Kennaway 		if (buffer_len(&c->output) > 0) {
397a04a10f8SKris Kennaway 			FD_SET(c->wfd, writeset);
398a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
399a04a10f8SKris Kennaway 			chan_obuf_empty(c);
400a04a10f8SKris Kennaway 		}
401a04a10f8SKris Kennaway 	}
402a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
403a04a10f8SKris Kennaway 	if (c->efd != -1) {
404a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
405a04a10f8SKris Kennaway 		    buffer_len(&c->extended) > 0)
406a04a10f8SKris Kennaway 			FD_SET(c->efd, writeset);
407a04a10f8SKris Kennaway 		else if (c->extended_usage == CHAN_EXTENDED_READ &&
408a04a10f8SKris Kennaway 		    buffer_len(&c->extended) < c->remote_window)
409a04a10f8SKris Kennaway 			FD_SET(c->efd, readset);
410a04a10f8SKris Kennaway 	}
411a04a10f8SKris Kennaway }
412a04a10f8SKris Kennaway 
413a04a10f8SKris Kennaway void
414a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
415a04a10f8SKris Kennaway {
416a04a10f8SKris Kennaway 	if (buffer_len(&c->input) == 0) {
417a04a10f8SKris Kennaway 		packet_start(SSH_MSG_CHANNEL_CLOSE);
418a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
419a04a10f8SKris Kennaway 		packet_send();
420a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_CLOSED;
421a04a10f8SKris Kennaway 		debug("Closing channel %d after input drain.", c->self);
422a04a10f8SKris Kennaway 	}
423a04a10f8SKris Kennaway }
424a04a10f8SKris Kennaway 
425a04a10f8SKris Kennaway void
426a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
427a04a10f8SKris Kennaway {
428a04a10f8SKris Kennaway 	if (buffer_len(&c->output) == 0)
429a04a10f8SKris Kennaway 		channel_free(c->self);
430a04a10f8SKris Kennaway 	else
431a04a10f8SKris Kennaway 		FD_SET(c->sock, writeset);
432a04a10f8SKris Kennaway }
433a04a10f8SKris Kennaway 
434a04a10f8SKris Kennaway /*
435a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
436a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
437a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
438a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
439a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
440a04a10f8SKris Kennaway  * XXX All this happens at the client side.
441a04a10f8SKris Kennaway  */
442a04a10f8SKris Kennaway int
443a04a10f8SKris Kennaway x11_open_helper(Channel *c)
444a04a10f8SKris Kennaway {
445511b41d2SMark Murray 	unsigned char *ucp;
446511b41d2SMark Murray 	unsigned int proto_len, data_len;
447511b41d2SMark Murray 
448511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
449a04a10f8SKris Kennaway 	if (buffer_len(&c->output) < 12)
450a04a10f8SKris Kennaway 		return 0;
451511b41d2SMark Murray 
452511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
453a04a10f8SKris Kennaway 	ucp = (unsigned char *) buffer_ptr(&c->output);
454511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
455511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
456511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
457511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
458511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
459511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
460511b41d2SMark Murray 	} else {
461511b41d2SMark Murray 		debug("Initial X11 packet contains bad byte order byte: 0x%x",
462511b41d2SMark Murray 		      ucp[0]);
463a04a10f8SKris Kennaway 		return -1;
464511b41d2SMark Murray 	}
465511b41d2SMark Murray 
466511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
467a04a10f8SKris Kennaway 	if (buffer_len(&c->output) <
468511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
469a04a10f8SKris Kennaway 		return 0;
470511b41d2SMark Murray 
471511b41d2SMark Murray 	/* Check if authentication protocol matches. */
472511b41d2SMark Murray 	if (proto_len != strlen(x11_saved_proto) ||
473511b41d2SMark Murray 	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
474511b41d2SMark Murray 		debug("X11 connection uses different authentication protocol.");
475a04a10f8SKris Kennaway 		return -1;
476511b41d2SMark Murray 	}
477511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
478511b41d2SMark Murray 	if (data_len != x11_fake_data_len ||
479511b41d2SMark Murray 	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
480511b41d2SMark Murray 		x11_fake_data, x11_fake_data_len) != 0) {
481511b41d2SMark Murray 		debug("X11 auth data does not match fake data.");
482a04a10f8SKris Kennaway 		return -1;
483511b41d2SMark Murray 	}
484511b41d2SMark Murray 	/* Check fake data length */
485511b41d2SMark Murray 	if (x11_fake_data_len != x11_saved_data_len) {
486511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
487511b41d2SMark Murray 		    x11_fake_data_len, x11_saved_data_len);
488a04a10f8SKris Kennaway 		return -1;
489511b41d2SMark Murray 	}
490511b41d2SMark Murray 	/*
491511b41d2SMark Murray 	 * Received authentication protocol and data match
492511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
493511b41d2SMark Murray 	 * data.
494511b41d2SMark Murray 	 */
495511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
496511b41d2SMark Murray 	    x11_saved_data, x11_saved_data_len);
497a04a10f8SKris Kennaway 	return 1;
498a04a10f8SKris Kennaway }
499511b41d2SMark Murray 
500a04a10f8SKris Kennaway void
501a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
502a04a10f8SKris Kennaway {
503a04a10f8SKris Kennaway 	int ret = x11_open_helper(c);
504a04a10f8SKris Kennaway 	if (ret == 1) {
505511b41d2SMark Murray 		/* Start normal processing for the channel. */
506a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
507a04a10f8SKris Kennaway 		channel_pre_open_13(c, readset, writeset);
508a04a10f8SKris Kennaway 	} else if (ret == -1) {
509511b41d2SMark Murray 		/*
510511b41d2SMark Murray 		 * We have received an X11 connection that has bad
511511b41d2SMark Murray 		 * authentication information.
512511b41d2SMark Murray 		 */
513511b41d2SMark Murray 		log("X11 connection rejected because of wrong authentication.\r\n");
514a04a10f8SKris Kennaway 		buffer_clear(&c->input);
515a04a10f8SKris Kennaway 		buffer_clear(&c->output);
516a04a10f8SKris Kennaway 		close(c->sock);
517a04a10f8SKris Kennaway 		c->sock = -1;
518a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_CLOSED;
519511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_CLOSE);
520a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
521511b41d2SMark Murray 		packet_send();
522511b41d2SMark Murray 	}
523511b41d2SMark Murray }
524511b41d2SMark Murray 
525511b41d2SMark Murray void
526a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
527a04a10f8SKris Kennaway {
528a04a10f8SKris Kennaway 	int ret = x11_open_helper(c);
529a04a10f8SKris Kennaway 	if (ret == 1) {
530a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
531a04a10f8SKris Kennaway 		if (compat20)
532a04a10f8SKris Kennaway 			channel_pre_open_20(c, readset, writeset);
533a04a10f8SKris Kennaway 		else
534a04a10f8SKris Kennaway 			channel_pre_open_15(c, readset, writeset);
535a04a10f8SKris Kennaway 	} else if (ret == -1) {
536a04a10f8SKris Kennaway 		debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
537a04a10f8SKris Kennaway 		chan_read_failed(c);	/** force close? */
538a04a10f8SKris Kennaway 		chan_write_failed(c);
539a04a10f8SKris Kennaway 		debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
540a04a10f8SKris Kennaway 	}
541a04a10f8SKris Kennaway }
542a04a10f8SKris Kennaway 
543a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
544a04a10f8SKris Kennaway void
545a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
546511b41d2SMark Murray {
547511b41d2SMark Murray 	struct sockaddr addr;
548a04a10f8SKris Kennaway 	int newsock, newch;
549511b41d2SMark Murray 	socklen_t addrlen;
550511b41d2SMark Murray 	char buf[16384], *remote_hostname;
551a04a10f8SKris Kennaway 	int remote_port;
552511b41d2SMark Murray 
553a04a10f8SKris Kennaway 	if (FD_ISSET(c->sock, readset)) {
554511b41d2SMark Murray 		debug("X11 connection requested.");
555511b41d2SMark Murray 		addrlen = sizeof(addr);
556a04a10f8SKris Kennaway 		newsock = accept(c->sock, &addr, &addrlen);
557511b41d2SMark Murray 		if (newsock < 0) {
558511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
559a04a10f8SKris Kennaway 			return;
560511b41d2SMark Murray 		}
561511b41d2SMark Murray 		remote_hostname = get_remote_hostname(newsock);
562a04a10f8SKris Kennaway 		remote_port = get_peer_port(newsock);
563511b41d2SMark Murray 		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
564a04a10f8SKris Kennaway 		    remote_hostname, remote_port);
565a04a10f8SKris Kennaway 
566a04a10f8SKris Kennaway 		newch = channel_new("x11",
567a04a10f8SKris Kennaway 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
568a04a10f8SKris Kennaway 		    c->local_window_max, c->local_maxpacket,
5695b9b2fafSBrian Feldman 		    0, xstrdup(buf), 1);
570a04a10f8SKris Kennaway 		if (compat20) {
571a04a10f8SKris Kennaway 			packet_start(SSH2_MSG_CHANNEL_OPEN);
572a04a10f8SKris Kennaway 			packet_put_cstring("x11");
573a04a10f8SKris Kennaway 			packet_put_int(newch);
574a04a10f8SKris Kennaway 			packet_put_int(c->local_window_max);
575a04a10f8SKris Kennaway 			packet_put_int(c->local_maxpacket);
576a04a10f8SKris Kennaway 			/* originator host and port */
577a04a10f8SKris Kennaway 			packet_put_cstring(remote_hostname);
578a04a10f8SKris Kennaway 			if (datafellows & SSH_BUG_X11FWD) {
579a04a10f8SKris Kennaway 				debug("ssh2 x11 bug compat mode");
580a04a10f8SKris Kennaway 			} else {
581a04a10f8SKris Kennaway 				packet_put_int(remote_port);
582a04a10f8SKris Kennaway 			}
583a04a10f8SKris Kennaway 			packet_send();
584a04a10f8SKris Kennaway 		} else {
585511b41d2SMark Murray 			packet_start(SSH_SMSG_X11_OPEN);
586511b41d2SMark Murray 			packet_put_int(newch);
587511b41d2SMark Murray 			if (have_hostname_in_open)
588511b41d2SMark Murray 				packet_put_string(buf, strlen(buf));
589511b41d2SMark Murray 			packet_send();
590511b41d2SMark Murray 		}
591a04a10f8SKris Kennaway 		xfree(remote_hostname);
592a04a10f8SKris Kennaway 	}
593a04a10f8SKris Kennaway }
594511b41d2SMark Murray 
595511b41d2SMark Murray /*
596a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
597511b41d2SMark Murray  */
598a04a10f8SKris Kennaway void
599a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
600a04a10f8SKris Kennaway {
601a04a10f8SKris Kennaway 	struct sockaddr addr;
602a04a10f8SKris Kennaway 	int newsock, newch;
603a04a10f8SKris Kennaway 	socklen_t addrlen;
604a04a10f8SKris Kennaway 	char buf[1024], *remote_hostname;
605a04a10f8SKris Kennaway 	int remote_port;
606a04a10f8SKris Kennaway 
607a04a10f8SKris Kennaway 	if (FD_ISSET(c->sock, readset)) {
608a04a10f8SKris Kennaway 		debug("Connection to port %d forwarding "
609a04a10f8SKris Kennaway 		    "to %.100s port %d requested.",
610a04a10f8SKris Kennaway 		    c->listening_port, c->path, c->host_port);
611511b41d2SMark Murray 		addrlen = sizeof(addr);
612a04a10f8SKris Kennaway 		newsock = accept(c->sock, &addr, &addrlen);
613511b41d2SMark Murray 		if (newsock < 0) {
614511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
615a04a10f8SKris Kennaway 			return;
616511b41d2SMark Murray 		}
617511b41d2SMark Murray 		remote_hostname = get_remote_hostname(newsock);
618a04a10f8SKris Kennaway 		remote_port = get_peer_port(newsock);
619a04a10f8SKris Kennaway 		snprintf(buf, sizeof buf,
620a04a10f8SKris Kennaway 		    "listen port %d for %.100s port %d, "
621a04a10f8SKris Kennaway 		    "connect from %.200s port %d",
622a04a10f8SKris Kennaway 		    c->listening_port, c->path, c->host_port,
623a04a10f8SKris Kennaway 		    remote_hostname, remote_port);
624a04a10f8SKris Kennaway 		newch = channel_new("direct-tcpip",
625a04a10f8SKris Kennaway 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
626a04a10f8SKris Kennaway 		    c->local_window_max, c->local_maxpacket,
6275b9b2fafSBrian Feldman 		    0, xstrdup(buf), 1);
628a04a10f8SKris Kennaway 		if (compat20) {
629a04a10f8SKris Kennaway 			packet_start(SSH2_MSG_CHANNEL_OPEN);
630a04a10f8SKris Kennaway 			packet_put_cstring("direct-tcpip");
631a04a10f8SKris Kennaway 			packet_put_int(newch);
632a04a10f8SKris Kennaway 			packet_put_int(c->local_window_max);
633a04a10f8SKris Kennaway 			packet_put_int(c->local_maxpacket);
634a04a10f8SKris Kennaway 			/* target host and port */
635a04a10f8SKris Kennaway 			packet_put_string(c->path, strlen(c->path));
636a04a10f8SKris Kennaway 			packet_put_int(c->host_port);
637a04a10f8SKris Kennaway 			/* originator host and port */
638a04a10f8SKris Kennaway 			packet_put_cstring(remote_hostname);
639a04a10f8SKris Kennaway 			packet_put_int(remote_port);
640a04a10f8SKris Kennaway 			packet_send();
641a04a10f8SKris Kennaway 		} else {
642511b41d2SMark Murray 			packet_start(SSH_MSG_PORT_OPEN);
643511b41d2SMark Murray 			packet_put_int(newch);
644a04a10f8SKris Kennaway 			packet_put_string(c->path, strlen(c->path));
645a04a10f8SKris Kennaway 			packet_put_int(c->host_port);
646a04a10f8SKris Kennaway 			if (have_hostname_in_open) {
647511b41d2SMark Murray 				packet_put_string(buf, strlen(buf));
648a04a10f8SKris Kennaway 			}
649511b41d2SMark Murray 			packet_send();
650511b41d2SMark Murray 		}
651a04a10f8SKris Kennaway 		xfree(remote_hostname);
652a04a10f8SKris Kennaway 	}
653a04a10f8SKris Kennaway }
654511b41d2SMark Murray 
655511b41d2SMark Murray /*
656a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
657a04a10f8SKris Kennaway  * clients.
658511b41d2SMark Murray  */
659a04a10f8SKris Kennaway void
660a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
661a04a10f8SKris Kennaway {
662a04a10f8SKris Kennaway 	struct sockaddr addr;
663a04a10f8SKris Kennaway 	int newsock, newch;
664a04a10f8SKris Kennaway 	socklen_t addrlen;
665a04a10f8SKris Kennaway 
666a04a10f8SKris Kennaway 	if (FD_ISSET(c->sock, readset)) {
667511b41d2SMark Murray 		addrlen = sizeof(addr);
668a04a10f8SKris Kennaway 		newsock = accept(c->sock, &addr, &addrlen);
669511b41d2SMark Murray 		if (newsock < 0) {
670511b41d2SMark Murray 			error("accept from auth socket: %.100s", strerror(errno));
671a04a10f8SKris Kennaway 			return;
672511b41d2SMark Murray 		}
673511b41d2SMark Murray 		newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
674511b41d2SMark Murray 		    xstrdup("accepted auth socket"));
675511b41d2SMark Murray 		packet_start(SSH_SMSG_AGENT_OPEN);
676511b41d2SMark Murray 		packet_put_int(newch);
677511b41d2SMark Murray 		packet_send();
678511b41d2SMark Murray 	}
679a04a10f8SKris Kennaway }
680511b41d2SMark Murray 
681a04a10f8SKris Kennaway int
682a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
683a04a10f8SKris Kennaway {
684a04a10f8SKris Kennaway 	char buf[16*1024];
685a04a10f8SKris Kennaway 	int len;
686511b41d2SMark Murray 
687a04a10f8SKris Kennaway 	if (c->rfd != -1 &&
688a04a10f8SKris Kennaway 	    FD_ISSET(c->rfd, readset)) {
689a04a10f8SKris Kennaway 		len = read(c->rfd, buf, sizeof(buf));
690a04a10f8SKris Kennaway 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
691a04a10f8SKris Kennaway 			return 1;
692a04a10f8SKris Kennaway 		if (len <= 0) {
693a04a10f8SKris Kennaway 			debug("channel %d: read<=0 rfd %d len %d",
694a04a10f8SKris Kennaway 			    c->self, c->rfd, len);
695a04a10f8SKris Kennaway 			if (compat13) {
696a04a10f8SKris Kennaway 				buffer_consume(&c->output, buffer_len(&c->output));
697a04a10f8SKris Kennaway 				c->type = SSH_CHANNEL_INPUT_DRAINING;
698a04a10f8SKris Kennaway 				debug("Channel %d status set to input draining.", c->self);
699a04a10f8SKris Kennaway 			} else {
700a04a10f8SKris Kennaway 				chan_read_failed(c);
701a04a10f8SKris Kennaway 			}
702a04a10f8SKris Kennaway 			return -1;
703a04a10f8SKris Kennaway 		}
704b66f2d16SKris Kennaway 		if(c->input_filter != NULL) {
705b66f2d16SKris Kennaway 			if (c->input_filter(c, buf, len) == -1) {
706b66f2d16SKris Kennaway 				debug("filter stops channel %d", c->self);
707b66f2d16SKris Kennaway 				chan_read_failed(c);
708b66f2d16SKris Kennaway 			}
709b66f2d16SKris Kennaway 		} else {
710a04a10f8SKris Kennaway 			buffer_append(&c->input, buf, len);
711a04a10f8SKris Kennaway 		}
712b66f2d16SKris Kennaway 	}
713a04a10f8SKris Kennaway 	return 1;
714a04a10f8SKris Kennaway }
715a04a10f8SKris Kennaway int
716a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
717a04a10f8SKris Kennaway {
718a04a10f8SKris Kennaway 	int len;
719a04a10f8SKris Kennaway 
720a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
721a04a10f8SKris Kennaway 	if (c->wfd != -1 &&
722a04a10f8SKris Kennaway 	    FD_ISSET(c->wfd, writeset) &&
723a04a10f8SKris Kennaway 	    buffer_len(&c->output) > 0) {
724a04a10f8SKris Kennaway 		len = write(c->wfd, buffer_ptr(&c->output),
725a04a10f8SKris Kennaway 		    buffer_len(&c->output));
726a04a10f8SKris Kennaway 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
727a04a10f8SKris Kennaway 			return 1;
728511b41d2SMark Murray 		if (len <= 0) {
729511b41d2SMark Murray 			if (compat13) {
730a04a10f8SKris Kennaway 				buffer_consume(&c->output, buffer_len(&c->output));
731a04a10f8SKris Kennaway 				debug("Channel %d status set to input draining.", c->self);
732a04a10f8SKris Kennaway 				c->type = SSH_CHANNEL_INPUT_DRAINING;
733511b41d2SMark Murray 			} else {
734a04a10f8SKris Kennaway 				chan_write_failed(c);
735511b41d2SMark Murray 			}
736a04a10f8SKris Kennaway 			return -1;
737511b41d2SMark Murray 		}
738e0fbb1d2SBrian Feldman 		if (compat20 && c->isatty) {
739e0fbb1d2SBrian Feldman 			struct termios tio;
740e0fbb1d2SBrian Feldman 			if (tcgetattr(c->wfd, &tio) == 0 &&
741e0fbb1d2SBrian Feldman 			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
742e0fbb1d2SBrian Feldman 				/*
743e0fbb1d2SBrian Feldman 				 * Simulate echo to reduce the impact of
744e0fbb1d2SBrian Feldman 				 * traffic analysis.
745e0fbb1d2SBrian Feldman 				 */
746e0fbb1d2SBrian Feldman 				packet_start(SSH2_MSG_IGNORE);
747e0fbb1d2SBrian Feldman 				memset(buffer_ptr(&c->output), 0, len);
748e0fbb1d2SBrian Feldman 				packet_put_string(buffer_ptr(&c->output), len);
749e0fbb1d2SBrian Feldman 				packet_send();
750e0fbb1d2SBrian Feldman 			}
751e0fbb1d2SBrian Feldman 		}
752a04a10f8SKris Kennaway 		buffer_consume(&c->output, len);
753a04a10f8SKris Kennaway 		if (compat20 && len > 0) {
754a04a10f8SKris Kennaway 			c->local_consumed += len;
755511b41d2SMark Murray 		}
756511b41d2SMark Murray 	}
757a04a10f8SKris Kennaway 	return 1;
758511b41d2SMark Murray }
759a04a10f8SKris Kennaway int
760a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
761a04a10f8SKris Kennaway {
762a04a10f8SKris Kennaway 	char buf[16*1024];
763a04a10f8SKris Kennaway 	int len;
764511b41d2SMark Murray 
765a04a10f8SKris Kennaway /** XXX handle drain efd, too */
766a04a10f8SKris Kennaway 	if (c->efd != -1) {
767a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
768a04a10f8SKris Kennaway 		    FD_ISSET(c->efd, writeset) &&
769a04a10f8SKris Kennaway 		    buffer_len(&c->extended) > 0) {
770a04a10f8SKris Kennaway 			len = write(c->efd, buffer_ptr(&c->extended),
771a04a10f8SKris Kennaway 			    buffer_len(&c->extended));
7725b9b2fafSBrian Feldman 			debug2("channel %d: written %d to efd %d",
773a04a10f8SKris Kennaway 			    c->self, len, c->efd);
774a04a10f8SKris Kennaway 			if (len > 0) {
775a04a10f8SKris Kennaway 				buffer_consume(&c->extended, len);
776a04a10f8SKris Kennaway 				c->local_consumed += len;
777a04a10f8SKris Kennaway 			}
778a04a10f8SKris Kennaway 		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
779a04a10f8SKris Kennaway 		    FD_ISSET(c->efd, readset)) {
780a04a10f8SKris Kennaway 			len = read(c->efd, buf, sizeof(buf));
7815b9b2fafSBrian Feldman 			debug2("channel %d: read %d from efd %d",
782a04a10f8SKris Kennaway 			     c->self, len, c->efd);
783a04a10f8SKris Kennaway 			if (len == 0) {
784a04a10f8SKris Kennaway 				debug("channel %d: closing efd %d",
785a04a10f8SKris Kennaway 				    c->self, c->efd);
786a04a10f8SKris Kennaway 				close(c->efd);
787a04a10f8SKris Kennaway 				c->efd = -1;
788a04a10f8SKris Kennaway 			} else if (len > 0)
789a04a10f8SKris Kennaway 				buffer_append(&c->extended, buf, len);
790a04a10f8SKris Kennaway 		}
791a04a10f8SKris Kennaway 	}
792a04a10f8SKris Kennaway 	return 1;
793a04a10f8SKris Kennaway }
794a04a10f8SKris Kennaway int
795a04a10f8SKris Kennaway channel_check_window(Channel *c, fd_set * readset, fd_set * writeset)
796a04a10f8SKris Kennaway {
797a04a10f8SKris Kennaway 	if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
798a04a10f8SKris Kennaway 	    c->local_window < c->local_window_max/2 &&
799a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
800a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
801a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
802a04a10f8SKris Kennaway 		packet_put_int(c->local_consumed);
803a04a10f8SKris Kennaway 		packet_send();
8045b9b2fafSBrian Feldman 		debug2("channel %d: window %d sent adjust %d",
805a04a10f8SKris Kennaway 		    c->self, c->local_window,
806a04a10f8SKris Kennaway 		    c->local_consumed);
807a04a10f8SKris Kennaway 		c->local_window += c->local_consumed;
808a04a10f8SKris Kennaway 		c->local_consumed = 0;
809a04a10f8SKris Kennaway 	}
810a04a10f8SKris Kennaway 	return 1;
811a04a10f8SKris Kennaway }
812a04a10f8SKris Kennaway 
813a04a10f8SKris Kennaway void
814a04a10f8SKris Kennaway channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset)
815a04a10f8SKris Kennaway {
816a04a10f8SKris Kennaway 	channel_handle_rfd(c, readset, writeset);
817a04a10f8SKris Kennaway 	channel_handle_wfd(c, readset, writeset);
818a04a10f8SKris Kennaway }
819a04a10f8SKris Kennaway 
820a04a10f8SKris Kennaway void
821a04a10f8SKris Kennaway channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset)
822a04a10f8SKris Kennaway {
823a04a10f8SKris Kennaway 	channel_handle_rfd(c, readset, writeset);
824a04a10f8SKris Kennaway 	channel_handle_wfd(c, readset, writeset);
825a04a10f8SKris Kennaway 	channel_handle_efd(c, readset, writeset);
826a04a10f8SKris Kennaway 	channel_check_window(c, readset, writeset);
827a04a10f8SKris Kennaway }
828a04a10f8SKris Kennaway 
829a04a10f8SKris Kennaway void
830a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
831a04a10f8SKris Kennaway {
832a04a10f8SKris Kennaway 	int len;
833511b41d2SMark Murray 	/* Send buffered output data to the socket. */
834a04a10f8SKris Kennaway 	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
835a04a10f8SKris Kennaway 		len = write(c->sock, buffer_ptr(&c->output),
836a04a10f8SKris Kennaway 			    buffer_len(&c->output));
837511b41d2SMark Murray 		if (len <= 0)
838a04a10f8SKris Kennaway 			buffer_consume(&c->output, buffer_len(&c->output));
839511b41d2SMark Murray 		else
840a04a10f8SKris Kennaway 			buffer_consume(&c->output, len);
841511b41d2SMark Murray 	}
842a04a10f8SKris Kennaway }
843511b41d2SMark Murray 
844a04a10f8SKris Kennaway void
845a04a10f8SKris Kennaway channel_handler_init_20(void)
846a04a10f8SKris Kennaway {
847a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_20;
848a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
849a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
850a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
851a04a10f8SKris Kennaway 
852a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_2;
853a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
854a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
855a04a10f8SKris Kennaway }
856a04a10f8SKris Kennaway 
857a04a10f8SKris Kennaway void
858a04a10f8SKris Kennaway channel_handler_init_13(void)
859a04a10f8SKris Kennaway {
860a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
861a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
862a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
863a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
864a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
865a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
866a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
867a04a10f8SKris Kennaway 
868a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_1;
869a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
870a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
871a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
872a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
873a04a10f8SKris Kennaway }
874a04a10f8SKris Kennaway 
875a04a10f8SKris Kennaway void
876a04a10f8SKris Kennaway channel_handler_init_15(void)
877a04a10f8SKris Kennaway {
878a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_15;
879a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
880a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
881a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
882a04a10f8SKris Kennaway 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
883a04a10f8SKris Kennaway 
884a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
885a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
886a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
887a04a10f8SKris Kennaway 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_1;
888a04a10f8SKris Kennaway }
889a04a10f8SKris Kennaway 
890a04a10f8SKris Kennaway void
891a04a10f8SKris Kennaway channel_handler_init(void)
892a04a10f8SKris Kennaway {
893a04a10f8SKris Kennaway 	int i;
894a04a10f8SKris Kennaway 	for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
895a04a10f8SKris Kennaway 		channel_pre[i] = NULL;
896a04a10f8SKris Kennaway 		channel_post[i] = NULL;
897a04a10f8SKris Kennaway 	}
898a04a10f8SKris Kennaway 	if (compat20)
899a04a10f8SKris Kennaway 		channel_handler_init_20();
900a04a10f8SKris Kennaway 	else if (compat13)
901a04a10f8SKris Kennaway 		channel_handler_init_13();
902a04a10f8SKris Kennaway 	else
903a04a10f8SKris Kennaway 		channel_handler_init_15();
904a04a10f8SKris Kennaway }
905a04a10f8SKris Kennaway 
906a04a10f8SKris Kennaway void
907a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
908a04a10f8SKris Kennaway {
909a04a10f8SKris Kennaway 	static int did_init = 0;
910a04a10f8SKris Kennaway 	int i;
911a04a10f8SKris Kennaway 	Channel *c;
912a04a10f8SKris Kennaway 
913a04a10f8SKris Kennaway 	if (!did_init) {
914a04a10f8SKris Kennaway 		channel_handler_init();
915a04a10f8SKris Kennaway 		did_init = 1;
916a04a10f8SKris Kennaway 	}
917a04a10f8SKris Kennaway 	for (i = 0; i < channels_alloc; i++) {
918a04a10f8SKris Kennaway 		c = &channels[i];
919a04a10f8SKris Kennaway 		if (c->type == SSH_CHANNEL_FREE)
920511b41d2SMark Murray 			continue;
921a04a10f8SKris Kennaway 		if (ftab[c->type] == NULL)
922a04a10f8SKris Kennaway 			continue;
923a04a10f8SKris Kennaway 		(*ftab[c->type])(c, readset, writeset);
924a04a10f8SKris Kennaway 		chan_delete_if_full_closed(c);
925511b41d2SMark Murray 	}
926511b41d2SMark Murray }
927a04a10f8SKris Kennaway 
928a04a10f8SKris Kennaway void
929a04a10f8SKris Kennaway channel_prepare_select(fd_set * readset, fd_set * writeset)
930a04a10f8SKris Kennaway {
931a04a10f8SKris Kennaway 	channel_handler(channel_pre, readset, writeset);
932a04a10f8SKris Kennaway }
933a04a10f8SKris Kennaway 
934a04a10f8SKris Kennaway void
935a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset)
936a04a10f8SKris Kennaway {
937a04a10f8SKris Kennaway 	channel_handler(channel_post, readset, writeset);
938511b41d2SMark Murray }
939511b41d2SMark Murray 
940511b41d2SMark Murray /* If there is data to send to the connection, send some of it now. */
941511b41d2SMark Murray 
942511b41d2SMark Murray void
943511b41d2SMark Murray channel_output_poll()
944511b41d2SMark Murray {
945511b41d2SMark Murray 	int len, i;
946a04a10f8SKris Kennaway 	Channel *c;
947511b41d2SMark Murray 
948511b41d2SMark Murray 	for (i = 0; i < channels_alloc; i++) {
949a04a10f8SKris Kennaway 		c = &channels[i];
950511b41d2SMark Murray 
951511b41d2SMark Murray 		/* We are only interested in channels that can have buffered incoming data. */
952511b41d2SMark Murray 		if (compat13) {
953a04a10f8SKris Kennaway 			if (c->type != SSH_CHANNEL_OPEN &&
954a04a10f8SKris Kennaway 			    c->type != SSH_CHANNEL_INPUT_DRAINING)
955511b41d2SMark Murray 				continue;
956511b41d2SMark Murray 		} else {
957a04a10f8SKris Kennaway 			if (c->type != SSH_CHANNEL_OPEN)
958511b41d2SMark Murray 				continue;
959a04a10f8SKris Kennaway 			if (c->istate != CHAN_INPUT_OPEN &&
960a04a10f8SKris Kennaway 			    c->istate != CHAN_INPUT_WAIT_DRAIN)
961a04a10f8SKris Kennaway 				continue;
962a04a10f8SKris Kennaway 		}
963a04a10f8SKris Kennaway 		if (compat20 &&
964a04a10f8SKris Kennaway 		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
965a04a10f8SKris Kennaway 			debug("channel: %d: no data after CLOSE", c->self);
966511b41d2SMark Murray 			continue;
967511b41d2SMark Murray 		}
968511b41d2SMark Murray 
969511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
970a04a10f8SKris Kennaway 		len = buffer_len(&c->input);
971511b41d2SMark Murray 		if (len > 0) {
972511b41d2SMark Murray 			/* Send some data for the other side over the secure connection. */
973a04a10f8SKris Kennaway 			if (compat20) {
974a04a10f8SKris Kennaway 				if (len > c->remote_window)
975a04a10f8SKris Kennaway 					len = c->remote_window;
976a04a10f8SKris Kennaway 				if (len > c->remote_maxpacket)
977a04a10f8SKris Kennaway 					len = c->remote_maxpacket;
978a04a10f8SKris Kennaway 			} else {
979511b41d2SMark Murray 				if (packet_is_interactive()) {
980511b41d2SMark Murray 					if (len > 1024)
981511b41d2SMark Murray 						len = 512;
982511b41d2SMark Murray 				} else {
983511b41d2SMark Murray 					/* Keep the packets at reasonable size. */
984511b41d2SMark Murray 					if (len > packet_get_maxsize()/2)
985511b41d2SMark Murray 						len = packet_get_maxsize()/2;
986511b41d2SMark Murray 				}
987a04a10f8SKris Kennaway 			}
988a04a10f8SKris Kennaway 			if (len > 0) {
989a04a10f8SKris Kennaway 				packet_start(compat20 ?
990a04a10f8SKris Kennaway 				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
991a04a10f8SKris Kennaway 				packet_put_int(c->remote_id);
992a04a10f8SKris Kennaway 				packet_put_string(buffer_ptr(&c->input), len);
993511b41d2SMark Murray 				packet_send();
994a04a10f8SKris Kennaway 				buffer_consume(&c->input, len);
995a04a10f8SKris Kennaway 				c->remote_window -= len;
996a04a10f8SKris Kennaway 			}
997a04a10f8SKris Kennaway 		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
998511b41d2SMark Murray 			if (compat13)
999511b41d2SMark Murray 				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
1000511b41d2SMark Murray 			/*
1001511b41d2SMark Murray 			 * input-buffer is empty and read-socket shutdown:
1002511b41d2SMark Murray 			 * tell peer, that we will not send more data: send IEOF
1003511b41d2SMark Murray 			 */
1004a04a10f8SKris Kennaway 			chan_ibuf_empty(c);
1005a04a10f8SKris Kennaway 		}
1006a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
1007a04a10f8SKris Kennaway 		if (compat20 &&
1008a04a10f8SKris Kennaway 		    c->remote_window > 0 &&
1009a04a10f8SKris Kennaway 		    (len = buffer_len(&c->extended)) > 0 &&
1010a04a10f8SKris Kennaway 		    c->extended_usage == CHAN_EXTENDED_READ) {
1011a04a10f8SKris Kennaway 			if (len > c->remote_window)
1012a04a10f8SKris Kennaway 				len = c->remote_window;
1013a04a10f8SKris Kennaway 			if (len > c->remote_maxpacket)
1014a04a10f8SKris Kennaway 				len = c->remote_maxpacket;
1015a04a10f8SKris Kennaway 			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
1016a04a10f8SKris Kennaway 			packet_put_int(c->remote_id);
1017a04a10f8SKris Kennaway 			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
1018a04a10f8SKris Kennaway 			packet_put_string(buffer_ptr(&c->extended), len);
1019a04a10f8SKris Kennaway 			packet_send();
1020a04a10f8SKris Kennaway 			buffer_consume(&c->extended, len);
1021a04a10f8SKris Kennaway 			c->remote_window -= len;
1022511b41d2SMark Murray 		}
1023511b41d2SMark Murray 	}
1024511b41d2SMark Murray }
1025511b41d2SMark Murray 
1026511b41d2SMark Murray /*
1027511b41d2SMark Murray  * This is called when a packet of type CHANNEL_DATA has just been received.
1028511b41d2SMark Murray  * The message type has already been consumed, but channel number and data is
1029511b41d2SMark Murray  * still there.
1030511b41d2SMark Murray  */
1031511b41d2SMark Murray 
1032511b41d2SMark Murray void
10335b9b2fafSBrian Feldman channel_input_data(int type, int plen, void *ctxt)
1034511b41d2SMark Murray {
1035511b41d2SMark Murray 	int id;
1036511b41d2SMark Murray 	char *data;
1037511b41d2SMark Murray 	unsigned int data_len;
1038a04a10f8SKris Kennaway 	Channel *c;
1039511b41d2SMark Murray 
1040511b41d2SMark Murray 	/* Get the channel number and verify it. */
1041511b41d2SMark Murray 	id = packet_get_int();
1042a04a10f8SKris Kennaway 	c = channel_lookup(id);
1043a04a10f8SKris Kennaway 	if (c == NULL)
1044511b41d2SMark Murray 		packet_disconnect("Received data for nonexistent channel %d.", id);
1045511b41d2SMark Murray 
1046511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
1047a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
1048a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
1049511b41d2SMark Murray 		return;
1050511b41d2SMark Murray 
1051511b41d2SMark Murray 	/* same for protocol 1.5 if output end is no longer open */
1052a04a10f8SKris Kennaway 	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
1053511b41d2SMark Murray 		return;
1054511b41d2SMark Murray 
1055511b41d2SMark Murray 	/* Get the data. */
1056511b41d2SMark Murray 	data = packet_get_string(&data_len);
1057a04a10f8SKris Kennaway 	packet_done();
1058a04a10f8SKris Kennaway 
1059a04a10f8SKris Kennaway 	if (compat20){
1060a04a10f8SKris Kennaway 		if (data_len > c->local_maxpacket) {
1061a04a10f8SKris Kennaway 			log("channel %d: rcvd big packet %d, maxpack %d",
1062a04a10f8SKris Kennaway 			    c->self, data_len, c->local_maxpacket);
1063a04a10f8SKris Kennaway 		}
1064a04a10f8SKris Kennaway 		if (data_len > c->local_window) {
1065a04a10f8SKris Kennaway 			log("channel %d: rcvd too much data %d, win %d",
1066a04a10f8SKris Kennaway 			    c->self, data_len, c->local_window);
1067a04a10f8SKris Kennaway 			xfree(data);
1068a04a10f8SKris Kennaway 			return;
1069a04a10f8SKris Kennaway 		}
1070a04a10f8SKris Kennaway 		c->local_window -= data_len;
1071a04a10f8SKris Kennaway 	}else{
1072a04a10f8SKris Kennaway 		packet_integrity_check(plen, 4 + 4 + data_len, type);
1073a04a10f8SKris Kennaway 	}
1074a04a10f8SKris Kennaway 	buffer_append(&c->output, data, data_len);
1075511b41d2SMark Murray 	xfree(data);
1076511b41d2SMark Murray }
1077a04a10f8SKris Kennaway void
10785b9b2fafSBrian Feldman channel_input_extended_data(int type, int plen, void *ctxt)
1079a04a10f8SKris Kennaway {
1080a04a10f8SKris Kennaway 	int id;
1081a04a10f8SKris Kennaway 	int tcode;
1082a04a10f8SKris Kennaway 	char *data;
1083a04a10f8SKris Kennaway 	unsigned int data_len;
1084a04a10f8SKris Kennaway 	Channel *c;
1085a04a10f8SKris Kennaway 
1086a04a10f8SKris Kennaway 	/* Get the channel number and verify it. */
1087a04a10f8SKris Kennaway 	id = packet_get_int();
1088a04a10f8SKris Kennaway 	c = channel_lookup(id);
1089a04a10f8SKris Kennaway 
1090a04a10f8SKris Kennaway 	if (c == NULL)
1091a04a10f8SKris Kennaway 		packet_disconnect("Received extended_data for bad channel %d.", id);
1092a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
1093a04a10f8SKris Kennaway 		log("channel %d: ext data for non open", id);
1094a04a10f8SKris Kennaway 		return;
1095a04a10f8SKris Kennaway 	}
1096a04a10f8SKris Kennaway 	tcode = packet_get_int();
1097a04a10f8SKris Kennaway 	if (c->efd == -1 ||
1098a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
1099a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
1100a04a10f8SKris Kennaway 		log("channel %d: bad ext data", c->self);
1101a04a10f8SKris Kennaway 		return;
1102a04a10f8SKris Kennaway 	}
1103a04a10f8SKris Kennaway 	data = packet_get_string(&data_len);
1104a04a10f8SKris Kennaway 	packet_done();
1105a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
1106a04a10f8SKris Kennaway 		log("channel %d: rcvd too much extended_data %d, win %d",
1107a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
1108a04a10f8SKris Kennaway 		xfree(data);
1109a04a10f8SKris Kennaway 		return;
1110a04a10f8SKris Kennaway 	}
11115b9b2fafSBrian Feldman 	debug2("channel %d: rcvd ext data %d", c->self, data_len);
1112a04a10f8SKris Kennaway 	c->local_window -= data_len;
1113a04a10f8SKris Kennaway 	buffer_append(&c->extended, data, data_len);
1114a04a10f8SKris Kennaway 	xfree(data);
1115a04a10f8SKris Kennaway }
1116a04a10f8SKris Kennaway 
1117511b41d2SMark Murray 
1118511b41d2SMark Murray /*
1119511b41d2SMark Murray  * Returns true if no channel has too much buffered data, and false if one or
1120511b41d2SMark Murray  * more channel is overfull.
1121511b41d2SMark Murray  */
1122511b41d2SMark Murray 
1123511b41d2SMark Murray int
1124511b41d2SMark Murray channel_not_very_much_buffered_data()
1125511b41d2SMark Murray {
1126511b41d2SMark Murray 	unsigned int i;
1127a04a10f8SKris Kennaway 	Channel *c;
1128511b41d2SMark Murray 
1129511b41d2SMark Murray 	for (i = 0; i < channels_alloc; i++) {
1130a04a10f8SKris Kennaway 		c = &channels[i];
1131a04a10f8SKris Kennaway 		if (c->type == SSH_CHANNEL_OPEN) {
1132a04a10f8SKris Kennaway 			if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) {
1133a04a10f8SKris Kennaway 				debug("channel %d: big input buffer %d",
1134a04a10f8SKris Kennaway 				    c->self, buffer_len(&c->input));
1135511b41d2SMark Murray 				return 0;
1136a04a10f8SKris Kennaway 			}
1137a04a10f8SKris Kennaway 			if (buffer_len(&c->output) > packet_get_maxsize()) {
1138a04a10f8SKris Kennaway 				debug("channel %d: big output buffer %d",
1139a04a10f8SKris Kennaway 				    c->self, buffer_len(&c->output));
1140511b41d2SMark Murray 				return 0;
1141511b41d2SMark Murray 			}
1142511b41d2SMark Murray 		}
1143a04a10f8SKris Kennaway 	}
1144511b41d2SMark Murray 	return 1;
1145511b41d2SMark Murray }
1146511b41d2SMark Murray 
1147a04a10f8SKris Kennaway void
11485b9b2fafSBrian Feldman channel_input_ieof(int type, int plen, void *ctxt)
1149a04a10f8SKris Kennaway {
1150a04a10f8SKris Kennaway 	int id;
1151a04a10f8SKris Kennaway 	Channel *c;
1152a04a10f8SKris Kennaway 
1153a04a10f8SKris Kennaway 	packet_integrity_check(plen, 4, type);
1154a04a10f8SKris Kennaway 
1155a04a10f8SKris Kennaway 	id = packet_get_int();
1156a04a10f8SKris Kennaway 	c = channel_lookup(id);
1157a04a10f8SKris Kennaway 	if (c == NULL)
1158a04a10f8SKris Kennaway 		packet_disconnect("Received ieof for nonexistent channel %d.", id);
1159a04a10f8SKris Kennaway 	chan_rcvd_ieof(c);
1160a04a10f8SKris Kennaway }
1161511b41d2SMark Murray 
1162511b41d2SMark Murray void
11635b9b2fafSBrian Feldman channel_input_close(int type, int plen, void *ctxt)
1164511b41d2SMark Murray {
1165a04a10f8SKris Kennaway 	int id;
1166a04a10f8SKris Kennaway 	Channel *c;
1167511b41d2SMark Murray 
1168a04a10f8SKris Kennaway 	packet_integrity_check(plen, 4, type);
1169511b41d2SMark Murray 
1170a04a10f8SKris Kennaway 	id = packet_get_int();
1171a04a10f8SKris Kennaway 	c = channel_lookup(id);
1172a04a10f8SKris Kennaway 	if (c == NULL)
1173a04a10f8SKris Kennaway 		packet_disconnect("Received close for nonexistent channel %d.", id);
1174511b41d2SMark Murray 
1175511b41d2SMark Murray 	/*
1176511b41d2SMark Murray 	 * Send a confirmation that we have closed the channel and no more
1177511b41d2SMark Murray 	 * data is coming for it.
1178511b41d2SMark Murray 	 */
1179511b41d2SMark Murray 	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
1180a04a10f8SKris Kennaway 	packet_put_int(c->remote_id);
1181511b41d2SMark Murray 	packet_send();
1182511b41d2SMark Murray 
1183511b41d2SMark Murray 	/*
1184511b41d2SMark Murray 	 * If the channel is in closed state, we have sent a close request,
1185511b41d2SMark Murray 	 * and the other side will eventually respond with a confirmation.
1186511b41d2SMark Murray 	 * Thus, we cannot free the channel here, because then there would be
1187511b41d2SMark Murray 	 * no-one to receive the confirmation.  The channel gets freed when
1188511b41d2SMark Murray 	 * the confirmation arrives.
1189511b41d2SMark Murray 	 */
1190a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_CLOSED) {
1191511b41d2SMark Murray 		/*
1192511b41d2SMark Murray 		 * Not a closed channel - mark it as draining, which will
1193511b41d2SMark Murray 		 * cause it to be freed later.
1194511b41d2SMark Murray 		 */
1195a04a10f8SKris Kennaway 		buffer_consume(&c->input, buffer_len(&c->input));
1196a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
1197511b41d2SMark Murray 	}
1198511b41d2SMark Murray }
1199511b41d2SMark Murray 
1200a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
1201a04a10f8SKris Kennaway void
12025b9b2fafSBrian Feldman channel_input_oclose(int type, int plen, void *ctxt)
1203a04a10f8SKris Kennaway {
1204a04a10f8SKris Kennaway 	int id = packet_get_int();
1205a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
1206a04a10f8SKris Kennaway 	packet_integrity_check(plen, 4, type);
1207a04a10f8SKris Kennaway 	if (c == NULL)
1208a04a10f8SKris Kennaway 		packet_disconnect("Received oclose for nonexistent channel %d.", id);
1209a04a10f8SKris Kennaway 	chan_rcvd_oclose(c);
1210a04a10f8SKris Kennaway }
1211511b41d2SMark Murray 
1212511b41d2SMark Murray void
12135b9b2fafSBrian Feldman channel_input_close_confirmation(int type, int plen, void *ctxt)
1214511b41d2SMark Murray {
1215a04a10f8SKris Kennaway 	int id = packet_get_int();
1216a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
1217a04a10f8SKris Kennaway 
1218a04a10f8SKris Kennaway 	packet_done();
1219a04a10f8SKris Kennaway 	if (c == NULL)
1220a04a10f8SKris Kennaway 		packet_disconnect("Received close confirmation for "
1221a04a10f8SKris Kennaway 		    "out-of-range channel %d.", id);
1222a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_CLOSED)
1223a04a10f8SKris Kennaway 		packet_disconnect("Received close confirmation for "
1224a04a10f8SKris Kennaway 		    "non-closed channel %d (type %d).", id, c->type);
1225a04a10f8SKris Kennaway 	channel_free(c->self);
1226a04a10f8SKris Kennaway }
1227a04a10f8SKris Kennaway 
1228a04a10f8SKris Kennaway void
12295b9b2fafSBrian Feldman channel_input_open_confirmation(int type, int plen, void *ctxt)
1230a04a10f8SKris Kennaway {
1231a04a10f8SKris Kennaway 	int id, remote_id;
1232a04a10f8SKris Kennaway 	Channel *c;
1233a04a10f8SKris Kennaway 
1234a04a10f8SKris Kennaway 	if (!compat20)
1235a04a10f8SKris Kennaway 		packet_integrity_check(plen, 4 + 4, type);
1236a04a10f8SKris Kennaway 
1237a04a10f8SKris Kennaway 	id = packet_get_int();
1238a04a10f8SKris Kennaway 	c = channel_lookup(id);
1239a04a10f8SKris Kennaway 
1240a04a10f8SKris Kennaway 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
1241a04a10f8SKris Kennaway 		packet_disconnect("Received open confirmation for "
1242a04a10f8SKris Kennaway 		    "non-opening channel %d.", id);
1243a04a10f8SKris Kennaway 	remote_id = packet_get_int();
1244a04a10f8SKris Kennaway 	/* Record the remote channel number and mark that the channel is now open. */
1245a04a10f8SKris Kennaway 	c->remote_id = remote_id;
1246a04a10f8SKris Kennaway 	c->type = SSH_CHANNEL_OPEN;
1247a04a10f8SKris Kennaway 
1248a04a10f8SKris Kennaway 	if (compat20) {
1249a04a10f8SKris Kennaway 		c->remote_window = packet_get_int();
1250a04a10f8SKris Kennaway 		c->remote_maxpacket = packet_get_int();
1251a04a10f8SKris Kennaway 		packet_done();
1252a04a10f8SKris Kennaway 		if (c->cb_fn != NULL && c->cb_event == type) {
12535b9b2fafSBrian Feldman 			debug2("callback start");
1254a04a10f8SKris Kennaway 			c->cb_fn(c->self, c->cb_arg);
12555b9b2fafSBrian Feldman 			debug2("callback done");
1256a04a10f8SKris Kennaway 		}
1257a04a10f8SKris Kennaway 		debug("channel %d: open confirm rwindow %d rmax %d", c->self,
1258a04a10f8SKris Kennaway 		    c->remote_window, c->remote_maxpacket);
1259a04a10f8SKris Kennaway 	}
1260a04a10f8SKris Kennaway }
1261a04a10f8SKris Kennaway 
1262a04a10f8SKris Kennaway void
12635b9b2fafSBrian Feldman channel_input_open_failure(int type, int plen, void *ctxt)
1264a04a10f8SKris Kennaway {
1265a04a10f8SKris Kennaway 	int id;
1266a04a10f8SKris Kennaway 	Channel *c;
1267a04a10f8SKris Kennaway 
1268a04a10f8SKris Kennaway 	if (!compat20)
1269a04a10f8SKris Kennaway 		packet_integrity_check(plen, 4, type);
1270a04a10f8SKris Kennaway 
1271a04a10f8SKris Kennaway 	id = packet_get_int();
1272a04a10f8SKris Kennaway 	c = channel_lookup(id);
1273a04a10f8SKris Kennaway 
1274a04a10f8SKris Kennaway 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
1275a04a10f8SKris Kennaway 		packet_disconnect("Received open failure for "
1276a04a10f8SKris Kennaway 		    "non-opening channel %d.", id);
1277a04a10f8SKris Kennaway 	if (compat20) {
1278a04a10f8SKris Kennaway 		int reason = packet_get_int();
1279a04a10f8SKris Kennaway 		char *msg  = packet_get_string(NULL);
1280a04a10f8SKris Kennaway 		char *lang  = packet_get_string(NULL);
1281a04a10f8SKris Kennaway 		log("channel_open_failure: %d: reason %d: %s", id, reason, msg);
1282a04a10f8SKris Kennaway 		packet_done();
1283a04a10f8SKris Kennaway 		xfree(msg);
1284a04a10f8SKris Kennaway 		xfree(lang);
1285a04a10f8SKris Kennaway 	}
1286a04a10f8SKris Kennaway 	/* Free the channel.  This will also close the socket. */
1287a04a10f8SKris Kennaway 	channel_free(id);
1288a04a10f8SKris Kennaway }
1289a04a10f8SKris Kennaway 
1290a04a10f8SKris Kennaway void
12915b9b2fafSBrian Feldman channel_input_channel_request(int type, int plen, void *ctxt)
1292a04a10f8SKris Kennaway {
1293a04a10f8SKris Kennaway 	int id;
1294a04a10f8SKris Kennaway 	Channel *c;
1295a04a10f8SKris Kennaway 
1296a04a10f8SKris Kennaway 	id = packet_get_int();
1297a04a10f8SKris Kennaway 	c = channel_lookup(id);
1298a04a10f8SKris Kennaway 
1299a04a10f8SKris Kennaway 	if (c == NULL ||
1300a04a10f8SKris Kennaway 	    (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL))
1301a04a10f8SKris Kennaway 		packet_disconnect("Received request for "
1302a04a10f8SKris Kennaway 		    "non-open channel %d.", id);
1303a04a10f8SKris Kennaway 	if (c->cb_fn != NULL && c->cb_event == type) {
13045b9b2fafSBrian Feldman 		debug2("callback start");
1305a04a10f8SKris Kennaway 		c->cb_fn(c->self, c->cb_arg);
13065b9b2fafSBrian Feldman 		debug2("callback done");
1307a04a10f8SKris Kennaway 	} else {
1308a04a10f8SKris Kennaway 		char *service = packet_get_string(NULL);
1309a04a10f8SKris Kennaway 		debug("channel: %d rcvd request for %s", c->self, service);
1310a04a10f8SKris Kennaway 		debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event);
1311a04a10f8SKris Kennaway 		xfree(service);
1312a04a10f8SKris Kennaway 	}
1313a04a10f8SKris Kennaway }
1314a04a10f8SKris Kennaway 
1315a04a10f8SKris Kennaway void
13165b9b2fafSBrian Feldman channel_input_window_adjust(int type, int plen, void *ctxt)
1317a04a10f8SKris Kennaway {
1318a04a10f8SKris Kennaway 	Channel *c;
1319a04a10f8SKris Kennaway 	int id, adjust;
1320a04a10f8SKris Kennaway 
1321a04a10f8SKris Kennaway 	if (!compat20)
1322a04a10f8SKris Kennaway 		return;
1323511b41d2SMark Murray 
1324511b41d2SMark Murray 	/* Get the channel number and verify it. */
1325a04a10f8SKris Kennaway 	id = packet_get_int();
1326a04a10f8SKris Kennaway 	c = channel_lookup(id);
1327511b41d2SMark Murray 
1328a04a10f8SKris Kennaway 	if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
1329a04a10f8SKris Kennaway 		log("Received window adjust for "
1330a04a10f8SKris Kennaway 		    "non-open channel %d.", id);
1331511b41d2SMark Murray 		return;
1332511b41d2SMark Murray 	}
1333a04a10f8SKris Kennaway 	adjust = packet_get_int();
1334a04a10f8SKris Kennaway 	packet_done();
13355b9b2fafSBrian Feldman 	debug2("channel %d: rcvd adjust %d", id, adjust);
1336a04a10f8SKris Kennaway 	c->remote_window += adjust;
1337511b41d2SMark Murray }
1338511b41d2SMark Murray 
1339511b41d2SMark Murray /*
1340511b41d2SMark Murray  * Stops listening for channels, and removes any unix domain sockets that we
1341511b41d2SMark Murray  * might have.
1342511b41d2SMark Murray  */
1343511b41d2SMark Murray 
1344511b41d2SMark Murray void
1345511b41d2SMark Murray channel_stop_listening()
1346511b41d2SMark Murray {
1347511b41d2SMark Murray 	int i;
1348511b41d2SMark Murray 	for (i = 0; i < channels_alloc; i++) {
1349511b41d2SMark Murray 		switch (channels[i].type) {
1350511b41d2SMark Murray 		case SSH_CHANNEL_AUTH_SOCKET:
1351511b41d2SMark Murray 			close(channels[i].sock);
1352511b41d2SMark Murray 			remove(channels[i].path);
1353511b41d2SMark Murray 			channel_free(i);
1354511b41d2SMark Murray 			break;
1355511b41d2SMark Murray 		case SSH_CHANNEL_PORT_LISTENER:
1356511b41d2SMark Murray 		case SSH_CHANNEL_X11_LISTENER:
1357511b41d2SMark Murray 			close(channels[i].sock);
1358511b41d2SMark Murray 			channel_free(i);
1359511b41d2SMark Murray 			break;
1360511b41d2SMark Murray 		default:
1361511b41d2SMark Murray 			break;
1362511b41d2SMark Murray 		}
1363511b41d2SMark Murray 	}
1364511b41d2SMark Murray }
1365511b41d2SMark Murray 
1366511b41d2SMark Murray /*
1367a04a10f8SKris Kennaway  * Closes the sockets/fds of all channels.  This is used to close extra file
1368511b41d2SMark Murray  * descriptors after a fork.
1369511b41d2SMark Murray  */
1370511b41d2SMark Murray 
1371511b41d2SMark Murray void
1372511b41d2SMark Murray channel_close_all()
1373511b41d2SMark Murray {
1374511b41d2SMark Murray 	int i;
1375a04a10f8SKris Kennaway 	for (i = 0; i < channels_alloc; i++)
1376511b41d2SMark Murray 		if (channels[i].type != SSH_CHANNEL_FREE)
1377a04a10f8SKris Kennaway 			channel_close_fds(&channels[i]);
1378511b41d2SMark Murray }
1379511b41d2SMark Murray 
1380511b41d2SMark Murray /* Returns the maximum file descriptor number used by the channels. */
1381511b41d2SMark Murray 
1382511b41d2SMark Murray int
1383511b41d2SMark Murray channel_max_fd()
1384511b41d2SMark Murray {
1385511b41d2SMark Murray 	return channel_max_fd_value;
1386511b41d2SMark Murray }
1387511b41d2SMark Murray 
1388511b41d2SMark Murray /* Returns true if any channel is still open. */
1389511b41d2SMark Murray 
1390511b41d2SMark Murray int
1391511b41d2SMark Murray channel_still_open()
1392511b41d2SMark Murray {
1393511b41d2SMark Murray 	unsigned int i;
1394511b41d2SMark Murray 	for (i = 0; i < channels_alloc; i++)
1395511b41d2SMark Murray 		switch (channels[i].type) {
1396511b41d2SMark Murray 		case SSH_CHANNEL_FREE:
1397511b41d2SMark Murray 		case SSH_CHANNEL_X11_LISTENER:
1398511b41d2SMark Murray 		case SSH_CHANNEL_PORT_LISTENER:
1399511b41d2SMark Murray 		case SSH_CHANNEL_CLOSED:
1400511b41d2SMark Murray 		case SSH_CHANNEL_AUTH_SOCKET:
1401511b41d2SMark Murray 			continue;
1402a04a10f8SKris Kennaway 		case SSH_CHANNEL_LARVAL:
1403a04a10f8SKris Kennaway 			if (!compat20)
1404a04a10f8SKris Kennaway 				fatal("cannot happen: SSH_CHANNEL_LARVAL");
1405a04a10f8SKris Kennaway 			continue;
1406511b41d2SMark Murray 		case SSH_CHANNEL_OPENING:
1407511b41d2SMark Murray 		case SSH_CHANNEL_OPEN:
1408511b41d2SMark Murray 		case SSH_CHANNEL_X11_OPEN:
1409511b41d2SMark Murray 			return 1;
1410511b41d2SMark Murray 		case SSH_CHANNEL_INPUT_DRAINING:
1411511b41d2SMark Murray 		case SSH_CHANNEL_OUTPUT_DRAINING:
1412511b41d2SMark Murray 			if (!compat13)
1413511b41d2SMark Murray 				fatal("cannot happen: OUT_DRAIN");
1414511b41d2SMark Murray 			return 1;
1415511b41d2SMark Murray 		default:
1416511b41d2SMark Murray 			fatal("channel_still_open: bad channel type %d", channels[i].type);
1417511b41d2SMark Murray 			/* NOTREACHED */
1418511b41d2SMark Murray 		}
1419511b41d2SMark Murray 	return 0;
1420511b41d2SMark Murray }
1421511b41d2SMark Murray 
1422511b41d2SMark Murray /*
1423511b41d2SMark Murray  * Returns a message describing the currently open forwarded connections,
1424511b41d2SMark Murray  * suitable for sending to the client.  The message contains crlf pairs for
1425511b41d2SMark Murray  * newlines.
1426511b41d2SMark Murray  */
1427511b41d2SMark Murray 
1428511b41d2SMark Murray char *
1429511b41d2SMark Murray channel_open_message()
1430511b41d2SMark Murray {
1431511b41d2SMark Murray 	Buffer buffer;
1432511b41d2SMark Murray 	int i;
1433511b41d2SMark Murray 	char buf[512], *cp;
1434511b41d2SMark Murray 
1435511b41d2SMark Murray 	buffer_init(&buffer);
1436511b41d2SMark Murray 	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
1437511b41d2SMark Murray 	buffer_append(&buffer, buf, strlen(buf));
1438511b41d2SMark Murray 	for (i = 0; i < channels_alloc; i++) {
1439511b41d2SMark Murray 		Channel *c = &channels[i];
1440511b41d2SMark Murray 		switch (c->type) {
1441511b41d2SMark Murray 		case SSH_CHANNEL_FREE:
1442511b41d2SMark Murray 		case SSH_CHANNEL_X11_LISTENER:
1443511b41d2SMark Murray 		case SSH_CHANNEL_PORT_LISTENER:
1444511b41d2SMark Murray 		case SSH_CHANNEL_CLOSED:
1445511b41d2SMark Murray 		case SSH_CHANNEL_AUTH_SOCKET:
1446511b41d2SMark Murray 			continue;
1447a04a10f8SKris Kennaway 		case SSH_CHANNEL_LARVAL:
1448511b41d2SMark Murray 		case SSH_CHANNEL_OPENING:
1449511b41d2SMark Murray 		case SSH_CHANNEL_OPEN:
1450511b41d2SMark Murray 		case SSH_CHANNEL_X11_OPEN:
1451511b41d2SMark Murray 		case SSH_CHANNEL_INPUT_DRAINING:
1452511b41d2SMark Murray 		case SSH_CHANNEL_OUTPUT_DRAINING:
1453a04a10f8SKris Kennaway 			snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
1454511b41d2SMark Murray 			    c->self, c->remote_name,
1455511b41d2SMark Murray 			    c->type, c->remote_id,
1456511b41d2SMark Murray 			    c->istate, buffer_len(&c->input),
1457a04a10f8SKris Kennaway 			    c->ostate, buffer_len(&c->output),
1458a04a10f8SKris Kennaway 			    c->rfd, c->wfd);
1459511b41d2SMark Murray 			buffer_append(&buffer, buf, strlen(buf));
1460511b41d2SMark Murray 			continue;
1461511b41d2SMark Murray 		default:
1462a04a10f8SKris Kennaway 			fatal("channel_open_message: bad channel type %d", c->type);
1463511b41d2SMark Murray 			/* NOTREACHED */
1464511b41d2SMark Murray 		}
1465511b41d2SMark Murray 	}
1466511b41d2SMark Murray 	buffer_append(&buffer, "\0", 1);
1467511b41d2SMark Murray 	cp = xstrdup(buffer_ptr(&buffer));
1468511b41d2SMark Murray 	buffer_free(&buffer);
1469511b41d2SMark Murray 	return cp;
1470511b41d2SMark Murray }
1471511b41d2SMark Murray 
1472511b41d2SMark Murray /*
1473511b41d2SMark Murray  * Initiate forwarding of connections to local port "port" through the secure
1474511b41d2SMark Murray  * channel to host:port from remote side.
1475511b41d2SMark Murray  */
1476511b41d2SMark Murray 
1477511b41d2SMark Murray void
1478511b41d2SMark Murray channel_request_local_forwarding(u_short port, const char *host,
1479511b41d2SMark Murray 				 u_short host_port, int gateway_ports)
1480511b41d2SMark Murray {
1481511b41d2SMark Murray 	int success, ch, sock, on = 1;
1482511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
1483511b41d2SMark Murray 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
1484511b41d2SMark Murray 	struct linger linger;
1485511b41d2SMark Murray 
1486511b41d2SMark Murray 	if (strlen(host) > sizeof(channels[0].path) - 1)
1487511b41d2SMark Murray 		packet_disconnect("Forward host name too long.");
1488511b41d2SMark Murray 
1489511b41d2SMark Murray 	/*
1490511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
1491511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
1492511b41d2SMark Murray 	 */
1493511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
1494511b41d2SMark Murray 	hints.ai_family = IPv4or6;
1495511b41d2SMark Murray 	hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
1496511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
1497511b41d2SMark Murray 	snprintf(strport, sizeof strport, "%d", port);
1498511b41d2SMark Murray 	if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
1499511b41d2SMark Murray 		packet_disconnect("getaddrinfo: fatal error");
1500511b41d2SMark Murray 
1501511b41d2SMark Murray 	success = 0;
1502511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
1503511b41d2SMark Murray 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
1504511b41d2SMark Murray 			continue;
1505511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
1506511b41d2SMark Murray 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
1507511b41d2SMark Murray 			error("channel_request_local_forwarding: getnameinfo failed");
1508511b41d2SMark Murray 			continue;
1509511b41d2SMark Murray 		}
1510511b41d2SMark Murray 		/* Create a port to listen for the host. */
1511511b41d2SMark Murray 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
1512511b41d2SMark Murray 		if (sock < 0) {
1513511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
1514511b41d2SMark Murray 			verbose("socket: %.100s", strerror(errno));
1515511b41d2SMark Murray 			continue;
1516511b41d2SMark Murray 		}
1517511b41d2SMark Murray 		/*
1518511b41d2SMark Murray 		 * Set socket options.  We would like the socket to disappear
1519511b41d2SMark Murray 		 * as soon as it has been closed for whatever reason.
1520511b41d2SMark Murray 		 */
1521511b41d2SMark Murray 		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1522511b41d2SMark Murray 		linger.l_onoff = 1;
1523511b41d2SMark Murray 		linger.l_linger = 5;
1524511b41d2SMark Murray 		setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
1525511b41d2SMark Murray 		debug("Local forwarding listening on %s port %s.", ntop, strport);
1526511b41d2SMark Murray 
1527511b41d2SMark Murray 		/* Bind the socket to the address. */
1528511b41d2SMark Murray 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
1529511b41d2SMark Murray 			/* address can be in use ipv6 address is already bound */
1530511b41d2SMark Murray 			verbose("bind: %.100s", strerror(errno));
1531511b41d2SMark Murray 			close(sock);
1532511b41d2SMark Murray 			continue;
1533511b41d2SMark Murray 		}
1534511b41d2SMark Murray 		/* Start listening for connections on the socket. */
1535511b41d2SMark Murray 		if (listen(sock, 5) < 0) {
1536511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
1537511b41d2SMark Murray 			close(sock);
1538511b41d2SMark Murray 			continue;
1539511b41d2SMark Murray 		}
1540511b41d2SMark Murray 		/* Allocate a channel number for the socket. */
1541a04a10f8SKris Kennaway 		ch = channel_new(
1542a04a10f8SKris Kennaway 		    "port listener", SSH_CHANNEL_PORT_LISTENER,
1543a04a10f8SKris Kennaway 		    sock, sock, -1,
1544a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
15455b9b2fafSBrian Feldman 		    0, xstrdup("port listener"), 1);
1546511b41d2SMark Murray 		strlcpy(channels[ch].path, host, sizeof(channels[ch].path));
1547511b41d2SMark Murray 		channels[ch].host_port = host_port;
1548511b41d2SMark Murray 		channels[ch].listening_port = port;
1549511b41d2SMark Murray 		success = 1;
1550511b41d2SMark Murray 	}
1551511b41d2SMark Murray 	if (success == 0)
1552511b41d2SMark Murray 		packet_disconnect("cannot listen port: %d", port);
1553511b41d2SMark Murray 	freeaddrinfo(aitop);
1554511b41d2SMark Murray }
1555511b41d2SMark Murray 
1556511b41d2SMark Murray /*
1557511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
1558511b41d2SMark Murray  * the secure channel to host:port from local side.
1559511b41d2SMark Murray  */
1560511b41d2SMark Murray 
1561511b41d2SMark Murray void
1562a04a10f8SKris Kennaway channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect,
1563a04a10f8SKris Kennaway 				  u_short port_to_connect)
1564511b41d2SMark Murray {
1565511b41d2SMark Murray 	int payload_len;
1566511b41d2SMark Murray 	/* Record locally that connection to this host/port is permitted. */
1567511b41d2SMark Murray 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
1568511b41d2SMark Murray 		fatal("channel_request_remote_forwarding: too many forwards");
1569511b41d2SMark Murray 
1570a04a10f8SKris Kennaway 	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
1571a04a10f8SKris Kennaway 	permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
1572a04a10f8SKris Kennaway 	permitted_opens[num_permitted_opens].listen_port = listen_port;
1573511b41d2SMark Murray 	num_permitted_opens++;
1574511b41d2SMark Murray 
1575511b41d2SMark Murray 	/* Send the forward request to the remote side. */
1576a04a10f8SKris Kennaway 	if (compat20) {
1577a04a10f8SKris Kennaway 		const char *address_to_bind = "0.0.0.0";
1578a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_GLOBAL_REQUEST);
1579a04a10f8SKris Kennaway 		packet_put_cstring("tcpip-forward");
1580a04a10f8SKris Kennaway 		packet_put_char(0);			/* boolean: want reply */
1581a04a10f8SKris Kennaway 		packet_put_cstring(address_to_bind);
1582a04a10f8SKris Kennaway 		packet_put_int(listen_port);
1583a04a10f8SKris Kennaway 	} else {
1584511b41d2SMark Murray 		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
1585a04a10f8SKris Kennaway 		packet_put_int(listen_port);
1586a04a10f8SKris Kennaway 		packet_put_cstring(host_to_connect);
1587a04a10f8SKris Kennaway 		packet_put_int(port_to_connect);
1588511b41d2SMark Murray 		packet_send();
1589511b41d2SMark Murray 		packet_write_wait();
1590511b41d2SMark Murray 		/*
1591511b41d2SMark Murray 		 * Wait for response from the remote side.  It will send a disconnect
1592511b41d2SMark Murray 		 * message on failure, and we will never see it here.
1593511b41d2SMark Murray 		 */
1594511b41d2SMark Murray 		packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
1595511b41d2SMark Murray 	}
1596a04a10f8SKris Kennaway }
1597511b41d2SMark Murray 
1598511b41d2SMark Murray /*
1599511b41d2SMark Murray  * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
1600511b41d2SMark Murray  * listening for the port, and sends back a success reply (or disconnect
1601511b41d2SMark Murray  * message if there was an error).  This never returns if there was an error.
1602511b41d2SMark Murray  */
1603511b41d2SMark Murray 
1604511b41d2SMark Murray void
1605a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports)
1606511b41d2SMark Murray {
1607511b41d2SMark Murray 	u_short port, host_port;
1608511b41d2SMark Murray 	char *hostname;
1609511b41d2SMark Murray 
1610511b41d2SMark Murray 	/* Get arguments from the packet. */
1611511b41d2SMark Murray 	port = packet_get_int();
1612511b41d2SMark Murray 	hostname = packet_get_string(NULL);
1613511b41d2SMark Murray 	host_port = packet_get_int();
1614511b41d2SMark Murray 
1615511b41d2SMark Murray 	/*
1616511b41d2SMark Murray 	 * Check that an unprivileged user is not trying to forward a
1617511b41d2SMark Murray 	 * privileged port.
1618511b41d2SMark Murray 	 */
1619511b41d2SMark Murray 	if (port < IPPORT_RESERVED && !is_root)
1620511b41d2SMark Murray 		packet_disconnect("Requested forwarding of port %d but user is not root.",
1621511b41d2SMark Murray 				  port);
1622511b41d2SMark Murray 	/*
1623511b41d2SMark Murray 	 * Initiate forwarding,
1624511b41d2SMark Murray 	 */
1625a04a10f8SKris Kennaway 	channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
1626511b41d2SMark Murray 
1627511b41d2SMark Murray 	/* Free the argument string. */
1628511b41d2SMark Murray 	xfree(hostname);
1629511b41d2SMark Murray }
1630511b41d2SMark Murray 
1631a04a10f8SKris Kennaway /* XXX move to aux.c */
1632a04a10f8SKris Kennaway int
1633a04a10f8SKris Kennaway channel_connect_to(const char *host, u_short host_port)
1634511b41d2SMark Murray {
1635511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
1636511b41d2SMark Murray 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
1637511b41d2SMark Murray 	int gaierr;
1638a04a10f8SKris Kennaway 	int sock = -1;
1639511b41d2SMark Murray 
1640511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
1641511b41d2SMark Murray 	hints.ai_family = IPv4or6;
1642511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
1643511b41d2SMark Murray 	snprintf(strport, sizeof strport, "%d", host_port);
1644511b41d2SMark Murray 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
1645511b41d2SMark Murray 		error("%.100s: unknown host (%s)", host, gai_strerror(gaierr));
1646a04a10f8SKris Kennaway 		return -1;
1647511b41d2SMark Murray 	}
1648511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
1649511b41d2SMark Murray 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
1650511b41d2SMark Murray 			continue;
1651511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
1652511b41d2SMark Murray 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
1653a04a10f8SKris Kennaway 			error("channel_connect_to: getnameinfo failed");
1654511b41d2SMark Murray 			continue;
1655511b41d2SMark Murray 		}
1656511b41d2SMark Murray 		/* Create the socket. */
1657511b41d2SMark Murray 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
1658511b41d2SMark Murray 		if (sock < 0) {
1659511b41d2SMark Murray 			error("socket: %.100s", strerror(errno));
1660511b41d2SMark Murray 			continue;
1661511b41d2SMark Murray 		}
1662511b41d2SMark Murray 		/* Connect to the host/port. */
1663511b41d2SMark Murray 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
1664511b41d2SMark Murray 			error("connect %.100s port %s: %.100s", ntop, strport,
1665511b41d2SMark Murray 			    strerror(errno));
1666511b41d2SMark Murray 			close(sock);
1667511b41d2SMark Murray 			continue;	/* fail -- try next */
1668511b41d2SMark Murray 		}
1669511b41d2SMark Murray 		break; /* success */
1670511b41d2SMark Murray 
1671511b41d2SMark Murray 	}
1672511b41d2SMark Murray 	freeaddrinfo(aitop);
1673511b41d2SMark Murray 	if (!ai) {
1674511b41d2SMark Murray 		error("connect %.100s port %d: failed.", host, host_port);
1675a04a10f8SKris Kennaway 		return -1;
1676a04a10f8SKris Kennaway 	}
1677a04a10f8SKris Kennaway 	/* success */
1678a04a10f8SKris Kennaway 	return sock;
1679a04a10f8SKris Kennaway }
1680a04a10f8SKris Kennaway /*
1681a04a10f8SKris Kennaway  * This is called after receiving PORT_OPEN message.  This attempts to
1682a04a10f8SKris Kennaway  * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
1683a04a10f8SKris Kennaway  * or CHANNEL_OPEN_FAILURE.
1684a04a10f8SKris Kennaway  */
1685a04a10f8SKris Kennaway 
1686a04a10f8SKris Kennaway void
16875b9b2fafSBrian Feldman channel_input_port_open(int type, int plen, void *ctxt)
1688a04a10f8SKris Kennaway {
1689a04a10f8SKris Kennaway 	u_short host_port;
1690a04a10f8SKris Kennaway 	char *host, *originator_string;
1691a04a10f8SKris Kennaway 	int remote_channel, sock = -1, newch, i, denied;
1692a04a10f8SKris Kennaway 	unsigned int host_len, originator_len;
1693a04a10f8SKris Kennaway 
1694a04a10f8SKris Kennaway 	/* Get remote channel number. */
1695a04a10f8SKris Kennaway 	remote_channel = packet_get_int();
1696a04a10f8SKris Kennaway 
1697a04a10f8SKris Kennaway 	/* Get host name to connect to. */
1698a04a10f8SKris Kennaway 	host = packet_get_string(&host_len);
1699a04a10f8SKris Kennaway 
1700a04a10f8SKris Kennaway 	/* Get port to connect to. */
1701a04a10f8SKris Kennaway 	host_port = packet_get_int();
1702a04a10f8SKris Kennaway 
1703a04a10f8SKris Kennaway 	/* Get remote originator name. */
1704a04a10f8SKris Kennaway 	if (have_hostname_in_open) {
1705a04a10f8SKris Kennaway 		originator_string = packet_get_string(&originator_len);
1706a04a10f8SKris Kennaway 		originator_len += 4;	/* size of packet_int */
1707a04a10f8SKris Kennaway 	} else {
1708a04a10f8SKris Kennaway 		originator_string = xstrdup("unknown (remote did not supply name)");
1709a04a10f8SKris Kennaway 		originator_len = 0;	/* no originator supplied */
1710511b41d2SMark Murray 	}
1711511b41d2SMark Murray 
1712a04a10f8SKris Kennaway 	packet_integrity_check(plen,
1713a04a10f8SKris Kennaway 	    4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN);
1714511b41d2SMark Murray 
1715a04a10f8SKris Kennaway 	/* Check if opening that port is permitted. */
1716a04a10f8SKris Kennaway 	denied = 0;
1717a04a10f8SKris Kennaway 	if (!all_opens_permitted) {
1718a04a10f8SKris Kennaway 		/* Go trough all permitted ports. */
1719a04a10f8SKris Kennaway 		for (i = 0; i < num_permitted_opens; i++)
1720a04a10f8SKris Kennaway 			if (permitted_opens[i].port_to_connect == host_port &&
1721a04a10f8SKris Kennaway 			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
1722a04a10f8SKris Kennaway 				break;
1723a04a10f8SKris Kennaway 
1724a04a10f8SKris Kennaway 		/* Check if we found the requested port among those permitted. */
1725a04a10f8SKris Kennaway 		if (i >= num_permitted_opens) {
1726a04a10f8SKris Kennaway 			/* The port is not permitted. */
1727a04a10f8SKris Kennaway 			log("Received request to connect to %.100s:%d, but the request was denied.",
1728a04a10f8SKris Kennaway 			    host, host_port);
1729a04a10f8SKris Kennaway 			denied = 1;
1730a04a10f8SKris Kennaway 		}
1731a04a10f8SKris Kennaway 	}
1732a04a10f8SKris Kennaway 	sock = denied ? -1 : channel_connect_to(host, host_port);
1733a04a10f8SKris Kennaway 	if (sock > 0) {
1734511b41d2SMark Murray 		/* Allocate a channel for this connection. */
1735511b41d2SMark Murray 		newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
1736511b41d2SMark Murray 		channels[newch].remote_id = remote_channel;
1737511b41d2SMark Murray 
1738511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1739511b41d2SMark Murray 		packet_put_int(remote_channel);
1740511b41d2SMark Murray 		packet_put_int(newch);
1741511b41d2SMark Murray 		packet_send();
1742a04a10f8SKris Kennaway 	} else {
1743511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1744511b41d2SMark Murray 		packet_put_int(remote_channel);
1745511b41d2SMark Murray 		packet_send();
1746511b41d2SMark Murray 	}
1747a04a10f8SKris Kennaway 	xfree(host);
1748a04a10f8SKris Kennaway }
1749511b41d2SMark Murray 
1750511b41d2SMark Murray /*
1751511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
1752511b41d2SMark Murray  * Returns a suitable value for the DISPLAY variable, or NULL if an error
1753511b41d2SMark Murray  * occurs.
1754511b41d2SMark Murray  */
1755511b41d2SMark Murray 
1756511b41d2SMark Murray #define	NUM_SOCKS	10
1757511b41d2SMark Murray 
1758511b41d2SMark Murray char *
1759511b41d2SMark Murray x11_create_display_inet(int screen_number, int x11_display_offset)
1760511b41d2SMark Murray {
1761511b41d2SMark Murray 	int display_number, sock;
1762511b41d2SMark Murray 	u_short port;
1763511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
1764511b41d2SMark Murray 	char strport[NI_MAXSERV];
1765511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
1766511b41d2SMark Murray 	char display[512];
1767511b41d2SMark Murray 	char hostname[MAXHOSTNAMELEN];
1768511b41d2SMark Murray 
1769511b41d2SMark Murray 	for (display_number = x11_display_offset;
1770511b41d2SMark Murray 	     display_number < MAX_DISPLAYS;
1771511b41d2SMark Murray 	     display_number++) {
1772511b41d2SMark Murray 		port = 6000 + display_number;
1773511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
1774511b41d2SMark Murray 		hints.ai_family = IPv4or6;
1775511b41d2SMark Murray 		hints.ai_flags = AI_PASSIVE;		/* XXX loopback only ? */
1776511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
1777511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
1778511b41d2SMark Murray 		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
1779511b41d2SMark Murray 			error("getaddrinfo: %.100s", gai_strerror(gaierr));
1780511b41d2SMark Murray 			return NULL;
1781511b41d2SMark Murray 		}
1782511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
1783511b41d2SMark Murray 			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
1784511b41d2SMark Murray 				continue;
1785511b41d2SMark Murray 			sock = socket(ai->ai_family, SOCK_STREAM, 0);
1786511b41d2SMark Murray 			if (sock < 0) {
1787511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
1788511b41d2SMark Murray 				return NULL;
1789511b41d2SMark Murray 			}
1790511b41d2SMark Murray 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
1791511b41d2SMark Murray 				debug("bind port %d: %.100s", port, strerror(errno));
1792511b41d2SMark Murray 				shutdown(sock, SHUT_RDWR);
1793511b41d2SMark Murray 				close(sock);
1794511b41d2SMark Murray 				for (n = 0; n < num_socks; n++) {
1795511b41d2SMark Murray 					shutdown(socks[n], SHUT_RDWR);
1796511b41d2SMark Murray 					close(socks[n]);
1797511b41d2SMark Murray 				}
1798511b41d2SMark Murray 				num_socks = 0;
1799511b41d2SMark Murray 				break;
1800511b41d2SMark Murray 			}
1801511b41d2SMark Murray 			socks[num_socks++] = sock;
1802511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
1803511b41d2SMark Murray 				break;
1804511b41d2SMark Murray 		}
1805511b41d2SMark Murray 		if (num_socks > 0)
1806511b41d2SMark Murray 			break;
1807511b41d2SMark Murray 	}
1808511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
1809511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
1810511b41d2SMark Murray 		return NULL;
1811511b41d2SMark Murray 	}
1812511b41d2SMark Murray 	/* Start listening for connections on the socket. */
1813511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
1814511b41d2SMark Murray 		sock = socks[n];
1815511b41d2SMark Murray 		if (listen(sock, 5) < 0) {
1816511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
1817511b41d2SMark Murray 			shutdown(sock, SHUT_RDWR);
1818511b41d2SMark Murray 			close(sock);
1819511b41d2SMark Murray 			return NULL;
1820511b41d2SMark Murray 		}
1821511b41d2SMark Murray 	}
1822511b41d2SMark Murray 
1823511b41d2SMark Murray 	/* Set up a suitable value for the DISPLAY variable. */
1824511b41d2SMark Murray 	if (gethostname(hostname, sizeof(hostname)) < 0)
1825511b41d2SMark Murray 		fatal("gethostname: %.100s", strerror(errno));
1826511b41d2SMark Murray 	snprintf(display, sizeof display, "%.400s:%d.%d", hostname,
1827511b41d2SMark Murray 		 display_number, screen_number);
1828511b41d2SMark Murray 
1829511b41d2SMark Murray 	/* Allocate a channel for each socket. */
1830511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
1831511b41d2SMark Murray 		sock = socks[n];
1832a04a10f8SKris Kennaway 		(void) channel_new("x11 listener",
1833a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
1834a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
18355b9b2fafSBrian Feldman 		    0, xstrdup("X11 inet listener"), 1);
1836511b41d2SMark Murray 	}
1837511b41d2SMark Murray 
1838511b41d2SMark Murray 	/* Return a suitable value for the DISPLAY environment variable. */
1839511b41d2SMark Murray 	return xstrdup(display);
1840511b41d2SMark Murray }
1841511b41d2SMark Murray 
1842511b41d2SMark Murray #ifndef X_UNIX_PATH
1843511b41d2SMark Murray #define X_UNIX_PATH "/tmp/.X11-unix/X"
1844511b41d2SMark Murray #endif
1845511b41d2SMark Murray 
1846511b41d2SMark Murray static
1847511b41d2SMark Murray int
1848511b41d2SMark Murray connect_local_xsocket(unsigned int dnr)
1849511b41d2SMark Murray {
1850511b41d2SMark Murray 	static const char *const x_sockets[] = {
1851511b41d2SMark Murray 		X_UNIX_PATH "%u",
1852511b41d2SMark Murray 		"/var/X/.X11-unix/X" "%u",
1853511b41d2SMark Murray 		"/usr/spool/sockets/X11/" "%u",
1854511b41d2SMark Murray 		NULL
1855511b41d2SMark Murray 	};
1856511b41d2SMark Murray 	int sock;
1857511b41d2SMark Murray 	struct sockaddr_un addr;
1858511b41d2SMark Murray 	const char *const * path;
1859511b41d2SMark Murray 
1860511b41d2SMark Murray 	for (path = x_sockets; *path; ++path) {
1861511b41d2SMark Murray 		sock = socket(AF_UNIX, SOCK_STREAM, 0);
1862511b41d2SMark Murray 		if (sock < 0)
1863511b41d2SMark Murray 			error("socket: %.100s", strerror(errno));
1864511b41d2SMark Murray 		memset(&addr, 0, sizeof(addr));
1865511b41d2SMark Murray 		addr.sun_family = AF_UNIX;
1866511b41d2SMark Murray 		snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr);
1867511b41d2SMark Murray 		if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
1868511b41d2SMark Murray 			return sock;
1869511b41d2SMark Murray 		close(sock);
1870511b41d2SMark Murray 	}
1871511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
1872511b41d2SMark Murray 	return -1;
1873511b41d2SMark Murray }
1874511b41d2SMark Murray 
1875a04a10f8SKris Kennaway int
1876a04a10f8SKris Kennaway x11_connect_display(void)
1877511b41d2SMark Murray {
1878a04a10f8SKris Kennaway 	int display_number, sock = 0;
1879511b41d2SMark Murray 	const char *display;
1880a04a10f8SKris Kennaway 	char buf[1024], *cp;
1881511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
1882511b41d2SMark Murray 	char strport[NI_MAXSERV];
1883511b41d2SMark Murray 	int gaierr;
1884511b41d2SMark Murray 
1885511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
1886511b41d2SMark Murray 	display = getenv("DISPLAY");
1887511b41d2SMark Murray 	if (!display) {
1888511b41d2SMark Murray 		error("DISPLAY not set.");
1889a04a10f8SKris Kennaway 		return -1;
1890511b41d2SMark Murray 	}
1891511b41d2SMark Murray 	/*
1892511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
1893511b41d2SMark Murray 	 * connection to the real X server.
1894511b41d2SMark Murray 	 */
1895511b41d2SMark Murray 
1896511b41d2SMark Murray 	/*
1897511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
1898511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
1899511b41d2SMark Murray 	 */
1900511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
1901511b41d2SMark Murray 	    display[0] == ':') {
1902511b41d2SMark Murray 		/* Connect to the unix domain socket. */
1903511b41d2SMark Murray 		if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
1904511b41d2SMark Murray 			error("Could not parse display number from DISPLAY: %.100s",
1905511b41d2SMark Murray 			      display);
1906a04a10f8SKris Kennaway 			return -1;
1907511b41d2SMark Murray 		}
1908511b41d2SMark Murray 		/* Create a socket. */
1909511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
1910511b41d2SMark Murray 		if (sock < 0)
1911a04a10f8SKris Kennaway 			return -1;
1912511b41d2SMark Murray 
1913511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
1914a04a10f8SKris Kennaway 		return sock;
1915511b41d2SMark Murray 	}
1916511b41d2SMark Murray 	/*
1917511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
1918511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
1919511b41d2SMark Murray 	 */
1920511b41d2SMark Murray 	strncpy(buf, display, sizeof(buf));
1921511b41d2SMark Murray 	buf[sizeof(buf) - 1] = 0;
1922511b41d2SMark Murray 	cp = strchr(buf, ':');
1923511b41d2SMark Murray 	if (!cp) {
1924511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
1925a04a10f8SKris Kennaway 		return -1;
1926511b41d2SMark Murray 	}
1927511b41d2SMark Murray 	*cp = 0;
1928511b41d2SMark Murray 	/* buf now contains the host name.  But first we parse the display number. */
1929511b41d2SMark Murray 	if (sscanf(cp + 1, "%d", &display_number) != 1) {
1930511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
1931511b41d2SMark Murray 		      display);
1932a04a10f8SKris Kennaway 		return -1;
1933511b41d2SMark Murray 	}
1934511b41d2SMark Murray 
1935511b41d2SMark Murray 	/* Look up the host address */
1936511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
1937511b41d2SMark Murray 	hints.ai_family = IPv4or6;
1938511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
1939511b41d2SMark Murray 	snprintf(strport, sizeof strport, "%d", 6000 + display_number);
1940511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
1941511b41d2SMark Murray 		error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
1942a04a10f8SKris Kennaway 		return -1;
1943511b41d2SMark Murray 	}
1944511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
1945511b41d2SMark Murray 		/* Create a socket. */
1946511b41d2SMark Murray 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
1947511b41d2SMark Murray 		if (sock < 0) {
1948511b41d2SMark Murray 			debug("socket: %.100s", strerror(errno));
1949511b41d2SMark Murray 			continue;
1950511b41d2SMark Murray 		}
1951511b41d2SMark Murray 		/* Connect it to the display. */
1952511b41d2SMark Murray 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
1953a04a10f8SKris Kennaway 			debug("connect %.100s port %d: %.100s", buf,
1954a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
1955511b41d2SMark Murray 			close(sock);
1956511b41d2SMark Murray 			continue;
1957511b41d2SMark Murray 		}
1958511b41d2SMark Murray 		/* Success */
1959511b41d2SMark Murray 		break;
1960a04a10f8SKris Kennaway 	}
1961511b41d2SMark Murray 	freeaddrinfo(aitop);
1962511b41d2SMark Murray 	if (!ai) {
1963511b41d2SMark Murray 		error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
1964511b41d2SMark Murray 		    strerror(errno));
1965a04a10f8SKris Kennaway 		return -1;
1966511b41d2SMark Murray 	}
1967a04a10f8SKris Kennaway 	return sock;
1968a04a10f8SKris Kennaway }
1969511b41d2SMark Murray 
1970a04a10f8SKris Kennaway /*
1971a04a10f8SKris Kennaway  * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
1972a04a10f8SKris Kennaway  * the remote channel number.  We should do whatever we want, and respond
1973a04a10f8SKris Kennaway  * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
1974a04a10f8SKris Kennaway  */
1975a04a10f8SKris Kennaway 
1976a04a10f8SKris Kennaway void
19775b9b2fafSBrian Feldman x11_input_open(int type, int plen, void *ctxt)
1978a04a10f8SKris Kennaway {
1979a04a10f8SKris Kennaway 	int remote_channel, sock = 0, newch;
1980a04a10f8SKris Kennaway 	char *remote_host;
1981a04a10f8SKris Kennaway 	unsigned int remote_len;
1982a04a10f8SKris Kennaway 
1983a04a10f8SKris Kennaway 	/* Get remote channel number. */
1984a04a10f8SKris Kennaway 	remote_channel = packet_get_int();
1985a04a10f8SKris Kennaway 
1986a04a10f8SKris Kennaway 	/* Get remote originator name. */
1987a04a10f8SKris Kennaway 	if (have_hostname_in_open) {
1988a04a10f8SKris Kennaway 		remote_host = packet_get_string(&remote_len);
1989a04a10f8SKris Kennaway 		remote_len += 4;
1990a04a10f8SKris Kennaway 	} else {
1991a04a10f8SKris Kennaway 		remote_host = xstrdup("unknown (remote did not supply name)");
1992a04a10f8SKris Kennaway 		remote_len = 0;
1993a04a10f8SKris Kennaway 	}
1994a04a10f8SKris Kennaway 
1995a04a10f8SKris Kennaway 	debug("Received X11 open request.");
1996a04a10f8SKris Kennaway 	packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN);
1997a04a10f8SKris Kennaway 
1998a04a10f8SKris Kennaway 	/* Obtain a connection to the real X display. */
1999a04a10f8SKris Kennaway 	sock = x11_connect_display();
2000a04a10f8SKris Kennaway 	if (sock == -1) {
2001a04a10f8SKris Kennaway 		/* Send refusal to the remote host. */
2002a04a10f8SKris Kennaway 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
2003a04a10f8SKris Kennaway 		packet_put_int(remote_channel);
2004a04a10f8SKris Kennaway 		packet_send();
2005a04a10f8SKris Kennaway 	} else {
2006511b41d2SMark Murray 		/* Allocate a channel for this connection. */
2007a04a10f8SKris Kennaway 		newch = channel_allocate(
2008a04a10f8SKris Kennaway 		     (x11_saved_proto == NULL) ?
2009a04a10f8SKris Kennaway 		     SSH_CHANNEL_OPEN : SSH_CHANNEL_X11_OPEN,
2010a04a10f8SKris Kennaway 		     sock, remote_host);
2011511b41d2SMark Murray 		channels[newch].remote_id = remote_channel;
2012511b41d2SMark Murray 
2013511b41d2SMark Murray 		/* Send a confirmation to the remote host. */
2014511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
2015511b41d2SMark Murray 		packet_put_int(remote_channel);
2016511b41d2SMark Murray 		packet_put_int(newch);
2017511b41d2SMark Murray 		packet_send();
2018a04a10f8SKris Kennaway 	}
2019511b41d2SMark Murray }
2020511b41d2SMark Murray 
20215b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */
20225b9b2fafSBrian Feldman void
20235b9b2fafSBrian Feldman deny_input_open(int type, int plen, void *ctxt)
20245b9b2fafSBrian Feldman {
20255b9b2fafSBrian Feldman 	int rchan = packet_get_int();
20265b9b2fafSBrian Feldman 	switch(type){
20275b9b2fafSBrian Feldman 	case SSH_SMSG_AGENT_OPEN:
20285b9b2fafSBrian Feldman 		error("Warning: ssh server tried agent forwarding.");
20295b9b2fafSBrian Feldman 		break;
20305b9b2fafSBrian Feldman 	case SSH_SMSG_X11_OPEN:
20315b9b2fafSBrian Feldman 		error("Warning: ssh server tried X11 forwarding.");
20325b9b2fafSBrian Feldman 		break;
20335b9b2fafSBrian Feldman 	default:
20345b9b2fafSBrian Feldman 		error("deny_input_open: type %d plen %d", type, plen);
20355b9b2fafSBrian Feldman 		break;
20365b9b2fafSBrian Feldman 	}
20375b9b2fafSBrian Feldman 	error("Warning: this is probably a break in attempt by a malicious server.");
20385b9b2fafSBrian Feldman 	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
20395b9b2fafSBrian Feldman 	packet_put_int(rchan);
20405b9b2fafSBrian Feldman 	packet_send();
20415b9b2fafSBrian Feldman }
20425b9b2fafSBrian Feldman 
2043511b41d2SMark Murray /*
2044511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
2045511b41d2SMark Murray  * data, and enables authentication spoofing.
2046511b41d2SMark Murray  */
2047511b41d2SMark Murray 
2048511b41d2SMark Murray void
2049a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id,
2050a04a10f8SKris Kennaway     const char *proto, const char *data)
2051511b41d2SMark Murray {
2052511b41d2SMark Murray 	unsigned int data_len = (unsigned int) strlen(data) / 2;
2053511b41d2SMark Murray 	unsigned int i, value;
2054511b41d2SMark Murray 	char *new_data;
2055511b41d2SMark Murray 	int screen_number;
2056511b41d2SMark Murray 	const char *cp;
2057511b41d2SMark Murray 	u_int32_t rand = 0;
2058511b41d2SMark Murray 
2059511b41d2SMark Murray 	cp = getenv("DISPLAY");
2060511b41d2SMark Murray 	if (cp)
2061511b41d2SMark Murray 		cp = strchr(cp, ':');
2062511b41d2SMark Murray 	if (cp)
2063511b41d2SMark Murray 		cp = strchr(cp, '.');
2064511b41d2SMark Murray 	if (cp)
2065511b41d2SMark Murray 		screen_number = atoi(cp + 1);
2066511b41d2SMark Murray 	else
2067511b41d2SMark Murray 		screen_number = 0;
2068511b41d2SMark Murray 
2069511b41d2SMark Murray 	/* Save protocol name. */
2070511b41d2SMark Murray 	x11_saved_proto = xstrdup(proto);
2071511b41d2SMark Murray 
2072511b41d2SMark Murray 	/*
2073511b41d2SMark Murray 	 * Extract real authentication data and generate fake data of the
2074511b41d2SMark Murray 	 * same length.
2075511b41d2SMark Murray 	 */
2076511b41d2SMark Murray 	x11_saved_data = xmalloc(data_len);
2077511b41d2SMark Murray 	x11_fake_data = xmalloc(data_len);
2078511b41d2SMark Murray 	for (i = 0; i < data_len; i++) {
2079511b41d2SMark Murray 		if (sscanf(data + 2 * i, "%2x", &value) != 1)
2080511b41d2SMark Murray 			fatal("x11_request_forwarding: bad authentication data: %.100s", data);
2081511b41d2SMark Murray 		if (i % 4 == 0)
2082511b41d2SMark Murray 			rand = arc4random();
2083511b41d2SMark Murray 		x11_saved_data[i] = value;
2084511b41d2SMark Murray 		x11_fake_data[i] = rand & 0xff;
2085511b41d2SMark Murray 		rand >>= 8;
2086511b41d2SMark Murray 	}
2087511b41d2SMark Murray 	x11_saved_data_len = data_len;
2088511b41d2SMark Murray 	x11_fake_data_len = data_len;
2089511b41d2SMark Murray 
2090511b41d2SMark Murray 	/* Convert the fake data into hex. */
2091511b41d2SMark Murray 	new_data = xmalloc(2 * data_len + 1);
2092511b41d2SMark Murray 	for (i = 0; i < data_len; i++)
2093511b41d2SMark Murray 		sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]);
2094511b41d2SMark Murray 
2095511b41d2SMark Murray 	/* Send the request packet. */
2096a04a10f8SKris Kennaway 	if (compat20) {
2097a04a10f8SKris Kennaway 		channel_request_start(client_session_id, "x11-req", 0);
2098a04a10f8SKris Kennaway 		packet_put_char(0);	/* XXX bool single connection */
2099a04a10f8SKris Kennaway 	} else {
2100511b41d2SMark Murray 		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
2101a04a10f8SKris Kennaway 	}
2102a04a10f8SKris Kennaway 	packet_put_cstring(proto);
2103a04a10f8SKris Kennaway 	packet_put_cstring(new_data);
2104511b41d2SMark Murray 	packet_put_int(screen_number);
2105511b41d2SMark Murray 	packet_send();
2106511b41d2SMark Murray 	packet_write_wait();
2107511b41d2SMark Murray 	xfree(new_data);
2108511b41d2SMark Murray }
2109511b41d2SMark Murray 
2110511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */
2111511b41d2SMark Murray 
2112511b41d2SMark Murray void
2113511b41d2SMark Murray auth_request_forwarding()
2114511b41d2SMark Murray {
2115511b41d2SMark Murray 	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
2116511b41d2SMark Murray 	packet_send();
2117511b41d2SMark Murray 	packet_write_wait();
2118511b41d2SMark Murray }
2119511b41d2SMark Murray 
2120511b41d2SMark Murray /*
2121511b41d2SMark Murray  * Returns the name of the forwarded authentication socket.  Returns NULL if
2122511b41d2SMark Murray  * there is no forwarded authentication socket.  The returned value points to
2123511b41d2SMark Murray  * a static buffer.
2124511b41d2SMark Murray  */
2125511b41d2SMark Murray 
2126511b41d2SMark Murray char *
2127511b41d2SMark Murray auth_get_socket_name()
2128511b41d2SMark Murray {
2129511b41d2SMark Murray 	return channel_forwarded_auth_socket_name;
2130511b41d2SMark Murray }
2131511b41d2SMark Murray 
2132511b41d2SMark Murray /* removes the agent forwarding socket */
2133511b41d2SMark Murray 
2134511b41d2SMark Murray void
2135511b41d2SMark Murray cleanup_socket(void)
2136511b41d2SMark Murray {
2137511b41d2SMark Murray 	remove(channel_forwarded_auth_socket_name);
2138511b41d2SMark Murray 	rmdir(channel_forwarded_auth_socket_dir);
2139511b41d2SMark Murray }
2140511b41d2SMark Murray 
2141511b41d2SMark Murray /*
2142fcee55a2SKris Kennaway  * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
2143511b41d2SMark Murray  * This starts forwarding authentication requests.
2144511b41d2SMark Murray  */
2145511b41d2SMark Murray 
2146fcee55a2SKris Kennaway int
2147511b41d2SMark Murray auth_input_request_forwarding(struct passwd * pw)
2148511b41d2SMark Murray {
2149511b41d2SMark Murray 	int sock, newch;
2150511b41d2SMark Murray 	struct sockaddr_un sunaddr;
2151511b41d2SMark Murray 
2152511b41d2SMark Murray 	if (auth_get_socket_name() != NULL)
2153511b41d2SMark Murray 		fatal("Protocol error: authentication forwarding requested twice.");
2154511b41d2SMark Murray 
2155511b41d2SMark Murray 	/* Temporarily drop privileged uid for mkdir/bind. */
2156511b41d2SMark Murray 	temporarily_use_uid(pw->pw_uid);
2157511b41d2SMark Murray 
2158511b41d2SMark Murray 	/* Allocate a buffer for the socket name, and format the name. */
2159511b41d2SMark Murray 	channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME);
2160511b41d2SMark Murray 	channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME);
2161511b41d2SMark Murray 	strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME);
2162511b41d2SMark Murray 
2163511b41d2SMark Murray 	/* Create private directory for socket */
2164fcee55a2SKris Kennaway 	if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) {
2165fcee55a2SKris Kennaway 		packet_send_debug("Agent forwarding disabled: mkdtemp() failed: %.100s",
2166fcee55a2SKris Kennaway 		    strerror(errno));
2167fcee55a2SKris Kennaway 		restore_uid();
2168fcee55a2SKris Kennaway 		xfree(channel_forwarded_auth_socket_name);
2169fcee55a2SKris Kennaway 		xfree(channel_forwarded_auth_socket_dir);
2170fcee55a2SKris Kennaway 		channel_forwarded_auth_socket_name = NULL;
2171fcee55a2SKris Kennaway 		channel_forwarded_auth_socket_dir = NULL;
2172fcee55a2SKris Kennaway 		return 0;
2173fcee55a2SKris Kennaway 	}
2174511b41d2SMark Murray 	snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d",
2175511b41d2SMark Murray 		 channel_forwarded_auth_socket_dir, (int) getpid());
2176511b41d2SMark Murray 
2177511b41d2SMark Murray 	if (atexit(cleanup_socket) < 0) {
2178511b41d2SMark Murray 		int saved = errno;
2179511b41d2SMark Murray 		cleanup_socket();
2180511b41d2SMark Murray 		packet_disconnect("socket: %.100s", strerror(saved));
2181511b41d2SMark Murray 	}
2182511b41d2SMark Murray 	/* Create the socket. */
2183511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
2184511b41d2SMark Murray 	if (sock < 0)
2185511b41d2SMark Murray 		packet_disconnect("socket: %.100s", strerror(errno));
2186511b41d2SMark Murray 
2187511b41d2SMark Murray 	/* Bind it to the name. */
2188511b41d2SMark Murray 	memset(&sunaddr, 0, sizeof(sunaddr));
2189511b41d2SMark Murray 	sunaddr.sun_family = AF_UNIX;
2190511b41d2SMark Murray 	strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name,
2191511b41d2SMark Murray 		sizeof(sunaddr.sun_path));
2192511b41d2SMark Murray 
2193511b41d2SMark Murray 	if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0)
2194511b41d2SMark Murray 		packet_disconnect("bind: %.100s", strerror(errno));
2195511b41d2SMark Murray 
2196511b41d2SMark Murray 	/* Restore the privileged uid. */
2197511b41d2SMark Murray 	restore_uid();
2198511b41d2SMark Murray 
2199511b41d2SMark Murray 	/* Start listening on the socket. */
2200511b41d2SMark Murray 	if (listen(sock, 5) < 0)
2201511b41d2SMark Murray 		packet_disconnect("listen: %.100s", strerror(errno));
2202511b41d2SMark Murray 
2203511b41d2SMark Murray 	/* Allocate a channel for the authentication agent socket. */
2204511b41d2SMark Murray 	newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock,
2205511b41d2SMark Murray 				 xstrdup("auth socket"));
2206511b41d2SMark Murray 	strlcpy(channels[newch].path, channel_forwarded_auth_socket_name,
2207511b41d2SMark Murray 	    sizeof(channels[newch].path));
2208fcee55a2SKris Kennaway 	return 1;
2209511b41d2SMark Murray }
2210511b41d2SMark Murray 
2211511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
2212511b41d2SMark Murray 
2213511b41d2SMark Murray void
22145b9b2fafSBrian Feldman auth_input_open_request(int type, int plen, void *ctxt)
2215511b41d2SMark Murray {
2216511b41d2SMark Murray 	int remch, sock, newch;
2217511b41d2SMark Murray 	char *dummyname;
2218511b41d2SMark Murray 
2219a04a10f8SKris Kennaway 	packet_integrity_check(plen, 4, type);
2220a04a10f8SKris Kennaway 
2221511b41d2SMark Murray 	/* Read the remote channel number from the message. */
2222511b41d2SMark Murray 	remch = packet_get_int();
2223511b41d2SMark Murray 
2224511b41d2SMark Murray 	/*
2225511b41d2SMark Murray 	 * Get a connection to the local authentication agent (this may again
2226511b41d2SMark Murray 	 * get forwarded).
2227511b41d2SMark Murray 	 */
2228511b41d2SMark Murray 	sock = ssh_get_authentication_socket();
2229511b41d2SMark Murray 
2230511b41d2SMark Murray 	/*
2231511b41d2SMark Murray 	 * If we could not connect the agent, send an error message back to
2232511b41d2SMark Murray 	 * the server. This should never happen unless the agent dies,
2233511b41d2SMark Murray 	 * because authentication forwarding is only enabled if we have an
2234511b41d2SMark Murray 	 * agent.
2235511b41d2SMark Murray 	 */
2236511b41d2SMark Murray 	if (sock < 0) {
2237511b41d2SMark Murray 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
2238511b41d2SMark Murray 		packet_put_int(remch);
2239511b41d2SMark Murray 		packet_send();
2240511b41d2SMark Murray 		return;
2241511b41d2SMark Murray 	}
2242511b41d2SMark Murray 	debug("Forwarding authentication connection.");
2243511b41d2SMark Murray 
2244511b41d2SMark Murray 	/*
2245511b41d2SMark Murray 	 * Dummy host name.  This will be freed when the channel is freed; it
2246511b41d2SMark Murray 	 * will still be valid in the packet_put_string below since the
2247511b41d2SMark Murray 	 * channel cannot yet be freed at that point.
2248511b41d2SMark Murray 	 */
2249511b41d2SMark Murray 	dummyname = xstrdup("authentication agent connection");
2250511b41d2SMark Murray 
2251511b41d2SMark Murray 	newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname);
2252511b41d2SMark Murray 	channels[newch].remote_id = remch;
2253511b41d2SMark Murray 
2254511b41d2SMark Murray 	/* Send a confirmation to the remote host. */
2255511b41d2SMark Murray 	packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
2256511b41d2SMark Murray 	packet_put_int(remch);
2257511b41d2SMark Murray 	packet_put_int(newch);
2258511b41d2SMark Murray 	packet_send();
2259511b41d2SMark Murray }
2260a04a10f8SKris Kennaway 
2261a04a10f8SKris Kennaway void
2262a04a10f8SKris Kennaway channel_start_open(int id)
2263a04a10f8SKris Kennaway {
2264a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2265a04a10f8SKris Kennaway 	if (c == NULL) {
2266a04a10f8SKris Kennaway 		log("channel_open: %d: bad id", id);
2267a04a10f8SKris Kennaway 		return;
2268a04a10f8SKris Kennaway 	}
2269a04a10f8SKris Kennaway 	debug("send channel open %d", id);
2270a04a10f8SKris Kennaway 	packet_start(SSH2_MSG_CHANNEL_OPEN);
2271a04a10f8SKris Kennaway 	packet_put_cstring(c->ctype);
2272a04a10f8SKris Kennaway 	packet_put_int(c->self);
2273a04a10f8SKris Kennaway 	packet_put_int(c->local_window);
2274a04a10f8SKris Kennaway 	packet_put_int(c->local_maxpacket);
2275a04a10f8SKris Kennaway }
2276a04a10f8SKris Kennaway void
2277a04a10f8SKris Kennaway channel_open(int id)
2278a04a10f8SKris Kennaway {
2279a04a10f8SKris Kennaway 	/* XXX REMOVE ME */
2280a04a10f8SKris Kennaway 	channel_start_open(id);
2281a04a10f8SKris Kennaway 	packet_send();
2282a04a10f8SKris Kennaway }
2283a04a10f8SKris Kennaway void
2284a04a10f8SKris Kennaway channel_request(int id, char *service, int wantconfirm)
2285a04a10f8SKris Kennaway {
2286a04a10f8SKris Kennaway 	channel_request_start(id, service, wantconfirm);
2287a04a10f8SKris Kennaway 	packet_send();
2288a04a10f8SKris Kennaway 	debug("channel request %d: %s", id, service) ;
2289a04a10f8SKris Kennaway }
2290a04a10f8SKris Kennaway void
2291a04a10f8SKris Kennaway channel_request_start(int id, char *service, int wantconfirm)
2292a04a10f8SKris Kennaway {
2293a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2294a04a10f8SKris Kennaway 	if (c == NULL) {
2295a04a10f8SKris Kennaway 		log("channel_request: %d: bad id", id);
2296a04a10f8SKris Kennaway 		return;
2297a04a10f8SKris Kennaway 	}
2298a04a10f8SKris Kennaway 	packet_start(SSH2_MSG_CHANNEL_REQUEST);
2299a04a10f8SKris Kennaway 	packet_put_int(c->remote_id);
2300a04a10f8SKris Kennaway 	packet_put_cstring(service);
2301a04a10f8SKris Kennaway 	packet_put_char(wantconfirm);
2302a04a10f8SKris Kennaway }
2303a04a10f8SKris Kennaway void
2304a04a10f8SKris Kennaway channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg)
2305a04a10f8SKris Kennaway {
2306a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2307a04a10f8SKris Kennaway 	if (c == NULL) {
2308a04a10f8SKris Kennaway 		log("channel_register_callback: %d: bad id", id);
2309a04a10f8SKris Kennaway 		return;
2310a04a10f8SKris Kennaway 	}
2311a04a10f8SKris Kennaway 	c->cb_event = mtype;
2312a04a10f8SKris Kennaway 	c->cb_fn = fn;
2313a04a10f8SKris Kennaway 	c->cb_arg = arg;
2314a04a10f8SKris Kennaway }
2315a04a10f8SKris Kennaway void
2316a04a10f8SKris Kennaway channel_register_cleanup(int id, channel_callback_fn *fn)
2317a04a10f8SKris Kennaway {
2318a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2319a04a10f8SKris Kennaway 	if (c == NULL) {
2320a04a10f8SKris Kennaway 		log("channel_register_cleanup: %d: bad id", id);
2321a04a10f8SKris Kennaway 		return;
2322a04a10f8SKris Kennaway 	}
2323a04a10f8SKris Kennaway 	c->dettach_user = fn;
2324a04a10f8SKris Kennaway }
2325a04a10f8SKris Kennaway void
2326a04a10f8SKris Kennaway channel_cancel_cleanup(int id)
2327a04a10f8SKris Kennaway {
2328a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2329a04a10f8SKris Kennaway 	if (c == NULL) {
2330a04a10f8SKris Kennaway 		log("channel_cancel_cleanup: %d: bad id", id);
2331a04a10f8SKris Kennaway 		return;
2332a04a10f8SKris Kennaway 	}
2333a04a10f8SKris Kennaway 	c->dettach_user = NULL;
2334a04a10f8SKris Kennaway }
2335b66f2d16SKris Kennaway void
2336b66f2d16SKris Kennaway channel_register_filter(int id, channel_filter_fn *fn)
2337b66f2d16SKris Kennaway {
2338b66f2d16SKris Kennaway 	Channel *c = channel_lookup(id);
2339b66f2d16SKris Kennaway 	if (c == NULL) {
2340b66f2d16SKris Kennaway 		log("channel_register_filter: %d: bad id", id);
2341b66f2d16SKris Kennaway 		return;
2342b66f2d16SKris Kennaway 	}
2343b66f2d16SKris Kennaway 	c->input_filter = fn;
2344b66f2d16SKris Kennaway }
2345a04a10f8SKris Kennaway 
2346a04a10f8SKris Kennaway void
23475b9b2fafSBrian Feldman channel_set_fds(int id, int rfd, int wfd, int efd,
23485b9b2fafSBrian Feldman     int extusage, int nonblock)
2349a04a10f8SKris Kennaway {
2350a04a10f8SKris Kennaway 	Channel *c = channel_lookup(id);
2351a04a10f8SKris Kennaway 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
2352a04a10f8SKris Kennaway 		fatal("channel_activate for non-larval channel %d.", id);
23535b9b2fafSBrian Feldman 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
2354a04a10f8SKris Kennaway 	c->type = SSH_CHANNEL_OPEN;
2355a04a10f8SKris Kennaway 	/* XXX window size? */
2356b66f2d16SKris Kennaway 	c->local_window = c->local_window_max = c->local_maxpacket * 2;
2357a04a10f8SKris Kennaway 	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
2358a04a10f8SKris Kennaway 	packet_put_int(c->remote_id);
2359a04a10f8SKris Kennaway 	packet_put_int(c->local_window);
2360a04a10f8SKris Kennaway 	packet_send();
2361a04a10f8SKris Kennaway }
2362