channels.c revision 147005
157429Smarkm/*
257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
457429Smarkm *                    All rights reserved
557429Smarkm * This file contains functions for generic socket connection forwarding.
657429Smarkm * There is also code for initiating connection forwarding for X11 connections,
757429Smarkm * arbitrary tcp/ip connections, and the authentication agent connection.
860573Skris *
965668Skris * As far as I am concerned, the code I have written for this software
1065668Skris * can be used freely for any purpose.  Any derived versions of this
1165668Skris * software must be clearly marked as such, and if the derived work is
1265668Skris * incompatible with the protocol description in the RFC file, it must be
1365668Skris * called by a name other than "ssh" or "Secure Shell".
1465668Skris *
1560573Skris * SSH2 support added by Markus Friedl.
1692559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
1765668Skris * Copyright (c) 1999 Dug Song.  All rights reserved.
1865668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
1965668Skris *
2065668Skris * Redistribution and use in source and binary forms, with or without
2165668Skris * modification, are permitted provided that the following conditions
2265668Skris * are met:
2365668Skris * 1. Redistributions of source code must retain the above copyright
2465668Skris *    notice, this list of conditions and the following disclaimer.
2565668Skris * 2. Redistributions in binary form must reproduce the above copyright
2665668Skris *    notice, this list of conditions and the following disclaimer in the
2765668Skris *    documentation and/or other materials provided with the distribution.
2865668Skris *
2965668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
3065668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3165668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
3265668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3365668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3465668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3565668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3665668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3765668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3865668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3957429Smarkm */
4057429Smarkm
4157429Smarkm#include "includes.h"
42147005SdesRCSID("$OpenBSD: channels.c,v 1.214 2005/03/14 11:46:56 markus Exp $");
4357429Smarkm
4457429Smarkm#include "ssh.h"
4576262Sgreen#include "ssh1.h"
4676262Sgreen#include "ssh2.h"
4757429Smarkm#include "packet.h"
4857429Smarkm#include "xmalloc.h"
4976262Sgreen#include "log.h"
5076262Sgreen#include "misc.h"
5157429Smarkm#include "channels.h"
5257429Smarkm#include "compat.h"
5376262Sgreen#include "canohost.h"
5465668Skris#include "key.h"
5565668Skris#include "authfd.h"
5692559Sdes#include "pathnames.h"
57124207Sdes#include "bufaux.h"
5865668Skris
5992559Sdes/* -- channel core */
6057429Smarkm
61147005Sdes#define CHAN_RBUF	16*1024
62147005Sdes
6357429Smarkm/*
6457429Smarkm * Pointer to an array containing all allocated channels.  The array is
6557429Smarkm * dynamically extended as needed.
6657429Smarkm */
6792559Sdesstatic Channel **channels = NULL;
6857429Smarkm
6957429Smarkm/*
7057429Smarkm * Size of the channel array.  All slots of the array must always be
7192559Sdes * initialized (at least the type field); unused slots set to NULL
7257429Smarkm */
73137019Sdesstatic u_int channels_alloc = 0;
7457429Smarkm
7557429Smarkm/*
7657429Smarkm * Maximum file descriptor value used in any of the channels.  This is
7792559Sdes * updated in channel_new.
7857429Smarkm */
7976262Sgreenstatic int channel_max_fd = 0;
8057429Smarkm
8157429Smarkm
8292559Sdes/* -- tcp forwarding */
8357429Smarkm
8457429Smarkm/*
8557429Smarkm * Data structure for storing which hosts are permitted for forward requests.
8657429Smarkm * The local sides of any remote forwards are stored in this array to prevent
8757429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
8857429Smarkm * network (which might be behind a firewall).
8957429Smarkm */
9057429Smarkmtypedef struct {
9160573Skris	char *host_to_connect;		/* Connect to 'host'. */
9260573Skris	u_short port_to_connect;	/* Connect to 'port'. */
9360573Skris	u_short listen_port;		/* Remote side should listen port number. */
9457429Smarkm} ForwardPermission;
9557429Smarkm
9657429Smarkm/* List of all permitted host/port pairs to connect. */
9757429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
9892559Sdes
9957429Smarkm/* Number of permitted host/port pairs in the array. */
10057429Smarkmstatic int num_permitted_opens = 0;
10157429Smarkm/*
10257429Smarkm * If this is true, all opens are permitted.  This is the case on the server
10357429Smarkm * on which we have to trust the client anyway, and the user could do
10457429Smarkm * anything after logging in anyway.
10557429Smarkm */
10657429Smarkmstatic int all_opens_permitted = 0;
10757429Smarkm
10857429Smarkm
10992559Sdes/* -- X11 forwarding */
11092559Sdes
11192559Sdes/* Maximum number of fake X11 displays to try. */
11292559Sdes#define MAX_DISPLAYS  1000
11392559Sdes
11492559Sdes/* Saved X11 authentication protocol name. */
11592559Sdesstatic char *x11_saved_proto = NULL;
11692559Sdes
11792559Sdes/* Saved X11 authentication data.  This is the real data. */
11892559Sdesstatic char *x11_saved_data = NULL;
11992559Sdesstatic u_int x11_saved_data_len = 0;
12092559Sdes
12192559Sdes/*
12292559Sdes * Fake X11 authentication data.  This is what the server will be sending us;
12392559Sdes * we should replace any occurrences of this by the real data.
12492559Sdes */
12592559Sdesstatic char *x11_fake_data = NULL;
12692559Sdesstatic u_int x11_fake_data_len;
12792559Sdes
12892559Sdes
12992559Sdes/* -- agent forwarding */
13092559Sdes
13192559Sdes#define	NUM_SOCKS	10
13292559Sdes
13376262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */
13498941Sdesstatic int IPv4or6 = AF_UNSPEC;
13576262Sgreen
13692559Sdes/* helper */
13792559Sdesstatic void port_open_helper(Channel *c, char *rtype);
13876262Sgreen
13992559Sdes/* -- channel core */
14057429Smarkm
14160573SkrisChannel *
14260573Skrischannel_lookup(int id)
14360573Skris{
14460573Skris	Channel *c;
14592559Sdes
146137019Sdes	if (id < 0 || (u_int)id >= channels_alloc) {
147124207Sdes		logit("channel_lookup: %d: bad id", id);
14860573Skris		return NULL;
14960573Skris	}
15092559Sdes	c = channels[id];
15192559Sdes	if (c == NULL) {
152124207Sdes		logit("channel_lookup: %d: bad id: channel free", id);
15360573Skris		return NULL;
15460573Skris	}
15560573Skris	return c;
15660573Skris}
15760573Skris
15857429Smarkm/*
15960573Skris * Register filedescriptors for a channel, used when allocating a channel or
16060573Skris * when the channel consumer/producer is ready, e.g. shell exec'd
16160573Skris */
16260573Skris
16392559Sdesstatic void
16469587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd,
16569587Sgreen    int extusage, int nonblock)
16660573Skris{
16760573Skris	/* Update the maximum file descriptor value. */
16876262Sgreen	channel_max_fd = MAX(channel_max_fd, rfd);
16976262Sgreen	channel_max_fd = MAX(channel_max_fd, wfd);
17076262Sgreen	channel_max_fd = MAX(channel_max_fd, efd);
17176262Sgreen
17260573Skris	/* XXX set close-on-exec -markus */
17360573Skris
17460573Skris	c->rfd = rfd;
17560573Skris	c->wfd = wfd;
17660573Skris	c->sock = (rfd == wfd) ? rfd : -1;
177137019Sdes	c->ctl_fd = -1; /* XXX: set elsewhere */
17860573Skris	c->efd = efd;
17960573Skris	c->extended_usage = extusage;
18069587Sgreen
18174500Sgreen	/* XXX ugly hack: nonblock is only set by the server */
18274500Sgreen	if (nonblock && isatty(c->rfd)) {
183124207Sdes		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
18474500Sgreen		c->isatty = 1;
18574500Sgreen		if (!isatty(c->wfd)) {
18676262Sgreen			error("channel %d: wfd %d is not a tty?",
18774500Sgreen			    c->self, c->wfd);
18874500Sgreen		}
18974500Sgreen	} else {
19074500Sgreen		c->isatty = 0;
19174500Sgreen	}
192106130Sdes	c->wfd_isatty = isatty(c->wfd);
19374500Sgreen
19469587Sgreen	/* enable nonblocking mode */
19569587Sgreen	if (nonblock) {
19669587Sgreen		if (rfd != -1)
19769587Sgreen			set_nonblock(rfd);
19869587Sgreen		if (wfd != -1)
19969587Sgreen			set_nonblock(wfd);
20069587Sgreen		if (efd != -1)
20169587Sgreen			set_nonblock(efd);
20269587Sgreen	}
20360573Skris}
20460573Skris
20560573Skris/*
20657429Smarkm * Allocate a new channel object and set its type and socket. This will cause
20757429Smarkm * remote_name to be freed.
20857429Smarkm */
20957429Smarkm
21092559SdesChannel *
21160573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd,
21299063Sdes    u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
21357429Smarkm{
214137019Sdes	int found;
215137019Sdes	u_int i;
21657429Smarkm	Channel *c;
21757429Smarkm
21857429Smarkm	/* Do initial allocation if this is the first call. */
21957429Smarkm	if (channels_alloc == 0) {
22057429Smarkm		channels_alloc = 10;
22192559Sdes		channels = xmalloc(channels_alloc * sizeof(Channel *));
22257429Smarkm		for (i = 0; i < channels_alloc; i++)
22392559Sdes			channels[i] = NULL;
22457429Smarkm	}
22557429Smarkm	/* Try to find a free slot where to put the new channel. */
22657429Smarkm	for (found = -1, i = 0; i < channels_alloc; i++)
22792559Sdes		if (channels[i] == NULL) {
22857429Smarkm			/* Found a free slot. */
229137019Sdes			found = (int)i;
23057429Smarkm			break;
23157429Smarkm		}
232137019Sdes	if (found < 0) {
23357429Smarkm		/* There are no free slots.  Take last+1 slot and expand the array.  */
23457429Smarkm		found = channels_alloc;
23599063Sdes		if (channels_alloc > 10000)
23699063Sdes			fatal("channel_new: internal error: channels_alloc %d "
23799063Sdes			    "too big.", channels_alloc);
238120489Sjoe		channels = xrealloc(channels,
239120489Sjoe		    (channels_alloc + 10) * sizeof(Channel *));
240120489Sjoe		channels_alloc += 10;
24169587Sgreen		debug2("channel: expanding %d", channels_alloc);
24257429Smarkm		for (i = found; i < channels_alloc; i++)
24392559Sdes			channels[i] = NULL;
24457429Smarkm	}
24592559Sdes	/* Initialize and return new channel. */
24692559Sdes	c = channels[found] = xmalloc(sizeof(Channel));
24792559Sdes	memset(c, 0, sizeof(Channel));
24857429Smarkm	buffer_init(&c->input);
24957429Smarkm	buffer_init(&c->output);
25060573Skris	buffer_init(&c->extended);
25192559Sdes	c->ostate = CHAN_OUTPUT_OPEN;
25292559Sdes	c->istate = CHAN_INPUT_OPEN;
25392559Sdes	c->flags = 0;
25469587Sgreen	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
25557429Smarkm	c->self = found;
25657429Smarkm	c->type = type;
25760573Skris	c->ctype = ctype;
25860573Skris	c->local_window = window;
25960573Skris	c->local_window_max = window;
26060573Skris	c->local_consumed = 0;
26160573Skris	c->local_maxpacket = maxpack;
26257429Smarkm	c->remote_id = -1;
263124207Sdes	c->remote_name = xstrdup(remote_name);
26460573Skris	c->remote_window = 0;
26560573Skris	c->remote_maxpacket = 0;
26692559Sdes	c->force_drain = 0;
26792559Sdes	c->single_connection = 0;
26892559Sdes	c->detach_user = NULL;
26992559Sdes	c->confirm = NULL;
270137019Sdes	c->confirm_ctx = NULL;
27165668Skris	c->input_filter = NULL;
27257429Smarkm	debug("channel %d: new [%s]", found, remote_name);
27392559Sdes	return c;
27457429Smarkm}
27592559Sdes
27692559Sdesstatic int
27792559Sdeschannel_find_maxfd(void)
27892559Sdes{
279137019Sdes	u_int i;
280137019Sdes	int max = 0;
28192559Sdes	Channel *c;
28292559Sdes
28392559Sdes	for (i = 0; i < channels_alloc; i++) {
28492559Sdes		c = channels[i];
28592559Sdes		if (c != NULL) {
28692559Sdes			max = MAX(max, c->rfd);
28792559Sdes			max = MAX(max, c->wfd);
28892559Sdes			max = MAX(max, c->efd);
28992559Sdes		}
29092559Sdes	}
29192559Sdes	return max;
29292559Sdes}
29392559Sdes
29460573Skrisint
29592559Sdeschannel_close_fd(int *fdp)
29660573Skris{
29792559Sdes	int ret = 0, fd = *fdp;
29892559Sdes
29992559Sdes	if (fd != -1) {
30092559Sdes		ret = close(fd);
30192559Sdes		*fdp = -1;
30292559Sdes		if (fd == channel_max_fd)
30392559Sdes			channel_max_fd = channel_find_maxfd();
30492559Sdes	}
30592559Sdes	return ret;
30660573Skris}
30757429Smarkm
30860573Skris/* Close all channel fd/socket. */
30960573Skris
31092559Sdesstatic void
31160573Skrischannel_close_fds(Channel *c)
31257429Smarkm{
313137019Sdes	debug3("channel %d: close_fds r %d w %d e %d c %d",
314137019Sdes	    c->self, c->rfd, c->wfd, c->efd, c->ctl_fd);
31592559Sdes
31692559Sdes	channel_close_fd(&c->sock);
317137019Sdes	channel_close_fd(&c->ctl_fd);
31892559Sdes	channel_close_fd(&c->rfd);
31992559Sdes	channel_close_fd(&c->wfd);
32092559Sdes	channel_close_fd(&c->efd);
32160573Skris}
32257429Smarkm
32360573Skris/* Free the channel and close its fd/socket. */
32460573Skris
32560573Skrisvoid
32692559Sdeschannel_free(Channel *c)
32760573Skris{
32892559Sdes	char *s;
329137019Sdes	u_int i, n;
33076262Sgreen
33192559Sdes	for (n = 0, i = 0; i < channels_alloc; i++)
33292559Sdes		if (channels[i])
33392559Sdes			n++;
334137019Sdes	debug("channel %d: free: %s, nchannels %u", c->self,
33592559Sdes	    c->remote_name ? c->remote_name : "???", n);
33692559Sdes
33792559Sdes	s = channel_open_message();
338124207Sdes	debug3("channel %d: status: %s", c->self, s);
33976262Sgreen	xfree(s);
34076262Sgreen
34160573Skris	if (c->sock != -1)
34260573Skris		shutdown(c->sock, SHUT_RDWR);
343137019Sdes	if (c->ctl_fd != -1)
344137019Sdes		shutdown(c->ctl_fd, SHUT_RDWR);
34560573Skris	channel_close_fds(c);
34660573Skris	buffer_free(&c->input);
34760573Skris	buffer_free(&c->output);
34860573Skris	buffer_free(&c->extended);
34960573Skris	if (c->remote_name) {
35060573Skris		xfree(c->remote_name);
35160573Skris		c->remote_name = NULL;
35260573Skris	}
35392559Sdes	channels[c->self] = NULL;
35492559Sdes	xfree(c);
35557429Smarkm}
35657429Smarkm
35792559Sdesvoid
35892559Sdeschannel_free_all(void)
35992559Sdes{
360137019Sdes	u_int i;
36192559Sdes
36292559Sdes	for (i = 0; i < channels_alloc; i++)
36392559Sdes		if (channels[i] != NULL)
36492559Sdes			channel_free(channels[i]);
36592559Sdes}
36692559Sdes
36757429Smarkm/*
36892559Sdes * Closes the sockets/fds of all channels.  This is used to close extra file
36992559Sdes * descriptors after a fork.
37092559Sdes */
37192559Sdes
37292559Sdesvoid
37392559Sdeschannel_close_all(void)
37492559Sdes{
375137019Sdes	u_int i;
37692559Sdes
37792559Sdes	for (i = 0; i < channels_alloc; i++)
37892559Sdes		if (channels[i] != NULL)
37992559Sdes			channel_close_fds(channels[i]);
38092559Sdes}
38192559Sdes
38292559Sdes/*
38392559Sdes * Stop listening to channels.
38492559Sdes */
38592559Sdes
38692559Sdesvoid
38792559Sdeschannel_stop_listening(void)
38892559Sdes{
389137019Sdes	u_int i;
39092559Sdes	Channel *c;
39192559Sdes
39292559Sdes	for (i = 0; i < channels_alloc; i++) {
39392559Sdes		c = channels[i];
39492559Sdes		if (c != NULL) {
39592559Sdes			switch (c->type) {
39692559Sdes			case SSH_CHANNEL_AUTH_SOCKET:
39792559Sdes			case SSH_CHANNEL_PORT_LISTENER:
39892559Sdes			case SSH_CHANNEL_RPORT_LISTENER:
39992559Sdes			case SSH_CHANNEL_X11_LISTENER:
40092559Sdes				channel_close_fd(&c->sock);
40192559Sdes				channel_free(c);
40292559Sdes				break;
40392559Sdes			}
40492559Sdes		}
40592559Sdes	}
40692559Sdes}
40792559Sdes
40892559Sdes/*
40992559Sdes * Returns true if no channel has too much buffered data, and false if one or
41092559Sdes * more channel is overfull.
41192559Sdes */
41292559Sdes
41392559Sdesint
41492559Sdeschannel_not_very_much_buffered_data(void)
41592559Sdes{
41692559Sdes	u_int i;
41792559Sdes	Channel *c;
41892559Sdes
41992559Sdes	for (i = 0; i < channels_alloc; i++) {
42092559Sdes		c = channels[i];
42192559Sdes		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
42292559Sdes#if 0
42392559Sdes			if (!compat20 &&
42492559Sdes			    buffer_len(&c->input) > packet_get_maxsize()) {
425113911Sdes				debug2("channel %d: big input buffer %d",
42692559Sdes				    c->self, buffer_len(&c->input));
42792559Sdes				return 0;
42892559Sdes			}
42992559Sdes#endif
43092559Sdes			if (buffer_len(&c->output) > packet_get_maxsize()) {
431124207Sdes				debug2("channel %d: big output buffer %u > %u",
43292559Sdes				    c->self, buffer_len(&c->output),
43392559Sdes				    packet_get_maxsize());
43492559Sdes				return 0;
43592559Sdes			}
43692559Sdes		}
43792559Sdes	}
43892559Sdes	return 1;
43992559Sdes}
44092559Sdes
44192559Sdes/* Returns true if any channel is still open. */
44292559Sdes
44392559Sdesint
44492559Sdeschannel_still_open(void)
44592559Sdes{
446137019Sdes	u_int i;
44792559Sdes	Channel *c;
44892559Sdes
44992559Sdes	for (i = 0; i < channels_alloc; i++) {
45092559Sdes		c = channels[i];
45192559Sdes		if (c == NULL)
45292559Sdes			continue;
45392559Sdes		switch (c->type) {
45492559Sdes		case SSH_CHANNEL_X11_LISTENER:
45592559Sdes		case SSH_CHANNEL_PORT_LISTENER:
45692559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
45792559Sdes		case SSH_CHANNEL_CLOSED:
45892559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
45992559Sdes		case SSH_CHANNEL_DYNAMIC:
46092559Sdes		case SSH_CHANNEL_CONNECTING:
46192559Sdes		case SSH_CHANNEL_ZOMBIE:
46292559Sdes			continue;
46392559Sdes		case SSH_CHANNEL_LARVAL:
46492559Sdes			if (!compat20)
46592559Sdes				fatal("cannot happen: SSH_CHANNEL_LARVAL");
46692559Sdes			continue;
46792559Sdes		case SSH_CHANNEL_OPENING:
46892559Sdes		case SSH_CHANNEL_OPEN:
46992559Sdes		case SSH_CHANNEL_X11_OPEN:
47092559Sdes			return 1;
47192559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
47292559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
47392559Sdes			if (!compat13)
47492559Sdes				fatal("cannot happen: OUT_DRAIN");
47592559Sdes			return 1;
47692559Sdes		default:
47792559Sdes			fatal("channel_still_open: bad channel type %d", c->type);
47892559Sdes			/* NOTREACHED */
47992559Sdes		}
48092559Sdes	}
48192559Sdes	return 0;
48292559Sdes}
48392559Sdes
48492559Sdes/* Returns the id of an open channel suitable for keepaliving */
48592559Sdes
48692559Sdesint
48792559Sdeschannel_find_open(void)
48892559Sdes{
489137019Sdes	u_int i;
49092559Sdes	Channel *c;
49192559Sdes
49292559Sdes	for (i = 0; i < channels_alloc; i++) {
49392559Sdes		c = channels[i];
494137019Sdes		if (c == NULL || c->remote_id < 0)
49592559Sdes			continue;
49692559Sdes		switch (c->type) {
49792559Sdes		case SSH_CHANNEL_CLOSED:
49892559Sdes		case SSH_CHANNEL_DYNAMIC:
49992559Sdes		case SSH_CHANNEL_X11_LISTENER:
50092559Sdes		case SSH_CHANNEL_PORT_LISTENER:
50192559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
50292559Sdes		case SSH_CHANNEL_OPENING:
50392559Sdes		case SSH_CHANNEL_CONNECTING:
50492559Sdes		case SSH_CHANNEL_ZOMBIE:
50592559Sdes			continue;
50692559Sdes		case SSH_CHANNEL_LARVAL:
50792559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
50892559Sdes		case SSH_CHANNEL_OPEN:
50992559Sdes		case SSH_CHANNEL_X11_OPEN:
51092559Sdes			return i;
51192559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
51292559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
51392559Sdes			if (!compat13)
51492559Sdes				fatal("cannot happen: OUT_DRAIN");
51592559Sdes			return i;
51692559Sdes		default:
51792559Sdes			fatal("channel_find_open: bad channel type %d", c->type);
51892559Sdes			/* NOTREACHED */
51992559Sdes		}
52092559Sdes	}
52192559Sdes	return -1;
52292559Sdes}
52392559Sdes
52492559Sdes
52592559Sdes/*
52692559Sdes * Returns a message describing the currently open forwarded connections,
52792559Sdes * suitable for sending to the client.  The message contains crlf pairs for
52892559Sdes * newlines.
52992559Sdes */
53092559Sdes
53192559Sdeschar *
53292559Sdeschannel_open_message(void)
53392559Sdes{
53492559Sdes	Buffer buffer;
53592559Sdes	Channel *c;
53692559Sdes	char buf[1024], *cp;
537137019Sdes	u_int i;
53892559Sdes
53992559Sdes	buffer_init(&buffer);
54092559Sdes	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
54192559Sdes	buffer_append(&buffer, buf, strlen(buf));
54292559Sdes	for (i = 0; i < channels_alloc; i++) {
54392559Sdes		c = channels[i];
54492559Sdes		if (c == NULL)
54592559Sdes			continue;
54692559Sdes		switch (c->type) {
54792559Sdes		case SSH_CHANNEL_X11_LISTENER:
54892559Sdes		case SSH_CHANNEL_PORT_LISTENER:
54992559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
55092559Sdes		case SSH_CHANNEL_CLOSED:
55192559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
55292559Sdes		case SSH_CHANNEL_ZOMBIE:
55392559Sdes			continue;
55492559Sdes		case SSH_CHANNEL_LARVAL:
55592559Sdes		case SSH_CHANNEL_OPENING:
55692559Sdes		case SSH_CHANNEL_CONNECTING:
55792559Sdes		case SSH_CHANNEL_DYNAMIC:
55892559Sdes		case SSH_CHANNEL_OPEN:
55992559Sdes		case SSH_CHANNEL_X11_OPEN:
56092559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
56192559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
562137019Sdes			snprintf(buf, sizeof buf,
563137019Sdes			    "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n",
56492559Sdes			    c->self, c->remote_name,
56592559Sdes			    c->type, c->remote_id,
56692559Sdes			    c->istate, buffer_len(&c->input),
56792559Sdes			    c->ostate, buffer_len(&c->output),
568137019Sdes			    c->rfd, c->wfd, c->ctl_fd);
56992559Sdes			buffer_append(&buffer, buf, strlen(buf));
57092559Sdes			continue;
57192559Sdes		default:
57292559Sdes			fatal("channel_open_message: bad channel type %d", c->type);
57392559Sdes			/* NOTREACHED */
57492559Sdes		}
57592559Sdes	}
57692559Sdes	buffer_append(&buffer, "\0", 1);
57792559Sdes	cp = xstrdup(buffer_ptr(&buffer));
57892559Sdes	buffer_free(&buffer);
57992559Sdes	return cp;
58092559Sdes}
58192559Sdes
58292559Sdesvoid
58392559Sdeschannel_send_open(int id)
58492559Sdes{
58592559Sdes	Channel *c = channel_lookup(id);
586106130Sdes
58792559Sdes	if (c == NULL) {
588124207Sdes		logit("channel_send_open: %d: bad id", id);
58992559Sdes		return;
59092559Sdes	}
591113911Sdes	debug2("channel %d: send open", id);
59292559Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
59392559Sdes	packet_put_cstring(c->ctype);
59492559Sdes	packet_put_int(c->self);
59592559Sdes	packet_put_int(c->local_window);
59692559Sdes	packet_put_int(c->local_maxpacket);
59792559Sdes	packet_send();
59892559Sdes}
59992559Sdes
60092559Sdesvoid
601113911Sdeschannel_request_start(int id, char *service, int wantconfirm)
60292559Sdes{
603113911Sdes	Channel *c = channel_lookup(id);
604106130Sdes
60592559Sdes	if (c == NULL) {
606124207Sdes		logit("channel_request_start: %d: unknown channel id", id);
60792559Sdes		return;
60892559Sdes	}
609137019Sdes	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
61092559Sdes	packet_start(SSH2_MSG_CHANNEL_REQUEST);
61192559Sdes	packet_put_int(c->remote_id);
61292559Sdes	packet_put_cstring(service);
61392559Sdes	packet_put_char(wantconfirm);
61492559Sdes}
61592559Sdesvoid
616137019Sdeschannel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
61792559Sdes{
61892559Sdes	Channel *c = channel_lookup(id);
619106130Sdes
62092559Sdes	if (c == NULL) {
621124207Sdes		logit("channel_register_comfirm: %d: bad id", id);
62292559Sdes		return;
62392559Sdes	}
62492559Sdes	c->confirm = fn;
625137019Sdes	c->confirm_ctx = ctx;
62692559Sdes}
62792559Sdesvoid
62892559Sdeschannel_register_cleanup(int id, channel_callback_fn *fn)
62992559Sdes{
63092559Sdes	Channel *c = channel_lookup(id);
631106130Sdes
63292559Sdes	if (c == NULL) {
633124207Sdes		logit("channel_register_cleanup: %d: bad id", id);
63492559Sdes		return;
63592559Sdes	}
63692559Sdes	c->detach_user = fn;
63792559Sdes}
63892559Sdesvoid
63992559Sdeschannel_cancel_cleanup(int id)
64092559Sdes{
64192559Sdes	Channel *c = channel_lookup(id);
642106130Sdes
64392559Sdes	if (c == NULL) {
644124207Sdes		logit("channel_cancel_cleanup: %d: bad id", id);
64592559Sdes		return;
64692559Sdes	}
64792559Sdes	c->detach_user = NULL;
64892559Sdes}
64992559Sdesvoid
65092559Sdeschannel_register_filter(int id, channel_filter_fn *fn)
65192559Sdes{
65292559Sdes	Channel *c = channel_lookup(id);
653106130Sdes
65492559Sdes	if (c == NULL) {
655124207Sdes		logit("channel_register_filter: %d: bad id", id);
65692559Sdes		return;
65792559Sdes	}
65892559Sdes	c->input_filter = fn;
65992559Sdes}
66092559Sdes
66192559Sdesvoid
66292559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd,
66392559Sdes    int extusage, int nonblock, u_int window_max)
66492559Sdes{
66592559Sdes	Channel *c = channel_lookup(id);
666106130Sdes
66792559Sdes	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
66892559Sdes		fatal("channel_activate for non-larval channel %d.", id);
66992559Sdes	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
67092559Sdes	c->type = SSH_CHANNEL_OPEN;
67192559Sdes	c->local_window = c->local_window_max = window_max;
67292559Sdes	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
67392559Sdes	packet_put_int(c->remote_id);
67492559Sdes	packet_put_int(c->local_window);
67592559Sdes	packet_send();
67692559Sdes}
67792559Sdes
67892559Sdes/*
67960573Skris * 'channel_pre*' are called just before select() to add any bits relevant to
68060573Skris * channels in the select bitmasks.
68157429Smarkm */
68260573Skris/*
68360573Skris * 'channel_post*': perform any appropriate operations for channels which
68460573Skris * have events pending.
68560573Skris */
68660573Skristypedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
68760573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
68860573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
68957429Smarkm
69092559Sdesstatic void
69160573Skrischannel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
69257429Smarkm{
69360573Skris	FD_SET(c->sock, readset);
69460573Skris}
69560573Skris
69692559Sdesstatic void
69776262Sgreenchannel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset)
69876262Sgreen{
69976262Sgreen	debug3("channel %d: waiting for connection", c->self);
70076262Sgreen	FD_SET(c->sock, writeset);
70176262Sgreen}
70276262Sgreen
70392559Sdesstatic void
70460573Skrischannel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
70560573Skris{
70660573Skris	if (buffer_len(&c->input) < packet_get_maxsize())
70760573Skris		FD_SET(c->sock, readset);
70860573Skris	if (buffer_len(&c->output) > 0)
70960573Skris		FD_SET(c->sock, writeset);
71060573Skris}
71160573Skris
71292559Sdesstatic void
71392559Sdeschannel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
71460573Skris{
71592559Sdes	u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
71660573Skris
717147005Sdes	/* check buffer limits */
718147005Sdes	limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF));
719147005Sdes
72060573Skris	if (c->istate == CHAN_INPUT_OPEN &&
72192559Sdes	    limit > 0 &&
72292559Sdes	    buffer_len(&c->input) < limit)
72360573Skris		FD_SET(c->rfd, readset);
72460573Skris	if (c->ostate == CHAN_OUTPUT_OPEN ||
72560573Skris	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
72660573Skris		if (buffer_len(&c->output) > 0) {
72760573Skris			FD_SET(c->wfd, writeset);
72860573Skris		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
72998684Sdes			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
73098684Sdes			       debug2("channel %d: obuf_empty delayed efd %d/(%d)",
73198684Sdes				   c->self, c->efd, buffer_len(&c->extended));
73298684Sdes			else
73398684Sdes				chan_obuf_empty(c);
73460573Skris		}
73560573Skris	}
73660573Skris	/** XXX check close conditions, too */
73792559Sdes	if (compat20 && c->efd != -1) {
73860573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
73960573Skris		    buffer_len(&c->extended) > 0)
74060573Skris			FD_SET(c->efd, writeset);
74198684Sdes		else if (!(c->flags & CHAN_EOF_SENT) &&
74298684Sdes		    c->extended_usage == CHAN_EXTENDED_READ &&
74360573Skris		    buffer_len(&c->extended) < c->remote_window)
74460573Skris			FD_SET(c->efd, readset);
74560573Skris	}
746137019Sdes	/* XXX: What about efd? races? */
747137019Sdes	if (compat20 && c->ctl_fd != -1 &&
748137019Sdes	    c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN)
749137019Sdes		FD_SET(c->ctl_fd, readset);
75060573Skris}
75160573Skris
75292559Sdesstatic void
75360573Skrischannel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
75460573Skris{
75560573Skris	if (buffer_len(&c->input) == 0) {
75660573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
75760573Skris		packet_put_int(c->remote_id);
75860573Skris		packet_send();
75960573Skris		c->type = SSH_CHANNEL_CLOSED;
760124207Sdes		debug2("channel %d: closing after input drain.", c->self);
76160573Skris	}
76260573Skris}
76360573Skris
76492559Sdesstatic void
76560573Skrischannel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
76660573Skris{
76760573Skris	if (buffer_len(&c->output) == 0)
76892559Sdes		chan_mark_dead(c);
76960573Skris	else
77060573Skris		FD_SET(c->sock, writeset);
77160573Skris}
77260573Skris
77360573Skris/*
77460573Skris * This is a special state for X11 authentication spoofing.  An opened X11
77560573Skris * connection (when authentication spoofing is being done) remains in this
77660573Skris * state until the first packet has been completely read.  The authentication
77760573Skris * data in that packet is then substituted by the real data if it matches the
77860573Skris * fake data, and the channel is put into normal mode.
77960573Skris * XXX All this happens at the client side.
78092559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
78160573Skris */
78292559Sdesstatic int
78392559Sdesx11_open_helper(Buffer *b)
78460573Skris{
78576262Sgreen	u_char *ucp;
78676262Sgreen	u_int proto_len, data_len;
78757429Smarkm
78860573Skris	/* Check if the fixed size part of the packet is in buffer. */
78992559Sdes	if (buffer_len(b) < 12)
79060573Skris		return 0;
79157429Smarkm
79260573Skris	/* Parse the lengths of variable-length fields. */
79392559Sdes	ucp = buffer_ptr(b);
79460573Skris	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
79560573Skris		proto_len = 256 * ucp[6] + ucp[7];
79660573Skris		data_len = 256 * ucp[8] + ucp[9];
79760573Skris	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
79860573Skris		proto_len = ucp[6] + 256 * ucp[7];
79960573Skris		data_len = ucp[8] + 256 * ucp[9];
80060573Skris	} else {
801124207Sdes		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
80292559Sdes		    ucp[0]);
80360573Skris		return -1;
80460573Skris	}
80557429Smarkm
80660573Skris	/* Check if the whole packet is in buffer. */
80792559Sdes	if (buffer_len(b) <
80860573Skris	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
80960573Skris		return 0;
81057429Smarkm
81160573Skris	/* Check if authentication protocol matches. */
81260573Skris	if (proto_len != strlen(x11_saved_proto) ||
81360573Skris	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
814124207Sdes		debug2("X11 connection uses different authentication protocol.");
81560573Skris		return -1;
81660573Skris	}
81760573Skris	/* Check if authentication data matches our fake data. */
81860573Skris	if (data_len != x11_fake_data_len ||
81960573Skris	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
82060573Skris		x11_fake_data, x11_fake_data_len) != 0) {
821124207Sdes		debug2("X11 auth data does not match fake data.");
82260573Skris		return -1;
82360573Skris	}
82460573Skris	/* Check fake data length */
82560573Skris	if (x11_fake_data_len != x11_saved_data_len) {
82660573Skris		error("X11 fake_data_len %d != saved_data_len %d",
82760573Skris		    x11_fake_data_len, x11_saved_data_len);
82860573Skris		return -1;
82960573Skris	}
83060573Skris	/*
83160573Skris	 * Received authentication protocol and data match
83260573Skris	 * our fake data. Substitute the fake data with real
83360573Skris	 * data.
83460573Skris	 */
83560573Skris	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
83660573Skris	    x11_saved_data, x11_saved_data_len);
83760573Skris	return 1;
83860573Skris}
83957429Smarkm
84092559Sdesstatic void
84160573Skrischannel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
84260573Skris{
84392559Sdes	int ret = x11_open_helper(&c->output);
844106130Sdes
84560573Skris	if (ret == 1) {
84660573Skris		/* Start normal processing for the channel. */
84760573Skris		c->type = SSH_CHANNEL_OPEN;
84860573Skris		channel_pre_open_13(c, readset, writeset);
84960573Skris	} else if (ret == -1) {
85060573Skris		/*
85160573Skris		 * We have received an X11 connection that has bad
85260573Skris		 * authentication information.
85360573Skris		 */
854124207Sdes		logit("X11 connection rejected because of wrong authentication.");
85560573Skris		buffer_clear(&c->input);
85660573Skris		buffer_clear(&c->output);
85792559Sdes		channel_close_fd(&c->sock);
85860573Skris		c->sock = -1;
85960573Skris		c->type = SSH_CHANNEL_CLOSED;
86060573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
86160573Skris		packet_put_int(c->remote_id);
86260573Skris		packet_send();
86360573Skris	}
86460573Skris}
86557429Smarkm
86692559Sdesstatic void
86760573Skrischannel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
86860573Skris{
86992559Sdes	int ret = x11_open_helper(&c->output);
87092559Sdes
87192559Sdes	/* c->force_drain = 1; */
87292559Sdes
87360573Skris	if (ret == 1) {
87460573Skris		c->type = SSH_CHANNEL_OPEN;
87592559Sdes		channel_pre_open(c, readset, writeset);
87692559Sdes	} else if (ret == -1) {
877124207Sdes		logit("X11 connection rejected because of wrong authentication.");
878124207Sdes		debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
87992559Sdes		chan_read_failed(c);
88092559Sdes		buffer_clear(&c->input);
88192559Sdes		chan_ibuf_empty(c);
88292559Sdes		buffer_clear(&c->output);
88392559Sdes		/* for proto v1, the peer will send an IEOF */
88460573Skris		if (compat20)
88592559Sdes			chan_write_failed(c);
88660573Skris		else
88792559Sdes			c->type = SSH_CHANNEL_OPEN;
888124207Sdes		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
88960573Skris	}
89060573Skris}
89157429Smarkm
89276262Sgreen/* try to decode a socks4 header */
89392559Sdesstatic int
89476262Sgreenchannel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
89576262Sgreen{
896106130Sdes	char *p, *host;
89776262Sgreen	int len, have, i, found;
89892559Sdes	char username[256];
89976262Sgreen	struct {
90076262Sgreen		u_int8_t version;
90176262Sgreen		u_int8_t command;
90276262Sgreen		u_int16_t dest_port;
90376262Sgreen		struct in_addr dest_addr;
90476262Sgreen	} s4_req, s4_rsp;
90576262Sgreen
90676262Sgreen	debug2("channel %d: decode socks4", c->self);
90776262Sgreen
90876262Sgreen	have = buffer_len(&c->input);
90976262Sgreen	len = sizeof(s4_req);
91076262Sgreen	if (have < len)
91176262Sgreen		return 0;
91276262Sgreen	p = buffer_ptr(&c->input);
91376262Sgreen	for (found = 0, i = len; i < have; i++) {
91476262Sgreen		if (p[i] == '\0') {
91576262Sgreen			found = 1;
91676262Sgreen			break;
91776262Sgreen		}
91876262Sgreen		if (i > 1024) {
91976262Sgreen			/* the peer is probably sending garbage */
92076262Sgreen			debug("channel %d: decode socks4: too long",
92176262Sgreen			    c->self);
92276262Sgreen			return -1;
92376262Sgreen		}
92476262Sgreen	}
92576262Sgreen	if (!found)
92676262Sgreen		return 0;
92776262Sgreen	buffer_get(&c->input, (char *)&s4_req.version, 1);
92876262Sgreen	buffer_get(&c->input, (char *)&s4_req.command, 1);
92976262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
93076262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
93176262Sgreen	have = buffer_len(&c->input);
93276262Sgreen	p = buffer_ptr(&c->input);
93376262Sgreen	len = strlen(p);
93476262Sgreen	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
93576262Sgreen	if (len > have)
93676262Sgreen		fatal("channel %d: decode socks4: len %d > have %d",
93776262Sgreen		    c->self, len, have);
93876262Sgreen	strlcpy(username, p, sizeof(username));
93976262Sgreen	buffer_consume(&c->input, len);
94076262Sgreen	buffer_consume(&c->input, 1);		/* trailing '\0' */
94176262Sgreen
94276262Sgreen	host = inet_ntoa(s4_req.dest_addr);
94376262Sgreen	strlcpy(c->path, host, sizeof(c->path));
94476262Sgreen	c->host_port = ntohs(s4_req.dest_port);
94592559Sdes
946124207Sdes	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
94776262Sgreen	    c->self, host, c->host_port, s4_req.command);
94876262Sgreen
94976262Sgreen	if (s4_req.command != 1) {
95076262Sgreen		debug("channel %d: cannot handle: socks4 cn %d",
95176262Sgreen		    c->self, s4_req.command);
95276262Sgreen		return -1;
95376262Sgreen	}
95476262Sgreen	s4_rsp.version = 0;			/* vn: 0 for reply */
95576262Sgreen	s4_rsp.command = 90;			/* cd: req granted */
95676262Sgreen	s4_rsp.dest_port = 0;			/* ignored */
95776262Sgreen	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
95876262Sgreen	buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
95976262Sgreen	return 1;
96076262Sgreen}
96176262Sgreen
962124207Sdes/* try to decode a socks5 header */
963124207Sdes#define SSH_SOCKS5_AUTHDONE	0x1000
964124207Sdes#define SSH_SOCKS5_NOAUTH	0x00
965124207Sdes#define SSH_SOCKS5_IPV4		0x01
966124207Sdes#define SSH_SOCKS5_DOMAIN	0x03
967124207Sdes#define SSH_SOCKS5_IPV6		0x04
968124207Sdes#define SSH_SOCKS5_CONNECT	0x01
969124207Sdes#define SSH_SOCKS5_SUCCESS	0x00
970124207Sdes
971124207Sdesstatic int
972124207Sdeschannel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
973124207Sdes{
974124207Sdes	struct {
975124207Sdes		u_int8_t version;
976124207Sdes		u_int8_t command;
977124207Sdes		u_int8_t reserved;
978124207Sdes		u_int8_t atyp;
979124207Sdes	} s5_req, s5_rsp;
980124207Sdes	u_int16_t dest_port;
981124207Sdes	u_char *p, dest_addr[255+1];
982124207Sdes	int i, have, found, nmethods, addrlen, af;
983124207Sdes
984124207Sdes	debug2("channel %d: decode socks5", c->self);
985124207Sdes	p = buffer_ptr(&c->input);
986124207Sdes	if (p[0] != 0x05)
987124207Sdes		return -1;
988124207Sdes	have = buffer_len(&c->input);
989124207Sdes	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
990124207Sdes		/* format: ver | nmethods | methods */
991126273Sdes		if (have < 2)
992124207Sdes			return 0;
993124207Sdes		nmethods = p[1];
994124207Sdes		if (have < nmethods + 2)
995124207Sdes			return 0;
996124207Sdes		/* look for method: "NO AUTHENTICATION REQUIRED" */
997124207Sdes		for (found = 0, i = 2 ; i < nmethods + 2; i++) {
998124207Sdes			if (p[i] == SSH_SOCKS5_NOAUTH ) {
999124207Sdes				found = 1;
1000124207Sdes				break;
1001124207Sdes			}
1002124207Sdes		}
1003124207Sdes		if (!found) {
1004124207Sdes			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1005124207Sdes			    c->self);
1006124207Sdes			return -1;
1007124207Sdes		}
1008124207Sdes		buffer_consume(&c->input, nmethods + 2);
1009124207Sdes		buffer_put_char(&c->output, 0x05);		/* version */
1010124207Sdes		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
1011124207Sdes		FD_SET(c->sock, writeset);
1012124207Sdes		c->flags |= SSH_SOCKS5_AUTHDONE;
1013124207Sdes		debug2("channel %d: socks5 auth done", c->self);
1014124207Sdes		return 0;				/* need more */
1015124207Sdes	}
1016124207Sdes	debug2("channel %d: socks5 post auth", c->self);
1017124207Sdes	if (have < sizeof(s5_req)+1)
1018124207Sdes		return 0;			/* need more */
1019124207Sdes	memcpy((char *)&s5_req, p, sizeof(s5_req));
1020124207Sdes	if (s5_req.version != 0x05 ||
1021124207Sdes	    s5_req.command != SSH_SOCKS5_CONNECT ||
1022124207Sdes	    s5_req.reserved != 0x00) {
1023124207Sdes		debug2("channel %d: only socks5 connect supported", c->self);
1024124207Sdes		return -1;
1025124207Sdes	}
1026147005Sdes	switch (s5_req.atyp){
1027124207Sdes	case SSH_SOCKS5_IPV4:
1028124207Sdes		addrlen = 4;
1029124207Sdes		af = AF_INET;
1030124207Sdes		break;
1031124207Sdes	case SSH_SOCKS5_DOMAIN:
1032124207Sdes		addrlen = p[sizeof(s5_req)];
1033124207Sdes		af = -1;
1034124207Sdes		break;
1035124207Sdes	case SSH_SOCKS5_IPV6:
1036124207Sdes		addrlen = 16;
1037124207Sdes		af = AF_INET6;
1038124207Sdes		break;
1039124207Sdes	default:
1040124207Sdes		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1041124207Sdes		return -1;
1042124207Sdes	}
1043124207Sdes	if (have < 4 + addrlen + 2)
1044124207Sdes		return 0;
1045124207Sdes	buffer_consume(&c->input, sizeof(s5_req));
1046124207Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1047124207Sdes		buffer_consume(&c->input, 1);    /* host string length */
1048124207Sdes	buffer_get(&c->input, (char *)&dest_addr, addrlen);
1049124207Sdes	buffer_get(&c->input, (char *)&dest_port, 2);
1050124207Sdes	dest_addr[addrlen] = '\0';
1051124207Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1052137019Sdes		strlcpy(c->path, (char *)dest_addr, sizeof(c->path));
1053124207Sdes	else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL)
1054124207Sdes		return -1;
1055124207Sdes	c->host_port = ntohs(dest_port);
1056126273Sdes
1057124207Sdes	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1058124207Sdes	    c->self, c->path, c->host_port, s5_req.command);
1059124207Sdes
1060124207Sdes	s5_rsp.version = 0x05;
1061124207Sdes	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1062124207Sdes	s5_rsp.reserved = 0;			/* ignored */
1063124207Sdes	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1064124207Sdes	((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
1065124207Sdes	dest_port = 0;				/* ignored */
1066124207Sdes
1067124207Sdes	buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp));
1068124207Sdes	buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr));
1069124207Sdes	buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port));
1070124207Sdes	return 1;
1071124207Sdes}
1072124207Sdes
107376262Sgreen/* dynamic port forwarding */
107492559Sdesstatic void
107576262Sgreenchannel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
107676262Sgreen{
107776262Sgreen	u_char *p;
107876262Sgreen	int have, ret;
107976262Sgreen
108076262Sgreen	have = buffer_len(&c->input);
108192559Sdes	c->delayed = 0;
108276262Sgreen	debug2("channel %d: pre_dynamic: have %d", c->self, have);
108376262Sgreen	/* buffer_dump(&c->input); */
108476262Sgreen	/* check if the fixed size part of the packet is in buffer. */
1085124207Sdes	if (have < 3) {
108676262Sgreen		/* need more */
108776262Sgreen		FD_SET(c->sock, readset);
108876262Sgreen		return;
108976262Sgreen	}
109076262Sgreen	/* try to guess the protocol */
109176262Sgreen	p = buffer_ptr(&c->input);
109276262Sgreen	switch (p[0]) {
109376262Sgreen	case 0x04:
109476262Sgreen		ret = channel_decode_socks4(c, readset, writeset);
109576262Sgreen		break;
1096124207Sdes	case 0x05:
1097124207Sdes		ret = channel_decode_socks5(c, readset, writeset);
1098124207Sdes		break;
109976262Sgreen	default:
110076262Sgreen		ret = -1;
110176262Sgreen		break;
110276262Sgreen	}
110376262Sgreen	if (ret < 0) {
110492559Sdes		chan_mark_dead(c);
110576262Sgreen	} else if (ret == 0) {
110676262Sgreen		debug2("channel %d: pre_dynamic: need more", c->self);
110776262Sgreen		/* need more */
110876262Sgreen		FD_SET(c->sock, readset);
110976262Sgreen	} else {
111076262Sgreen		/* switch to the next state */
111176262Sgreen		c->type = SSH_CHANNEL_OPENING;
111276262Sgreen		port_open_helper(c, "direct-tcpip");
111376262Sgreen	}
111476262Sgreen}
111576262Sgreen
111660573Skris/* This is our fake X11 server socket. */
111792559Sdesstatic void
111860573Skrischannel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
111960573Skris{
112092559Sdes	Channel *nc;
112160573Skris	struct sockaddr addr;
112292559Sdes	int newsock;
112360573Skris	socklen_t addrlen;
112476262Sgreen	char buf[16384], *remote_ipaddr;
112560573Skris	int remote_port;
112657429Smarkm
112760573Skris	if (FD_ISSET(c->sock, readset)) {
112860573Skris		debug("X11 connection requested.");
112960573Skris		addrlen = sizeof(addr);
113060573Skris		newsock = accept(c->sock, &addr, &addrlen);
113192559Sdes		if (c->single_connection) {
1132124207Sdes			debug2("single_connection: closing X11 listener.");
113392559Sdes			channel_close_fd(&c->sock);
113492559Sdes			chan_mark_dead(c);
113592559Sdes		}
113660573Skris		if (newsock < 0) {
113760573Skris			error("accept: %.100s", strerror(errno));
113860573Skris			return;
113960573Skris		}
114092559Sdes		set_nodelay(newsock);
114176262Sgreen		remote_ipaddr = get_peer_ipaddr(newsock);
114260573Skris		remote_port = get_peer_port(newsock);
114360573Skris		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
114476262Sgreen		    remote_ipaddr, remote_port);
114557429Smarkm
114692559Sdes		nc = channel_new("accepted x11 socket",
114760573Skris		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1148124207Sdes		    c->local_window_max, c->local_maxpacket, 0, buf, 1);
114960573Skris		if (compat20) {
115060573Skris			packet_start(SSH2_MSG_CHANNEL_OPEN);
115160573Skris			packet_put_cstring("x11");
115292559Sdes			packet_put_int(nc->self);
115392559Sdes			packet_put_int(nc->local_window_max);
115492559Sdes			packet_put_int(nc->local_maxpacket);
115576262Sgreen			/* originator ipaddr and port */
115676262Sgreen			packet_put_cstring(remote_ipaddr);
115760573Skris			if (datafellows & SSH_BUG_X11FWD) {
1158124207Sdes				debug2("ssh2 x11 bug compat mode");
115957429Smarkm			} else {
116060573Skris				packet_put_int(remote_port);
116157429Smarkm			}
116260573Skris			packet_send();
116360573Skris		} else {
116460573Skris			packet_start(SSH_SMSG_X11_OPEN);
116592559Sdes			packet_put_int(nc->self);
116692559Sdes			if (packet_get_protocol_flags() &
116792559Sdes			    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
116892559Sdes				packet_put_cstring(buf);
116960573Skris			packet_send();
117057429Smarkm		}
117176262Sgreen		xfree(remote_ipaddr);
117257429Smarkm	}
117357429Smarkm}
117457429Smarkm
117592559Sdesstatic void
117676262Sgreenport_open_helper(Channel *c, char *rtype)
117776262Sgreen{
117876262Sgreen	int direct;
117976262Sgreen	char buf[1024];
118076262Sgreen	char *remote_ipaddr = get_peer_ipaddr(c->sock);
118176262Sgreen	u_short remote_port = get_peer_port(c->sock);
118276262Sgreen
118376262Sgreen	direct = (strcmp(rtype, "direct-tcpip") == 0);
118476262Sgreen
118576262Sgreen	snprintf(buf, sizeof buf,
118676262Sgreen	    "%s: listening port %d for %.100s port %d, "
118776262Sgreen	    "connect from %.200s port %d",
118876262Sgreen	    rtype, c->listening_port, c->path, c->host_port,
118976262Sgreen	    remote_ipaddr, remote_port);
119076262Sgreen
119176262Sgreen	xfree(c->remote_name);
119276262Sgreen	c->remote_name = xstrdup(buf);
119376262Sgreen
119476262Sgreen	if (compat20) {
119576262Sgreen		packet_start(SSH2_MSG_CHANNEL_OPEN);
119676262Sgreen		packet_put_cstring(rtype);
119776262Sgreen		packet_put_int(c->self);
119876262Sgreen		packet_put_int(c->local_window_max);
119976262Sgreen		packet_put_int(c->local_maxpacket);
120076262Sgreen		if (direct) {
120176262Sgreen			/* target host, port */
120276262Sgreen			packet_put_cstring(c->path);
120376262Sgreen			packet_put_int(c->host_port);
120476262Sgreen		} else {
120576262Sgreen			/* listen address, port */
120676262Sgreen			packet_put_cstring(c->path);
120776262Sgreen			packet_put_int(c->listening_port);
120876262Sgreen		}
120976262Sgreen		/* originator host and port */
121076262Sgreen		packet_put_cstring(remote_ipaddr);
121176262Sgreen		packet_put_int(remote_port);
121276262Sgreen		packet_send();
121376262Sgreen	} else {
121476262Sgreen		packet_start(SSH_MSG_PORT_OPEN);
121576262Sgreen		packet_put_int(c->self);
121676262Sgreen		packet_put_cstring(c->path);
121776262Sgreen		packet_put_int(c->host_port);
121892559Sdes		if (packet_get_protocol_flags() &
121992559Sdes		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
122076262Sgreen			packet_put_cstring(c->remote_name);
122176262Sgreen		packet_send();
122276262Sgreen	}
122376262Sgreen	xfree(remote_ipaddr);
122476262Sgreen}
122576262Sgreen
122657429Smarkm/*
122760573Skris * This socket is listening for connections to a forwarded TCP/IP port.
122857429Smarkm */
122992559Sdesstatic void
123060573Skrischannel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
123157429Smarkm{
123276262Sgreen	Channel *nc;
123357429Smarkm	struct sockaddr addr;
123492559Sdes	int newsock, nextstate;
123557429Smarkm	socklen_t addrlen;
123676262Sgreen	char *rtype;
123757429Smarkm
123860573Skris	if (FD_ISSET(c->sock, readset)) {
123960573Skris		debug("Connection to port %d forwarding "
124060573Skris		    "to %.100s port %d requested.",
124160573Skris		    c->listening_port, c->path, c->host_port);
124276262Sgreen
124392559Sdes		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
124492559Sdes			nextstate = SSH_CHANNEL_OPENING;
124592559Sdes			rtype = "forwarded-tcpip";
124692559Sdes		} else {
124792559Sdes			if (c->host_port == 0) {
124892559Sdes				nextstate = SSH_CHANNEL_DYNAMIC;
124992559Sdes				rtype = "dynamic-tcpip";
125092559Sdes			} else {
125192559Sdes				nextstate = SSH_CHANNEL_OPENING;
125292559Sdes				rtype = "direct-tcpip";
125392559Sdes			}
125492559Sdes		}
125576262Sgreen
125660573Skris		addrlen = sizeof(addr);
125760573Skris		newsock = accept(c->sock, &addr, &addrlen);
125860573Skris		if (newsock < 0) {
125960573Skris			error("accept: %.100s", strerror(errno));
126060573Skris			return;
126160573Skris		}
126292559Sdes		set_nodelay(newsock);
1263124207Sdes		nc = channel_new(rtype, nextstate, newsock, newsock, -1,
1264124207Sdes		    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
126576262Sgreen		nc->listening_port = c->listening_port;
126676262Sgreen		nc->host_port = c->host_port;
126776262Sgreen		strlcpy(nc->path, c->path, sizeof(nc->path));
126876262Sgreen
126992559Sdes		if (nextstate == SSH_CHANNEL_DYNAMIC) {
127092559Sdes			/*
127192559Sdes			 * do not call the channel_post handler until
127292559Sdes			 * this flag has been reset by a pre-handler.
127392559Sdes			 * otherwise the FD_ISSET calls might overflow
127492559Sdes			 */
127592559Sdes			nc->delayed = 1;
127692559Sdes		} else {
127776262Sgreen			port_open_helper(nc, rtype);
127892559Sdes		}
127960573Skris	}
128060573Skris}
128157429Smarkm
128260573Skris/*
128360573Skris * This is the authentication agent socket listening for connections from
128460573Skris * clients.
128560573Skris */
128692559Sdesstatic void
128760573Skrischannel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
128860573Skris{
128992559Sdes	Channel *nc;
129092559Sdes	int newsock;
129160573Skris	struct sockaddr addr;
129260573Skris	socklen_t addrlen;
129357429Smarkm
129460573Skris	if (FD_ISSET(c->sock, readset)) {
129560573Skris		addrlen = sizeof(addr);
129660573Skris		newsock = accept(c->sock, &addr, &addrlen);
129760573Skris		if (newsock < 0) {
129860573Skris			error("accept from auth socket: %.100s", strerror(errno));
129960573Skris			return;
130060573Skris		}
130192559Sdes		nc = channel_new("accepted auth socket",
130276262Sgreen		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
130376262Sgreen		    c->local_window_max, c->local_maxpacket,
1304124207Sdes		    0, "accepted auth socket", 1);
130576262Sgreen		if (compat20) {
130676262Sgreen			packet_start(SSH2_MSG_CHANNEL_OPEN);
130776262Sgreen			packet_put_cstring("auth-agent@openssh.com");
130892559Sdes			packet_put_int(nc->self);
130976262Sgreen			packet_put_int(c->local_window_max);
131076262Sgreen			packet_put_int(c->local_maxpacket);
131176262Sgreen		} else {
131276262Sgreen			packet_start(SSH_SMSG_AGENT_OPEN);
131392559Sdes			packet_put_int(nc->self);
131476262Sgreen		}
131560573Skris		packet_send();
131660573Skris	}
131760573Skris}
131857429Smarkm
131992559Sdesstatic void
132076262Sgreenchannel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
132176262Sgreen{
132292559Sdes	int err = 0;
132392559Sdes	socklen_t sz = sizeof(err);
132492559Sdes
132576262Sgreen	if (FD_ISSET(c->sock, writeset)) {
132692559Sdes		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
132792559Sdes			err = errno;
132892559Sdes			error("getsockopt SO_ERROR failed");
132992559Sdes		}
133092559Sdes		if (err == 0) {
133192559Sdes			debug("channel %d: connected", c->self);
133292559Sdes			c->type = SSH_CHANNEL_OPEN;
133392559Sdes			if (compat20) {
133492559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
133592559Sdes				packet_put_int(c->remote_id);
133692559Sdes				packet_put_int(c->self);
133792559Sdes				packet_put_int(c->local_window);
133892559Sdes				packet_put_int(c->local_maxpacket);
133992559Sdes			} else {
134092559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
134192559Sdes				packet_put_int(c->remote_id);
134292559Sdes				packet_put_int(c->self);
134392559Sdes			}
134476262Sgreen		} else {
134592559Sdes			debug("channel %d: not connected: %s",
134692559Sdes			    c->self, strerror(err));
134792559Sdes			if (compat20) {
134892559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
134992559Sdes				packet_put_int(c->remote_id);
135092559Sdes				packet_put_int(SSH2_OPEN_CONNECT_FAILED);
135192559Sdes				if (!(datafellows & SSH_BUG_OPENFAILURE)) {
135292559Sdes					packet_put_cstring(strerror(err));
135392559Sdes					packet_put_cstring("");
135492559Sdes				}
135576262Sgreen			} else {
135692559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
135792559Sdes				packet_put_int(c->remote_id);
135876262Sgreen			}
135992559Sdes			chan_mark_dead(c);
136076262Sgreen		}
136192559Sdes		packet_send();
136276262Sgreen	}
136376262Sgreen}
136476262Sgreen
136592559Sdesstatic int
136660573Skrischannel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
136760573Skris{
1368147005Sdes	char buf[CHAN_RBUF];
136960573Skris	int len;
137057429Smarkm
137160573Skris	if (c->rfd != -1 &&
137260573Skris	    FD_ISSET(c->rfd, readset)) {
137360573Skris		len = read(c->rfd, buf, sizeof(buf));
137460573Skris		if (len < 0 && (errno == EINTR || errno == EAGAIN))
137560573Skris			return 1;
137678827Sgreen		if (len <= 0) {
1377124207Sdes			debug2("channel %d: read<=0 rfd %d len %d",
137860573Skris			    c->self, c->rfd, len);
137976262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1380124207Sdes				debug2("channel %d: not open", c->self);
138192559Sdes				chan_mark_dead(c);
138276262Sgreen				return -1;
138376262Sgreen			} else if (compat13) {
138492559Sdes				buffer_clear(&c->output);
138560573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
1386124207Sdes				debug2("channel %d: input draining.", c->self);
138760573Skris			} else {
138860573Skris				chan_read_failed(c);
138957429Smarkm			}
139060573Skris			return -1;
139160573Skris		}
139292559Sdes		if (c->input_filter != NULL) {
139365668Skris			if (c->input_filter(c, buf, len) == -1) {
1394124207Sdes				debug2("channel %d: filter stops", c->self);
139565668Skris				chan_read_failed(c);
139665668Skris			}
139765668Skris		} else {
139865668Skris			buffer_append(&c->input, buf, len);
139965668Skris		}
140060573Skris	}
140160573Skris	return 1;
140260573Skris}
140392559Sdesstatic int
140460573Skrischannel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
140560573Skris{
140676262Sgreen	struct termios tio;
140792559Sdes	u_char *data;
140892559Sdes	u_int dlen;
140960573Skris	int len;
141060573Skris
141160573Skris	/* Send buffered output data to the socket. */
141260573Skris	if (c->wfd != -1 &&
141360573Skris	    FD_ISSET(c->wfd, writeset) &&
141460573Skris	    buffer_len(&c->output) > 0) {
141592559Sdes		data = buffer_ptr(&c->output);
141692559Sdes		dlen = buffer_len(&c->output);
1417106130Sdes#ifdef _AIX
1418126273Sdes		/* XXX: Later AIX versions can't push as much data to tty */
1419126273Sdes		if (compat20 && c->wfd_isatty)
1420126273Sdes			dlen = MIN(dlen, 8*1024);
1421106130Sdes#endif
142292559Sdes		len = write(c->wfd, data, dlen);
142360573Skris		if (len < 0 && (errno == EINTR || errno == EAGAIN))
142460573Skris			return 1;
142560573Skris		if (len <= 0) {
142676262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1427124207Sdes				debug2("channel %d: not open", c->self);
142892559Sdes				chan_mark_dead(c);
142976262Sgreen				return -1;
143076262Sgreen			} else if (compat13) {
143192559Sdes				buffer_clear(&c->output);
1432124207Sdes				debug2("channel %d: input draining.", c->self);
143360573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
143460573Skris			} else {
143560573Skris				chan_write_failed(c);
143657429Smarkm			}
143760573Skris			return -1;
143860573Skris		}
143992559Sdes		if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') {
144074500Sgreen			if (tcgetattr(c->wfd, &tio) == 0 &&
144174500Sgreen			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
144274500Sgreen				/*
144374500Sgreen				 * Simulate echo to reduce the impact of
144476262Sgreen				 * traffic analysis. We need to match the
144576262Sgreen				 * size of a SSH2_MSG_CHANNEL_DATA message
144676262Sgreen				 * (4 byte channel id + data)
144774500Sgreen				 */
144876262Sgreen				packet_send_ignore(4 + len);
144974500Sgreen				packet_send();
145074500Sgreen			}
145174500Sgreen		}
145260573Skris		buffer_consume(&c->output, len);
145360573Skris		if (compat20 && len > 0) {
145460573Skris			c->local_consumed += len;
145560573Skris		}
145660573Skris	}
145760573Skris	return 1;
145860573Skris}
145992559Sdesstatic int
146060573Skrischannel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
146160573Skris{
1462147005Sdes	char buf[CHAN_RBUF];
146360573Skris	int len;
146457429Smarkm
146560573Skris/** XXX handle drain efd, too */
146660573Skris	if (c->efd != -1) {
146760573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
146860573Skris		    FD_ISSET(c->efd, writeset) &&
146960573Skris		    buffer_len(&c->extended) > 0) {
147060573Skris			len = write(c->efd, buffer_ptr(&c->extended),
147160573Skris			    buffer_len(&c->extended));
147269587Sgreen			debug2("channel %d: written %d to efd %d",
147360573Skris			    c->self, len, c->efd);
147476262Sgreen			if (len < 0 && (errno == EINTR || errno == EAGAIN))
147576262Sgreen				return 1;
147676262Sgreen			if (len <= 0) {
147776262Sgreen				debug2("channel %d: closing write-efd %d",
147876262Sgreen				    c->self, c->efd);
147992559Sdes				channel_close_fd(&c->efd);
148076262Sgreen			} else {
148160573Skris				buffer_consume(&c->extended, len);
148260573Skris				c->local_consumed += len;
148357429Smarkm			}
148460573Skris		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
148560573Skris		    FD_ISSET(c->efd, readset)) {
148660573Skris			len = read(c->efd, buf, sizeof(buf));
148769587Sgreen			debug2("channel %d: read %d from efd %d",
148892559Sdes			    c->self, len, c->efd);
148976262Sgreen			if (len < 0 && (errno == EINTR || errno == EAGAIN))
149076262Sgreen				return 1;
149176262Sgreen			if (len <= 0) {
149276262Sgreen				debug2("channel %d: closing read-efd %d",
149360573Skris				    c->self, c->efd);
149492559Sdes				channel_close_fd(&c->efd);
149576262Sgreen			} else {
149660573Skris				buffer_append(&c->extended, buf, len);
149776262Sgreen			}
149860573Skris		}
149960573Skris	}
150060573Skris	return 1;
150160573Skris}
150292559Sdesstatic int
1503137019Sdeschannel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
1504137019Sdes{
1505137019Sdes	char buf[16];
1506137019Sdes	int len;
1507137019Sdes
1508137019Sdes	/* Monitor control fd to detect if the slave client exits */
1509137019Sdes	if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
1510137019Sdes		len = read(c->ctl_fd, buf, sizeof(buf));
1511137019Sdes		if (len < 0 && (errno == EINTR || errno == EAGAIN))
1512137019Sdes			return 1;
1513137019Sdes		if (len <= 0) {
1514137019Sdes			debug2("channel %d: ctl read<=0", c->self);
1515137019Sdes			if (c->type != SSH_CHANNEL_OPEN) {
1516137019Sdes				debug2("channel %d: not open", c->self);
1517137019Sdes				chan_mark_dead(c);
1518137019Sdes				return -1;
1519137019Sdes			} else {
1520137019Sdes				chan_read_failed(c);
1521137019Sdes				chan_write_failed(c);
1522137019Sdes			}
1523137019Sdes			return -1;
1524137019Sdes		} else
1525137019Sdes			fatal("%s: unexpected data on ctl fd", __func__);
1526137019Sdes	}
1527137019Sdes	return 1;
1528137019Sdes}
1529137019Sdesstatic int
153076262Sgreenchannel_check_window(Channel *c)
153160573Skris{
153276262Sgreen	if (c->type == SSH_CHANNEL_OPEN &&
153376262Sgreen	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
153460573Skris	    c->local_window < c->local_window_max/2 &&
153560573Skris	    c->local_consumed > 0) {
153660573Skris		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
153760573Skris		packet_put_int(c->remote_id);
153860573Skris		packet_put_int(c->local_consumed);
153960573Skris		packet_send();
154069587Sgreen		debug2("channel %d: window %d sent adjust %d",
154160573Skris		    c->self, c->local_window,
154260573Skris		    c->local_consumed);
154360573Skris		c->local_window += c->local_consumed;
154460573Skris		c->local_consumed = 0;
154560573Skris	}
154660573Skris	return 1;
154760573Skris}
154857429Smarkm
154992559Sdesstatic void
155092559Sdeschannel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
155160573Skris{
155292559Sdes	if (c->delayed)
155392559Sdes		return;
155460573Skris	channel_handle_rfd(c, readset, writeset);
155560573Skris	channel_handle_wfd(c, readset, writeset);
155692559Sdes	if (!compat20)
155792559Sdes		return;
155860573Skris	channel_handle_efd(c, readset, writeset);
1559137019Sdes	channel_handle_ctl(c, readset, writeset);
156076262Sgreen	channel_check_window(c);
156160573Skris}
156260573Skris
156392559Sdesstatic void
156460573Skrischannel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
156560573Skris{
156660573Skris	int len;
1567106130Sdes
156860573Skris	/* Send buffered output data to the socket. */
156960573Skris	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
157060573Skris		len = write(c->sock, buffer_ptr(&c->output),
157160573Skris			    buffer_len(&c->output));
157260573Skris		if (len <= 0)
157392559Sdes			buffer_clear(&c->output);
157460573Skris		else
157560573Skris			buffer_consume(&c->output, len);
157660573Skris	}
157760573Skris}
157860573Skris
157992559Sdesstatic void
158060573Skrischannel_handler_init_20(void)
158160573Skris{
158292559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
158360573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
158460573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
158576262Sgreen	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
158660573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
158776262Sgreen	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
158876262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
158976262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
159060573Skris
159192559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
159260573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
159376262Sgreen	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
159460573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
159576262Sgreen	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
159676262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
159792559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
159860573Skris}
159960573Skris
160092559Sdesstatic void
160160573Skrischannel_handler_init_13(void)
160260573Skris{
160360573Skris	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
160460573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
160560573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
160660573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
160760573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
160860573Skris	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
160960573Skris	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
161076262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
161176262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
161260573Skris
161392559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
161460573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
161560573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
161660573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
161760573Skris	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
161876262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
161992559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
162060573Skris}
162160573Skris
162292559Sdesstatic void
162360573Skrischannel_handler_init_15(void)
162460573Skris{
162592559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
162660573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
162760573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
162860573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
162960573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
163076262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
163176262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
163260573Skris
163360573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
163460573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
163560573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
163692559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
163776262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
163892559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
163960573Skris}
164060573Skris
164192559Sdesstatic void
164260573Skrischannel_handler_init(void)
164360573Skris{
164460573Skris	int i;
1645106130Sdes
164692559Sdes	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
164760573Skris		channel_pre[i] = NULL;
164860573Skris		channel_post[i] = NULL;
164960573Skris	}
165060573Skris	if (compat20)
165160573Skris		channel_handler_init_20();
165260573Skris	else if (compat13)
165360573Skris		channel_handler_init_13();
165460573Skris	else
165560573Skris		channel_handler_init_15();
165660573Skris}
165760573Skris
165892559Sdes/* gc dead channels */
165992559Sdesstatic void
166092559Sdeschannel_garbage_collect(Channel *c)
166192559Sdes{
166292559Sdes	if (c == NULL)
166392559Sdes		return;
166492559Sdes	if (c->detach_user != NULL) {
166592559Sdes		if (!chan_is_dead(c, 0))
166692559Sdes			return;
1667124207Sdes		debug2("channel %d: gc: notify user", c->self);
166892559Sdes		c->detach_user(c->self, NULL);
166992559Sdes		/* if we still have a callback */
167092559Sdes		if (c->detach_user != NULL)
167192559Sdes			return;
1672124207Sdes		debug2("channel %d: gc: user detached", c->self);
167392559Sdes	}
167492559Sdes	if (!chan_is_dead(c, 1))
167592559Sdes		return;
1676124207Sdes	debug2("channel %d: garbage collecting", c->self);
167792559Sdes	channel_free(c);
167892559Sdes}
167992559Sdes
168092559Sdesstatic void
168160573Skrischannel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
168260573Skris{
168360573Skris	static int did_init = 0;
1684137019Sdes	u_int i;
168560573Skris	Channel *c;
168660573Skris
168760573Skris	if (!did_init) {
168860573Skris		channel_handler_init();
168960573Skris		did_init = 1;
169060573Skris	}
169160573Skris	for (i = 0; i < channels_alloc; i++) {
169292559Sdes		c = channels[i];
169392559Sdes		if (c == NULL)
169457429Smarkm			continue;
169592559Sdes		if (ftab[c->type] != NULL)
169692559Sdes			(*ftab[c->type])(c, readset, writeset);
169792559Sdes		channel_garbage_collect(c);
169857429Smarkm	}
169957429Smarkm}
170057429Smarkm
170192559Sdes/*
170292559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in
170392559Sdes * select bitmasks.
170492559Sdes */
170560573Skrisvoid
170676262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
1707137019Sdes    u_int *nallocp, int rekeying)
170860573Skris{
1709137019Sdes	u_int n, sz;
171076262Sgreen
171176262Sgreen	n = MAX(*maxfdp, channel_max_fd);
171276262Sgreen
171376262Sgreen	sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
171492559Sdes	/* perhaps check sz < nalloc/2 and shrink? */
171592559Sdes	if (*readsetp == NULL || sz > *nallocp) {
171692559Sdes		*readsetp = xrealloc(*readsetp, sz);
171792559Sdes		*writesetp = xrealloc(*writesetp, sz);
171892559Sdes		*nallocp = sz;
171976262Sgreen	}
172092559Sdes	*maxfdp = n;
172176262Sgreen	memset(*readsetp, 0, sz);
172276262Sgreen	memset(*writesetp, 0, sz);
172376262Sgreen
172476262Sgreen	if (!rekeying)
172576262Sgreen		channel_handler(channel_pre, *readsetp, *writesetp);
172660573Skris}
172760573Skris
172892559Sdes/*
172992559Sdes * After select, perform any appropriate operations for channels which have
173092559Sdes * events pending.
173192559Sdes */
173260573Skrisvoid
173360573Skrischannel_after_select(fd_set * readset, fd_set * writeset)
173460573Skris{
173560573Skris	channel_handler(channel_post, readset, writeset);
173660573Skris}
173760573Skris
173892559Sdes
173976262Sgreen/* If there is data to send to the connection, enqueue some of it now. */
174057429Smarkm
174160573Skrisvoid
174292559Sdeschannel_output_poll(void)
174357429Smarkm{
174460573Skris	Channel *c;
1745137019Sdes	u_int i, len;
174657429Smarkm
174757429Smarkm	for (i = 0; i < channels_alloc; i++) {
174892559Sdes		c = channels[i];
174992559Sdes		if (c == NULL)
175092559Sdes			continue;
175157429Smarkm
175292559Sdes		/*
175392559Sdes		 * We are only interested in channels that can have buffered
175492559Sdes		 * incoming data.
175592559Sdes		 */
175657429Smarkm		if (compat13) {
175760573Skris			if (c->type != SSH_CHANNEL_OPEN &&
175860573Skris			    c->type != SSH_CHANNEL_INPUT_DRAINING)
175957429Smarkm				continue;
176057429Smarkm		} else {
176160573Skris			if (c->type != SSH_CHANNEL_OPEN)
176257429Smarkm				continue;
176357429Smarkm		}
176460573Skris		if (compat20 &&
176560573Skris		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
176676262Sgreen			/* XXX is this true? */
176792559Sdes			debug3("channel %d: will not send data after close", c->self);
176860573Skris			continue;
176960573Skris		}
177057429Smarkm
177157429Smarkm		/* Get the amount of buffered data for this channel. */
177276262Sgreen		if ((c->istate == CHAN_INPUT_OPEN ||
177376262Sgreen		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
177476262Sgreen		    (len = buffer_len(&c->input)) > 0) {
177592559Sdes			/*
177692559Sdes			 * Send some data for the other side over the secure
177792559Sdes			 * connection.
177892559Sdes			 */
177960573Skris			if (compat20) {
178060573Skris				if (len > c->remote_window)
178160573Skris					len = c->remote_window;
178260573Skris				if (len > c->remote_maxpacket)
178360573Skris					len = c->remote_maxpacket;
178457429Smarkm			} else {
178560573Skris				if (packet_is_interactive()) {
178660573Skris					if (len > 1024)
178760573Skris						len = 512;
178860573Skris				} else {
178960573Skris					/* Keep the packets at reasonable size. */
179060573Skris					if (len > packet_get_maxsize()/2)
179160573Skris						len = packet_get_maxsize()/2;
179260573Skris				}
179357429Smarkm			}
179460573Skris			if (len > 0) {
179560573Skris				packet_start(compat20 ?
179660573Skris				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
179760573Skris				packet_put_int(c->remote_id);
179860573Skris				packet_put_string(buffer_ptr(&c->input), len);
179960573Skris				packet_send();
180060573Skris				buffer_consume(&c->input, len);
180160573Skris				c->remote_window -= len;
180260573Skris			}
180360573Skris		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
180457429Smarkm			if (compat13)
180557429Smarkm				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
180657429Smarkm			/*
180757429Smarkm			 * input-buffer is empty and read-socket shutdown:
180898684Sdes			 * tell peer, that we will not send more data: send IEOF.
180998684Sdes			 * hack for extended data: delay EOF if EFD still in use.
181057429Smarkm			 */
181198684Sdes			if (CHANNEL_EFD_INPUT_ACTIVE(c))
181298684Sdes			       debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
181398684Sdes				   c->self, c->efd, buffer_len(&c->extended));
181498684Sdes			else
181598684Sdes				chan_ibuf_empty(c);
181657429Smarkm		}
181760573Skris		/* Send extended data, i.e. stderr */
181860573Skris		if (compat20 &&
181998684Sdes		    !(c->flags & CHAN_EOF_SENT) &&
182060573Skris		    c->remote_window > 0 &&
182160573Skris		    (len = buffer_len(&c->extended)) > 0 &&
182260573Skris		    c->extended_usage == CHAN_EXTENDED_READ) {
182399063Sdes			debug2("channel %d: rwin %u elen %u euse %d",
182476262Sgreen			    c->self, c->remote_window, buffer_len(&c->extended),
182576262Sgreen			    c->extended_usage);
182660573Skris			if (len > c->remote_window)
182760573Skris				len = c->remote_window;
182860573Skris			if (len > c->remote_maxpacket)
182960573Skris				len = c->remote_maxpacket;
183060573Skris			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
183160573Skris			packet_put_int(c->remote_id);
183260573Skris			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
183360573Skris			packet_put_string(buffer_ptr(&c->extended), len);
183460573Skris			packet_send();
183560573Skris			buffer_consume(&c->extended, len);
183660573Skris			c->remote_window -= len;
183776262Sgreen			debug2("channel %d: sent ext data %d", c->self, len);
183860573Skris		}
183957429Smarkm	}
184057429Smarkm}
184157429Smarkm
184257429Smarkm
184392559Sdes/* -- protocol input */
184492559Sdes
184560573Skrisvoid
184692559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt)
184757429Smarkm{
184857429Smarkm	int id;
184957429Smarkm	char *data;
185076262Sgreen	u_int data_len;
185160573Skris	Channel *c;
185257429Smarkm
185357429Smarkm	/* Get the channel number and verify it. */
185457429Smarkm	id = packet_get_int();
185560573Skris	c = channel_lookup(id);
185660573Skris	if (c == NULL)
185757429Smarkm		packet_disconnect("Received data for nonexistent channel %d.", id);
185857429Smarkm
185957429Smarkm	/* Ignore any data for non-open channels (might happen on close) */
186060573Skris	if (c->type != SSH_CHANNEL_OPEN &&
186160573Skris	    c->type != SSH_CHANNEL_X11_OPEN)
186257429Smarkm		return;
186357429Smarkm
186457429Smarkm	/* Get the data. */
186557429Smarkm	data = packet_get_string(&data_len);
186660573Skris
1867126273Sdes	/*
1868126273Sdes	 * Ignore data for protocol > 1.3 if output end is no longer open.
1869126273Sdes	 * For protocol 2 the sending side is reducing its window as it sends
1870126273Sdes	 * data, so we must 'fake' consumption of the data in order to ensure
1871126273Sdes	 * that window updates are sent back.  Otherwise the connection might
1872126273Sdes	 * deadlock.
1873126273Sdes	 */
1874126273Sdes	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) {
1875126273Sdes		if (compat20) {
1876126273Sdes			c->local_window -= data_len;
1877126273Sdes			c->local_consumed += data_len;
1878126273Sdes		}
1879126273Sdes		xfree(data);
1880126273Sdes		return;
1881126273Sdes	}
1882126273Sdes
188392559Sdes	if (compat20) {
188460573Skris		if (data_len > c->local_maxpacket) {
1885124207Sdes			logit("channel %d: rcvd big packet %d, maxpack %d",
188660573Skris			    c->self, data_len, c->local_maxpacket);
188760573Skris		}
188860573Skris		if (data_len > c->local_window) {
1889124207Sdes			logit("channel %d: rcvd too much data %d, win %d",
189060573Skris			    c->self, data_len, c->local_window);
189160573Skris			xfree(data);
189260573Skris			return;
189360573Skris		}
189460573Skris		c->local_window -= data_len;
189560573Skris	}
189692559Sdes	packet_check_eom();
189760573Skris	buffer_append(&c->output, data, data_len);
189857429Smarkm	xfree(data);
189957429Smarkm}
190092559Sdes
190160573Skrisvoid
190292559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt)
190360573Skris{
190460573Skris	int id;
190560573Skris	char *data;
190699063Sdes	u_int data_len, tcode;
190760573Skris	Channel *c;
190857429Smarkm
190960573Skris	/* Get the channel number and verify it. */
191060573Skris	id = packet_get_int();
191160573Skris	c = channel_lookup(id);
191260573Skris
191360573Skris	if (c == NULL)
191460573Skris		packet_disconnect("Received extended_data for bad channel %d.", id);
191560573Skris	if (c->type != SSH_CHANNEL_OPEN) {
1916124207Sdes		logit("channel %d: ext data for non open", id);
191760573Skris		return;
191860573Skris	}
191998684Sdes	if (c->flags & CHAN_EOF_RCVD) {
192098684Sdes		if (datafellows & SSH_BUG_EXTEOF)
192198684Sdes			debug("channel %d: accepting ext data after eof", id);
192298684Sdes		else
192398684Sdes			packet_disconnect("Received extended_data after EOF "
192498684Sdes			    "on channel %d.", id);
192598684Sdes	}
192660573Skris	tcode = packet_get_int();
192760573Skris	if (c->efd == -1 ||
192860573Skris	    c->extended_usage != CHAN_EXTENDED_WRITE ||
192960573Skris	    tcode != SSH2_EXTENDED_DATA_STDERR) {
1930124207Sdes		logit("channel %d: bad ext data", c->self);
193160573Skris		return;
193260573Skris	}
193360573Skris	data = packet_get_string(&data_len);
193492559Sdes	packet_check_eom();
193560573Skris	if (data_len > c->local_window) {
1936124207Sdes		logit("channel %d: rcvd too much extended_data %d, win %d",
193760573Skris		    c->self, data_len, c->local_window);
193860573Skris		xfree(data);
193960573Skris		return;
194060573Skris	}
194169587Sgreen	debug2("channel %d: rcvd ext data %d", c->self, data_len);
194260573Skris	c->local_window -= data_len;
194360573Skris	buffer_append(&c->extended, data, data_len);
194460573Skris	xfree(data);
194560573Skris}
194660573Skris
194760573Skrisvoid
194892559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt)
194960573Skris{
195060573Skris	int id;
195160573Skris	Channel *c;
195257429Smarkm
195360573Skris	id = packet_get_int();
195492559Sdes	packet_check_eom();
195560573Skris	c = channel_lookup(id);
195660573Skris	if (c == NULL)
195760573Skris		packet_disconnect("Received ieof for nonexistent channel %d.", id);
195860573Skris	chan_rcvd_ieof(c);
195992559Sdes
196092559Sdes	/* XXX force input close */
196192559Sdes	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
196292559Sdes		debug("channel %d: FORCE input drain", c->self);
196392559Sdes		c->istate = CHAN_INPUT_WAIT_DRAIN;
196492559Sdes		if (buffer_len(&c->input) == 0)
196592559Sdes			chan_ibuf_empty(c);
196692559Sdes	}
196792559Sdes
196860573Skris}
196960573Skris
197060573Skrisvoid
197192559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt)
197257429Smarkm{
197360573Skris	int id;
197460573Skris	Channel *c;
197557429Smarkm
197660573Skris	id = packet_get_int();
197792559Sdes	packet_check_eom();
197860573Skris	c = channel_lookup(id);
197960573Skris	if (c == NULL)
198060573Skris		packet_disconnect("Received close for nonexistent channel %d.", id);
198157429Smarkm
198257429Smarkm	/*
198357429Smarkm	 * Send a confirmation that we have closed the channel and no more
198457429Smarkm	 * data is coming for it.
198557429Smarkm	 */
198657429Smarkm	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
198760573Skris	packet_put_int(c->remote_id);
198857429Smarkm	packet_send();
198957429Smarkm
199057429Smarkm	/*
199157429Smarkm	 * If the channel is in closed state, we have sent a close request,
199257429Smarkm	 * and the other side will eventually respond with a confirmation.
199357429Smarkm	 * Thus, we cannot free the channel here, because then there would be
199457429Smarkm	 * no-one to receive the confirmation.  The channel gets freed when
199557429Smarkm	 * the confirmation arrives.
199657429Smarkm	 */
199760573Skris	if (c->type != SSH_CHANNEL_CLOSED) {
199857429Smarkm		/*
199957429Smarkm		 * Not a closed channel - mark it as draining, which will
200057429Smarkm		 * cause it to be freed later.
200157429Smarkm		 */
200292559Sdes		buffer_clear(&c->input);
200360573Skris		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
200457429Smarkm	}
200557429Smarkm}
200657429Smarkm
200760573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
200860573Skrisvoid
200992559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt)
201060573Skris{
201160573Skris	int id = packet_get_int();
201260573Skris	Channel *c = channel_lookup(id);
201392559Sdes
201492559Sdes	packet_check_eom();
201560573Skris	if (c == NULL)
201660573Skris		packet_disconnect("Received oclose for nonexistent channel %d.", id);
201760573Skris	chan_rcvd_oclose(c);
201860573Skris}
201957429Smarkm
202060573Skrisvoid
202192559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
202257429Smarkm{
202360573Skris	int id = packet_get_int();
202460573Skris	Channel *c = channel_lookup(id);
202557429Smarkm
202692559Sdes	packet_check_eom();
202760573Skris	if (c == NULL)
202860573Skris		packet_disconnect("Received close confirmation for "
202960573Skris		    "out-of-range channel %d.", id);
203060573Skris	if (c->type != SSH_CHANNEL_CLOSED)
203160573Skris		packet_disconnect("Received close confirmation for "
203260573Skris		    "non-closed channel %d (type %d).", id, c->type);
203392559Sdes	channel_free(c);
203460573Skris}
203557429Smarkm
203660573Skrisvoid
203792559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
203860573Skris{
203960573Skris	int id, remote_id;
204060573Skris	Channel *c;
204160573Skris
204260573Skris	id = packet_get_int();
204360573Skris	c = channel_lookup(id);
204460573Skris
204560573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
204660573Skris		packet_disconnect("Received open confirmation for "
204760573Skris		    "non-opening channel %d.", id);
204860573Skris	remote_id = packet_get_int();
204960573Skris	/* Record the remote channel number and mark that the channel is now open. */
205060573Skris	c->remote_id = remote_id;
205160573Skris	c->type = SSH_CHANNEL_OPEN;
205260573Skris
205360573Skris	if (compat20) {
205460573Skris		c->remote_window = packet_get_int();
205560573Skris		c->remote_maxpacket = packet_get_int();
205692559Sdes		if (c->confirm) {
205769587Sgreen			debug2("callback start");
2058137019Sdes			c->confirm(c->self, c->confirm_ctx);
205969587Sgreen			debug2("callback done");
206060573Skris		}
2061124207Sdes		debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
206260573Skris		    c->remote_window, c->remote_maxpacket);
206357429Smarkm	}
206492559Sdes	packet_check_eom();
206557429Smarkm}
206657429Smarkm
206792559Sdesstatic char *
206892559Sdesreason2txt(int reason)
206992559Sdes{
207092559Sdes	switch (reason) {
207192559Sdes	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
207292559Sdes		return "administratively prohibited";
207392559Sdes	case SSH2_OPEN_CONNECT_FAILED:
207492559Sdes		return "connect failed";
207592559Sdes	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
207692559Sdes		return "unknown channel type";
207792559Sdes	case SSH2_OPEN_RESOURCE_SHORTAGE:
207892559Sdes		return "resource shortage";
207992559Sdes	}
208092559Sdes	return "unknown reason";
208192559Sdes}
208292559Sdes
208360573Skrisvoid
208492559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt)
208557429Smarkm{
208676262Sgreen	int id, reason;
208776262Sgreen	char *msg = NULL, *lang = NULL;
208860573Skris	Channel *c;
208957429Smarkm
209060573Skris	id = packet_get_int();
209160573Skris	c = channel_lookup(id);
209257429Smarkm
209360573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
209460573Skris		packet_disconnect("Received open failure for "
209560573Skris		    "non-opening channel %d.", id);
209660573Skris	if (compat20) {
209776262Sgreen		reason = packet_get_int();
209892559Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
209976262Sgreen			msg  = packet_get_string(NULL);
210076262Sgreen			lang = packet_get_string(NULL);
210176262Sgreen		}
2102124207Sdes		logit("channel %d: open failed: %s%s%s", id,
210392559Sdes		    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
210476262Sgreen		if (msg != NULL)
210576262Sgreen			xfree(msg);
210676262Sgreen		if (lang != NULL)
210776262Sgreen			xfree(lang);
210860573Skris	}
210992559Sdes	packet_check_eom();
211060573Skris	/* Free the channel.  This will also close the socket. */
211192559Sdes	channel_free(c);
211257429Smarkm}
211357429Smarkm
211460573Skrisvoid
211592559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
211660573Skris{
211760573Skris	Channel *c;
211899063Sdes	int id;
211999063Sdes	u_int adjust;
212057429Smarkm
212160573Skris	if (!compat20)
212260573Skris		return;
212360573Skris
212457429Smarkm	/* Get the channel number and verify it. */
212560573Skris	id = packet_get_int();
212660573Skris	c = channel_lookup(id);
212757429Smarkm
212860573Skris	if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
2129124207Sdes		logit("Received window adjust for "
213060573Skris		    "non-open channel %d.", id);
213160573Skris		return;
213260573Skris	}
213360573Skris	adjust = packet_get_int();
213492559Sdes	packet_check_eom();
213599063Sdes	debug2("channel %d: rcvd adjust %u", id, adjust);
213660573Skris	c->remote_window += adjust;
213757429Smarkm}
213857429Smarkm
213960573Skrisvoid
214092559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt)
214157429Smarkm{
214292559Sdes	Channel *c = NULL;
214392559Sdes	u_short host_port;
214492559Sdes	char *host, *originator_string;
214592559Sdes	int remote_id, sock = -1;
214657429Smarkm
214792559Sdes	remote_id = packet_get_int();
214892559Sdes	host = packet_get_string(NULL);
214992559Sdes	host_port = packet_get_int();
215057429Smarkm
215192559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
215292559Sdes		originator_string = packet_get_string(NULL);
215392559Sdes	} else {
215492559Sdes		originator_string = xstrdup("unknown (remote did not supply name)");
215592559Sdes	}
215692559Sdes	packet_check_eom();
215792559Sdes	sock = channel_connect_to(host, host_port);
215892559Sdes	if (sock != -1) {
215992559Sdes		c = channel_new("connected socket",
216092559Sdes		    SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
216192559Sdes		    originator_string, 1);
216292559Sdes		c->remote_id = remote_id;
216392559Sdes	}
2164124207Sdes	xfree(originator_string);
216592559Sdes	if (c == NULL) {
216692559Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
216792559Sdes		packet_put_int(remote_id);
216892559Sdes		packet_send();
216992559Sdes	}
217092559Sdes	xfree(host);
217157429Smarkm}
217257429Smarkm
217357429Smarkm
217492559Sdes/* -- tcp forwarding */
217557429Smarkm
217692559Sdesvoid
217792559Sdeschannel_set_af(int af)
217876262Sgreen{
217992559Sdes	IPv4or6 = af;
218076262Sgreen}
218176262Sgreen
218292559Sdesstatic int
218392559Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
218492559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
218557429Smarkm{
218692559Sdes	Channel *c;
2187147005Sdes	int sock, r, success = 0, on = 1, wildcard = 0, is_client;
218857429Smarkm	struct addrinfo hints, *ai, *aitop;
2189147005Sdes	const char *host, *addr;
219057429Smarkm	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
219157429Smarkm
219292559Sdes	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
219392559Sdes	    listen_addr : host_to_connect;
2194147005Sdes	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
219557429Smarkm
219692559Sdes	if (host == NULL) {
219792559Sdes		error("No forward host name.");
219892559Sdes		return success;
219976262Sgreen	}
220092559Sdes	if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
220176262Sgreen		error("Forward host name too long.");
220276262Sgreen		return success;
220376262Sgreen	}
220476262Sgreen
220557429Smarkm	/*
2206147005Sdes	 * Determine whether or not a port forward listens to loopback,
2207147005Sdes	 * specified address or wildcard. On the client, a specified bind
2208147005Sdes	 * address will always override gateway_ports. On the server, a
2209147005Sdes	 * gateway_ports of 1 (``yes'') will override the client's
2210147005Sdes	 * specification and force a wildcard bind, whereas a value of 2
2211147005Sdes	 * (``clientspecified'') will bind to whatever address the client
2212147005Sdes	 * asked for.
2213147005Sdes	 *
2214147005Sdes	 * Special-case listen_addrs are:
2215147005Sdes	 *
2216147005Sdes	 * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
2217147005Sdes	 * "" (empty string), "*"  -> wildcard v4/v6
2218147005Sdes	 * "localhost"             -> loopback v4/v6
2219147005Sdes	 */
2220147005Sdes	addr = NULL;
2221147005Sdes	if (listen_addr == NULL) {
2222147005Sdes		/* No address specified: default to gateway_ports setting */
2223147005Sdes		if (gateway_ports)
2224147005Sdes			wildcard = 1;
2225147005Sdes	} else if (gateway_ports || is_client) {
2226147005Sdes		if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
2227147005Sdes		    strcmp(listen_addr, "0.0.0.0") == 0) ||
2228147005Sdes		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
2229147005Sdes		    (!is_client && gateway_ports == 1))
2230147005Sdes			wildcard = 1;
2231147005Sdes		else if (strcmp(listen_addr, "localhost") != 0)
2232147005Sdes			addr = listen_addr;
2233147005Sdes	}
2234147005Sdes
2235147005Sdes	debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
2236147005Sdes	    type, wildcard, (addr == NULL) ? "NULL" : addr);
2237147005Sdes
2238147005Sdes	/*
223957429Smarkm	 * getaddrinfo returns a loopback address if the hostname is
224057429Smarkm	 * set to NULL and hints.ai_flags is not AI_PASSIVE
224157429Smarkm	 */
224257429Smarkm	memset(&hints, 0, sizeof(hints));
224357429Smarkm	hints.ai_family = IPv4or6;
2244147005Sdes	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
224557429Smarkm	hints.ai_socktype = SOCK_STREAM;
224676262Sgreen	snprintf(strport, sizeof strport, "%d", listen_port);
2247147005Sdes	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
2248147005Sdes		if (addr == NULL) {
2249147005Sdes			/* This really shouldn't happen */
2250147005Sdes			packet_disconnect("getaddrinfo: fatal error: %s",
2251147005Sdes			    gai_strerror(r));
2252147005Sdes		} else {
2253147005Sdes			verbose("channel_setup_fwd_listener: "
2254147005Sdes			    "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
2255147005Sdes			packet_send_debug("channel_setup_fwd_listener: "
2256147005Sdes			    "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
2257147005Sdes		}
2258147005Sdes		aitop = NULL;
2259147005Sdes	}
226057429Smarkm
226157429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
226257429Smarkm		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
226357429Smarkm			continue;
226457429Smarkm		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
226557429Smarkm		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
226692559Sdes			error("channel_setup_fwd_listener: getnameinfo failed");
226757429Smarkm			continue;
226857429Smarkm		}
226957429Smarkm		/* Create a port to listen for the host. */
2270124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
227157429Smarkm		if (sock < 0) {
227257429Smarkm			/* this is no error since kernel may not support ipv6 */
227357429Smarkm			verbose("socket: %.100s", strerror(errno));
227457429Smarkm			continue;
227557429Smarkm		}
227657429Smarkm		/*
2277106130Sdes		 * Set socket options.
2278106130Sdes		 * Allow local port reuse in TIME_WAIT.
227957429Smarkm		 */
2280106130Sdes		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
2281106130Sdes		    sizeof(on)) == -1)
2282106130Sdes			error("setsockopt SO_REUSEADDR: %s", strerror(errno));
2283106130Sdes
228457429Smarkm		debug("Local forwarding listening on %s port %s.", ntop, strport);
228557429Smarkm
228657429Smarkm		/* Bind the socket to the address. */
228757429Smarkm		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
228857429Smarkm			/* address can be in use ipv6 address is already bound */
228998941Sdes			if (!ai->ai_next)
229098941Sdes				error("bind: %.100s", strerror(errno));
229198941Sdes			else
229298941Sdes				verbose("bind: %.100s", strerror(errno));
229398941Sdes
229457429Smarkm			close(sock);
229557429Smarkm			continue;
229657429Smarkm		}
229757429Smarkm		/* Start listening for connections on the socket. */
2298126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
229957429Smarkm			error("listen: %.100s", strerror(errno));
230057429Smarkm			close(sock);
230157429Smarkm			continue;
230257429Smarkm		}
230357429Smarkm		/* Allocate a channel number for the socket. */
230492559Sdes		c = channel_new("port listener", type, sock, sock, -1,
230560573Skris		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
2306124207Sdes		    0, "port listener", 1);
230792559Sdes		strlcpy(c->path, host, sizeof(c->path));
230892559Sdes		c->host_port = port_to_connect;
230992559Sdes		c->listening_port = listen_port;
231057429Smarkm		success = 1;
231157429Smarkm	}
231257429Smarkm	if (success == 0)
231392559Sdes		error("channel_setup_fwd_listener: cannot listen to port: %d",
231476262Sgreen		    listen_port);
231557429Smarkm	freeaddrinfo(aitop);
231676262Sgreen	return success;
231757429Smarkm}
231857429Smarkm
2319137019Sdesint
2320137019Sdeschannel_cancel_rport_listener(const char *host, u_short port)
2321137019Sdes{
2322137019Sdes	u_int i;
2323137019Sdes	int found = 0;
2324137019Sdes
2325147005Sdes	for (i = 0; i < channels_alloc; i++) {
2326137019Sdes		Channel *c = channels[i];
2327137019Sdes
2328137019Sdes		if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
2329137019Sdes		    strncmp(c->path, host, sizeof(c->path)) == 0 &&
2330137019Sdes		    c->listening_port == port) {
2331147005Sdes			debug2("%s: close channel %d", __func__, i);
2332137019Sdes			channel_free(c);
2333137019Sdes			found = 1;
2334137019Sdes		}
2335137019Sdes	}
2336137019Sdes
2337137019Sdes	return (found);
2338137019Sdes}
2339137019Sdes
234092559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */
234192559Sdesint
2342147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
234392559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
234492559Sdes{
234592559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
2346147005Sdes	    listen_host, listen_port, host_to_connect, port_to_connect,
2347147005Sdes	    gateway_ports);
234892559Sdes}
234992559Sdes
235092559Sdes/* protocol v2 remote port fwd, used by sshd */
235192559Sdesint
235292559Sdeschannel_setup_remote_fwd_listener(const char *listen_address,
235392559Sdes    u_short listen_port, int gateway_ports)
235492559Sdes{
235592559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
235692559Sdes	    listen_address, listen_port, NULL, 0, gateway_ports);
235792559Sdes}
235892559Sdes
235957429Smarkm/*
236057429Smarkm * Initiate forwarding of connections to port "port" on remote host through
236157429Smarkm * the secure channel to host:port from local side.
236257429Smarkm */
236357429Smarkm
236460573Skrisvoid
2365147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port,
236676262Sgreen    const char *host_to_connect, u_short port_to_connect)
236757429Smarkm{
236892559Sdes	int type, success = 0;
236976262Sgreen
237057429Smarkm	/* Record locally that connection to this host/port is permitted. */
237157429Smarkm	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
237257429Smarkm		fatal("channel_request_remote_forwarding: too many forwards");
237357429Smarkm
237457429Smarkm	/* Send the forward request to the remote side. */
237560573Skris	if (compat20) {
2376147005Sdes		const char *address_to_bind;
2377147005Sdes		if (listen_host == NULL)
2378147005Sdes			address_to_bind = "localhost";
2379147005Sdes		else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0)
2380147005Sdes			address_to_bind = "";
2381147005Sdes		else
2382147005Sdes			address_to_bind = listen_host;
2383147005Sdes
238460573Skris		packet_start(SSH2_MSG_GLOBAL_REQUEST);
238560573Skris		packet_put_cstring("tcpip-forward");
238698684Sdes		packet_put_char(1);			/* boolean: want reply */
238760573Skris		packet_put_cstring(address_to_bind);
238860573Skris		packet_put_int(listen_port);
238976262Sgreen		packet_send();
239076262Sgreen		packet_write_wait();
239176262Sgreen		/* Assume that server accepts the request */
239276262Sgreen		success = 1;
239360573Skris	} else {
239460573Skris		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
239560573Skris		packet_put_int(listen_port);
239660573Skris		packet_put_cstring(host_to_connect);
239760573Skris		packet_put_int(port_to_connect);
239860573Skris		packet_send();
239960573Skris		packet_write_wait();
240076262Sgreen
240176262Sgreen		/* Wait for response from the remote side. */
240292559Sdes		type = packet_read();
240376262Sgreen		switch (type) {
240476262Sgreen		case SSH_SMSG_SUCCESS:
240576262Sgreen			success = 1;
240676262Sgreen			break;
240776262Sgreen		case SSH_SMSG_FAILURE:
2408124207Sdes			logit("Warning: Server denied remote port forwarding.");
240976262Sgreen			break;
241076262Sgreen		default:
241176262Sgreen			/* Unknown packet */
241276262Sgreen			packet_disconnect("Protocol error for port forward request:"
241376262Sgreen			    "received packet type %d.", type);
241476262Sgreen		}
241560573Skris	}
241676262Sgreen	if (success) {
241776262Sgreen		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
241876262Sgreen		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
241976262Sgreen		permitted_opens[num_permitted_opens].listen_port = listen_port;
242076262Sgreen		num_permitted_opens++;
242176262Sgreen	}
242257429Smarkm}
242357429Smarkm
242457429Smarkm/*
2425137019Sdes * Request cancellation of remote forwarding of connection host:port from
2426137019Sdes * local side.
2427137019Sdes */
2428137019Sdesvoid
2429147005Sdeschannel_request_rforward_cancel(const char *host, u_short port)
2430137019Sdes{
2431137019Sdes	int i;
2432137019Sdes
2433137019Sdes	if (!compat20)
2434137019Sdes		return;
2435137019Sdes
2436137019Sdes	for (i = 0; i < num_permitted_opens; i++) {
2437137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
2438137019Sdes		    permitted_opens[i].listen_port == port)
2439137019Sdes			break;
2440137019Sdes	}
2441137019Sdes	if (i >= num_permitted_opens) {
2442137019Sdes		debug("%s: requested forward not found", __func__);
2443137019Sdes		return;
2444137019Sdes	}
2445137019Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
2446137019Sdes	packet_put_cstring("cancel-tcpip-forward");
2447137019Sdes	packet_put_char(0);
2448147005Sdes	packet_put_cstring(host == NULL ? "" : host);
2449137019Sdes	packet_put_int(port);
2450137019Sdes	packet_send();
2451137019Sdes
2452137019Sdes	permitted_opens[i].listen_port = 0;
2453137019Sdes	permitted_opens[i].port_to_connect = 0;
2454137019Sdes	free(permitted_opens[i].host_to_connect);
2455137019Sdes	permitted_opens[i].host_to_connect = NULL;
2456137019Sdes}
2457137019Sdes
2458137019Sdes/*
245957429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
246057429Smarkm * listening for the port, and sends back a success reply (or disconnect
246157429Smarkm * message if there was an error).  This never returns if there was an error.
246257429Smarkm */
246357429Smarkm
246460573Skrisvoid
246560573Skrischannel_input_port_forward_request(int is_root, int gateway_ports)
246657429Smarkm{
246757429Smarkm	u_short port, host_port;
246857429Smarkm	char *hostname;
246957429Smarkm
247057429Smarkm	/* Get arguments from the packet. */
247157429Smarkm	port = packet_get_int();
247257429Smarkm	hostname = packet_get_string(NULL);
247357429Smarkm	host_port = packet_get_int();
247457429Smarkm
247598941Sdes#ifndef HAVE_CYGWIN
247657429Smarkm	/*
247757429Smarkm	 * Check that an unprivileged user is not trying to forward a
247857429Smarkm	 * privileged port.
247957429Smarkm	 */
248057429Smarkm	if (port < IPPORT_RESERVED && !is_root)
2481124207Sdes		packet_disconnect(
2482124207Sdes		    "Requested forwarding of port %d but user is not root.",
2483124207Sdes		    port);
2484124207Sdes	if (host_port == 0)
2485124207Sdes		packet_disconnect("Dynamic forwarding denied.");
248698941Sdes#endif
2487124207Sdes
248876262Sgreen	/* Initiate forwarding */
2489147005Sdes	channel_setup_local_fwd_listener(NULL, port, hostname,
2490147005Sdes	    host_port, gateway_ports);
249157429Smarkm
249257429Smarkm	/* Free the argument string. */
249357429Smarkm	xfree(hostname);
249457429Smarkm}
249557429Smarkm
249676262Sgreen/*
249776262Sgreen * Permits opening to any host/port if permitted_opens[] is empty.  This is
249876262Sgreen * usually called by the server, because the user could connect to any port
249976262Sgreen * anyway, and the server has no way to know but to trust the client anyway.
250076262Sgreen */
250176262Sgreenvoid
250292559Sdeschannel_permit_all_opens(void)
250376262Sgreen{
250476262Sgreen	if (num_permitted_opens == 0)
250576262Sgreen		all_opens_permitted = 1;
250676262Sgreen}
250776262Sgreen
250876262Sgreenvoid
250976262Sgreenchannel_add_permitted_opens(char *host, int port)
251076262Sgreen{
251176262Sgreen	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
251276262Sgreen		fatal("channel_request_remote_forwarding: too many forwards");
251376262Sgreen	debug("allow port forwarding to host %s port %d", host, port);
251476262Sgreen
251576262Sgreen	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
251676262Sgreen	permitted_opens[num_permitted_opens].port_to_connect = port;
251776262Sgreen	num_permitted_opens++;
251876262Sgreen
251976262Sgreen	all_opens_permitted = 0;
252076262Sgreen}
252176262Sgreen
252276262Sgreenvoid
252376262Sgreenchannel_clear_permitted_opens(void)
252476262Sgreen{
252576262Sgreen	int i;
252676262Sgreen
252776262Sgreen	for (i = 0; i < num_permitted_opens; i++)
2528137019Sdes		if (permitted_opens[i].host_to_connect != NULL)
2529137019Sdes			xfree(permitted_opens[i].host_to_connect);
253076262Sgreen	num_permitted_opens = 0;
253176262Sgreen
253276262Sgreen}
253376262Sgreen
253476262Sgreen
253576262Sgreen/* return socket to remote host, port */
253692559Sdesstatic int
253776262Sgreenconnect_to(const char *host, u_short port)
253860573Skris{
253960573Skris	struct addrinfo hints, *ai, *aitop;
254060573Skris	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
254160573Skris	int gaierr;
254260573Skris	int sock = -1;
254360573Skris
254460573Skris	memset(&hints, 0, sizeof(hints));
254560573Skris	hints.ai_family = IPv4or6;
254660573Skris	hints.ai_socktype = SOCK_STREAM;
254776262Sgreen	snprintf(strport, sizeof strport, "%d", port);
254860573Skris	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
254976262Sgreen		error("connect_to %.100s: unknown host (%s)", host,
255076262Sgreen		    gai_strerror(gaierr));
255160573Skris		return -1;
255260573Skris	}
255360573Skris	for (ai = aitop; ai; ai = ai->ai_next) {
255460573Skris		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
255560573Skris			continue;
255660573Skris		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
255760573Skris		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
255876262Sgreen			error("connect_to: getnameinfo failed");
255960573Skris			continue;
256060573Skris		}
2561124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
256260573Skris		if (sock < 0) {
2563113911Sdes			if (ai->ai_next == NULL)
2564113911Sdes				error("socket: %.100s", strerror(errno));
2565113911Sdes			else
2566113911Sdes				verbose("socket: %.100s", strerror(errno));
256760573Skris			continue;
256860573Skris		}
2569137019Sdes		if (set_nonblock(sock) == -1)
2570137019Sdes			fatal("%s: set_nonblock(%d)", __func__, sock);
257176262Sgreen		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
257276262Sgreen		    errno != EINPROGRESS) {
257376262Sgreen			error("connect_to %.100s port %s: %.100s", ntop, strport,
257460573Skris			    strerror(errno));
257560573Skris			close(sock);
257676262Sgreen			continue;	/* fail -- try next */
257760573Skris		}
257860573Skris		break; /* success */
257960573Skris
258060573Skris	}
258160573Skris	freeaddrinfo(aitop);
258260573Skris	if (!ai) {
258376262Sgreen		error("connect_to %.100s port %d: failed.", host, port);
258460573Skris		return -1;
258560573Skris	}
258660573Skris	/* success */
258792559Sdes	set_nodelay(sock);
258860573Skris	return sock;
258960573Skris}
259076262Sgreen
259176262Sgreenint
259292559Sdeschannel_connect_by_listen_address(u_short listen_port)
259376262Sgreen{
259476262Sgreen	int i;
259576262Sgreen
259676262Sgreen	for (i = 0; i < num_permitted_opens; i++)
2597137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
2598137019Sdes		    permitted_opens[i].listen_port == listen_port)
259976262Sgreen			return connect_to(
260076262Sgreen			    permitted_opens[i].host_to_connect,
260176262Sgreen			    permitted_opens[i].port_to_connect);
260276262Sgreen	error("WARNING: Server requests forwarding for unknown listen_port %d",
260376262Sgreen	    listen_port);
260476262Sgreen	return -1;
260576262Sgreen}
260676262Sgreen
260776262Sgreen/* Check if connecting to that port is permitted and connect. */
260876262Sgreenint
260976262Sgreenchannel_connect_to(const char *host, u_short port)
261076262Sgreen{
261176262Sgreen	int i, permit;
261276262Sgreen
261376262Sgreen	permit = all_opens_permitted;
261476262Sgreen	if (!permit) {
261576262Sgreen		for (i = 0; i < num_permitted_opens; i++)
2616137019Sdes			if (permitted_opens[i].host_to_connect != NULL &&
2617137019Sdes			    permitted_opens[i].port_to_connect == port &&
261876262Sgreen			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
261976262Sgreen				permit = 1;
262076262Sgreen
262176262Sgreen	}
262276262Sgreen	if (!permit) {
2623124207Sdes		logit("Received request to connect to host %.100s port %d, "
262476262Sgreen		    "but the request was denied.", host, port);
262576262Sgreen		return -1;
262676262Sgreen	}
262776262Sgreen	return connect_to(host, port);
262876262Sgreen}
262976262Sgreen
2630137019Sdesvoid
2631137019Sdeschannel_send_window_changes(void)
2632137019Sdes{
2633137019Sdes	u_int i;
2634137019Sdes	struct winsize ws;
2635137019Sdes
2636137019Sdes	for (i = 0; i < channels_alloc; i++) {
2637147005Sdes		if (channels[i] == NULL || !channels[i]->client_tty ||
2638137019Sdes		    channels[i]->type != SSH_CHANNEL_OPEN)
2639137019Sdes			continue;
2640137019Sdes		if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
2641137019Sdes			continue;
2642137019Sdes		channel_request_start(i, "window-change", 0);
2643137019Sdes		packet_put_int(ws.ws_col);
2644137019Sdes		packet_put_int(ws.ws_row);
2645137019Sdes		packet_put_int(ws.ws_xpixel);
2646137019Sdes		packet_put_int(ws.ws_ypixel);
2647137019Sdes		packet_send();
2648137019Sdes	}
2649137019Sdes}
2650137019Sdes
265192559Sdes/* -- X11 forwarding */
265257429Smarkm
265357429Smarkm/*
265457429Smarkm * Creates an internet domain socket for listening for X11 connections.
265599063Sdes * Returns 0 and a suitable display number for the DISPLAY variable
265699063Sdes * stored in display_numberp , or -1 if an error occurs.
265757429Smarkm */
265892559Sdesint
265992559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost,
266099063Sdes    int single_connection, u_int *display_numberp)
266157429Smarkm{
266292559Sdes	Channel *nc = NULL;
266357429Smarkm	int display_number, sock;
266457429Smarkm	u_short port;
266557429Smarkm	struct addrinfo hints, *ai, *aitop;
266657429Smarkm	char strport[NI_MAXSERV];
266757429Smarkm	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
266857429Smarkm
266957429Smarkm	for (display_number = x11_display_offset;
267092559Sdes	    display_number < MAX_DISPLAYS;
267192559Sdes	    display_number++) {
267257429Smarkm		port = 6000 + display_number;
267357429Smarkm		memset(&hints, 0, sizeof(hints));
267457429Smarkm		hints.ai_family = IPv4or6;
267592559Sdes		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
267657429Smarkm		hints.ai_socktype = SOCK_STREAM;
267757429Smarkm		snprintf(strport, sizeof strport, "%d", port);
267857429Smarkm		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
267957429Smarkm			error("getaddrinfo: %.100s", gai_strerror(gaierr));
268092559Sdes			return -1;
268157429Smarkm		}
268257429Smarkm		for (ai = aitop; ai; ai = ai->ai_next) {
268357429Smarkm			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
268457429Smarkm				continue;
2685124207Sdes			sock = socket(ai->ai_family, ai->ai_socktype,
2686124207Sdes			    ai->ai_protocol);
268757429Smarkm			if (sock < 0) {
268898941Sdes				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
268998941Sdes					error("socket: %.100s", strerror(errno));
2690137019Sdes					freeaddrinfo(aitop);
269198941Sdes					return -1;
269298941Sdes				} else {
269398941Sdes					debug("x11_create_display_inet: Socket family %d not supported",
269498941Sdes						 ai->ai_family);
269598941Sdes					continue;
269698941Sdes				}
269757429Smarkm			}
269898941Sdes#ifdef IPV6_V6ONLY
269998941Sdes			if (ai->ai_family == AF_INET6) {
270098941Sdes				int on = 1;
270198941Sdes				if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
270298941Sdes					error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
270398941Sdes			}
270498941Sdes#endif
270557429Smarkm			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
2706124207Sdes				debug2("bind port %d: %.100s", port, strerror(errno));
270757429Smarkm				close(sock);
270898941Sdes
270998941Sdes				if (ai->ai_next)
271098941Sdes					continue;
271198941Sdes
271257429Smarkm				for (n = 0; n < num_socks; n++) {
271357429Smarkm					close(socks[n]);
271457429Smarkm				}
271557429Smarkm				num_socks = 0;
271657429Smarkm				break;
271757429Smarkm			}
271857429Smarkm			socks[num_socks++] = sock;
271998941Sdes#ifndef DONT_TRY_OTHER_AF
272057429Smarkm			if (num_socks == NUM_SOCKS)
272157429Smarkm				break;
272298941Sdes#else
272398941Sdes			if (x11_use_localhost) {
272498941Sdes				if (num_socks == NUM_SOCKS)
272598941Sdes					break;
272698941Sdes			} else {
272798941Sdes				break;
272898941Sdes			}
272998941Sdes#endif
273057429Smarkm		}
273176262Sgreen		freeaddrinfo(aitop);
273257429Smarkm		if (num_socks > 0)
273357429Smarkm			break;
273457429Smarkm	}
273557429Smarkm	if (display_number >= MAX_DISPLAYS) {
273657429Smarkm		error("Failed to allocate internet-domain X11 display socket.");
273792559Sdes		return -1;
273857429Smarkm	}
273957429Smarkm	/* Start listening for connections on the socket. */
274057429Smarkm	for (n = 0; n < num_socks; n++) {
274157429Smarkm		sock = socks[n];
2742126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
274357429Smarkm			error("listen: %.100s", strerror(errno));
274457429Smarkm			close(sock);
274592559Sdes			return -1;
274657429Smarkm		}
274757429Smarkm	}
274857429Smarkm
274957429Smarkm	/* Allocate a channel for each socket. */
275057429Smarkm	for (n = 0; n < num_socks; n++) {
275157429Smarkm		sock = socks[n];
275292559Sdes		nc = channel_new("x11 listener",
275360573Skris		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
275460573Skris		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
2755124207Sdes		    0, "X11 inet listener", 1);
275692559Sdes		nc->single_connection = single_connection;
275757429Smarkm	}
275857429Smarkm
275992559Sdes	/* Return the display number for the DISPLAY environment variable. */
276099063Sdes	*display_numberp = display_number;
276199063Sdes	return (0);
276257429Smarkm}
276357429Smarkm
276492559Sdesstatic int
276576262Sgreenconnect_local_xsocket(u_int dnr)
276657429Smarkm{
276757429Smarkm	int sock;
276857429Smarkm	struct sockaddr_un addr;
276957429Smarkm
277092559Sdes	sock = socket(AF_UNIX, SOCK_STREAM, 0);
277192559Sdes	if (sock < 0)
277292559Sdes		error("socket: %.100s", strerror(errno));
277392559Sdes	memset(&addr, 0, sizeof(addr));
277492559Sdes	addr.sun_family = AF_UNIX;
277592559Sdes	snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
277692559Sdes	if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
277792559Sdes		return sock;
277892559Sdes	close(sock);
277957429Smarkm	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
278057429Smarkm	return -1;
278157429Smarkm}
278257429Smarkm
278360573Skrisint
278460573Skrisx11_connect_display(void)
278557429Smarkm{
278660573Skris	int display_number, sock = 0;
278757429Smarkm	const char *display;
278860573Skris	char buf[1024], *cp;
278957429Smarkm	struct addrinfo hints, *ai, *aitop;
279057429Smarkm	char strport[NI_MAXSERV];
279157429Smarkm	int gaierr;
279257429Smarkm
279357429Smarkm	/* Try to open a socket for the local X server. */
279457429Smarkm	display = getenv("DISPLAY");
279557429Smarkm	if (!display) {
279657429Smarkm		error("DISPLAY not set.");
279760573Skris		return -1;
279857429Smarkm	}
279957429Smarkm	/*
280057429Smarkm	 * Now we decode the value of the DISPLAY variable and make a
280157429Smarkm	 * connection to the real X server.
280257429Smarkm	 */
280357429Smarkm
280457429Smarkm	/*
280557429Smarkm	 * Check if it is a unix domain socket.  Unix domain displays are in
280657429Smarkm	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
280757429Smarkm	 */
280857429Smarkm	if (strncmp(display, "unix:", 5) == 0 ||
280957429Smarkm	    display[0] == ':') {
281057429Smarkm		/* Connect to the unix domain socket. */
281157429Smarkm		if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
281257429Smarkm			error("Could not parse display number from DISPLAY: %.100s",
281392559Sdes			    display);
281460573Skris			return -1;
281557429Smarkm		}
281657429Smarkm		/* Create a socket. */
281757429Smarkm		sock = connect_local_xsocket(display_number);
281857429Smarkm		if (sock < 0)
281960573Skris			return -1;
282057429Smarkm
282157429Smarkm		/* OK, we now have a connection to the display. */
282260573Skris		return sock;
282357429Smarkm	}
282457429Smarkm	/*
282557429Smarkm	 * Connect to an inet socket.  The DISPLAY value is supposedly
282657429Smarkm	 * hostname:d[.s], where hostname may also be numeric IP address.
282757429Smarkm	 */
282892559Sdes	strlcpy(buf, display, sizeof(buf));
282957429Smarkm	cp = strchr(buf, ':');
283057429Smarkm	if (!cp) {
283157429Smarkm		error("Could not find ':' in DISPLAY: %.100s", display);
283260573Skris		return -1;
283357429Smarkm	}
283457429Smarkm	*cp = 0;
283557429Smarkm	/* buf now contains the host name.  But first we parse the display number. */
283657429Smarkm	if (sscanf(cp + 1, "%d", &display_number) != 1) {
283757429Smarkm		error("Could not parse display number from DISPLAY: %.100s",
283892559Sdes		    display);
283960573Skris		return -1;
284057429Smarkm	}
284157429Smarkm
284257429Smarkm	/* Look up the host address */
284357429Smarkm	memset(&hints, 0, sizeof(hints));
284457429Smarkm	hints.ai_family = IPv4or6;
284557429Smarkm	hints.ai_socktype = SOCK_STREAM;
284657429Smarkm	snprintf(strport, sizeof strport, "%d", 6000 + display_number);
284757429Smarkm	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
284857429Smarkm		error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
284960573Skris		return -1;
285057429Smarkm	}
285157429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
285257429Smarkm		/* Create a socket. */
2853124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
285457429Smarkm		if (sock < 0) {
2855124207Sdes			debug2("socket: %.100s", strerror(errno));
285660573Skris			continue;
285760573Skris		}
285860573Skris		/* Connect it to the display. */
285960573Skris		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
2860124207Sdes			debug2("connect %.100s port %d: %.100s", buf,
286160573Skris			    6000 + display_number, strerror(errno));
286260573Skris			close(sock);
286360573Skris			continue;
286460573Skris		}
286560573Skris		/* Success */
286660573Skris		break;
286757429Smarkm	}
286857429Smarkm	freeaddrinfo(aitop);
286957429Smarkm	if (!ai) {
287060573Skris		error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
287157429Smarkm		    strerror(errno));
287260573Skris		return -1;
287357429Smarkm	}
287492559Sdes	set_nodelay(sock);
287560573Skris	return sock;
287660573Skris}
287757429Smarkm
287860573Skris/*
287960573Skris * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
288060573Skris * the remote channel number.  We should do whatever we want, and respond
288160573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
288260573Skris */
288357429Smarkm
288460573Skrisvoid
288592559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt)
288660573Skris{
288792559Sdes	Channel *c = NULL;
288892559Sdes	int remote_id, sock = 0;
288960573Skris	char *remote_host;
289057429Smarkm
289192559Sdes	debug("Received X11 open request.");
289257429Smarkm
289392559Sdes	remote_id = packet_get_int();
289492559Sdes
289592559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
289692559Sdes		remote_host = packet_get_string(NULL);
289760573Skris	} else {
289860573Skris		remote_host = xstrdup("unknown (remote did not supply name)");
289960573Skris	}
290092559Sdes	packet_check_eom();
290160573Skris
290260573Skris	/* Obtain a connection to the real X display. */
290360573Skris	sock = x11_connect_display();
290492559Sdes	if (sock != -1) {
290592559Sdes		/* Allocate a channel for this connection. */
290692559Sdes		c = channel_new("connected x11 socket",
290792559Sdes		    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
290892559Sdes		    remote_host, 1);
290992559Sdes		c->remote_id = remote_id;
291092559Sdes		c->force_drain = 1;
291192559Sdes	}
2912124207Sdes	xfree(remote_host);
291392559Sdes	if (c == NULL) {
291460573Skris		/* Send refusal to the remote host. */
291560573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
291692559Sdes		packet_put_int(remote_id);
291760573Skris	} else {
291860573Skris		/* Send a confirmation to the remote host. */
291960573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
292092559Sdes		packet_put_int(remote_id);
292192559Sdes		packet_put_int(c->self);
292260573Skris	}
292392559Sdes	packet_send();
292457429Smarkm}
292557429Smarkm
292669587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */
292769587Sgreenvoid
292892559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt)
292969587Sgreen{
293069587Sgreen	int rchan = packet_get_int();
2931106130Sdes
293292559Sdes	switch (type) {
293369587Sgreen	case SSH_SMSG_AGENT_OPEN:
293469587Sgreen		error("Warning: ssh server tried agent forwarding.");
293569587Sgreen		break;
293669587Sgreen	case SSH_SMSG_X11_OPEN:
293769587Sgreen		error("Warning: ssh server tried X11 forwarding.");
293869587Sgreen		break;
293969587Sgreen	default:
294092559Sdes		error("deny_input_open: type %d", type);
294169587Sgreen		break;
294269587Sgreen	}
294369587Sgreen	error("Warning: this is probably a break in attempt by a malicious server.");
294469587Sgreen	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
294569587Sgreen	packet_put_int(rchan);
294669587Sgreen	packet_send();
294769587Sgreen}
294869587Sgreen
294957429Smarkm/*
295057429Smarkm * Requests forwarding of X11 connections, generates fake authentication
295157429Smarkm * data, and enables authentication spoofing.
295292559Sdes * This should be called in the client only.
295357429Smarkm */
295460573Skrisvoid
295560573Skrisx11_request_forwarding_with_spoofing(int client_session_id,
295660573Skris    const char *proto, const char *data)
295757429Smarkm{
295876262Sgreen	u_int data_len = (u_int) strlen(data) / 2;
295976262Sgreen	u_int i, value, len;
296057429Smarkm	char *new_data;
296157429Smarkm	int screen_number;
296257429Smarkm	const char *cp;
2963137019Sdes	u_int32_t rnd = 0;
296457429Smarkm
296557429Smarkm	cp = getenv("DISPLAY");
296657429Smarkm	if (cp)
296757429Smarkm		cp = strchr(cp, ':');
296857429Smarkm	if (cp)
296957429Smarkm		cp = strchr(cp, '.');
297057429Smarkm	if (cp)
297157429Smarkm		screen_number = atoi(cp + 1);
297257429Smarkm	else
297357429Smarkm		screen_number = 0;
297457429Smarkm
297557429Smarkm	/* Save protocol name. */
297657429Smarkm	x11_saved_proto = xstrdup(proto);
297757429Smarkm
297857429Smarkm	/*
297957429Smarkm	 * Extract real authentication data and generate fake data of the
298057429Smarkm	 * same length.
298157429Smarkm	 */
298257429Smarkm	x11_saved_data = xmalloc(data_len);
298357429Smarkm	x11_fake_data = xmalloc(data_len);
298457429Smarkm	for (i = 0; i < data_len; i++) {
298557429Smarkm		if (sscanf(data + 2 * i, "%2x", &value) != 1)
298657429Smarkm			fatal("x11_request_forwarding: bad authentication data: %.100s", data);
298757429Smarkm		if (i % 4 == 0)
2988137019Sdes			rnd = arc4random();
298957429Smarkm		x11_saved_data[i] = value;
2990137019Sdes		x11_fake_data[i] = rnd & 0xff;
2991137019Sdes		rnd >>= 8;
299257429Smarkm	}
299357429Smarkm	x11_saved_data_len = data_len;
299457429Smarkm	x11_fake_data_len = data_len;
299557429Smarkm
299657429Smarkm	/* Convert the fake data into hex. */
299776262Sgreen	len = 2 * data_len + 1;
299876262Sgreen	new_data = xmalloc(len);
299957429Smarkm	for (i = 0; i < data_len; i++)
300076262Sgreen		snprintf(new_data + 2 * i, len - 2 * i,
300176262Sgreen		    "%02x", (u_char) x11_fake_data[i]);
300257429Smarkm
300357429Smarkm	/* Send the request packet. */
300460573Skris	if (compat20) {
300560573Skris		channel_request_start(client_session_id, "x11-req", 0);
300660573Skris		packet_put_char(0);	/* XXX bool single connection */
300760573Skris	} else {
300860573Skris		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
300960573Skris	}
301060573Skris	packet_put_cstring(proto);
301160573Skris	packet_put_cstring(new_data);
301257429Smarkm	packet_put_int(screen_number);
301357429Smarkm	packet_send();
301457429Smarkm	packet_write_wait();
301557429Smarkm	xfree(new_data);
301657429Smarkm}
301757429Smarkm
301892559Sdes
301992559Sdes/* -- agent forwarding */
302092559Sdes
302157429Smarkm/* Sends a message to the server to request authentication fd forwarding. */
302257429Smarkm
302360573Skrisvoid
302492559Sdesauth_request_forwarding(void)
302557429Smarkm{
302657429Smarkm	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
302757429Smarkm	packet_send();
302857429Smarkm	packet_write_wait();
302957429Smarkm}
3030