xref: /onnv-gate/usr/src/cmd/ssh/libssh/common/channels.c (revision 12551:b2ba48b14377)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Author: Tatu Ylonen <ylo@cs.hut.fi>
30Sstevel@tonic-gate  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
40Sstevel@tonic-gate  *                    All rights reserved
50Sstevel@tonic-gate  * This file contains functions for generic socket connection forwarding.
60Sstevel@tonic-gate  * There is also code for initiating connection forwarding for X11 connections,
70Sstevel@tonic-gate  * arbitrary tcp/ip connections, and the authentication agent connection.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * As far as I am concerned, the code I have written for this software
100Sstevel@tonic-gate  * can be used freely for any purpose.  Any derived versions of this
110Sstevel@tonic-gate  * software must be clearly marked as such, and if the derived work is
120Sstevel@tonic-gate  * incompatible with the protocol description in the RFC file, it must be
130Sstevel@tonic-gate  * called by a name other than "ssh" or "Secure Shell".
140Sstevel@tonic-gate  *
150Sstevel@tonic-gate  * SSH2 support added by Markus Friedl.
160Sstevel@tonic-gate  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
170Sstevel@tonic-gate  * Copyright (c) 1999 Dug Song.  All rights reserved.
180Sstevel@tonic-gate  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
210Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
220Sstevel@tonic-gate  * are met:
230Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
240Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
250Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
260Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
270Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
300Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
310Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
320Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
330Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
340Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
350Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
360Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
370Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
380Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
390Sstevel@tonic-gate  */
405334Sjp161948 /*
41*12551SZdenek.Kotala@Sun.COM  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
425334Sjp161948  */
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "includes.h"
450Sstevel@tonic-gate RCSID("$OpenBSD: channels.c,v 1.183 2002/09/17 07:47:02 itojun Exp $");
460Sstevel@tonic-gate 
470Sstevel@tonic-gate #include "ssh.h"
480Sstevel@tonic-gate #include "ssh1.h"
490Sstevel@tonic-gate #include "ssh2.h"
500Sstevel@tonic-gate #include "packet.h"
510Sstevel@tonic-gate #include "xmalloc.h"
520Sstevel@tonic-gate #include "log.h"
530Sstevel@tonic-gate #include "misc.h"
540Sstevel@tonic-gate #include "channels.h"
550Sstevel@tonic-gate #include "compat.h"
560Sstevel@tonic-gate #include "canohost.h"
570Sstevel@tonic-gate #include "key.h"
580Sstevel@tonic-gate #include "authfd.h"
590Sstevel@tonic-gate #include "pathnames.h"
604764Sjp161948 #include "bufaux.h"
610Sstevel@tonic-gate 
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* -- channel core */
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * Pointer to an array containing all allocated channels.  The array is
670Sstevel@tonic-gate  * dynamically extended as needed.
680Sstevel@tonic-gate  */
690Sstevel@tonic-gate static Channel **channels = NULL;
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate  * Size of the channel array.  All slots of the array must always be
730Sstevel@tonic-gate  * initialized (at least the type field); unused slots set to NULL
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate static int channels_alloc = 0;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate  * Maximum file descriptor value used in any of the channels.  This is
790Sstevel@tonic-gate  * updated in channel_new.
800Sstevel@tonic-gate  */
810Sstevel@tonic-gate static int channel_max_fd = 0;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /* -- tcp forwarding */
850Sstevel@tonic-gate 
860Sstevel@tonic-gate /*
870Sstevel@tonic-gate  * Data structure for storing which hosts are permitted for forward requests.
880Sstevel@tonic-gate  * The local sides of any remote forwards are stored in this array to prevent
890Sstevel@tonic-gate  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
900Sstevel@tonic-gate  * network (which might be behind a firewall).
910Sstevel@tonic-gate  */
920Sstevel@tonic-gate typedef struct {
930Sstevel@tonic-gate 	char *host_to_connect;		/* Connect to 'host'. */
940Sstevel@tonic-gate 	u_short port_to_connect;	/* Connect to 'port'. */
950Sstevel@tonic-gate 	u_short listen_port;		/* Remote side should listen port number. */
960Sstevel@tonic-gate } ForwardPermission;
970Sstevel@tonic-gate 
980Sstevel@tonic-gate /* List of all permitted host/port pairs to connect. */
990Sstevel@tonic-gate static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate /* Number of permitted host/port pairs in the array. */
1020Sstevel@tonic-gate static int num_permitted_opens = 0;
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate  * If this is true, all opens are permitted.  This is the case on the server
1050Sstevel@tonic-gate  * on which we have to trust the client anyway, and the user could do
1060Sstevel@tonic-gate  * anything after logging in anyway.
1070Sstevel@tonic-gate  */
1080Sstevel@tonic-gate static int all_opens_permitted = 0;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate /* -- X11 forwarding */
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate /* Maximum number of fake X11 displays to try. */
1140Sstevel@tonic-gate #define MAX_DISPLAYS  1000
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /* Saved X11 authentication protocol name. */
1170Sstevel@tonic-gate static char *x11_saved_proto = NULL;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate /* Saved X11 authentication data.  This is the real data. */
1200Sstevel@tonic-gate static char *x11_saved_data = NULL;
1210Sstevel@tonic-gate static u_int x11_saved_data_len = 0;
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate /*
1240Sstevel@tonic-gate  * Fake X11 authentication data.  This is what the server will be sending us;
1250Sstevel@tonic-gate  * we should replace any occurrences of this by the real data.
1260Sstevel@tonic-gate  */
1274907Sjp161948 static u_char *x11_fake_data = NULL;
1280Sstevel@tonic-gate static u_int x11_fake_data_len;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate /* -- agent forwarding */
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate #define	NUM_SOCKS	10
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate /* AF_UNSPEC or AF_INET or AF_INET6 */
1360Sstevel@tonic-gate static int IPv4or6 = AF_UNSPEC;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /* helper */
1390Sstevel@tonic-gate static void port_open_helper(Channel *c, char *rtype);
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate /* -- channel core */
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate Channel *
channel_lookup(int id)1440Sstevel@tonic-gate channel_lookup(int id)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	Channel *c;
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	if (id < 0 || id >= channels_alloc) {
1490Sstevel@tonic-gate 		log("channel_lookup: %d: bad id", id);
1500Sstevel@tonic-gate 		return NULL;
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 	c = channels[id];
1530Sstevel@tonic-gate 	if (c == NULL) {
1540Sstevel@tonic-gate 		log("channel_lookup: %d: bad id: channel free", id);
1550Sstevel@tonic-gate 		return NULL;
1560Sstevel@tonic-gate 	}
1570Sstevel@tonic-gate 	return c;
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate /*
1610Sstevel@tonic-gate  * Register filedescriptors for a channel, used when allocating a channel or
1620Sstevel@tonic-gate  * when the channel consumer/producer is ready, e.g. shell exec'd
1630Sstevel@tonic-gate  */
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate static void
channel_register_fds(Channel * c,int rfd,int wfd,int efd,int extusage,int nonblock)1660Sstevel@tonic-gate channel_register_fds(Channel *c, int rfd, int wfd, int efd,
1670Sstevel@tonic-gate     int extusage, int nonblock)
1680Sstevel@tonic-gate {
1690Sstevel@tonic-gate 	/* Update the maximum file descriptor value. */
1700Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, rfd);
1710Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, wfd);
1720Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, efd);
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	/* XXX set close-on-exec -markus */
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	c->rfd = rfd;
1770Sstevel@tonic-gate 	c->wfd = wfd;
1780Sstevel@tonic-gate 	c->sock = (rfd == wfd) ? rfd : -1;
1790Sstevel@tonic-gate 	c->efd = efd;
1800Sstevel@tonic-gate 	c->extended_usage = extusage;
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	/* XXX ugly hack: nonblock is only set by the server */
1830Sstevel@tonic-gate 	if (nonblock && isatty(c->rfd)) {
1840Sstevel@tonic-gate 		debug("channel %d: rfd %d isatty", c->self, c->rfd);
1850Sstevel@tonic-gate 		c->isatty = 1;
1860Sstevel@tonic-gate 		if (!isatty(c->wfd)) {
1870Sstevel@tonic-gate 			error("channel %d: wfd %d is not a tty?",
1880Sstevel@tonic-gate 			    c->self, c->wfd);
1890Sstevel@tonic-gate 		}
1900Sstevel@tonic-gate 	} else {
1910Sstevel@tonic-gate 		c->isatty = 0;
1920Sstevel@tonic-gate 	}
1930Sstevel@tonic-gate 	c->wfd_isatty = isatty(c->wfd);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	/* enable nonblocking mode */
1960Sstevel@tonic-gate 	if (nonblock) {
1970Sstevel@tonic-gate 		if (rfd != -1)
1980Sstevel@tonic-gate 			set_nonblock(rfd);
1990Sstevel@tonic-gate 		if (wfd != -1)
2000Sstevel@tonic-gate 			set_nonblock(wfd);
2010Sstevel@tonic-gate 		if (efd != -1)
2020Sstevel@tonic-gate 			set_nonblock(efd);
2030Sstevel@tonic-gate 	}
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate /*
2070Sstevel@tonic-gate  * Allocate a new channel object and set its type and socket. This will cause
2080Sstevel@tonic-gate  * remote_name to be freed.
2090Sstevel@tonic-gate  */
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate Channel *
channel_new(char * ctype,int type,int rfd,int wfd,int efd,u_int window,u_int maxpack,int extusage,char * remote_name,int nonblock)2120Sstevel@tonic-gate channel_new(char *ctype, int type, int rfd, int wfd, int efd,
2130Sstevel@tonic-gate     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
2140Sstevel@tonic-gate {
2150Sstevel@tonic-gate 	int i, found;
2160Sstevel@tonic-gate 	Channel *c;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	/* Do initial allocation if this is the first call. */
2190Sstevel@tonic-gate 	if (channels_alloc == 0) {
2200Sstevel@tonic-gate 		channels_alloc = 10;
2210Sstevel@tonic-gate 		channels = xmalloc(channels_alloc * sizeof(Channel *));
2220Sstevel@tonic-gate 		for (i = 0; i < channels_alloc; i++)
2230Sstevel@tonic-gate 			channels[i] = NULL;
2240Sstevel@tonic-gate 		fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL);
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate 	/* Try to find a free slot where to put the new channel. */
2270Sstevel@tonic-gate 	for (found = -1, i = 0; i < channels_alloc; i++)
2280Sstevel@tonic-gate 		if (channels[i] == NULL) {
2290Sstevel@tonic-gate 			/* Found a free slot. */
2300Sstevel@tonic-gate 			found = i;
2310Sstevel@tonic-gate 			break;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 	if (found == -1) {
2340Sstevel@tonic-gate 		/* There are no free slots.  Take last+1 slot and expand the array.  */
2350Sstevel@tonic-gate 		found = channels_alloc;
2360Sstevel@tonic-gate 		if (channels_alloc > 10000)
2370Sstevel@tonic-gate 			fatal("channel_new: internal error: channels_alloc %d "
2380Sstevel@tonic-gate 			    "too big.", channels_alloc);
2390Sstevel@tonic-gate 		channels = xrealloc(channels,
2400Sstevel@tonic-gate 		    (channels_alloc + 10) * sizeof(Channel *));
2410Sstevel@tonic-gate 		channels_alloc += 10;
2420Sstevel@tonic-gate 		debug2("channel: expanding %d", channels_alloc);
2430Sstevel@tonic-gate 		for (i = found; i < channels_alloc; i++)
2440Sstevel@tonic-gate 			channels[i] = NULL;
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 	/* Initialize and return new channel. */
2470Sstevel@tonic-gate 	c = channels[found] = xmalloc(sizeof(Channel));
2480Sstevel@tonic-gate 	memset(c, 0, sizeof(Channel));
2490Sstevel@tonic-gate 	buffer_init(&c->input);
2500Sstevel@tonic-gate 	buffer_init(&c->output);
2510Sstevel@tonic-gate 	buffer_init(&c->extended);
2520Sstevel@tonic-gate 	c->ostate = CHAN_OUTPUT_OPEN;
2530Sstevel@tonic-gate 	c->istate = CHAN_INPUT_OPEN;
2540Sstevel@tonic-gate 	c->flags = 0;
2550Sstevel@tonic-gate 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
2560Sstevel@tonic-gate 	c->self = found;
2570Sstevel@tonic-gate 	c->type = type;
2580Sstevel@tonic-gate 	c->ctype = ctype;
2590Sstevel@tonic-gate 	c->local_window = window;
2600Sstevel@tonic-gate 	c->local_window_max = window;
2610Sstevel@tonic-gate 	c->local_consumed = 0;
2620Sstevel@tonic-gate 	c->local_maxpacket = maxpack;
2630Sstevel@tonic-gate 	c->remote_id = -1;
2640Sstevel@tonic-gate 	c->remote_name = remote_name;
2650Sstevel@tonic-gate 	c->remote_window = 0;
2660Sstevel@tonic-gate 	c->remote_maxpacket = 0;
2670Sstevel@tonic-gate 	c->force_drain = 0;
2680Sstevel@tonic-gate 	c->single_connection = 0;
2690Sstevel@tonic-gate 	c->detach_user = NULL;
2700Sstevel@tonic-gate 	c->confirm = NULL;
2710Sstevel@tonic-gate 	c->input_filter = NULL;
272*12551SZdenek.Kotala@Sun.COM 	c->delayed = 1;		/* prevent call to channel_post handler */
2730Sstevel@tonic-gate 	debug("channel %d: new [%s]", found, remote_name);
2740Sstevel@tonic-gate 	return c;
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate static int
channel_find_maxfd(void)2780Sstevel@tonic-gate channel_find_maxfd(void)
2790Sstevel@tonic-gate {
2800Sstevel@tonic-gate 	int i, max = 0;
2810Sstevel@tonic-gate 	Channel *c;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
2840Sstevel@tonic-gate 		c = channels[i];
2850Sstevel@tonic-gate 		if (c != NULL) {
2860Sstevel@tonic-gate 			max = MAX(max, c->rfd);
2870Sstevel@tonic-gate 			max = MAX(max, c->wfd);
2880Sstevel@tonic-gate 			max = MAX(max, c->efd);
2890Sstevel@tonic-gate 		}
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 	return max;
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate int
channel_close_fd(int * fdp)2950Sstevel@tonic-gate channel_close_fd(int *fdp)
2960Sstevel@tonic-gate {
2970Sstevel@tonic-gate 	int ret = 0, fd = *fdp;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	if (fd != -1) {
3000Sstevel@tonic-gate 		ret = close(fd);
3010Sstevel@tonic-gate 		*fdp = -1;
3020Sstevel@tonic-gate 		if (fd == channel_max_fd)
3030Sstevel@tonic-gate 			channel_max_fd = channel_find_maxfd();
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 	return ret;
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate /* Close all channel fd/socket. */
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate static void
channel_close_fds(Channel * c)3110Sstevel@tonic-gate channel_close_fds(Channel *c)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate 	debug3("channel_close_fds: channel %d: r %d w %d e %d",
3140Sstevel@tonic-gate 	    c->self, c->rfd, c->wfd, c->efd);
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	channel_close_fd(&c->sock);
3170Sstevel@tonic-gate 	channel_close_fd(&c->rfd);
3180Sstevel@tonic-gate 	channel_close_fd(&c->wfd);
3190Sstevel@tonic-gate 	channel_close_fd(&c->efd);
3200Sstevel@tonic-gate }
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate /* Free the channel and close its fd/socket. */
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate void
channel_free(Channel * c)3250Sstevel@tonic-gate channel_free(Channel *c)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	char *s;
3280Sstevel@tonic-gate 	int i, n;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	for (n = 0, i = 0; i < channels_alloc; i++)
3310Sstevel@tonic-gate 		if (channels[i])
3320Sstevel@tonic-gate 			n++;
3330Sstevel@tonic-gate 	debug("channel_free: channel %d: %s, nchannels %d", c->self,
3340Sstevel@tonic-gate 	    c->remote_name ? c->remote_name : "???", n);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	s = channel_open_message();
3370Sstevel@tonic-gate 	debug3("channel_free: status: %s", s);
3380Sstevel@tonic-gate 	xfree(s);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	if (c->sock != -1)
3410Sstevel@tonic-gate 		shutdown(c->sock, SHUT_RDWR);
3420Sstevel@tonic-gate 	channel_close_fds(c);
3430Sstevel@tonic-gate 	buffer_free(&c->input);
3440Sstevel@tonic-gate 	buffer_free(&c->output);
3450Sstevel@tonic-gate 	buffer_free(&c->extended);
3460Sstevel@tonic-gate 	if (c->remote_name) {
3470Sstevel@tonic-gate 		xfree(c->remote_name);
3480Sstevel@tonic-gate 		c->remote_name = NULL;
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 	channels[c->self] = NULL;
3510Sstevel@tonic-gate 	xfree(c);
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate void
channel_free_all(void)3550Sstevel@tonic-gate channel_free_all(void)
3560Sstevel@tonic-gate {
3570Sstevel@tonic-gate 	int i;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++)
3600Sstevel@tonic-gate 		if (channels[i] != NULL)
3610Sstevel@tonic-gate 			channel_free(channels[i]);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate  * Closes the sockets/fds of all channels.  This is used to close extra file
3660Sstevel@tonic-gate  * descriptors after a fork.
3670Sstevel@tonic-gate  */
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate void
channel_close_all(void)3700Sstevel@tonic-gate channel_close_all(void)
3710Sstevel@tonic-gate {
3720Sstevel@tonic-gate 	int i;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++)
3750Sstevel@tonic-gate 		if (channels[i] != NULL)
3760Sstevel@tonic-gate 			channel_close_fds(channels[i]);
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate /*
3800Sstevel@tonic-gate  * Stop listening to channels.
3810Sstevel@tonic-gate  */
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate void
channel_stop_listening(void)3840Sstevel@tonic-gate channel_stop_listening(void)
3850Sstevel@tonic-gate {
3860Sstevel@tonic-gate 	int i;
3870Sstevel@tonic-gate 	Channel *c;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
3900Sstevel@tonic-gate 		c = channels[i];
3910Sstevel@tonic-gate 		if (c != NULL) {
3920Sstevel@tonic-gate 			switch (c->type) {
3930Sstevel@tonic-gate 			case SSH_CHANNEL_AUTH_SOCKET:
3940Sstevel@tonic-gate 			case SSH_CHANNEL_PORT_LISTENER:
3950Sstevel@tonic-gate 			case SSH_CHANNEL_RPORT_LISTENER:
3960Sstevel@tonic-gate 			case SSH_CHANNEL_X11_LISTENER:
3970Sstevel@tonic-gate 				channel_close_fd(&c->sock);
3980Sstevel@tonic-gate 				channel_free(c);
3990Sstevel@tonic-gate 				break;
4000Sstevel@tonic-gate 			}
4010Sstevel@tonic-gate 		}
4020Sstevel@tonic-gate 	}
4030Sstevel@tonic-gate }
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate /*
4060Sstevel@tonic-gate  * Returns true if no channel has too much buffered data, and false if one or
4070Sstevel@tonic-gate  * more channel is overfull.
4080Sstevel@tonic-gate  */
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate int
channel_not_very_much_buffered_data(void)4110Sstevel@tonic-gate channel_not_very_much_buffered_data(void)
4120Sstevel@tonic-gate {
4130Sstevel@tonic-gate 	u_int i;
4140Sstevel@tonic-gate 	Channel *c;
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4170Sstevel@tonic-gate 		c = channels[i];
4180Sstevel@tonic-gate 		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
4190Sstevel@tonic-gate #if 0
4200Sstevel@tonic-gate 			if (!compat20 &&
4210Sstevel@tonic-gate 			    buffer_len(&c->input) > packet_get_maxsize()) {
4220Sstevel@tonic-gate 				debug("channel %d: big input buffer %d",
4230Sstevel@tonic-gate 				    c->self, buffer_len(&c->input));
4240Sstevel@tonic-gate 				return 0;
4250Sstevel@tonic-gate 			}
4260Sstevel@tonic-gate #endif
4270Sstevel@tonic-gate 			if (buffer_len(&c->output) > packet_get_maxsize()) {
4280Sstevel@tonic-gate 				debug("channel %d: big output buffer %d > %d",
4290Sstevel@tonic-gate 				    c->self, buffer_len(&c->output),
4300Sstevel@tonic-gate 				    packet_get_maxsize());
4310Sstevel@tonic-gate 				return 0;
4320Sstevel@tonic-gate 			}
4330Sstevel@tonic-gate 		}
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 	return 1;
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate /* Returns true if any channel is still open. */
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate int
channel_still_open(void)4410Sstevel@tonic-gate channel_still_open(void)
4420Sstevel@tonic-gate {
4430Sstevel@tonic-gate 	int i;
4440Sstevel@tonic-gate 	Channel *c;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4470Sstevel@tonic-gate 		c = channels[i];
4480Sstevel@tonic-gate 		if (c == NULL)
4490Sstevel@tonic-gate 			continue;
4500Sstevel@tonic-gate 		switch (c->type) {
4510Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
4520Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
4530Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
4540Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
4550Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
4560Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
4570Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
4580Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
4590Sstevel@tonic-gate 			continue;
4600Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
4610Sstevel@tonic-gate 			if (!compat20)
4620Sstevel@tonic-gate 				fatal("cannot happen: SSH_CHANNEL_LARVAL");
4630Sstevel@tonic-gate 			continue;
4640Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
4650Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
4660Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
4670Sstevel@tonic-gate 			return 1;
4680Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
4690Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
4700Sstevel@tonic-gate 			if (!compat13)
4710Sstevel@tonic-gate 				fatal("cannot happen: OUT_DRAIN");
4720Sstevel@tonic-gate 			return 1;
4730Sstevel@tonic-gate 		default:
4740Sstevel@tonic-gate 			fatal("channel_still_open: bad channel type %d", c->type);
4750Sstevel@tonic-gate 			/* NOTREACHED */
4760Sstevel@tonic-gate 		}
4770Sstevel@tonic-gate 	}
4780Sstevel@tonic-gate 	return 0;
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate /* Returns the id of an open channel suitable for keepaliving */
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate int
channel_find_open(void)4840Sstevel@tonic-gate channel_find_open(void)
4850Sstevel@tonic-gate {
4860Sstevel@tonic-gate 	int i;
4870Sstevel@tonic-gate 	Channel *c;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4900Sstevel@tonic-gate 		c = channels[i];
4910Sstevel@tonic-gate 		if (c == NULL)
4920Sstevel@tonic-gate 			continue;
4930Sstevel@tonic-gate 		switch (c->type) {
4940Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
4950Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
4960Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
4970Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
4980Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
4990Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
5000Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
5010Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
5020Sstevel@tonic-gate 			continue;
5030Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
5040Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
5050Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
5060Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
5070Sstevel@tonic-gate 			return i;
5080Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
5090Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
5100Sstevel@tonic-gate 			if (!compat13)
5110Sstevel@tonic-gate 				fatal("cannot happen: OUT_DRAIN");
5120Sstevel@tonic-gate 			return i;
5130Sstevel@tonic-gate 		default:
5140Sstevel@tonic-gate 			fatal("channel_find_open: bad channel type %d", c->type);
5150Sstevel@tonic-gate 			/* NOTREACHED */
5160Sstevel@tonic-gate 		}
5170Sstevel@tonic-gate 	}
5180Sstevel@tonic-gate 	return -1;
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate /*
5230Sstevel@tonic-gate  * Returns a message describing the currently open forwarded connections,
5240Sstevel@tonic-gate  * suitable for sending to the client.  The message contains crlf pairs for
5250Sstevel@tonic-gate  * newlines.
5260Sstevel@tonic-gate  */
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate char *
channel_open_message(void)5290Sstevel@tonic-gate channel_open_message(void)
5300Sstevel@tonic-gate {
5310Sstevel@tonic-gate 	Buffer buffer;
5320Sstevel@tonic-gate 	Channel *c;
5330Sstevel@tonic-gate 	char buf[1024], *cp;
5340Sstevel@tonic-gate 	int i;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	buffer_init(&buffer);
5370Sstevel@tonic-gate 	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
5380Sstevel@tonic-gate 	buffer_append(&buffer, buf, strlen(buf));
5390Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
5400Sstevel@tonic-gate 		c = channels[i];
5410Sstevel@tonic-gate 		if (c == NULL)
5420Sstevel@tonic-gate 			continue;
5430Sstevel@tonic-gate 		switch (c->type) {
5440Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
5450Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
5460Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
5470Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
5480Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
5490Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
5500Sstevel@tonic-gate 			continue;
5510Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
5520Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
5530Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
5540Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
5550Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
5560Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
5570Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
5580Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
5590Sstevel@tonic-gate 			snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
5600Sstevel@tonic-gate 			    c->self, c->remote_name,
5610Sstevel@tonic-gate 			    c->type, c->remote_id,
5620Sstevel@tonic-gate 			    c->istate, buffer_len(&c->input),
5630Sstevel@tonic-gate 			    c->ostate, buffer_len(&c->output),
5640Sstevel@tonic-gate 			    c->rfd, c->wfd);
5650Sstevel@tonic-gate 			buffer_append(&buffer, buf, strlen(buf));
5660Sstevel@tonic-gate 			continue;
5670Sstevel@tonic-gate 		default:
5680Sstevel@tonic-gate 			fatal("channel_open_message: bad channel type %d", c->type);
5690Sstevel@tonic-gate 			/* NOTREACHED */
5700Sstevel@tonic-gate 		}
5710Sstevel@tonic-gate 	}
5720Sstevel@tonic-gate 	buffer_append(&buffer, "\0", 1);
5730Sstevel@tonic-gate 	cp = xstrdup(buffer_ptr(&buffer));
5740Sstevel@tonic-gate 	buffer_free(&buffer);
5750Sstevel@tonic-gate 	return cp;
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate void
channel_send_open(int id)5790Sstevel@tonic-gate channel_send_open(int id)
5800Sstevel@tonic-gate {
5810Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	if (c == NULL) {
5840Sstevel@tonic-gate 		log("channel_send_open: %d: bad id", id);
5850Sstevel@tonic-gate 		return;
5860Sstevel@tonic-gate 	}
5870Sstevel@tonic-gate 	debug("send channel open %d", id);
5880Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_OPEN);
5890Sstevel@tonic-gate 	packet_put_cstring(c->ctype);
5900Sstevel@tonic-gate 	packet_put_int(c->self);
5910Sstevel@tonic-gate 	packet_put_int(c->local_window);
5920Sstevel@tonic-gate 	packet_put_int(c->local_maxpacket);
5930Sstevel@tonic-gate 	packet_send();
5940Sstevel@tonic-gate }
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate void
channel_request_start(int local_id,char * service,int wantconfirm)5970Sstevel@tonic-gate channel_request_start(int local_id, char *service, int wantconfirm)
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate 	Channel *c = channel_lookup(local_id);
6000Sstevel@tonic-gate 
601*12551SZdenek.Kotala@Sun.COM 	debug("channel request %d: %s", local_id, service);
6020Sstevel@tonic-gate 	if (c == NULL) {
6030Sstevel@tonic-gate 		log("channel_request_start: %d: unknown channel id", local_id);
6040Sstevel@tonic-gate 		return;
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_REQUEST);
6070Sstevel@tonic-gate 	packet_put_int(c->remote_id);
6080Sstevel@tonic-gate 	packet_put_cstring(service);
6090Sstevel@tonic-gate 	packet_put_char(wantconfirm);
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate void
channel_register_confirm(int id,channel_callback_fn * fn)6120Sstevel@tonic-gate channel_register_confirm(int id, channel_callback_fn *fn)
6130Sstevel@tonic-gate {
6140Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	if (c == NULL) {
6170Sstevel@tonic-gate 		log("channel_register_comfirm: %d: bad id", id);
6180Sstevel@tonic-gate 		return;
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 	c->confirm = fn;
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate void
channel_register_cleanup(int id,channel_callback_fn * fn)6230Sstevel@tonic-gate channel_register_cleanup(int id, channel_callback_fn *fn)
6240Sstevel@tonic-gate {
6250Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	if (c == NULL) {
6280Sstevel@tonic-gate 		log("channel_register_cleanup: %d: bad id", id);
6290Sstevel@tonic-gate 		return;
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate 	c->detach_user = fn;
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate void
channel_cancel_cleanup(int id)6340Sstevel@tonic-gate channel_cancel_cleanup(int id)
6350Sstevel@tonic-gate {
6360Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	if (c == NULL) {
6390Sstevel@tonic-gate 		log("channel_cancel_cleanup: %d: bad id", id);
6400Sstevel@tonic-gate 		return;
6410Sstevel@tonic-gate 	}
6420Sstevel@tonic-gate 	c->detach_user = NULL;
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate void
channel_register_filter(int id,channel_filter_fn * fn)6450Sstevel@tonic-gate channel_register_filter(int id, channel_filter_fn *fn)
6460Sstevel@tonic-gate {
6470Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	if (c == NULL) {
6500Sstevel@tonic-gate 		log("channel_register_filter: %d: bad id", id);
6510Sstevel@tonic-gate 		return;
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 	c->input_filter = fn;
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate void
channel_set_fds(int id,int rfd,int wfd,int efd,int extusage,int nonblock,u_int window_max)6570Sstevel@tonic-gate channel_set_fds(int id, int rfd, int wfd, int efd,
6580Sstevel@tonic-gate     int extusage, int nonblock, u_int window_max)
6590Sstevel@tonic-gate {
6600Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
6630Sstevel@tonic-gate 		fatal("channel_activate for non-larval channel %d.", id);
6640Sstevel@tonic-gate 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
6650Sstevel@tonic-gate 	c->type = SSH_CHANNEL_OPEN;
6660Sstevel@tonic-gate 	c->local_window = c->local_window_max = window_max;
6670Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
6680Sstevel@tonic-gate 	packet_put_int(c->remote_id);
6690Sstevel@tonic-gate 	packet_put_int(c->local_window);
6700Sstevel@tonic-gate 	packet_send();
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate void
channel_set_wait_for_exit(int id,int wait_for_exit)6740Sstevel@tonic-gate channel_set_wait_for_exit(int id, int wait_for_exit)
6750Sstevel@tonic-gate {
6760Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_OPEN)
6790Sstevel@tonic-gate 		fatal("channel_set_wait_for_exit for non-open channel %d.", id);
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	debug3("channel_set_wait_for_exit %d, %d (type: %d)", id, wait_for_exit, c->type);
6820Sstevel@tonic-gate 	c->wait_for_exit = wait_for_exit;
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate /*
6860Sstevel@tonic-gate  * 'channel_pre*' are called just before select() to add any bits relevant to
6870Sstevel@tonic-gate  * channels in the select bitmasks.
6880Sstevel@tonic-gate  */
6890Sstevel@tonic-gate /*
6900Sstevel@tonic-gate  * 'channel_post*': perform any appropriate operations for channels which
6910Sstevel@tonic-gate  * have events pending.
6920Sstevel@tonic-gate  */
6930Sstevel@tonic-gate typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
6940Sstevel@tonic-gate chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
6950Sstevel@tonic-gate chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate static void
channel_pre_listener(Channel * c,fd_set * readset,fd_set * writeset)6980Sstevel@tonic-gate channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
6990Sstevel@tonic-gate {
7000Sstevel@tonic-gate 	FD_SET(c->sock, readset);
7010Sstevel@tonic-gate }
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate static void
channel_pre_connecting(Channel * c,fd_set * readset,fd_set * writeset)7040Sstevel@tonic-gate channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate 	debug3("channel %d: waiting for connection", c->self);
7070Sstevel@tonic-gate 	FD_SET(c->sock, writeset);
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate static void
channel_pre_open_13(Channel * c,fd_set * readset,fd_set * writeset)7110Sstevel@tonic-gate channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
7120Sstevel@tonic-gate {
7130Sstevel@tonic-gate 	if (buffer_len(&c->input) < packet_get_maxsize())
7140Sstevel@tonic-gate 		FD_SET(c->sock, readset);
7150Sstevel@tonic-gate 	if (buffer_len(&c->output) > 0)
7160Sstevel@tonic-gate 		FD_SET(c->sock, writeset);
7170Sstevel@tonic-gate }
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate static void
channel_pre_open(Channel * c,fd_set * readset,fd_set * writeset)7200Sstevel@tonic-gate channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	if (c->istate == CHAN_INPUT_OPEN &&
7250Sstevel@tonic-gate 	    limit > 0 &&
72610876SZdenek.Kotala@Sun.COM 	    buffer_len(&c->input) < limit &&
72710876SZdenek.Kotala@Sun.COM 	    buffer_check_alloc(&c->input, CHAN_RBUF))
7280Sstevel@tonic-gate 		FD_SET(c->rfd, readset);
7290Sstevel@tonic-gate 	if (c->ostate == CHAN_OUTPUT_OPEN ||
7300Sstevel@tonic-gate 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
7310Sstevel@tonic-gate 		if (buffer_len(&c->output) > 0) {
7320Sstevel@tonic-gate 			FD_SET(c->wfd, writeset);
7330Sstevel@tonic-gate 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
7340Sstevel@tonic-gate 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
7350Sstevel@tonic-gate 			       debug2("channel %d: obuf_empty delayed efd %d/(%d)",
7360Sstevel@tonic-gate 				   c->self, c->efd, buffer_len(&c->extended));
7370Sstevel@tonic-gate 			else
7380Sstevel@tonic-gate 				chan_obuf_empty(c);
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 	/** XXX check close conditions, too */
7420Sstevel@tonic-gate 	if (compat20 && c->efd != -1) {
7430Sstevel@tonic-gate 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
7440Sstevel@tonic-gate 		    buffer_len(&c->extended) > 0)
7450Sstevel@tonic-gate 			FD_SET(c->efd, writeset);
7460Sstevel@tonic-gate 		else if (!(c->flags & CHAN_EOF_SENT) &&
7470Sstevel@tonic-gate 		    c->extended_usage == CHAN_EXTENDED_READ &&
7480Sstevel@tonic-gate 		    buffer_len(&c->extended) < c->remote_window)
7490Sstevel@tonic-gate 			FD_SET(c->efd, readset);
7500Sstevel@tonic-gate 	}
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate static void
channel_pre_input_draining(Channel * c,fd_set * readset,fd_set * writeset)7540Sstevel@tonic-gate channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate 	if (buffer_len(&c->input) == 0) {
7570Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_CLOSE);
7580Sstevel@tonic-gate 		packet_put_int(c->remote_id);
7590Sstevel@tonic-gate 		packet_send();
7600Sstevel@tonic-gate 		c->type = SSH_CHANNEL_CLOSED;
7610Sstevel@tonic-gate 		debug("channel %d: closing after input drain.", c->self);
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate static void
channel_pre_output_draining(Channel * c,fd_set * readset,fd_set * writeset)7660Sstevel@tonic-gate channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
7670Sstevel@tonic-gate {
7680Sstevel@tonic-gate 	if (buffer_len(&c->output) == 0)
7690Sstevel@tonic-gate 		chan_mark_dead(c);
7700Sstevel@tonic-gate 	else
7710Sstevel@tonic-gate 		FD_SET(c->sock, writeset);
7720Sstevel@tonic-gate }
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate /*
7750Sstevel@tonic-gate  * This is a special state for X11 authentication spoofing.  An opened X11
7760Sstevel@tonic-gate  * connection (when authentication spoofing is being done) remains in this
7770Sstevel@tonic-gate  * state until the first packet has been completely read.  The authentication
7780Sstevel@tonic-gate  * data in that packet is then substituted by the real data if it matches the
7790Sstevel@tonic-gate  * fake data, and the channel is put into normal mode.
7800Sstevel@tonic-gate  * XXX All this happens at the client side.
7810Sstevel@tonic-gate  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
7820Sstevel@tonic-gate  */
7830Sstevel@tonic-gate static int
x11_open_helper(Buffer * b)7840Sstevel@tonic-gate x11_open_helper(Buffer *b)
7850Sstevel@tonic-gate {
7860Sstevel@tonic-gate 	u_char *ucp;
7870Sstevel@tonic-gate 	u_int proto_len, data_len;
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	/* Check if the fixed size part of the packet is in buffer. */
7900Sstevel@tonic-gate 	if (buffer_len(b) < 12)
7910Sstevel@tonic-gate 		return 0;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	/* Parse the lengths of variable-length fields. */
7940Sstevel@tonic-gate 	ucp = buffer_ptr(b);
7950Sstevel@tonic-gate 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
7960Sstevel@tonic-gate 		proto_len = 256 * ucp[6] + ucp[7];
7970Sstevel@tonic-gate 		data_len = 256 * ucp[8] + ucp[9];
7980Sstevel@tonic-gate 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
7990Sstevel@tonic-gate 		proto_len = ucp[6] + 256 * ucp[7];
8000Sstevel@tonic-gate 		data_len = ucp[8] + 256 * ucp[9];
8010Sstevel@tonic-gate 	} else {
8020Sstevel@tonic-gate 		debug("Initial X11 packet contains bad byte order byte: 0x%x",
8030Sstevel@tonic-gate 		    ucp[0]);
8040Sstevel@tonic-gate 		return -1;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	/* Check if the whole packet is in buffer. */
8080Sstevel@tonic-gate 	if (buffer_len(b) <
8090Sstevel@tonic-gate 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
8100Sstevel@tonic-gate 		return 0;
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	/* Check if authentication protocol matches. */
8130Sstevel@tonic-gate 	if (proto_len != strlen(x11_saved_proto) ||
8140Sstevel@tonic-gate 	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
8150Sstevel@tonic-gate 		debug("X11 connection uses different authentication protocol.");
8160Sstevel@tonic-gate 		return -1;
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 	/* Check if authentication data matches our fake data. */
8190Sstevel@tonic-gate 	if (data_len != x11_fake_data_len ||
8200Sstevel@tonic-gate 	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
8210Sstevel@tonic-gate 		x11_fake_data, x11_fake_data_len) != 0) {
8220Sstevel@tonic-gate 		debug("X11 auth data does not match fake data.");
8230Sstevel@tonic-gate 		return -1;
8240Sstevel@tonic-gate 	}
8250Sstevel@tonic-gate 	/* Check fake data length */
8260Sstevel@tonic-gate 	if (x11_fake_data_len != x11_saved_data_len) {
8270Sstevel@tonic-gate 		error("X11 fake_data_len %d != saved_data_len %d",
8280Sstevel@tonic-gate 		    x11_fake_data_len, x11_saved_data_len);
8290Sstevel@tonic-gate 		return -1;
8300Sstevel@tonic-gate 	}
8310Sstevel@tonic-gate 	/*
8320Sstevel@tonic-gate 	 * Received authentication protocol and data match
8330Sstevel@tonic-gate 	 * our fake data. Substitute the fake data with real
8340Sstevel@tonic-gate 	 * data.
8350Sstevel@tonic-gate 	 */
8360Sstevel@tonic-gate 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
8370Sstevel@tonic-gate 	    x11_saved_data, x11_saved_data_len);
8380Sstevel@tonic-gate 	return 1;
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate static void
channel_pre_x11_open_13(Channel * c,fd_set * readset,fd_set * writeset)8420Sstevel@tonic-gate channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
8430Sstevel@tonic-gate {
8440Sstevel@tonic-gate 	int ret = x11_open_helper(&c->output);
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	if (ret == 1) {
8470Sstevel@tonic-gate 		/* Start normal processing for the channel. */
8480Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPEN;
8490Sstevel@tonic-gate 		channel_pre_open_13(c, readset, writeset);
8500Sstevel@tonic-gate 	} else if (ret == -1) {
8510Sstevel@tonic-gate 		/*
8520Sstevel@tonic-gate 		 * We have received an X11 connection that has bad
8530Sstevel@tonic-gate 		 * authentication information.
8540Sstevel@tonic-gate 		 */
8550Sstevel@tonic-gate 		log("X11 connection rejected because of wrong authentication.");
8560Sstevel@tonic-gate 		buffer_clear(&c->input);
8570Sstevel@tonic-gate 		buffer_clear(&c->output);
8580Sstevel@tonic-gate 		channel_close_fd(&c->sock);
8590Sstevel@tonic-gate 		c->sock = -1;
8600Sstevel@tonic-gate 		c->type = SSH_CHANNEL_CLOSED;
8610Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_CLOSE);
8620Sstevel@tonic-gate 		packet_put_int(c->remote_id);
8630Sstevel@tonic-gate 		packet_send();
8640Sstevel@tonic-gate 	}
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate static void
channel_pre_x11_open(Channel * c,fd_set * readset,fd_set * writeset)8680Sstevel@tonic-gate channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
8690Sstevel@tonic-gate {
8700Sstevel@tonic-gate 	int ret = x11_open_helper(&c->output);
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	/* c->force_drain = 1; */
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	if (ret == 1) {
8750Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPEN;
8760Sstevel@tonic-gate 		channel_pre_open(c, readset, writeset);
8770Sstevel@tonic-gate 	} else if (ret == -1) {
8780Sstevel@tonic-gate 		log("X11 connection rejected because of wrong authentication.");
8790Sstevel@tonic-gate 		debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
8800Sstevel@tonic-gate 		chan_read_failed(c);
8810Sstevel@tonic-gate 		buffer_clear(&c->input);
8820Sstevel@tonic-gate 		chan_ibuf_empty(c);
8830Sstevel@tonic-gate 		buffer_clear(&c->output);
8840Sstevel@tonic-gate 		/* for proto v1, the peer will send an IEOF */
8850Sstevel@tonic-gate 		if (compat20)
8860Sstevel@tonic-gate 			chan_write_failed(c);
8870Sstevel@tonic-gate 		else
8880Sstevel@tonic-gate 			c->type = SSH_CHANNEL_OPEN;
8890Sstevel@tonic-gate 		debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate }
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate /* try to decode a socks4 header */
8940Sstevel@tonic-gate static int
channel_decode_socks4(Channel * c,fd_set * readset,fd_set * writeset)8950Sstevel@tonic-gate channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate 	char *p, *host;
8980Sstevel@tonic-gate 	int len, have, i, found;
8990Sstevel@tonic-gate 	char username[256];
9000Sstevel@tonic-gate 	struct {
9010Sstevel@tonic-gate 		u_int8_t version;
9020Sstevel@tonic-gate 		u_int8_t command;
9030Sstevel@tonic-gate 		u_int16_t dest_port;
9040Sstevel@tonic-gate 		struct in_addr dest_addr;
9050Sstevel@tonic-gate 	} s4_req, s4_rsp;
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	debug2("channel %d: decode socks4", c->self);
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	have = buffer_len(&c->input);
9100Sstevel@tonic-gate 	len = sizeof(s4_req);
9110Sstevel@tonic-gate 	if (have < len)
9120Sstevel@tonic-gate 		return 0;
9130Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
9140Sstevel@tonic-gate 	for (found = 0, i = len; i < have; i++) {
9150Sstevel@tonic-gate 		if (p[i] == '\0') {
9160Sstevel@tonic-gate 			found = 1;
9170Sstevel@tonic-gate 			break;
9180Sstevel@tonic-gate 		}
9190Sstevel@tonic-gate 		if (i > 1024) {
9200Sstevel@tonic-gate 			/* the peer is probably sending garbage */
9210Sstevel@tonic-gate 			debug("channel %d: decode socks4: too long",
9220Sstevel@tonic-gate 			    c->self);
9230Sstevel@tonic-gate 			return -1;
9240Sstevel@tonic-gate 		}
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 	if (!found)
9270Sstevel@tonic-gate 		return 0;
9280Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.version, 1);
9290Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.command, 1);
9300Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
9310Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
9320Sstevel@tonic-gate 	have = buffer_len(&c->input);
9330Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
9340Sstevel@tonic-gate 	len = strlen(p);
9350Sstevel@tonic-gate 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
9360Sstevel@tonic-gate 	if (len > have)
9370Sstevel@tonic-gate 		fatal("channel %d: decode socks4: len %d > have %d",
9380Sstevel@tonic-gate 		    c->self, len, have);
9390Sstevel@tonic-gate 	strlcpy(username, p, sizeof(username));
9400Sstevel@tonic-gate 	buffer_consume(&c->input, len);
9410Sstevel@tonic-gate 	buffer_consume(&c->input, 1);		/* trailing '\0' */
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	host = inet_ntoa(s4_req.dest_addr);
9440Sstevel@tonic-gate 	strlcpy(c->path, host, sizeof(c->path));
9450Sstevel@tonic-gate 	c->host_port = ntohs(s4_req.dest_port);
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	debug("channel %d: dynamic request: socks4 host %s port %u command %u",
9480Sstevel@tonic-gate 	    c->self, host, c->host_port, s4_req.command);
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	if (s4_req.command != 1) {
9510Sstevel@tonic-gate 		debug("channel %d: cannot handle: socks4 cn %d",
9520Sstevel@tonic-gate 		    c->self, s4_req.command);
9530Sstevel@tonic-gate 		return -1;
9540Sstevel@tonic-gate 	}
9550Sstevel@tonic-gate 	s4_rsp.version = 0;			/* vn: 0 for reply */
9560Sstevel@tonic-gate 	s4_rsp.command = 90;			/* cd: req granted */
9570Sstevel@tonic-gate 	s4_rsp.dest_port = 0;			/* ignored */
9580Sstevel@tonic-gate 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
9590Sstevel@tonic-gate 	buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
9600Sstevel@tonic-gate 	return 1;
9610Sstevel@tonic-gate }
9620Sstevel@tonic-gate 
9634764Sjp161948 /* try to decode a socks5 header */
9644764Sjp161948 #define SSH_SOCKS5_AUTHDONE	0x1000
9654764Sjp161948 #define SSH_SOCKS5_NOAUTH	0x00
9664764Sjp161948 #define SSH_SOCKS5_IPV4		0x01
9674764Sjp161948 #define SSH_SOCKS5_DOMAIN	0x03
9684764Sjp161948 #define SSH_SOCKS5_IPV6		0x04
9694764Sjp161948 #define SSH_SOCKS5_CONNECT	0x01
9704764Sjp161948 #define SSH_SOCKS5_SUCCESS	0x00
9714764Sjp161948 
9724764Sjp161948 /* ARGSUSED */
9734764Sjp161948 static int
channel_decode_socks5(Channel * c,fd_set * readset,fd_set * writeset)9744764Sjp161948 channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
9754764Sjp161948 {
9764764Sjp161948 	struct {
9774764Sjp161948 		u_int8_t version;
9784764Sjp161948 		u_int8_t command;
9794764Sjp161948 		u_int8_t reserved;
9804764Sjp161948 		u_int8_t atyp;
9814764Sjp161948 	} s5_req, s5_rsp;
9824764Sjp161948 	u_int16_t dest_port;
9834764Sjp161948 	u_char *p, dest_addr[255+1];
9844764Sjp161948 	u_int have, need, i, found, nmethods, addrlen;
9854764Sjp161948 	struct in_addr bnd_addr;
9864764Sjp161948 	int af;
9874764Sjp161948 
9884764Sjp161948 	debug2("channel %d: decode socks5", c->self);
9894764Sjp161948 	p = buffer_ptr(&c->input);
9904764Sjp161948 	if (p[0] != 0x05)
9914764Sjp161948 		return -1;
9924764Sjp161948 	have = buffer_len(&c->input);
9934764Sjp161948 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
9944764Sjp161948 		/* format: ver | nmethods | methods */
9954764Sjp161948 		if (have < 2)
9964764Sjp161948 			return 0;
9974764Sjp161948 		nmethods = p[1];
9984764Sjp161948 		if (have < nmethods + 2)
9994764Sjp161948 			return 0;
10004764Sjp161948 		/* look for method: "NO AUTHENTICATION REQUIRED" */
10014764Sjp161948 		for (found = 0, i = 2 ; i < nmethods + 2; i++) {
10024764Sjp161948 			if (p[i] == SSH_SOCKS5_NOAUTH) {
10034764Sjp161948 				found = 1;
10044764Sjp161948 				break;
10054764Sjp161948 			}
10064764Sjp161948 		}
10074764Sjp161948 		if (!found) {
10084764Sjp161948 			error("channel %d: socks5 authentication methods not implemented",
10094764Sjp161948 			    c->self);
10104764Sjp161948 			error("channel %d: forwarding failed: "
10114764Sjp161948 			    "SSH_SOCKS5_NOAUTH method not found", c->self);
10124764Sjp161948 			return -1;
10134764Sjp161948 		}
10144764Sjp161948 		buffer_consume(&c->input, nmethods + 2);
10154764Sjp161948 		buffer_put_char(&c->output, 0x05);		/* version */
10164764Sjp161948 		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
10174764Sjp161948 		FD_SET(c->sock, writeset);
10184764Sjp161948 		c->flags |= SSH_SOCKS5_AUTHDONE;
10194764Sjp161948 		debug2("channel %d: socks5 auth done", c->self);
10204764Sjp161948 		return 0;				/* need more */
10214764Sjp161948 	}
10224764Sjp161948 	debug2("channel %d: socks5 post auth", c->self);
10234764Sjp161948 	if (have < sizeof(s5_req)+1)
10244764Sjp161948 		return 0;			/* need more */
10254764Sjp161948 	memcpy(&s5_req, p, sizeof(s5_req));
10264764Sjp161948 	if (s5_req.version != 0x05 ||
10274764Sjp161948 	    s5_req.command != SSH_SOCKS5_CONNECT ||
10284764Sjp161948 	    s5_req.reserved != 0x00) {
10294764Sjp161948 		error("channel %d: forwarding failed: "
10304764Sjp161948 		    "only socks5 connect is supported", c->self);
10314764Sjp161948 		return -1;
10324764Sjp161948 	}
10334764Sjp161948 	switch (s5_req.atyp){
10344764Sjp161948 	case SSH_SOCKS5_IPV4:
10354764Sjp161948 		addrlen = 4;
10364764Sjp161948 		af = AF_INET;
10374764Sjp161948 		break;
10384764Sjp161948 	case SSH_SOCKS5_DOMAIN:
10394764Sjp161948 		addrlen = p[sizeof(s5_req)];
10404764Sjp161948 		af = -1;
10414764Sjp161948 		break;
10424764Sjp161948 	case SSH_SOCKS5_IPV6:
10434764Sjp161948 		addrlen = 16;
10444764Sjp161948 		af = AF_INET6;
10454764Sjp161948 		break;
10464764Sjp161948 	default:
10474764Sjp161948 		error("channel %d: forwarding failed: "
10484764Sjp161948 		    "bad socks5 atyp %d", c->self, s5_req.atyp);
10494764Sjp161948 		return -1;
10504764Sjp161948 	}
10514764Sjp161948 	need = sizeof(s5_req) + addrlen + 2;
10524764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10534764Sjp161948 		need++;
10544764Sjp161948 	if (have < need)
10554764Sjp161948 		return 0;
10564764Sjp161948 	buffer_consume(&c->input, sizeof(s5_req));
10574764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10584764Sjp161948 		buffer_consume(&c->input, 1);    /* host string length */
10594764Sjp161948 	buffer_get(&c->input, (char *)&dest_addr, addrlen);
10604764Sjp161948 	buffer_get(&c->input, (char *)&dest_port, 2);
10614764Sjp161948 	dest_addr[addrlen] = '\0';
10624764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10634764Sjp161948 		strlcpy(c->path, (char *)dest_addr, sizeof(c->path));
10644764Sjp161948 	else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL)
10654764Sjp161948 		return -1;
10664764Sjp161948 	c->host_port = ntohs(dest_port);
10674764Sjp161948 
10684764Sjp161948 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
10694764Sjp161948 	    c->self, c->path, c->host_port, s5_req.command);
10704764Sjp161948 
10714764Sjp161948 	s5_rsp.version = 0x05;
10724764Sjp161948 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
10734764Sjp161948 	s5_rsp.reserved = 0;			/* ignored */
10744764Sjp161948 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
10754764Sjp161948 	bzero(&bnd_addr, sizeof(bnd_addr));
10764764Sjp161948 	bnd_addr.s_addr = htonl(INADDR_ANY);
10774764Sjp161948 	dest_port = 0;				/* ignored */
10784764Sjp161948 
10794764Sjp161948 	buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
10804764Sjp161948 	buffer_append(&c->output, &bnd_addr, sizeof(struct in_addr));
10814764Sjp161948 	buffer_append(&c->output, &dest_port, sizeof(dest_port));
10824764Sjp161948 	return 1;
10834764Sjp161948 }
10844764Sjp161948 
10850Sstevel@tonic-gate /* dynamic port forwarding */
10860Sstevel@tonic-gate static void
channel_pre_dynamic(Channel * c,fd_set * readset,fd_set * writeset)10870Sstevel@tonic-gate channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate 	u_char *p;
10900Sstevel@tonic-gate 	int have, ret;
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	have = buffer_len(&c->input);
10930Sstevel@tonic-gate 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
10940Sstevel@tonic-gate 	/* buffer_dump(&c->input); */
10950Sstevel@tonic-gate 	/* check if the fixed size part of the packet is in buffer. */
10964764Sjp161948 	if (have < 3) {
10970Sstevel@tonic-gate 		/* need more */
10980Sstevel@tonic-gate 		FD_SET(c->sock, readset);
10990Sstevel@tonic-gate 		return;
11000Sstevel@tonic-gate 	}
11010Sstevel@tonic-gate 	/* try to guess the protocol */
11020Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
11030Sstevel@tonic-gate 	switch (p[0]) {
11040Sstevel@tonic-gate 	case 0x04:
11050Sstevel@tonic-gate 		ret = channel_decode_socks4(c, readset, writeset);
11060Sstevel@tonic-gate 		break;
11074764Sjp161948 	case 0x05:
11084764Sjp161948 		ret = channel_decode_socks5(c, readset, writeset);
11094764Sjp161948 		break;
11100Sstevel@tonic-gate 	default:
11114764Sjp161948 		error("channel %d: forwarding failed: unknown socks "
11124764Sjp161948 		    "version 0x%02X", c->self, p[0]);
11130Sstevel@tonic-gate 		ret = -1;
11140Sstevel@tonic-gate 		break;
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 	if (ret < 0) {
11170Sstevel@tonic-gate 		chan_mark_dead(c);
11180Sstevel@tonic-gate 	} else if (ret == 0) {
11190Sstevel@tonic-gate 		debug2("channel %d: pre_dynamic: need more", c->self);
11200Sstevel@tonic-gate 		/* need more */
11210Sstevel@tonic-gate 		FD_SET(c->sock, readset);
11220Sstevel@tonic-gate 	} else {
11230Sstevel@tonic-gate 		/* switch to the next state */
11240Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPENING;
11250Sstevel@tonic-gate 		port_open_helper(c, "direct-tcpip");
11260Sstevel@tonic-gate 	}
11270Sstevel@tonic-gate }
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate /* This is our fake X11 server socket. */
11300Sstevel@tonic-gate static void
channel_post_x11_listener(Channel * c,fd_set * readset,fd_set * writeset)11310Sstevel@tonic-gate channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
11320Sstevel@tonic-gate {
11330Sstevel@tonic-gate 	Channel *nc;
11340Sstevel@tonic-gate 	struct sockaddr addr;
11350Sstevel@tonic-gate 	int newsock;
11360Sstevel@tonic-gate 	socklen_t addrlen;
11370Sstevel@tonic-gate 	char buf[16384], *remote_ipaddr;
11380Sstevel@tonic-gate 	int remote_port;
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
11410Sstevel@tonic-gate 		debug("X11 connection requested.");
11420Sstevel@tonic-gate 		addrlen = sizeof(addr);
11430Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
11440Sstevel@tonic-gate 		if (c->single_connection) {
11450Sstevel@tonic-gate 			debug("single_connection: closing X11 listener.");
11460Sstevel@tonic-gate 			channel_close_fd(&c->sock);
11470Sstevel@tonic-gate 			chan_mark_dead(c);
11480Sstevel@tonic-gate 		}
11490Sstevel@tonic-gate 		if (newsock < 0) {
11500Sstevel@tonic-gate 			error("accept: %.100s", strerror(errno));
11510Sstevel@tonic-gate 			return;
11520Sstevel@tonic-gate 		}
11530Sstevel@tonic-gate 		set_nodelay(newsock);
11540Sstevel@tonic-gate 		remote_ipaddr = get_peer_ipaddr(newsock);
11550Sstevel@tonic-gate 		remote_port = get_peer_port(newsock);
11560Sstevel@tonic-gate 		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
11570Sstevel@tonic-gate 		    remote_ipaddr, remote_port);
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 		nc = channel_new("accepted x11 socket",
11600Sstevel@tonic-gate 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
11610Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
11620Sstevel@tonic-gate 		    0, xstrdup(buf), 1);
11630Sstevel@tonic-gate 		if (compat20) {
11640Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_OPEN);
11650Sstevel@tonic-gate 			packet_put_cstring("x11");
11660Sstevel@tonic-gate 			packet_put_int(nc->self);
11670Sstevel@tonic-gate 			packet_put_int(nc->local_window_max);
11680Sstevel@tonic-gate 			packet_put_int(nc->local_maxpacket);
11690Sstevel@tonic-gate 			/* originator ipaddr and port */
11700Sstevel@tonic-gate 			packet_put_cstring(remote_ipaddr);
11710Sstevel@tonic-gate 			if (datafellows & SSH_BUG_X11FWD) {
11720Sstevel@tonic-gate 				debug("ssh2 x11 bug compat mode");
11730Sstevel@tonic-gate 			} else {
11740Sstevel@tonic-gate 				packet_put_int(remote_port);
11750Sstevel@tonic-gate 			}
11760Sstevel@tonic-gate 			packet_send();
11770Sstevel@tonic-gate 		} else {
11780Sstevel@tonic-gate 			packet_start(SSH_SMSG_X11_OPEN);
11790Sstevel@tonic-gate 			packet_put_int(nc->self);
11800Sstevel@tonic-gate 			if (packet_get_protocol_flags() &
11810Sstevel@tonic-gate 			    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
11820Sstevel@tonic-gate 				packet_put_cstring(buf);
11830Sstevel@tonic-gate 			packet_send();
11840Sstevel@tonic-gate 		}
11850Sstevel@tonic-gate 		xfree(remote_ipaddr);
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate }
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate static void
port_open_helper(Channel * c,char * rtype)11900Sstevel@tonic-gate port_open_helper(Channel *c, char *rtype)
11910Sstevel@tonic-gate {
11920Sstevel@tonic-gate 	int direct;
11930Sstevel@tonic-gate 	char buf[1024];
11940Sstevel@tonic-gate 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
11950Sstevel@tonic-gate 	u_short remote_port = get_peer_port(c->sock);
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	direct = (strcmp(rtype, "direct-tcpip") == 0);
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	snprintf(buf, sizeof buf,
12000Sstevel@tonic-gate 	    "%s: listening port %d for %.100s port %d, "
12010Sstevel@tonic-gate 	    "connect from %.200s port %d",
12020Sstevel@tonic-gate 	    rtype, c->listening_port, c->path, c->host_port,
12030Sstevel@tonic-gate 	    remote_ipaddr, remote_port);
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	xfree(c->remote_name);
12060Sstevel@tonic-gate 	c->remote_name = xstrdup(buf);
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	if (compat20) {
12090Sstevel@tonic-gate 		packet_start(SSH2_MSG_CHANNEL_OPEN);
12100Sstevel@tonic-gate 		packet_put_cstring(rtype);
12110Sstevel@tonic-gate 		packet_put_int(c->self);
12120Sstevel@tonic-gate 		packet_put_int(c->local_window_max);
12130Sstevel@tonic-gate 		packet_put_int(c->local_maxpacket);
12140Sstevel@tonic-gate 		if (direct) {
12150Sstevel@tonic-gate 			/* target host, port */
12160Sstevel@tonic-gate 			packet_put_cstring(c->path);
12170Sstevel@tonic-gate 			packet_put_int(c->host_port);
12180Sstevel@tonic-gate 		} else {
12190Sstevel@tonic-gate 			/* listen address, port */
12200Sstevel@tonic-gate 			packet_put_cstring(c->path);
12210Sstevel@tonic-gate 			packet_put_int(c->listening_port);
12220Sstevel@tonic-gate 		}
12230Sstevel@tonic-gate 		/* originator host and port */
12240Sstevel@tonic-gate 		packet_put_cstring(remote_ipaddr);
12250Sstevel@tonic-gate 		packet_put_int(remote_port);
12260Sstevel@tonic-gate 		packet_send();
12270Sstevel@tonic-gate 	} else {
12280Sstevel@tonic-gate 		packet_start(SSH_MSG_PORT_OPEN);
12290Sstevel@tonic-gate 		packet_put_int(c->self);
12300Sstevel@tonic-gate 		packet_put_cstring(c->path);
12310Sstevel@tonic-gate 		packet_put_int(c->host_port);
12320Sstevel@tonic-gate 		if (packet_get_protocol_flags() &
12330Sstevel@tonic-gate 		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
12340Sstevel@tonic-gate 			packet_put_cstring(c->remote_name);
12350Sstevel@tonic-gate 		packet_send();
12360Sstevel@tonic-gate 	}
12370Sstevel@tonic-gate 	xfree(remote_ipaddr);
12380Sstevel@tonic-gate }
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate /*
12410Sstevel@tonic-gate  * This socket is listening for connections to a forwarded TCP/IP port.
12420Sstevel@tonic-gate  */
12430Sstevel@tonic-gate static void
channel_post_port_listener(Channel * c,fd_set * readset,fd_set * writeset)12440Sstevel@tonic-gate channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
12450Sstevel@tonic-gate {
12460Sstevel@tonic-gate 	Channel *nc;
12470Sstevel@tonic-gate 	struct sockaddr addr;
12480Sstevel@tonic-gate 	int newsock, nextstate;
12490Sstevel@tonic-gate 	socklen_t addrlen;
12500Sstevel@tonic-gate 	char *rtype;
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
12530Sstevel@tonic-gate 		debug("Connection to port %d forwarding "
12540Sstevel@tonic-gate 		    "to %.100s port %d requested.",
12550Sstevel@tonic-gate 		    c->listening_port, c->path, c->host_port);
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
12580Sstevel@tonic-gate 			nextstate = SSH_CHANNEL_OPENING;
12590Sstevel@tonic-gate 			rtype = "forwarded-tcpip";
12600Sstevel@tonic-gate 		} else {
12610Sstevel@tonic-gate 			if (c->host_port == 0) {
12620Sstevel@tonic-gate 				nextstate = SSH_CHANNEL_DYNAMIC;
12630Sstevel@tonic-gate 				rtype = "dynamic-tcpip";
12640Sstevel@tonic-gate 			} else {
12650Sstevel@tonic-gate 				nextstate = SSH_CHANNEL_OPENING;
12660Sstevel@tonic-gate 				rtype = "direct-tcpip";
12670Sstevel@tonic-gate 			}
12680Sstevel@tonic-gate 		}
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 		addrlen = sizeof(addr);
12710Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
12720Sstevel@tonic-gate 		if (newsock < 0) {
12730Sstevel@tonic-gate 			error("accept: %.100s", strerror(errno));
12740Sstevel@tonic-gate 			return;
12750Sstevel@tonic-gate 		}
12760Sstevel@tonic-gate 		set_nodelay(newsock);
12770Sstevel@tonic-gate 		nc = channel_new(rtype,
12780Sstevel@tonic-gate 		    nextstate, newsock, newsock, -1,
12790Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
12800Sstevel@tonic-gate 		    0, xstrdup(rtype), 1);
12810Sstevel@tonic-gate 		nc->listening_port = c->listening_port;
12820Sstevel@tonic-gate 		nc->host_port = c->host_port;
12830Sstevel@tonic-gate 		strlcpy(nc->path, c->path, sizeof(nc->path));
12840Sstevel@tonic-gate 
1285*12551SZdenek.Kotala@Sun.COM 		if (nextstate != SSH_CHANNEL_DYNAMIC)
12860Sstevel@tonic-gate 			port_open_helper(nc, rtype);
12870Sstevel@tonic-gate 	}
12880Sstevel@tonic-gate }
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate /*
12910Sstevel@tonic-gate  * This is the authentication agent socket listening for connections from
12920Sstevel@tonic-gate  * clients.
12930Sstevel@tonic-gate  */
12940Sstevel@tonic-gate static void
channel_post_auth_listener(Channel * c,fd_set * readset,fd_set * writeset)12950Sstevel@tonic-gate channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
12960Sstevel@tonic-gate {
12970Sstevel@tonic-gate 	Channel *nc;
12980Sstevel@tonic-gate 	char *name;
12990Sstevel@tonic-gate 	int newsock;
13000Sstevel@tonic-gate 	struct sockaddr addr;
13010Sstevel@tonic-gate 	socklen_t addrlen;
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
13040Sstevel@tonic-gate 		addrlen = sizeof(addr);
13050Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
13060Sstevel@tonic-gate 		if (newsock < 0) {
13070Sstevel@tonic-gate 			error("accept from auth socket: %.100s", strerror(errno));
13080Sstevel@tonic-gate 			return;
13090Sstevel@tonic-gate 		}
13100Sstevel@tonic-gate 		name = xstrdup("accepted auth socket");
13110Sstevel@tonic-gate 		nc = channel_new("accepted auth socket",
13120Sstevel@tonic-gate 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
13130Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
13140Sstevel@tonic-gate 		    0, name, 1);
13150Sstevel@tonic-gate 		if (compat20) {
13160Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_OPEN);
13170Sstevel@tonic-gate 			packet_put_cstring("auth-agent@openssh.com");
13180Sstevel@tonic-gate 			packet_put_int(nc->self);
13190Sstevel@tonic-gate 			packet_put_int(c->local_window_max);
13200Sstevel@tonic-gate 			packet_put_int(c->local_maxpacket);
13210Sstevel@tonic-gate 		} else {
13220Sstevel@tonic-gate 			packet_start(SSH_SMSG_AGENT_OPEN);
13230Sstevel@tonic-gate 			packet_put_int(nc->self);
13240Sstevel@tonic-gate 		}
13250Sstevel@tonic-gate 		packet_send();
13260Sstevel@tonic-gate 	}
13270Sstevel@tonic-gate }
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate static void
channel_post_connecting(Channel * c,fd_set * readset,fd_set * writeset)13300Sstevel@tonic-gate channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
13310Sstevel@tonic-gate {
13320Sstevel@tonic-gate 	int err = 0;
13330Sstevel@tonic-gate 	socklen_t sz = sizeof(err);
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 	if (FD_ISSET(c->sock, writeset)) {
13360Sstevel@tonic-gate 		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
13370Sstevel@tonic-gate 			err = errno;
13380Sstevel@tonic-gate 			error("getsockopt SO_ERROR failed");
13390Sstevel@tonic-gate 		}
13400Sstevel@tonic-gate 		if (err == 0) {
13410Sstevel@tonic-gate 			debug("channel %d: connected", c->self);
13420Sstevel@tonic-gate 			c->type = SSH_CHANNEL_OPEN;
13430Sstevel@tonic-gate 			if (compat20) {
13440Sstevel@tonic-gate 				packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
13450Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13460Sstevel@tonic-gate 				packet_put_int(c->self);
13470Sstevel@tonic-gate 				packet_put_int(c->local_window);
13480Sstevel@tonic-gate 				packet_put_int(c->local_maxpacket);
13490Sstevel@tonic-gate 			} else {
13500Sstevel@tonic-gate 				packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
13510Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13520Sstevel@tonic-gate 				packet_put_int(c->self);
13530Sstevel@tonic-gate 			}
13540Sstevel@tonic-gate 		} else {
13550Sstevel@tonic-gate 			debug("channel %d: not connected: %s",
13560Sstevel@tonic-gate 			    c->self, strerror(err));
13570Sstevel@tonic-gate 			if (compat20) {
13580Sstevel@tonic-gate 				packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
13590Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13600Sstevel@tonic-gate 				packet_put_int(SSH2_OPEN_CONNECT_FAILED);
13610Sstevel@tonic-gate 				if (!(datafellows & SSH_BUG_OPENFAILURE)) {
13629600SNobutomo.Nakano@Sun.COM 					packet_put_utf8_cstring(strerror(err));
13630Sstevel@tonic-gate 					packet_put_cstring("");
13640Sstevel@tonic-gate 				}
13650Sstevel@tonic-gate 			} else {
13660Sstevel@tonic-gate 				packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
13670Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13680Sstevel@tonic-gate 			}
13690Sstevel@tonic-gate 			chan_mark_dead(c);
13700Sstevel@tonic-gate 		}
13710Sstevel@tonic-gate 		packet_send();
13720Sstevel@tonic-gate 	}
13730Sstevel@tonic-gate }
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate static int
channel_handle_rfd(Channel * c,fd_set * readset,fd_set * writeset)13760Sstevel@tonic-gate channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
13770Sstevel@tonic-gate {
137810876SZdenek.Kotala@Sun.COM 	char buf[CHAN_RBUF];
13790Sstevel@tonic-gate 	int len;
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 	if (c->rfd != -1 &&
13820Sstevel@tonic-gate 	    FD_ISSET(c->rfd, readset)) {
13830Sstevel@tonic-gate 		len = read(c->rfd, buf, sizeof(buf));
13840Sstevel@tonic-gate 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
13850Sstevel@tonic-gate 			return 1;
13860Sstevel@tonic-gate 		if (len <= 0) {
13870Sstevel@tonic-gate 			debug("channel %d: read<=0 rfd %d len %d",
13880Sstevel@tonic-gate 			    c->self, c->rfd, len);
13890Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN) {
13900Sstevel@tonic-gate 				debug("channel %d: not open", c->self);
13910Sstevel@tonic-gate 				chan_mark_dead(c);
13920Sstevel@tonic-gate 				return -1;
13930Sstevel@tonic-gate 			} else if (compat13) {
13940Sstevel@tonic-gate 				buffer_clear(&c->output);
13950Sstevel@tonic-gate 				c->type = SSH_CHANNEL_INPUT_DRAINING;
13960Sstevel@tonic-gate 				debug("channel %d: input draining.", c->self);
13970Sstevel@tonic-gate 			} else {
13980Sstevel@tonic-gate 				chan_read_failed(c);
13990Sstevel@tonic-gate 			}
14000Sstevel@tonic-gate 			return -1;
14010Sstevel@tonic-gate 		}
14020Sstevel@tonic-gate 		if (c->input_filter != NULL) {
14030Sstevel@tonic-gate 			if (c->input_filter(c, buf, len) == -1) {
14040Sstevel@tonic-gate 				debug("channel %d: filter stops", c->self);
14050Sstevel@tonic-gate 				chan_read_failed(c);
14060Sstevel@tonic-gate 			}
14070Sstevel@tonic-gate 		} else {
14080Sstevel@tonic-gate 			buffer_append(&c->input, buf, len);
14090Sstevel@tonic-gate 		}
14100Sstevel@tonic-gate 	}
14110Sstevel@tonic-gate 	return 1;
14120Sstevel@tonic-gate }
14130Sstevel@tonic-gate static int
channel_handle_wfd(Channel * c,fd_set * readset,fd_set * writeset)14140Sstevel@tonic-gate channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
14150Sstevel@tonic-gate {
14160Sstevel@tonic-gate 	struct termios tio;
14170Sstevel@tonic-gate 	u_char *data;
14180Sstevel@tonic-gate 	u_int dlen;
14190Sstevel@tonic-gate 	int len;
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate 	/* Send buffered output data to the socket. */
14220Sstevel@tonic-gate 	if (c->wfd != -1 &&
14230Sstevel@tonic-gate 	    FD_ISSET(c->wfd, writeset) &&
14240Sstevel@tonic-gate 	    buffer_len(&c->output) > 0) {
14250Sstevel@tonic-gate 		data = buffer_ptr(&c->output);
14260Sstevel@tonic-gate 		dlen = buffer_len(&c->output);
14270Sstevel@tonic-gate #ifdef _AIX
14280Sstevel@tonic-gate 		/* XXX: Later AIX versions can't push as much data to tty */
14290Sstevel@tonic-gate 		if (compat20 && c->wfd_isatty && dlen > 8*1024)
14300Sstevel@tonic-gate 			dlen = 8*1024;
14310Sstevel@tonic-gate #endif
14320Sstevel@tonic-gate 		len = write(c->wfd, data, dlen);
14330Sstevel@tonic-gate 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
14340Sstevel@tonic-gate 			return 1;
14350Sstevel@tonic-gate 		if (len <= 0) {
14360Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN) {
14370Sstevel@tonic-gate 				debug("channel %d: not open", c->self);
14380Sstevel@tonic-gate 				chan_mark_dead(c);
14390Sstevel@tonic-gate 				return -1;
14400Sstevel@tonic-gate 			} else if (compat13) {
14410Sstevel@tonic-gate 				buffer_clear(&c->output);
14420Sstevel@tonic-gate 				debug("channel %d: input draining.", c->self);
14430Sstevel@tonic-gate 				c->type = SSH_CHANNEL_INPUT_DRAINING;
14440Sstevel@tonic-gate 			} else {
14450Sstevel@tonic-gate 				chan_write_failed(c);
14460Sstevel@tonic-gate 			}
14470Sstevel@tonic-gate 			return -1;
14480Sstevel@tonic-gate 		}
14490Sstevel@tonic-gate 		if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') {
14500Sstevel@tonic-gate 			if (tcgetattr(c->wfd, &tio) == 0 &&
14510Sstevel@tonic-gate 			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
14520Sstevel@tonic-gate 				/*
14530Sstevel@tonic-gate 				 * Simulate echo to reduce the impact of
14540Sstevel@tonic-gate 				 * traffic analysis. We need to match the
14550Sstevel@tonic-gate 				 * size of a SSH2_MSG_CHANNEL_DATA message
14560Sstevel@tonic-gate 				 * (4 byte channel id + data)
14570Sstevel@tonic-gate 				 */
14580Sstevel@tonic-gate 				packet_send_ignore(4 + len);
14590Sstevel@tonic-gate 				packet_send();
14600Sstevel@tonic-gate 			}
14610Sstevel@tonic-gate 		}
14620Sstevel@tonic-gate 		buffer_consume(&c->output, len);
14630Sstevel@tonic-gate 		if (compat20 && len > 0) {
14640Sstevel@tonic-gate 			c->local_consumed += len;
14650Sstevel@tonic-gate 		}
14660Sstevel@tonic-gate 	}
14670Sstevel@tonic-gate 	return 1;
14680Sstevel@tonic-gate }
14690Sstevel@tonic-gate static int
channel_handle_efd(Channel * c,fd_set * readset,fd_set * writeset)14700Sstevel@tonic-gate channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
14710Sstevel@tonic-gate {
147210876SZdenek.Kotala@Sun.COM 	char buf[CHAN_RBUF];
14730Sstevel@tonic-gate 	int len;
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate /** XXX handle drain efd, too */
14760Sstevel@tonic-gate 	if (c->efd != -1) {
14770Sstevel@tonic-gate 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
14780Sstevel@tonic-gate 		    FD_ISSET(c->efd, writeset) &&
14790Sstevel@tonic-gate 		    buffer_len(&c->extended) > 0) {
14800Sstevel@tonic-gate 			len = write(c->efd, buffer_ptr(&c->extended),
14810Sstevel@tonic-gate 			    buffer_len(&c->extended));
14820Sstevel@tonic-gate 			debug2("channel %d: written %d to efd %d",
14830Sstevel@tonic-gate 			    c->self, len, c->efd);
14840Sstevel@tonic-gate 			if (len < 0 && (errno == EINTR || errno == EAGAIN))
14850Sstevel@tonic-gate 				return 1;
14860Sstevel@tonic-gate 			if (len <= 0) {
14870Sstevel@tonic-gate 				debug2("channel %d: closing write-efd %d",
14880Sstevel@tonic-gate 				    c->self, c->efd);
14890Sstevel@tonic-gate 				channel_close_fd(&c->efd);
14900Sstevel@tonic-gate 			} else {
14910Sstevel@tonic-gate 				buffer_consume(&c->extended, len);
14920Sstevel@tonic-gate 				c->local_consumed += len;
14930Sstevel@tonic-gate 			}
14940Sstevel@tonic-gate 		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
14950Sstevel@tonic-gate 		    FD_ISSET(c->efd, readset)) {
14960Sstevel@tonic-gate 			len = read(c->efd, buf, sizeof(buf));
14970Sstevel@tonic-gate 			debug2("channel %d: read %d from efd %d",
14980Sstevel@tonic-gate 			    c->self, len, c->efd);
14990Sstevel@tonic-gate 			if (len < 0 && (errno == EINTR || errno == EAGAIN))
15000Sstevel@tonic-gate 				return 1;
15010Sstevel@tonic-gate 			if (len <= 0) {
15020Sstevel@tonic-gate 				debug2("channel %d: closing read-efd %d",
15030Sstevel@tonic-gate 				    c->self, c->efd);
15040Sstevel@tonic-gate 				channel_close_fd(&c->efd);
15050Sstevel@tonic-gate 			} else {
15060Sstevel@tonic-gate 				buffer_append(&c->extended, buf, len);
15070Sstevel@tonic-gate 			}
15080Sstevel@tonic-gate 		}
15090Sstevel@tonic-gate 	}
15100Sstevel@tonic-gate 	return 1;
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate static int
channel_check_window(Channel * c)15130Sstevel@tonic-gate channel_check_window(Channel *c)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate 	if (c->type == SSH_CHANNEL_OPEN &&
15160Sstevel@tonic-gate 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
15170Sstevel@tonic-gate 	    c->local_window < c->local_window_max/2 &&
15180Sstevel@tonic-gate 	    c->local_consumed > 0) {
15190Sstevel@tonic-gate 		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
15200Sstevel@tonic-gate 		packet_put_int(c->remote_id);
15210Sstevel@tonic-gate 		packet_put_int(c->local_consumed);
15220Sstevel@tonic-gate 		packet_send();
15230Sstevel@tonic-gate 		debug2("channel %d: window %d sent adjust %d",
15240Sstevel@tonic-gate 		    c->self, c->local_window,
15250Sstevel@tonic-gate 		    c->local_consumed);
15260Sstevel@tonic-gate 		c->local_window += c->local_consumed;
15270Sstevel@tonic-gate 		c->local_consumed = 0;
15280Sstevel@tonic-gate 	}
15290Sstevel@tonic-gate 	return 1;
15300Sstevel@tonic-gate }
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate static void
channel_post_open(Channel * c,fd_set * readset,fd_set * writeset)15330Sstevel@tonic-gate channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
15340Sstevel@tonic-gate {
15350Sstevel@tonic-gate 	channel_handle_rfd(c, readset, writeset);
15360Sstevel@tonic-gate 	channel_handle_wfd(c, readset, writeset);
15370Sstevel@tonic-gate 	if (!compat20)
15380Sstevel@tonic-gate 		return;
15390Sstevel@tonic-gate 	channel_handle_efd(c, readset, writeset);
15400Sstevel@tonic-gate 	channel_check_window(c);
15410Sstevel@tonic-gate }
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate static void
channel_post_output_drain_13(Channel * c,fd_set * readset,fd_set * writeset)15440Sstevel@tonic-gate channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
15450Sstevel@tonic-gate {
15460Sstevel@tonic-gate 	int len;
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate 	/* Send buffered output data to the socket. */
15490Sstevel@tonic-gate 	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
15500Sstevel@tonic-gate 		len = write(c->sock, buffer_ptr(&c->output),
15510Sstevel@tonic-gate 			    buffer_len(&c->output));
15520Sstevel@tonic-gate 		if (len <= 0)
15530Sstevel@tonic-gate 			buffer_clear(&c->output);
15540Sstevel@tonic-gate 		else
15550Sstevel@tonic-gate 			buffer_consume(&c->output, len);
15560Sstevel@tonic-gate 	}
15570Sstevel@tonic-gate }
15580Sstevel@tonic-gate 
15590Sstevel@tonic-gate static void
channel_handler_init_20(void)15600Sstevel@tonic-gate channel_handler_init_20(void)
15610Sstevel@tonic-gate {
15620Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
15630Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
15640Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
15650Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
15660Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
15670Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
15680Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
15690Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
15720Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
15730Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
15740Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
15750Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
15760Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
15770Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
15780Sstevel@tonic-gate }
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate static void
channel_handler_init_13(void)15810Sstevel@tonic-gate channel_handler_init_13(void)
15820Sstevel@tonic-gate {
15830Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
15840Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
15850Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
15860Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
15870Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
15880Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
15890Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
15900Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
15910Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
15940Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
15950Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
15960Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
15970Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
15980Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
15990Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
16000Sstevel@tonic-gate }
16010Sstevel@tonic-gate 
16020Sstevel@tonic-gate static void
channel_handler_init_15(void)16030Sstevel@tonic-gate channel_handler_init_15(void)
16040Sstevel@tonic-gate {
16050Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
16060Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
16070Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
16080Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
16090Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
16100Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
16110Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
16140Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
16150Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
16160Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
16170Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
16180Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate static void
channel_handler_init(void)16220Sstevel@tonic-gate channel_handler_init(void)
16230Sstevel@tonic-gate {
16240Sstevel@tonic-gate 	int i;
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
16270Sstevel@tonic-gate 		channel_pre[i] = NULL;
16280Sstevel@tonic-gate 		channel_post[i] = NULL;
16290Sstevel@tonic-gate 	}
16300Sstevel@tonic-gate 	if (compat20)
16310Sstevel@tonic-gate 		channel_handler_init_20();
16320Sstevel@tonic-gate 	else if (compat13)
16330Sstevel@tonic-gate 		channel_handler_init_13();
16340Sstevel@tonic-gate 	else
16350Sstevel@tonic-gate 		channel_handler_init_15();
16360Sstevel@tonic-gate }
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate /* gc dead channels */
16390Sstevel@tonic-gate static void
channel_garbage_collect(Channel * c)16400Sstevel@tonic-gate channel_garbage_collect(Channel *c)
16410Sstevel@tonic-gate {
16420Sstevel@tonic-gate 	if (c == NULL)
16430Sstevel@tonic-gate 		return;
16440Sstevel@tonic-gate 	if (c->detach_user != NULL) {
16450Sstevel@tonic-gate 		if (!chan_is_dead(c, 0))
16460Sstevel@tonic-gate 			return;
16470Sstevel@tonic-gate 		debug("channel %d: gc: notify user", c->self);
16480Sstevel@tonic-gate 		c->detach_user(c->self, NULL);
16490Sstevel@tonic-gate 		/* if we still have a callback */
16500Sstevel@tonic-gate 		if (c->detach_user != NULL)
16510Sstevel@tonic-gate 			return;
16520Sstevel@tonic-gate 		debug("channel %d: gc: user detached", c->self);
16530Sstevel@tonic-gate 	}
16540Sstevel@tonic-gate 	if (!c->wait_for_exit && !chan_is_dead(c, 1))
16550Sstevel@tonic-gate 		return;
16560Sstevel@tonic-gate 	debug("channel %d: garbage collecting", c->self);
16570Sstevel@tonic-gate 	channel_free(c);
16580Sstevel@tonic-gate }
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate static void
channel_handler(chan_fn * ftab[],fd_set * readset,fd_set * writeset)16610Sstevel@tonic-gate channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
16620Sstevel@tonic-gate {
16630Sstevel@tonic-gate 	static int did_init = 0;
1664*12551SZdenek.Kotala@Sun.COM 	int i, oalloc;
16650Sstevel@tonic-gate 	Channel *c;
16660Sstevel@tonic-gate 
16670Sstevel@tonic-gate 	if (!did_init) {
16680Sstevel@tonic-gate 		channel_handler_init();
16690Sstevel@tonic-gate 		did_init = 1;
16700Sstevel@tonic-gate 	}
1671*12551SZdenek.Kotala@Sun.COM 	for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
16720Sstevel@tonic-gate 		c = channels[i];
16730Sstevel@tonic-gate 		if (c == NULL)
16740Sstevel@tonic-gate 			continue;
1675*12551SZdenek.Kotala@Sun.COM 		if (c->delayed) {
1676*12551SZdenek.Kotala@Sun.COM 			if (ftab == channel_pre)
1677*12551SZdenek.Kotala@Sun.COM 				c->delayed = 0;
1678*12551SZdenek.Kotala@Sun.COM 			else
1679*12551SZdenek.Kotala@Sun.COM 				continue;
1680*12551SZdenek.Kotala@Sun.COM 		}
16810Sstevel@tonic-gate 		if (ftab[c->type] != NULL)
16820Sstevel@tonic-gate 			(*ftab[c->type])(c, readset, writeset);
16830Sstevel@tonic-gate 		channel_garbage_collect(c);
16840Sstevel@tonic-gate 	}
16850Sstevel@tonic-gate }
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate /*
16880Sstevel@tonic-gate  * Allocate/update select bitmasks and add any bits relevant to channels in
16890Sstevel@tonic-gate  * select bitmasks.
16900Sstevel@tonic-gate  */
16910Sstevel@tonic-gate void
channel_prepare_select(fd_set ** readsetp,fd_set ** writesetp,int * maxfdp,int * nallocp,int rekeying)16920Sstevel@tonic-gate channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
16930Sstevel@tonic-gate     int *nallocp, int rekeying)
16940Sstevel@tonic-gate {
16950Sstevel@tonic-gate 	int n;
16960Sstevel@tonic-gate 	u_int sz;
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 	n = MAX(*maxfdp, channel_max_fd);
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
17010Sstevel@tonic-gate 	/* perhaps check sz < nalloc/2 and shrink? */
17020Sstevel@tonic-gate 	if (*readsetp == NULL || sz > *nallocp) {
17030Sstevel@tonic-gate 		*readsetp = xrealloc(*readsetp, sz);
17040Sstevel@tonic-gate 		*writesetp = xrealloc(*writesetp, sz);
17050Sstevel@tonic-gate 		*nallocp = sz;
17060Sstevel@tonic-gate 	}
17070Sstevel@tonic-gate 	*maxfdp = n;
17080Sstevel@tonic-gate 	memset(*readsetp, 0, sz);
17090Sstevel@tonic-gate 	memset(*writesetp, 0, sz);
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	if (!rekeying)
17120Sstevel@tonic-gate 		channel_handler(channel_pre, *readsetp, *writesetp);
17130Sstevel@tonic-gate }
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate /*
17160Sstevel@tonic-gate  * After select, perform any appropriate operations for channels which have
17170Sstevel@tonic-gate  * events pending.
17180Sstevel@tonic-gate  */
17190Sstevel@tonic-gate void
channel_after_select(fd_set * readset,fd_set * writeset)17200Sstevel@tonic-gate channel_after_select(fd_set * readset, fd_set * writeset)
17210Sstevel@tonic-gate {
17220Sstevel@tonic-gate 	channel_handler(channel_post, readset, writeset);
17230Sstevel@tonic-gate }
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate /* If there is data to send to the connection, enqueue some of it now. */
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate void
channel_output_poll(void)17290Sstevel@tonic-gate channel_output_poll(void)
17300Sstevel@tonic-gate {
17310Sstevel@tonic-gate 	Channel *c;
17320Sstevel@tonic-gate 	int i;
17330Sstevel@tonic-gate 	u_int len;
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
17360Sstevel@tonic-gate 		c = channels[i];
17370Sstevel@tonic-gate 		if (c == NULL)
17380Sstevel@tonic-gate 			continue;
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 		/*
17410Sstevel@tonic-gate 		 * We are only interested in channels that can have buffered
17420Sstevel@tonic-gate 		 * incoming data.
17430Sstevel@tonic-gate 		 */
17440Sstevel@tonic-gate 		if (compat13) {
17450Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN &&
17460Sstevel@tonic-gate 			    c->type != SSH_CHANNEL_INPUT_DRAINING)
17470Sstevel@tonic-gate 				continue;
17480Sstevel@tonic-gate 		} else {
17490Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN)
17500Sstevel@tonic-gate 				continue;
17510Sstevel@tonic-gate 		}
17520Sstevel@tonic-gate 		if (compat20 &&
17530Sstevel@tonic-gate 		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
17540Sstevel@tonic-gate 			/* XXX is this true? */
17550Sstevel@tonic-gate 			debug3("channel %d: will not send data after close", c->self);
17560Sstevel@tonic-gate 			continue;
17570Sstevel@tonic-gate 		}
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 		/* Get the amount of buffered data for this channel. */
17600Sstevel@tonic-gate 		if ((c->istate == CHAN_INPUT_OPEN ||
17610Sstevel@tonic-gate 		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
17620Sstevel@tonic-gate 		    (len = buffer_len(&c->input)) > 0) {
17630Sstevel@tonic-gate 			/*
17640Sstevel@tonic-gate 			 * Send some data for the other side over the secure
17650Sstevel@tonic-gate 			 * connection.
17660Sstevel@tonic-gate 			 */
17670Sstevel@tonic-gate 			if (compat20) {
17680Sstevel@tonic-gate 				if (len > c->remote_window)
17690Sstevel@tonic-gate 					len = c->remote_window;
17700Sstevel@tonic-gate 				if (len > c->remote_maxpacket)
17710Sstevel@tonic-gate 					len = c->remote_maxpacket;
17720Sstevel@tonic-gate 			} else {
17730Sstevel@tonic-gate 				if (packet_is_interactive()) {
17740Sstevel@tonic-gate 					if (len > 1024)
17750Sstevel@tonic-gate 						len = 512;
17760Sstevel@tonic-gate 				} else {
17770Sstevel@tonic-gate 					/* Keep the packets at reasonable size. */
17780Sstevel@tonic-gate 					if (len > packet_get_maxsize()/2)
17790Sstevel@tonic-gate 						len = packet_get_maxsize()/2;
17800Sstevel@tonic-gate 				}
17810Sstevel@tonic-gate 			}
17820Sstevel@tonic-gate 			if (len > 0) {
17830Sstevel@tonic-gate 				packet_start(compat20 ?
17840Sstevel@tonic-gate 				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
17850Sstevel@tonic-gate 				packet_put_int(c->remote_id);
17860Sstevel@tonic-gate 				packet_put_string(buffer_ptr(&c->input), len);
17870Sstevel@tonic-gate 				packet_send();
17880Sstevel@tonic-gate 				buffer_consume(&c->input, len);
17890Sstevel@tonic-gate 				c->remote_window -= len;
17900Sstevel@tonic-gate 			}
17910Sstevel@tonic-gate 		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
17920Sstevel@tonic-gate 			if (compat13)
17930Sstevel@tonic-gate 				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
17940Sstevel@tonic-gate 			/*
17950Sstevel@tonic-gate 			 * input-buffer is empty and read-socket shutdown:
17960Sstevel@tonic-gate 			 * tell peer, that we will not send more data: send IEOF.
17970Sstevel@tonic-gate 			 * hack for extended data: delay EOF if EFD still in use.
17980Sstevel@tonic-gate 			 */
17990Sstevel@tonic-gate 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
18000Sstevel@tonic-gate 			       debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
18010Sstevel@tonic-gate 				   c->self, c->efd, buffer_len(&c->extended));
18020Sstevel@tonic-gate 			else
18030Sstevel@tonic-gate 				chan_ibuf_empty(c);
18040Sstevel@tonic-gate 		}
18050Sstevel@tonic-gate 		/* Send extended data, i.e. stderr */
18060Sstevel@tonic-gate 		if (compat20 &&
18070Sstevel@tonic-gate 		    !(c->flags & CHAN_EOF_SENT) &&
18080Sstevel@tonic-gate 		    c->remote_window > 0 &&
18090Sstevel@tonic-gate 		    (len = buffer_len(&c->extended)) > 0 &&
18100Sstevel@tonic-gate 		    c->extended_usage == CHAN_EXTENDED_READ) {
18110Sstevel@tonic-gate 			debug2("channel %d: rwin %u elen %u euse %d",
18120Sstevel@tonic-gate 			    c->self, c->remote_window, buffer_len(&c->extended),
18130Sstevel@tonic-gate 			    c->extended_usage);
18140Sstevel@tonic-gate 			if (len > c->remote_window)
18150Sstevel@tonic-gate 				len = c->remote_window;
18160Sstevel@tonic-gate 			if (len > c->remote_maxpacket)
18170Sstevel@tonic-gate 				len = c->remote_maxpacket;
18180Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
18190Sstevel@tonic-gate 			packet_put_int(c->remote_id);
18200Sstevel@tonic-gate 			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
18210Sstevel@tonic-gate 			packet_put_string(buffer_ptr(&c->extended), len);
18220Sstevel@tonic-gate 			packet_send();
18230Sstevel@tonic-gate 			buffer_consume(&c->extended, len);
18240Sstevel@tonic-gate 			c->remote_window -= len;
18250Sstevel@tonic-gate 			debug2("channel %d: sent ext data %d", c->self, len);
18260Sstevel@tonic-gate 		}
18270Sstevel@tonic-gate 	}
18280Sstevel@tonic-gate }
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate /* -- protocol input */
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate void
channel_input_data(int type,u_int32_t seq,void * ctxt)18340Sstevel@tonic-gate channel_input_data(int type, u_int32_t seq, void *ctxt)
18350Sstevel@tonic-gate {
18360Sstevel@tonic-gate 	int id;
18370Sstevel@tonic-gate 	char *data;
18380Sstevel@tonic-gate 	u_int data_len;
18390Sstevel@tonic-gate 	Channel *c;
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 	/* Get the channel number and verify it. */
18420Sstevel@tonic-gate 	id = packet_get_int();
18430Sstevel@tonic-gate 	c = channel_lookup(id);
18440Sstevel@tonic-gate 	if (c == NULL)
18450Sstevel@tonic-gate 		packet_disconnect("Received data for nonexistent channel %d.", id);
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	/* Ignore any data for non-open channels (might happen on close) */
18480Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_OPEN &&
18490Sstevel@tonic-gate 	    c->type != SSH_CHANNEL_X11_OPEN)
18500Sstevel@tonic-gate 		return;
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 	/* same for protocol 1.5 if output end is no longer open */
18530Sstevel@tonic-gate 	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
18540Sstevel@tonic-gate 		return;
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	/* Get the data. */
18570Sstevel@tonic-gate 	data = packet_get_string(&data_len);
18580Sstevel@tonic-gate 
18590Sstevel@tonic-gate 	if (compat20) {
18600Sstevel@tonic-gate 		if (data_len > c->local_maxpacket) {
18610Sstevel@tonic-gate 			log("channel %d: rcvd big packet %d, maxpack %d",
18620Sstevel@tonic-gate 			    c->self, data_len, c->local_maxpacket);
18630Sstevel@tonic-gate 		}
18640Sstevel@tonic-gate 		if (data_len > c->local_window) {
18650Sstevel@tonic-gate 			log("channel %d: rcvd too much data %d, win %d",
18660Sstevel@tonic-gate 			    c->self, data_len, c->local_window);
18670Sstevel@tonic-gate 			xfree(data);
18680Sstevel@tonic-gate 			return;
18690Sstevel@tonic-gate 		}
18700Sstevel@tonic-gate 		c->local_window -= data_len;
18710Sstevel@tonic-gate 	}
18720Sstevel@tonic-gate 	packet_check_eom();
18730Sstevel@tonic-gate 	buffer_append(&c->output, data, data_len);
18740Sstevel@tonic-gate 	xfree(data);
18750Sstevel@tonic-gate }
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate void
channel_input_extended_data(int type,u_int32_t seq,void * ctxt)18780Sstevel@tonic-gate channel_input_extended_data(int type, u_int32_t seq, void *ctxt)
18790Sstevel@tonic-gate {
18800Sstevel@tonic-gate 	int id;
18810Sstevel@tonic-gate 	char *data;
18820Sstevel@tonic-gate 	u_int data_len, tcode;
18830Sstevel@tonic-gate 	Channel *c;
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 	/* Get the channel number and verify it. */
18860Sstevel@tonic-gate 	id = packet_get_int();
18870Sstevel@tonic-gate 	c = channel_lookup(id);
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate 	if (c == NULL)
18900Sstevel@tonic-gate 		packet_disconnect("Received extended_data for bad channel %d.", id);
18910Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_OPEN) {
18920Sstevel@tonic-gate 		log("channel %d: ext data for non open", id);
18930Sstevel@tonic-gate 		return;
18940Sstevel@tonic-gate 	}
18950Sstevel@tonic-gate 	if (c->flags & CHAN_EOF_RCVD) {
18960Sstevel@tonic-gate 		if (datafellows & SSH_BUG_EXTEOF)
18970Sstevel@tonic-gate 			debug("channel %d: accepting ext data after eof", id);
18980Sstevel@tonic-gate 		else
18990Sstevel@tonic-gate 			packet_disconnect("Received extended_data after EOF "
19000Sstevel@tonic-gate 			    "on channel %d.", id);
19010Sstevel@tonic-gate 	}
19020Sstevel@tonic-gate 	tcode = packet_get_int();
19030Sstevel@tonic-gate 	if (c->efd == -1 ||
19040Sstevel@tonic-gate 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
19050Sstevel@tonic-gate 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
19060Sstevel@tonic-gate 		log("channel %d: bad ext data", c->self);
19070Sstevel@tonic-gate 		return;
19080Sstevel@tonic-gate 	}
19090Sstevel@tonic-gate 	data = packet_get_string(&data_len);
19100Sstevel@tonic-gate 	packet_check_eom();
19110Sstevel@tonic-gate 	if (data_len > c->local_window) {
19120Sstevel@tonic-gate 		log("channel %d: rcvd too much extended_data %d, win %d",
19130Sstevel@tonic-gate 		    c->self, data_len, c->local_window);
19140Sstevel@tonic-gate 		xfree(data);
19150Sstevel@tonic-gate 		return;
19160Sstevel@tonic-gate 	}
19170Sstevel@tonic-gate 	debug2("channel %d: rcvd ext data %d", c->self, data_len);
19180Sstevel@tonic-gate 	c->local_window -= data_len;
19190Sstevel@tonic-gate 	buffer_append(&c->extended, data, data_len);
19200Sstevel@tonic-gate 	xfree(data);
19210Sstevel@tonic-gate }
19220Sstevel@tonic-gate 
19230Sstevel@tonic-gate void
channel_input_ieof(int type,u_int32_t seq,void * ctxt)19240Sstevel@tonic-gate channel_input_ieof(int type, u_int32_t seq, void *ctxt)
19250Sstevel@tonic-gate {
19260Sstevel@tonic-gate 	int id;
19270Sstevel@tonic-gate 	Channel *c;
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 	id = packet_get_int();
19300Sstevel@tonic-gate 	packet_check_eom();
19310Sstevel@tonic-gate 	c = channel_lookup(id);
19320Sstevel@tonic-gate 	if (c == NULL)
19330Sstevel@tonic-gate 		packet_disconnect("Received ieof for nonexistent channel %d.", id);
19340Sstevel@tonic-gate 	chan_rcvd_ieof(c);
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	/* XXX force input close */
19370Sstevel@tonic-gate 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
19380Sstevel@tonic-gate 		debug("channel %d: FORCE input drain", c->self);
19390Sstevel@tonic-gate 		c->istate = CHAN_INPUT_WAIT_DRAIN;
19400Sstevel@tonic-gate 		if (buffer_len(&c->input) == 0)
19410Sstevel@tonic-gate 			chan_ibuf_empty(c);
19420Sstevel@tonic-gate 	}
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate }
19450Sstevel@tonic-gate 
19460Sstevel@tonic-gate void
channel_input_close(int type,u_int32_t seq,void * ctxt)19470Sstevel@tonic-gate channel_input_close(int type, u_int32_t seq, void *ctxt)
19480Sstevel@tonic-gate {
19490Sstevel@tonic-gate 	int id;
19500Sstevel@tonic-gate 	Channel *c;
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 	id = packet_get_int();
19530Sstevel@tonic-gate 	packet_check_eom();
19540Sstevel@tonic-gate 	c = channel_lookup(id);
19550Sstevel@tonic-gate 	if (c == NULL)
19560Sstevel@tonic-gate 		packet_disconnect("Received close for nonexistent channel %d.", id);
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	/*
19590Sstevel@tonic-gate 	 * Send a confirmation that we have closed the channel and no more
19600Sstevel@tonic-gate 	 * data is coming for it.
19610Sstevel@tonic-gate 	 */
19620Sstevel@tonic-gate 	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
19630Sstevel@tonic-gate 	packet_put_int(c->remote_id);
19640Sstevel@tonic-gate 	packet_send();
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate 	/*
19670Sstevel@tonic-gate 	 * If the channel is in closed state, we have sent a close request,
19680Sstevel@tonic-gate 	 * and the other side will eventually respond with a confirmation.
19690Sstevel@tonic-gate 	 * Thus, we cannot free the channel here, because then there would be
19700Sstevel@tonic-gate 	 * no-one to receive the confirmation.  The channel gets freed when
19710Sstevel@tonic-gate 	 * the confirmation arrives.
19720Sstevel@tonic-gate 	 */
19730Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_CLOSED) {
19740Sstevel@tonic-gate 		/*
19750Sstevel@tonic-gate 		 * Not a closed channel - mark it as draining, which will
19760Sstevel@tonic-gate 		 * cause it to be freed later.
19770Sstevel@tonic-gate 		 */
19780Sstevel@tonic-gate 		buffer_clear(&c->input);
19790Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
19800Sstevel@tonic-gate 	}
19810Sstevel@tonic-gate }
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
19840Sstevel@tonic-gate void
channel_input_oclose(int type,u_int32_t seq,void * ctxt)19850Sstevel@tonic-gate channel_input_oclose(int type, u_int32_t seq, void *ctxt)
19860Sstevel@tonic-gate {
19870Sstevel@tonic-gate 	int id = packet_get_int();
19880Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate 	packet_check_eom();
19910Sstevel@tonic-gate 	if (c == NULL)
19920Sstevel@tonic-gate 		packet_disconnect("Received oclose for nonexistent channel %d.", id);
19930Sstevel@tonic-gate 	chan_rcvd_oclose(c);
19940Sstevel@tonic-gate }
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate void
channel_input_close_confirmation(int type,u_int32_t seq,void * ctxt)19970Sstevel@tonic-gate channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
19980Sstevel@tonic-gate {
19990Sstevel@tonic-gate 	int id = packet_get_int();
20000Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 	packet_check_eom();
20030Sstevel@tonic-gate 	if (c == NULL)
20040Sstevel@tonic-gate 		packet_disconnect("Received close confirmation for "
20050Sstevel@tonic-gate 		    "out-of-range channel %d.", id);
20060Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_CLOSED)
20070Sstevel@tonic-gate 		packet_disconnect("Received close confirmation for "
20080Sstevel@tonic-gate 		    "non-closed channel %d (type %d).", id, c->type);
20090Sstevel@tonic-gate 	channel_free(c);
20100Sstevel@tonic-gate }
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate void
channel_input_open_confirmation(int type,u_int32_t seq,void * ctxt)20130Sstevel@tonic-gate channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
20140Sstevel@tonic-gate {
20150Sstevel@tonic-gate 	int id, remote_id;
20160Sstevel@tonic-gate 	Channel *c;
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 	id = packet_get_int();
20190Sstevel@tonic-gate 	c = channel_lookup(id);
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
20220Sstevel@tonic-gate 		packet_disconnect("Received open confirmation for "
20230Sstevel@tonic-gate 		    "non-opening channel %d.", id);
20240Sstevel@tonic-gate 	remote_id = packet_get_int();
20250Sstevel@tonic-gate 	/* Record the remote channel number and mark that the channel is now open. */
20260Sstevel@tonic-gate 	c->remote_id = remote_id;
20270Sstevel@tonic-gate 	c->type = SSH_CHANNEL_OPEN;
20280Sstevel@tonic-gate 
20290Sstevel@tonic-gate 	if (compat20) {
20300Sstevel@tonic-gate 		c->remote_window = packet_get_int();
20310Sstevel@tonic-gate 		c->remote_maxpacket = packet_get_int();
20320Sstevel@tonic-gate 		if (c->confirm) {
20330Sstevel@tonic-gate 			debug2("callback start");
20340Sstevel@tonic-gate 			c->confirm(c->self, NULL);
20350Sstevel@tonic-gate 			debug2("callback done");
20360Sstevel@tonic-gate 		}
20370Sstevel@tonic-gate 		debug("channel %d: open confirm rwindow %u rmax %u", c->self,
20380Sstevel@tonic-gate 		    c->remote_window, c->remote_maxpacket);
20390Sstevel@tonic-gate 	}
20400Sstevel@tonic-gate 	packet_check_eom();
20410Sstevel@tonic-gate }
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate static char *
reason2txt(int reason)20440Sstevel@tonic-gate reason2txt(int reason)
20450Sstevel@tonic-gate {
20460Sstevel@tonic-gate 	switch (reason) {
20470Sstevel@tonic-gate 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
20480Sstevel@tonic-gate 		return "administratively prohibited";
20490Sstevel@tonic-gate 	case SSH2_OPEN_CONNECT_FAILED:
20500Sstevel@tonic-gate 		return "connect failed";
20510Sstevel@tonic-gate 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
20520Sstevel@tonic-gate 		return "unknown channel type";
20530Sstevel@tonic-gate 	case SSH2_OPEN_RESOURCE_SHORTAGE:
20540Sstevel@tonic-gate 		return "resource shortage";
20550Sstevel@tonic-gate 	}
20560Sstevel@tonic-gate 	return "unknown reason";
20570Sstevel@tonic-gate }
20580Sstevel@tonic-gate 
20590Sstevel@tonic-gate void
channel_input_open_failure(int type,u_int32_t seq,void * ctxt)20600Sstevel@tonic-gate channel_input_open_failure(int type, u_int32_t seq, void *ctxt)
20610Sstevel@tonic-gate {
20620Sstevel@tonic-gate 	int id, reason;
20630Sstevel@tonic-gate 	char *msg = NULL, *lang = NULL;
20640Sstevel@tonic-gate 	Channel *c;
20650Sstevel@tonic-gate 
20660Sstevel@tonic-gate 	id = packet_get_int();
20670Sstevel@tonic-gate 	c = channel_lookup(id);
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
20700Sstevel@tonic-gate 		packet_disconnect("Received open failure for "
20710Sstevel@tonic-gate 		    "non-opening channel %d.", id);
20720Sstevel@tonic-gate 	if (compat20) {
20730Sstevel@tonic-gate 		reason = packet_get_int();
20740Sstevel@tonic-gate 		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
20750Sstevel@tonic-gate 			msg  = packet_get_string(NULL);
20760Sstevel@tonic-gate 			lang = packet_get_string(NULL);
20770Sstevel@tonic-gate 		}
20780Sstevel@tonic-gate 		log("channel %d: open failed: %s%s%s", id,
20790Sstevel@tonic-gate 		    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
20800Sstevel@tonic-gate 		if (msg != NULL)
20810Sstevel@tonic-gate 			xfree(msg);
20820Sstevel@tonic-gate 		if (lang != NULL)
20830Sstevel@tonic-gate 			xfree(lang);
20840Sstevel@tonic-gate 	}
20850Sstevel@tonic-gate 	packet_check_eom();
20860Sstevel@tonic-gate 	/* Free the channel.  This will also close the socket. */
20870Sstevel@tonic-gate 	channel_free(c);
20880Sstevel@tonic-gate }
20890Sstevel@tonic-gate 
20900Sstevel@tonic-gate void
channel_input_window_adjust(int type,u_int32_t seq,void * ctxt)20910Sstevel@tonic-gate channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
20920Sstevel@tonic-gate {
20930Sstevel@tonic-gate 	Channel *c;
20940Sstevel@tonic-gate 	int id;
20950Sstevel@tonic-gate 	u_int adjust;
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 	if (!compat20)
20980Sstevel@tonic-gate 		return;
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 	/* Get the channel number and verify it. */
21010Sstevel@tonic-gate 	id = packet_get_int();
21020Sstevel@tonic-gate 	c = channel_lookup(id);
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
21050Sstevel@tonic-gate 		log("Received window adjust for "
21060Sstevel@tonic-gate 		    "non-open channel %d.", id);
21070Sstevel@tonic-gate 		return;
21080Sstevel@tonic-gate 	}
21090Sstevel@tonic-gate 	adjust = packet_get_int();
21100Sstevel@tonic-gate 	packet_check_eom();
21110Sstevel@tonic-gate 	debug2("channel %d: rcvd adjust %u", id, adjust);
21120Sstevel@tonic-gate 	c->remote_window += adjust;
21130Sstevel@tonic-gate }
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate void
channel_input_port_open(int type,u_int32_t seq,void * ctxt)21160Sstevel@tonic-gate channel_input_port_open(int type, u_int32_t seq, void *ctxt)
21170Sstevel@tonic-gate {
21180Sstevel@tonic-gate 	Channel *c = NULL;
21190Sstevel@tonic-gate 	u_short host_port;
21200Sstevel@tonic-gate 	char *host, *originator_string;
21210Sstevel@tonic-gate 	int remote_id, sock = -1;
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate 	remote_id = packet_get_int();
21240Sstevel@tonic-gate 	host = packet_get_string(NULL);
21250Sstevel@tonic-gate 	host_port = packet_get_int();
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
21280Sstevel@tonic-gate 		originator_string = packet_get_string(NULL);
21290Sstevel@tonic-gate 	} else {
21300Sstevel@tonic-gate 		originator_string = xstrdup("unknown (remote did not supply name)");
21310Sstevel@tonic-gate 	}
21320Sstevel@tonic-gate 	packet_check_eom();
21330Sstevel@tonic-gate 	sock = channel_connect_to(host, host_port);
21340Sstevel@tonic-gate 	if (sock != -1) {
21350Sstevel@tonic-gate 		c = channel_new("connected socket",
21360Sstevel@tonic-gate 		    SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
21370Sstevel@tonic-gate 		    originator_string, 1);
21380Sstevel@tonic-gate 		c->remote_id = remote_id;
21390Sstevel@tonic-gate 	}
21400Sstevel@tonic-gate 	if (c == NULL) {
21410Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
21420Sstevel@tonic-gate 		packet_put_int(remote_id);
21430Sstevel@tonic-gate 		packet_send();
21440Sstevel@tonic-gate 	}
21450Sstevel@tonic-gate 	xfree(host);
21460Sstevel@tonic-gate }
21470Sstevel@tonic-gate 
21480Sstevel@tonic-gate 
21490Sstevel@tonic-gate /* -- tcp forwarding */
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate void
channel_set_af(int af)21520Sstevel@tonic-gate channel_set_af(int af)
21530Sstevel@tonic-gate {
21540Sstevel@tonic-gate 	IPv4or6 = af;
21550Sstevel@tonic-gate }
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate static int
channel_setup_fwd_listener(int type,const char * listen_addr,u_short listen_port,const char * host_to_connect,u_short port_to_connect,int gateway_ports)21580Sstevel@tonic-gate channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
21590Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
21600Sstevel@tonic-gate {
21610Sstevel@tonic-gate 	Channel *c;
21625334Sjp161948 	int sock, r, is_client, on = 1, wildcard = 0, success = 0;
21630Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
21645334Sjp161948 	const char *host, *addr;
21650Sstevel@tonic-gate 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
21680Sstevel@tonic-gate 	    listen_addr : host_to_connect;
21695334Sjp161948 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 	if (host == NULL) {
21720Sstevel@tonic-gate 		error("No forward host name.");
21735334Sjp161948 		return 0;
21740Sstevel@tonic-gate 	}
21750Sstevel@tonic-gate 	if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
21760Sstevel@tonic-gate 		error("Forward host name too long.");
21775334Sjp161948 		return 0;
21780Sstevel@tonic-gate 	}
21790Sstevel@tonic-gate 
21800Sstevel@tonic-gate 	/*
21815334Sjp161948 	 * Determine whether or not a port forward listens to loopback,
21825334Sjp161948 	 * specified address or wildcard. On the client, a specified bind
21835334Sjp161948 	 * address will always override gateway_ports. On the server, a
21845334Sjp161948 	 * gateway_ports of 1 (``yes'') will override the client's
21855334Sjp161948 	 * specification and force a wildcard bind, whereas a value of 2
21865334Sjp161948 	 * (``clientspecified'') will bind to whatever address the client
21875334Sjp161948 	 * asked for.
21885334Sjp161948 	 *
21895334Sjp161948 	 * Special-case listen_addrs are:
21905334Sjp161948 	 *
21915334Sjp161948 	 * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
21925334Sjp161948 	 * "" (empty string), "*"  -> wildcard v4/v6
21935334Sjp161948 	 * "localhost"             -> loopback v4/v6
21945334Sjp161948 	 */
21955334Sjp161948 	addr = NULL;
21965334Sjp161948 	if (listen_addr == NULL) {
21975334Sjp161948 		/* No address specified: default to gateway_ports setting */
21985334Sjp161948 		if (gateway_ports)
21995334Sjp161948 			wildcard = 1;
22005334Sjp161948 	} else if (gateway_ports || is_client) {
22015334Sjp161948 		if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
22025334Sjp161948 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
22035334Sjp161948 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
22045334Sjp161948 		    (!is_client && gateway_ports == 1))
22055334Sjp161948 			wildcard = 1;
22065334Sjp161948 		else if (strcmp(listen_addr, "localhost") != 0)
22075334Sjp161948 			addr = listen_addr;
22085334Sjp161948 	}
22095334Sjp161948 
22105334Sjp161948 	debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
22115334Sjp161948 	    type, wildcard, (addr == NULL) ? "NULL" : addr);
22125334Sjp161948 
22135334Sjp161948 	/*
22140Sstevel@tonic-gate 	 * getaddrinfo returns a loopback address if the hostname is
22150Sstevel@tonic-gate 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
22160Sstevel@tonic-gate 	 */
22170Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
22180Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
22195334Sjp161948 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
22200Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
22210Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", listen_port);
22225334Sjp161948 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
22235334Sjp161948 		if (addr == NULL) {
22245334Sjp161948 			/* This really shouldn't happen */
22255334Sjp161948 			packet_disconnect("getaddrinfo: fatal error: %s",
22265334Sjp161948 			    gai_strerror(r));
22275334Sjp161948 		} else {
22285334Sjp161948 			error("channel_setup_fwd_listener: "
22295334Sjp161948 			    "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
22305334Sjp161948 		}
22315334Sjp161948 		return 0;
22325334Sjp161948 	}
22330Sstevel@tonic-gate 
22340Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
22350Sstevel@tonic-gate 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
22360Sstevel@tonic-gate 			continue;
22370Sstevel@tonic-gate 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
22380Sstevel@tonic-gate 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
22390Sstevel@tonic-gate 			error("channel_setup_fwd_listener: getnameinfo failed");
22400Sstevel@tonic-gate 			continue;
22410Sstevel@tonic-gate 		}
22420Sstevel@tonic-gate 		/* Create a port to listen for the host. */
22435334Sjp161948 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
22440Sstevel@tonic-gate 		if (sock < 0) {
22450Sstevel@tonic-gate 			/* this is no error since kernel may not support ipv6 */
22460Sstevel@tonic-gate 			verbose("socket: %.100s", strerror(errno));
22470Sstevel@tonic-gate 			continue;
22480Sstevel@tonic-gate 		}
22490Sstevel@tonic-gate 		/*
22500Sstevel@tonic-gate 		 * Set socket options.
22510Sstevel@tonic-gate 		 * Allow local port reuse in TIME_WAIT.
22520Sstevel@tonic-gate 		 */
22530Sstevel@tonic-gate 		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
22540Sstevel@tonic-gate 		    sizeof(on)) == -1)
22550Sstevel@tonic-gate 			error("setsockopt SO_REUSEADDR: %s", strerror(errno));
22560Sstevel@tonic-gate 
22570Sstevel@tonic-gate 		debug("Local forwarding listening on %s port %s.", ntop, strport);
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 		/* Bind the socket to the address. */
22600Sstevel@tonic-gate 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
22610Sstevel@tonic-gate 			/* address can be in use ipv6 address is already bound */
22620Sstevel@tonic-gate 			if (!ai->ai_next)
22630Sstevel@tonic-gate 				error("bind: %.100s", strerror(errno));
22640Sstevel@tonic-gate 			else
22650Sstevel@tonic-gate 				verbose("bind: %.100s", strerror(errno));
22660Sstevel@tonic-gate 
22670Sstevel@tonic-gate 			close(sock);
22680Sstevel@tonic-gate 			continue;
22690Sstevel@tonic-gate 		}
22700Sstevel@tonic-gate 		/* Start listening for connections on the socket. */
22710Sstevel@tonic-gate 		if (listen(sock, 5) < 0) {
22720Sstevel@tonic-gate 			error("listen: %.100s", strerror(errno));
22730Sstevel@tonic-gate 			close(sock);
22740Sstevel@tonic-gate 			continue;
22750Sstevel@tonic-gate 		}
22760Sstevel@tonic-gate 		/* Allocate a channel number for the socket. */
22770Sstevel@tonic-gate 		c = channel_new("port listener", type, sock, sock, -1,
22780Sstevel@tonic-gate 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
22790Sstevel@tonic-gate 		    0, xstrdup("port listener"), 1);
22800Sstevel@tonic-gate 		strlcpy(c->path, host, sizeof(c->path));
22810Sstevel@tonic-gate 		c->host_port = port_to_connect;
22820Sstevel@tonic-gate 		c->listening_port = listen_port;
22830Sstevel@tonic-gate 		success = 1;
22840Sstevel@tonic-gate 	}
22850Sstevel@tonic-gate 	if (success == 0)
22860Sstevel@tonic-gate 		error("channel_setup_fwd_listener: cannot listen to port: %d",
22870Sstevel@tonic-gate 		    listen_port);
22880Sstevel@tonic-gate 	freeaddrinfo(aitop);
22890Sstevel@tonic-gate 	return success;
22900Sstevel@tonic-gate }
22910Sstevel@tonic-gate 
22925334Sjp161948 int
channel_cancel_rport_listener(const char * host,u_short port)22935334Sjp161948 channel_cancel_rport_listener(const char *host, u_short port)
22945334Sjp161948 {
22955334Sjp161948 	u_int i;
22965334Sjp161948 	int found = 0;
22975334Sjp161948 
22985334Sjp161948 	for (i = 0; i < channels_alloc; i++) {
22995334Sjp161948 		Channel *c = channels[i];
23005334Sjp161948 
23015334Sjp161948 		if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
23025334Sjp161948 		    strncmp(c->path, host, sizeof(c->path)) == 0 &&
23035334Sjp161948 		    c->listening_port == port) {
23045334Sjp161948 			debug2("%s: close channel %d", __func__, i);
23055334Sjp161948 			channel_free(c);
23065334Sjp161948 			found = 1;
23075334Sjp161948 		}
23085334Sjp161948 	}
23095334Sjp161948 
23105334Sjp161948 	return (found);
23115334Sjp161948 }
23125334Sjp161948 
23130Sstevel@tonic-gate /* protocol local port fwd, used by ssh (and sshd in v1) */
23140Sstevel@tonic-gate int
channel_setup_local_fwd_listener(const char * listen_host,u_short listen_port,const char * host_to_connect,u_short port_to_connect,int gateway_ports)23155334Sjp161948 channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
23160Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
23170Sstevel@tonic-gate {
23180Sstevel@tonic-gate 	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
23195334Sjp161948 	    listen_host, listen_port, host_to_connect, port_to_connect,
23205334Sjp161948 	    gateway_ports);
23210Sstevel@tonic-gate }
23220Sstevel@tonic-gate 
23230Sstevel@tonic-gate /* protocol v2 remote port fwd, used by sshd */
23240Sstevel@tonic-gate int
channel_setup_remote_fwd_listener(const char * listen_address,u_short listen_port,int gateway_ports)23250Sstevel@tonic-gate channel_setup_remote_fwd_listener(const char *listen_address,
23260Sstevel@tonic-gate     u_short listen_port, int gateway_ports)
23270Sstevel@tonic-gate {
23280Sstevel@tonic-gate 	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
23290Sstevel@tonic-gate 	    listen_address, listen_port, NULL, 0, gateway_ports);
23300Sstevel@tonic-gate }
23310Sstevel@tonic-gate 
23320Sstevel@tonic-gate /*
23330Sstevel@tonic-gate  * Initiate forwarding of connections to port "port" on remote host through
23340Sstevel@tonic-gate  * the secure channel to host:port from local side.
23350Sstevel@tonic-gate  */
23360Sstevel@tonic-gate 
23375334Sjp161948 int
channel_request_remote_forwarding(const char * listen_host,u_short listen_port,const char * host_to_connect,u_short port_to_connect)23385334Sjp161948 channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
23390Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect)
23400Sstevel@tonic-gate {
23410Sstevel@tonic-gate 	int type, success = 0;
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	/* Record locally that connection to this host/port is permitted. */
23440Sstevel@tonic-gate 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
23450Sstevel@tonic-gate 		fatal("channel_request_remote_forwarding: too many forwards");
23460Sstevel@tonic-gate 
23475334Sjp161948 	if (listen_host != NULL &&
23485334Sjp161948 	    strlen(listen_host) > SSH_CHANNEL_PATH_LEN - 1) {
23495334Sjp161948 		error("Binding address too long.");
23505334Sjp161948 		return -1;
23515334Sjp161948 	}
23525334Sjp161948 
23530Sstevel@tonic-gate 	/* Send the forward request to the remote side. */
23540Sstevel@tonic-gate 	if (compat20) {
23555334Sjp161948 		const char *address_to_bind;
23565334Sjp161948 		if (listen_host == NULL) {
23575334Sjp161948 			if (datafellows & SSH_BUG_RFWD_ADDR)
23585334Sjp161948 				address_to_bind = "127.0.0.1";
23595334Sjp161948 			else
23605334Sjp161948 				address_to_bind = "localhost";
23615334Sjp161948 		} else if (*listen_host == '\0' ||
23625334Sjp161948 			   strcmp(listen_host, "*") == 0) {
23635334Sjp161948 			if (datafellows & SSH_BUG_RFWD_ADDR)
23645334Sjp161948 				address_to_bind = "0.0.0.0";
23655334Sjp161948 			else
23665334Sjp161948 				address_to_bind = "";
23675334Sjp161948 		} else
23685334Sjp161948 			address_to_bind = listen_host;
23695334Sjp161948 
23700Sstevel@tonic-gate 		packet_start(SSH2_MSG_GLOBAL_REQUEST);
23710Sstevel@tonic-gate 		packet_put_cstring("tcpip-forward");
23720Sstevel@tonic-gate 		packet_put_char(1);			/* boolean: want reply */
23730Sstevel@tonic-gate 		packet_put_cstring(address_to_bind);
23740Sstevel@tonic-gate 		packet_put_int(listen_port);
23750Sstevel@tonic-gate 		packet_send();
23760Sstevel@tonic-gate 		packet_write_wait();
23770Sstevel@tonic-gate 		/* Assume that server accepts the request */
23780Sstevel@tonic-gate 		success = 1;
23790Sstevel@tonic-gate 	} else {
23800Sstevel@tonic-gate 		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
23810Sstevel@tonic-gate 		packet_put_int(listen_port);
23820Sstevel@tonic-gate 		packet_put_cstring(host_to_connect);
23830Sstevel@tonic-gate 		packet_put_int(port_to_connect);
23840Sstevel@tonic-gate 		packet_send();
23850Sstevel@tonic-gate 		packet_write_wait();
23860Sstevel@tonic-gate 
23870Sstevel@tonic-gate 		/* Wait for response from the remote side. */
23880Sstevel@tonic-gate 		type = packet_read();
23890Sstevel@tonic-gate 		switch (type) {
23900Sstevel@tonic-gate 		case SSH_SMSG_SUCCESS:
23910Sstevel@tonic-gate 			success = 1;
23920Sstevel@tonic-gate 			break;
23930Sstevel@tonic-gate 		case SSH_SMSG_FAILURE:
23940Sstevel@tonic-gate 			log("Warning: Server denied remote port forwarding.");
23950Sstevel@tonic-gate 			break;
23960Sstevel@tonic-gate 		default:
23970Sstevel@tonic-gate 			/* Unknown packet */
23980Sstevel@tonic-gate 			packet_disconnect("Protocol error for port forward request:"
23990Sstevel@tonic-gate 			    "received packet type %d.", type);
24000Sstevel@tonic-gate 		}
24010Sstevel@tonic-gate 	}
24020Sstevel@tonic-gate 	if (success) {
24030Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
24040Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
24050Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].listen_port = listen_port;
24060Sstevel@tonic-gate 		num_permitted_opens++;
24070Sstevel@tonic-gate 	}
24085334Sjp161948 	return (success ? 0 : -1);
24095334Sjp161948 }
24105334Sjp161948 
24115334Sjp161948 /*
24125334Sjp161948  * Request cancellation of remote forwarding of connection host:port from
24135334Sjp161948  * local side.
24145334Sjp161948  */
24155334Sjp161948 void
channel_request_rforward_cancel(const char * host,u_short port)24165334Sjp161948 channel_request_rforward_cancel(const char *host, u_short port)
24175334Sjp161948 {
24185334Sjp161948 	int i;
24195334Sjp161948 
24205334Sjp161948 	if (!compat20)
24215334Sjp161948 		return;
24225334Sjp161948 
24235334Sjp161948 	for (i = 0; i < num_permitted_opens; i++) {
24245334Sjp161948 		if (permitted_opens[i].host_to_connect != NULL &&
24255334Sjp161948 		    permitted_opens[i].listen_port == port)
24265334Sjp161948 			break;
24275334Sjp161948 	}
24285334Sjp161948 	if (i >= num_permitted_opens) {
24295334Sjp161948 		debug("%s: requested forward not found", __func__);
24305334Sjp161948 		return;
24315334Sjp161948 	}
24325334Sjp161948 	packet_start(SSH2_MSG_GLOBAL_REQUEST);
24335334Sjp161948 	packet_put_cstring("cancel-tcpip-forward");
24345334Sjp161948 	packet_put_char(0);
24355334Sjp161948 	packet_put_cstring(host == NULL ? "" : host);
24365334Sjp161948 	packet_put_int(port);
24375334Sjp161948 	packet_send();
24385334Sjp161948 
24395334Sjp161948 	permitted_opens[i].listen_port = 0;
24405334Sjp161948 	permitted_opens[i].port_to_connect = 0;
24415334Sjp161948 	xfree(permitted_opens[i].host_to_connect);
24425334Sjp161948 	permitted_opens[i].host_to_connect = NULL;
24430Sstevel@tonic-gate }
24440Sstevel@tonic-gate 
24450Sstevel@tonic-gate /*
24460Sstevel@tonic-gate  * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
24470Sstevel@tonic-gate  * listening for the port, and sends back a success reply (or disconnect
24480Sstevel@tonic-gate  * message if there was an error).  This never returns if there was an error.
24490Sstevel@tonic-gate  */
24500Sstevel@tonic-gate 
24510Sstevel@tonic-gate void
channel_input_port_forward_request(int is_root,int gateway_ports)24520Sstevel@tonic-gate channel_input_port_forward_request(int is_root, int gateway_ports)
24530Sstevel@tonic-gate {
24540Sstevel@tonic-gate 	u_short port, host_port;
24550Sstevel@tonic-gate 	char *hostname;
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 	/* Get arguments from the packet. */
24580Sstevel@tonic-gate 	port = packet_get_int();
24590Sstevel@tonic-gate 	hostname = packet_get_string(NULL);
24600Sstevel@tonic-gate 	host_port = packet_get_int();
24610Sstevel@tonic-gate 
24620Sstevel@tonic-gate #ifndef HAVE_CYGWIN
24630Sstevel@tonic-gate 	/*
24640Sstevel@tonic-gate 	 * Check that an unprivileged user is not trying to forward a
24650Sstevel@tonic-gate 	 * privileged port.
24660Sstevel@tonic-gate 	 */
24670Sstevel@tonic-gate 	if (port < IPPORT_RESERVED && !is_root)
24680Sstevel@tonic-gate 		packet_disconnect("Requested forwarding of port %d but user is not root.",
24690Sstevel@tonic-gate 				  port);
24700Sstevel@tonic-gate #endif
24710Sstevel@tonic-gate 	/* Initiate forwarding */
24725334Sjp161948 	channel_setup_local_fwd_listener(NULL, port, hostname,
24735334Sjp161948 	    host_port, gateway_ports);
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 	/* Free the argument string. */
24760Sstevel@tonic-gate 	xfree(hostname);
24770Sstevel@tonic-gate }
24780Sstevel@tonic-gate 
24790Sstevel@tonic-gate /*
24800Sstevel@tonic-gate  * Permits opening to any host/port if permitted_opens[] is empty.  This is
24810Sstevel@tonic-gate  * usually called by the server, because the user could connect to any port
24820Sstevel@tonic-gate  * anyway, and the server has no way to know but to trust the client anyway.
24830Sstevel@tonic-gate  */
24840Sstevel@tonic-gate void
channel_permit_all_opens(void)24850Sstevel@tonic-gate channel_permit_all_opens(void)
24860Sstevel@tonic-gate {
24870Sstevel@tonic-gate 	if (num_permitted_opens == 0)
24880Sstevel@tonic-gate 		all_opens_permitted = 1;
24890Sstevel@tonic-gate }
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate void
channel_add_permitted_opens(char * host,int port)24920Sstevel@tonic-gate channel_add_permitted_opens(char *host, int port)
24930Sstevel@tonic-gate {
24940Sstevel@tonic-gate 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
24955334Sjp161948 		fatal("channel_add_permitted_opens: too many forwards");
24960Sstevel@tonic-gate 	debug("allow port forwarding to host %s port %d", host, port);
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
24990Sstevel@tonic-gate 	permitted_opens[num_permitted_opens].port_to_connect = port;
25000Sstevel@tonic-gate 	num_permitted_opens++;
25010Sstevel@tonic-gate 
25020Sstevel@tonic-gate 	all_opens_permitted = 0;
25030Sstevel@tonic-gate }
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate void
channel_clear_permitted_opens(void)25060Sstevel@tonic-gate channel_clear_permitted_opens(void)
25070Sstevel@tonic-gate {
25080Sstevel@tonic-gate 	int i;
25090Sstevel@tonic-gate 
25100Sstevel@tonic-gate 	for (i = 0; i < num_permitted_opens; i++)
25110Sstevel@tonic-gate 		xfree(permitted_opens[i].host_to_connect);
25120Sstevel@tonic-gate 	num_permitted_opens = 0;
25130Sstevel@tonic-gate }
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate /* return socket to remote host, port */
25170Sstevel@tonic-gate static int
connect_to(const char * host,u_short port)25180Sstevel@tonic-gate connect_to(const char *host, u_short port)
25190Sstevel@tonic-gate {
25200Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
25210Sstevel@tonic-gate 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
25220Sstevel@tonic-gate 	int gaierr;
25230Sstevel@tonic-gate 	int sock = -1;
25240Sstevel@tonic-gate 
25250Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
25260Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
25270Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
25280Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", port);
25290Sstevel@tonic-gate 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
25300Sstevel@tonic-gate 		error("connect_to %.100s: unknown host (%s)", host,
25310Sstevel@tonic-gate 		    gai_strerror(gaierr));
25320Sstevel@tonic-gate 		return -1;
25330Sstevel@tonic-gate 	}
25340Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
25350Sstevel@tonic-gate 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
25360Sstevel@tonic-gate 			continue;
25370Sstevel@tonic-gate 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
25380Sstevel@tonic-gate 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
25390Sstevel@tonic-gate 			error("connect_to: getnameinfo failed");
25400Sstevel@tonic-gate 			continue;
25410Sstevel@tonic-gate 		}
25420Sstevel@tonic-gate 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
25430Sstevel@tonic-gate 		if (sock < 0) {
25440Sstevel@tonic-gate 			error("socket: %.100s", strerror(errno));
25450Sstevel@tonic-gate 			continue;
25460Sstevel@tonic-gate 		}
25470Sstevel@tonic-gate 		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
25480Sstevel@tonic-gate 			fatal("connect_to: F_SETFL: %s", strerror(errno));
25490Sstevel@tonic-gate 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
25500Sstevel@tonic-gate 		    errno != EINPROGRESS) {
25510Sstevel@tonic-gate 			error("connect_to %.100s port %s: %.100s", ntop, strport,
25520Sstevel@tonic-gate 			    strerror(errno));
25530Sstevel@tonic-gate 			close(sock);
25540Sstevel@tonic-gate 			continue;	/* fail -- try next */
25550Sstevel@tonic-gate 		}
25560Sstevel@tonic-gate 		break; /* success */
25570Sstevel@tonic-gate 
25580Sstevel@tonic-gate 	}
25590Sstevel@tonic-gate 	freeaddrinfo(aitop);
25600Sstevel@tonic-gate 	if (!ai) {
25610Sstevel@tonic-gate 		error("connect_to %.100s port %d: failed.", host, port);
25620Sstevel@tonic-gate 		return -1;
25630Sstevel@tonic-gate 	}
25640Sstevel@tonic-gate 	/* success */
25650Sstevel@tonic-gate 	set_nodelay(sock);
25660Sstevel@tonic-gate 	return sock;
25670Sstevel@tonic-gate }
25680Sstevel@tonic-gate 
25690Sstevel@tonic-gate int
channel_connect_by_listen_address(u_short listen_port)25700Sstevel@tonic-gate channel_connect_by_listen_address(u_short listen_port)
25710Sstevel@tonic-gate {
25720Sstevel@tonic-gate 	int i;
25730Sstevel@tonic-gate 
25740Sstevel@tonic-gate 	for (i = 0; i < num_permitted_opens; i++)
25750Sstevel@tonic-gate 		if (permitted_opens[i].listen_port == listen_port)
25760Sstevel@tonic-gate 			return connect_to(
25770Sstevel@tonic-gate 			    permitted_opens[i].host_to_connect,
25780Sstevel@tonic-gate 			    permitted_opens[i].port_to_connect);
25790Sstevel@tonic-gate 	error("WARNING: Server requests forwarding for unknown listen_port %d",
25800Sstevel@tonic-gate 	    listen_port);
25810Sstevel@tonic-gate 	return -1;
25820Sstevel@tonic-gate }
25830Sstevel@tonic-gate 
25840Sstevel@tonic-gate /* Check if connecting to that port is permitted and connect. */
25850Sstevel@tonic-gate int
channel_connect_to(const char * host,u_short port)25860Sstevel@tonic-gate channel_connect_to(const char *host, u_short port)
25870Sstevel@tonic-gate {
25880Sstevel@tonic-gate 	int i, permit;
25890Sstevel@tonic-gate 
25900Sstevel@tonic-gate 	permit = all_opens_permitted;
25910Sstevel@tonic-gate 	if (!permit) {
25920Sstevel@tonic-gate 		for (i = 0; i < num_permitted_opens; i++)
25930Sstevel@tonic-gate 			if (permitted_opens[i].port_to_connect == port &&
25940Sstevel@tonic-gate 			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
25950Sstevel@tonic-gate 				permit = 1;
25960Sstevel@tonic-gate 
25970Sstevel@tonic-gate 	}
25980Sstevel@tonic-gate 	if (!permit) {
25990Sstevel@tonic-gate 		log("Received request to connect to host %.100s port %d, "
26000Sstevel@tonic-gate 		    "but the request was denied.", host, port);
26010Sstevel@tonic-gate 		return -1;
26020Sstevel@tonic-gate 	}
26030Sstevel@tonic-gate 	return connect_to(host, port);
26040Sstevel@tonic-gate }
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate /* -- X11 forwarding */
26070Sstevel@tonic-gate 
26080Sstevel@tonic-gate /*
26090Sstevel@tonic-gate  * Creates an internet domain socket for listening for X11 connections.
26100Sstevel@tonic-gate  * Returns 0 and a suitable display number for the DISPLAY variable
26110Sstevel@tonic-gate  * stored in display_numberp , or -1 if an error occurs.
26120Sstevel@tonic-gate  */
26130Sstevel@tonic-gate int
x11_create_display_inet(int x11_display_offset,int x11_use_localhost,int single_connection,u_int * display_numberp)26140Sstevel@tonic-gate x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
26150Sstevel@tonic-gate     int single_connection, u_int *display_numberp)
26160Sstevel@tonic-gate {
26170Sstevel@tonic-gate 	Channel *nc = NULL;
26180Sstevel@tonic-gate 	int display_number, sock;
26190Sstevel@tonic-gate 	u_short port;
26200Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
26210Sstevel@tonic-gate 	char strport[NI_MAXSERV];
26220Sstevel@tonic-gate 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
26230Sstevel@tonic-gate 
26240Sstevel@tonic-gate 	for (display_number = x11_display_offset;
26250Sstevel@tonic-gate 	    display_number < MAX_DISPLAYS;
26260Sstevel@tonic-gate 	    display_number++) {
26270Sstevel@tonic-gate 		port = 6000 + display_number;
26280Sstevel@tonic-gate 		memset(&hints, 0, sizeof(hints));
26290Sstevel@tonic-gate 		hints.ai_family = IPv4or6;
26300Sstevel@tonic-gate 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
26310Sstevel@tonic-gate 		hints.ai_socktype = SOCK_STREAM;
26320Sstevel@tonic-gate 		snprintf(strport, sizeof strport, "%d", port);
26330Sstevel@tonic-gate 		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
26340Sstevel@tonic-gate 			error("getaddrinfo: %.100s", gai_strerror(gaierr));
26350Sstevel@tonic-gate 			return -1;
26360Sstevel@tonic-gate 		}
26370Sstevel@tonic-gate 		for (ai = aitop; ai; ai = ai->ai_next) {
26380Sstevel@tonic-gate 			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
26390Sstevel@tonic-gate 				continue;
26400Sstevel@tonic-gate 			sock = socket(ai->ai_family, SOCK_STREAM, 0);
26410Sstevel@tonic-gate 			if (sock < 0) {
26420Sstevel@tonic-gate 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
26430Sstevel@tonic-gate 					error("socket: %.100s", strerror(errno));
26449448SZdenek.Kotala@Sun.COM 					freeaddrinfo(aitop);
26459448SZdenek.Kotala@Sun.COM 					for (n = 0; n < num_socks; n++)
26469448SZdenek.Kotala@Sun.COM 						close(socks[n]);
26470Sstevel@tonic-gate 					return -1;
26480Sstevel@tonic-gate 				} else {
26490Sstevel@tonic-gate 					debug("x11_create_display_inet: Socket family %d not supported",
26500Sstevel@tonic-gate 						 ai->ai_family);
26510Sstevel@tonic-gate 					continue;
26520Sstevel@tonic-gate 				}
26530Sstevel@tonic-gate 			}
26540Sstevel@tonic-gate #ifdef IPV6_V6ONLY
26550Sstevel@tonic-gate 			if (ai->ai_family == AF_INET6) {
26560Sstevel@tonic-gate 				int on = 1;
26570Sstevel@tonic-gate 				if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
26580Sstevel@tonic-gate 					error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
26590Sstevel@tonic-gate 			}
26600Sstevel@tonic-gate #endif
26610Sstevel@tonic-gate 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
26629448SZdenek.Kotala@Sun.COM 				/*
26639448SZdenek.Kotala@Sun.COM 				 * If bind() fails  with EADDRNOTAVAIL, we
26649448SZdenek.Kotala@Sun.COM 				 * should not break immediately but rather
26659448SZdenek.Kotala@Sun.COM 				 * try the next address available.
26669448SZdenek.Kotala@Sun.COM 				 */
26679448SZdenek.Kotala@Sun.COM 				if (errno == EADDRNOTAVAIL) {
26689448SZdenek.Kotala@Sun.COM 					close(sock);
26699448SZdenek.Kotala@Sun.COM 					continue;
26709448SZdenek.Kotala@Sun.COM 				}
26719448SZdenek.Kotala@Sun.COM 
26726470Sjp161948 				debug("bind port %d: %.100s; skipping this port", port,
26736470Sjp161948 				    strerror(errno));
26740Sstevel@tonic-gate 				close(sock);
26750Sstevel@tonic-gate 
26760Sstevel@tonic-gate 				for (n = 0; n < num_socks; n++) {
26770Sstevel@tonic-gate 					close(socks[n]);
26780Sstevel@tonic-gate 				}
26790Sstevel@tonic-gate 				num_socks = 0;
26800Sstevel@tonic-gate 				break;
26810Sstevel@tonic-gate 			}
26820Sstevel@tonic-gate 			socks[num_socks++] = sock;
26830Sstevel@tonic-gate #ifndef DONT_TRY_OTHER_AF
26840Sstevel@tonic-gate 			if (num_socks == NUM_SOCKS)
26850Sstevel@tonic-gate 				break;
26860Sstevel@tonic-gate #else
26870Sstevel@tonic-gate 			if (x11_use_localhost) {
26880Sstevel@tonic-gate 				if (num_socks == NUM_SOCKS)
26890Sstevel@tonic-gate 					break;
26900Sstevel@tonic-gate 			} else {
26910Sstevel@tonic-gate 				break;
26920Sstevel@tonic-gate 			}
26930Sstevel@tonic-gate #endif
26940Sstevel@tonic-gate 		}
26950Sstevel@tonic-gate 		freeaddrinfo(aitop);
26960Sstevel@tonic-gate 		if (num_socks > 0)
26970Sstevel@tonic-gate 			break;
26980Sstevel@tonic-gate 	}
26990Sstevel@tonic-gate 	if (display_number >= MAX_DISPLAYS) {
27000Sstevel@tonic-gate 		error("Failed to allocate internet-domain X11 display socket.");
27010Sstevel@tonic-gate 		return -1;
27020Sstevel@tonic-gate 	}
27030Sstevel@tonic-gate 	/* Start listening for connections on the socket. */
27040Sstevel@tonic-gate 	for (n = 0; n < num_socks; n++) {
27050Sstevel@tonic-gate 		sock = socks[n];
27060Sstevel@tonic-gate 		if (listen(sock, 5) < 0) {
27079448SZdenek.Kotala@Sun.COM 			int i;
27080Sstevel@tonic-gate 			error("listen: %.100s", strerror(errno));
27099448SZdenek.Kotala@Sun.COM 			for (i = 0; i < num_socks; i++)
27109448SZdenek.Kotala@Sun.COM 				close(socks[i]);
27110Sstevel@tonic-gate 			return -1;
27120Sstevel@tonic-gate 		}
27130Sstevel@tonic-gate 	}
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 	/* Allocate a channel for each socket. */
27160Sstevel@tonic-gate 	for (n = 0; n < num_socks; n++) {
27170Sstevel@tonic-gate 		sock = socks[n];
27180Sstevel@tonic-gate 		nc = channel_new("x11 listener",
27190Sstevel@tonic-gate 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
27200Sstevel@tonic-gate 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
27210Sstevel@tonic-gate 		    0, xstrdup("X11 inet listener"), 1);
27220Sstevel@tonic-gate 		nc->single_connection = single_connection;
27230Sstevel@tonic-gate 	}
27240Sstevel@tonic-gate 
27250Sstevel@tonic-gate 	/* Return the display number for the DISPLAY environment variable. */
27260Sstevel@tonic-gate 	*display_numberp = display_number;
27270Sstevel@tonic-gate 	return (0);
27280Sstevel@tonic-gate }
27290Sstevel@tonic-gate 
27300Sstevel@tonic-gate static int
connect_local_xsocket(u_int dnr)27310Sstevel@tonic-gate connect_local_xsocket(u_int dnr)
27320Sstevel@tonic-gate {
27330Sstevel@tonic-gate 	int sock;
27340Sstevel@tonic-gate 	struct sockaddr_un addr;
27350Sstevel@tonic-gate 
27360Sstevel@tonic-gate 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
27370Sstevel@tonic-gate 	if (sock < 0)
27380Sstevel@tonic-gate 		error("socket: %.100s", strerror(errno));
27390Sstevel@tonic-gate 	memset(&addr, 0, sizeof(addr));
27400Sstevel@tonic-gate 	addr.sun_family = AF_UNIX;
27410Sstevel@tonic-gate 	snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
27420Sstevel@tonic-gate 	if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
27430Sstevel@tonic-gate 		return sock;
27440Sstevel@tonic-gate 	close(sock);
27450Sstevel@tonic-gate 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
27460Sstevel@tonic-gate 	return -1;
27470Sstevel@tonic-gate }
27480Sstevel@tonic-gate 
27490Sstevel@tonic-gate int
x11_connect_display(void)27500Sstevel@tonic-gate x11_connect_display(void)
27510Sstevel@tonic-gate {
27520Sstevel@tonic-gate 	int display_number, sock = 0;
27530Sstevel@tonic-gate 	const char *display;
27540Sstevel@tonic-gate 	char buf[1024], *cp;
27550Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
27560Sstevel@tonic-gate 	char strport[NI_MAXSERV];
27570Sstevel@tonic-gate 	int gaierr;
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate 	/* Try to open a socket for the local X server. */
27600Sstevel@tonic-gate 	display = getenv("DISPLAY");
27610Sstevel@tonic-gate 	if (!display) {
27620Sstevel@tonic-gate 		error("DISPLAY not set.");
27630Sstevel@tonic-gate 		return -1;
27640Sstevel@tonic-gate 	}
27650Sstevel@tonic-gate 	/*
27660Sstevel@tonic-gate 	 * Now we decode the value of the DISPLAY variable and make a
27670Sstevel@tonic-gate 	 * connection to the real X server.
27680Sstevel@tonic-gate 	 */
27690Sstevel@tonic-gate 
27700Sstevel@tonic-gate 	/*
27710Sstevel@tonic-gate 	 * Check if it is a unix domain socket.  Unix domain displays are in
27720Sstevel@tonic-gate 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
27730Sstevel@tonic-gate 	 */
27740Sstevel@tonic-gate 	if (strncmp(display, "unix:", 5) == 0 ||
27750Sstevel@tonic-gate 	    display[0] == ':') {
27760Sstevel@tonic-gate 		/* Connect to the unix domain socket. */
27770Sstevel@tonic-gate 		if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
27780Sstevel@tonic-gate 			error("Could not parse display number from DISPLAY: %.100s",
27790Sstevel@tonic-gate 			    display);
27800Sstevel@tonic-gate 			return -1;
27810Sstevel@tonic-gate 		}
27820Sstevel@tonic-gate 		/* Create a socket. */
27830Sstevel@tonic-gate 		sock = connect_local_xsocket(display_number);
27840Sstevel@tonic-gate 		if (sock < 0)
27850Sstevel@tonic-gate 			return -1;
27860Sstevel@tonic-gate 
27870Sstevel@tonic-gate 		/* OK, we now have a connection to the display. */
27880Sstevel@tonic-gate 		return sock;
27890Sstevel@tonic-gate 	}
27900Sstevel@tonic-gate 	/*
27910Sstevel@tonic-gate 	 * Connect to an inet socket.  The DISPLAY value is supposedly
27920Sstevel@tonic-gate 	 * hostname:d[.s], where hostname may also be numeric IP address.
27930Sstevel@tonic-gate 	 */
27940Sstevel@tonic-gate 	strlcpy(buf, display, sizeof(buf));
27950Sstevel@tonic-gate 	cp = strchr(buf, ':');
27960Sstevel@tonic-gate 	if (!cp) {
27970Sstevel@tonic-gate 		error("Could not find ':' in DISPLAY: %.100s", display);
27980Sstevel@tonic-gate 		return -1;
27990Sstevel@tonic-gate 	}
28000Sstevel@tonic-gate 	*cp = 0;
28010Sstevel@tonic-gate 	/* buf now contains the host name.  But first we parse the display number. */
28020Sstevel@tonic-gate 	if (sscanf(cp + 1, "%d", &display_number) != 1) {
28030Sstevel@tonic-gate 		error("Could not parse display number from DISPLAY: %.100s",
28040Sstevel@tonic-gate 		    display);
28050Sstevel@tonic-gate 		return -1;
28060Sstevel@tonic-gate 	}
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	/* Look up the host address */
28090Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
28100Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
28110Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
28120Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", 6000 + display_number);
28130Sstevel@tonic-gate 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
28140Sstevel@tonic-gate 		error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
28150Sstevel@tonic-gate 		return -1;
28160Sstevel@tonic-gate 	}
28170Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
28180Sstevel@tonic-gate 		/* Create a socket. */
28190Sstevel@tonic-gate 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
28200Sstevel@tonic-gate 		if (sock < 0) {
28210Sstevel@tonic-gate 			debug("socket: %.100s", strerror(errno));
28220Sstevel@tonic-gate 			continue;
28230Sstevel@tonic-gate 		}
28240Sstevel@tonic-gate 		/* Connect it to the display. */
28250Sstevel@tonic-gate 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
28260Sstevel@tonic-gate 			debug("connect %.100s port %d: %.100s", buf,
28270Sstevel@tonic-gate 			    6000 + display_number, strerror(errno));
28280Sstevel@tonic-gate 			close(sock);
28290Sstevel@tonic-gate 			continue;
28300Sstevel@tonic-gate 		}
28310Sstevel@tonic-gate 		/* Success */
28320Sstevel@tonic-gate 		break;
28330Sstevel@tonic-gate 	}
28340Sstevel@tonic-gate 	freeaddrinfo(aitop);
28350Sstevel@tonic-gate 	if (!ai) {
28360Sstevel@tonic-gate 		error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
28370Sstevel@tonic-gate 		    strerror(errno));
28380Sstevel@tonic-gate 		return -1;
28390Sstevel@tonic-gate 	}
28400Sstevel@tonic-gate 	set_nodelay(sock);
28410Sstevel@tonic-gate 	return sock;
28420Sstevel@tonic-gate }
28430Sstevel@tonic-gate 
28440Sstevel@tonic-gate /*
28450Sstevel@tonic-gate  * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
28460Sstevel@tonic-gate  * the remote channel number.  We should do whatever we want, and respond
28470Sstevel@tonic-gate  * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
28480Sstevel@tonic-gate  */
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate void
x11_input_open(int type,u_int32_t seq,void * ctxt)28510Sstevel@tonic-gate x11_input_open(int type, u_int32_t seq, void *ctxt)
28520Sstevel@tonic-gate {
28530Sstevel@tonic-gate 	Channel *c = NULL;
28540Sstevel@tonic-gate 	int remote_id, sock = 0;
28550Sstevel@tonic-gate 	char *remote_host;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 	debug("Received X11 open request.");
28580Sstevel@tonic-gate 
28590Sstevel@tonic-gate 	remote_id = packet_get_int();
28600Sstevel@tonic-gate 
28610Sstevel@tonic-gate 	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
28620Sstevel@tonic-gate 		remote_host = packet_get_string(NULL);
28630Sstevel@tonic-gate 	} else {
28640Sstevel@tonic-gate 		remote_host = xstrdup("unknown (remote did not supply name)");
28650Sstevel@tonic-gate 	}
28660Sstevel@tonic-gate 	packet_check_eom();
28670Sstevel@tonic-gate 
28680Sstevel@tonic-gate 	/* Obtain a connection to the real X display. */
28690Sstevel@tonic-gate 	sock = x11_connect_display();
28700Sstevel@tonic-gate 	if (sock != -1) {
28710Sstevel@tonic-gate 		/* Allocate a channel for this connection. */
28720Sstevel@tonic-gate 		c = channel_new("connected x11 socket",
28730Sstevel@tonic-gate 		    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
28740Sstevel@tonic-gate 		    remote_host, 1);
28750Sstevel@tonic-gate 		c->remote_id = remote_id;
28760Sstevel@tonic-gate 		c->force_drain = 1;
28770Sstevel@tonic-gate 	}
28780Sstevel@tonic-gate 	if (c == NULL) {
28790Sstevel@tonic-gate 		/* Send refusal to the remote host. */
28800Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
28810Sstevel@tonic-gate 		packet_put_int(remote_id);
28820Sstevel@tonic-gate 	} else {
28830Sstevel@tonic-gate 		/* Send a confirmation to the remote host. */
28840Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
28850Sstevel@tonic-gate 		packet_put_int(remote_id);
28860Sstevel@tonic-gate 		packet_put_int(c->self);
28870Sstevel@tonic-gate 	}
28880Sstevel@tonic-gate 	packet_send();
28890Sstevel@tonic-gate }
28900Sstevel@tonic-gate 
28910Sstevel@tonic-gate /* dummy protocol handler that denies SSH-1 requests (agent/x11) */
28920Sstevel@tonic-gate void
deny_input_open(int type,u_int32_t seq,void * ctxt)28930Sstevel@tonic-gate deny_input_open(int type, u_int32_t seq, void *ctxt)
28940Sstevel@tonic-gate {
28950Sstevel@tonic-gate 	int rchan = packet_get_int();
28960Sstevel@tonic-gate 
28970Sstevel@tonic-gate 	switch (type) {
28980Sstevel@tonic-gate 	case SSH_SMSG_AGENT_OPEN:
28990Sstevel@tonic-gate 		error("Warning: ssh server tried agent forwarding.");
29000Sstevel@tonic-gate 		break;
29010Sstevel@tonic-gate 	case SSH_SMSG_X11_OPEN:
29020Sstevel@tonic-gate 		error("Warning: ssh server tried X11 forwarding.");
29030Sstevel@tonic-gate 		break;
29040Sstevel@tonic-gate 	default:
29050Sstevel@tonic-gate 		error("deny_input_open: type %d", type);
29060Sstevel@tonic-gate 		break;
29070Sstevel@tonic-gate 	}
29080Sstevel@tonic-gate 	error("Warning: this is probably a break in attempt by a malicious server.");
29090Sstevel@tonic-gate 	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
29100Sstevel@tonic-gate 	packet_put_int(rchan);
29110Sstevel@tonic-gate 	packet_send();
29120Sstevel@tonic-gate }
29130Sstevel@tonic-gate 
29140Sstevel@tonic-gate /*
29150Sstevel@tonic-gate  * Requests forwarding of X11 connections, generates fake authentication
29160Sstevel@tonic-gate  * data, and enables authentication spoofing.
29170Sstevel@tonic-gate  * This should be called in the client only.
29180Sstevel@tonic-gate  */
29190Sstevel@tonic-gate void
x11_request_forwarding_with_spoofing(int client_session_id,const char * disp,const char * proto,const char * data)29204907Sjp161948 x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
29210Sstevel@tonic-gate     const char *proto, const char *data)
29220Sstevel@tonic-gate {
29230Sstevel@tonic-gate 	u_int data_len = (u_int) strlen(data) / 2;
29244907Sjp161948 	u_int i, value;
29250Sstevel@tonic-gate 	char *new_data;
29260Sstevel@tonic-gate 	int screen_number;
29270Sstevel@tonic-gate 	const char *cp;
29280Sstevel@tonic-gate 	u_int32_t rand = 0;
29290Sstevel@tonic-gate 
29304907Sjp161948 	cp = disp;
29314907Sjp161948 	if (disp)
29324907Sjp161948 		cp = strchr(disp, ':');
29330Sstevel@tonic-gate 	if (cp)
29340Sstevel@tonic-gate 		cp = strchr(cp, '.');
29350Sstevel@tonic-gate 	if (cp)
29360Sstevel@tonic-gate 		screen_number = atoi(cp + 1);
29370Sstevel@tonic-gate 	else
29380Sstevel@tonic-gate 		screen_number = 0;
29390Sstevel@tonic-gate 
29400Sstevel@tonic-gate 	/* Save protocol name. */
29410Sstevel@tonic-gate 	x11_saved_proto = xstrdup(proto);
29420Sstevel@tonic-gate 
29430Sstevel@tonic-gate 	/*
29440Sstevel@tonic-gate 	 * Extract real authentication data and generate fake data of the
29450Sstevel@tonic-gate 	 * same length.
29460Sstevel@tonic-gate 	 */
29470Sstevel@tonic-gate 	x11_saved_data = xmalloc(data_len);
29480Sstevel@tonic-gate 	x11_fake_data = xmalloc(data_len);
29490Sstevel@tonic-gate 	for (i = 0; i < data_len; i++) {
29500Sstevel@tonic-gate 		if (sscanf(data + 2 * i, "%2x", &value) != 1)
29510Sstevel@tonic-gate 			fatal("x11_request_forwarding: bad authentication data: %.100s", data);
29520Sstevel@tonic-gate 		if (i % 4 == 0)
29530Sstevel@tonic-gate 			rand = arc4random();
29540Sstevel@tonic-gate 		x11_saved_data[i] = value;
29550Sstevel@tonic-gate 		x11_fake_data[i] = rand & 0xff;
29560Sstevel@tonic-gate 		rand >>= 8;
29570Sstevel@tonic-gate 	}
29580Sstevel@tonic-gate 	x11_saved_data_len = data_len;
29590Sstevel@tonic-gate 	x11_fake_data_len = data_len;
29600Sstevel@tonic-gate 
29610Sstevel@tonic-gate 	/* Convert the fake data into hex. */
29624907Sjp161948 	new_data = tohex(x11_fake_data, data_len);
29630Sstevel@tonic-gate 
29640Sstevel@tonic-gate 	/* Send the request packet. */
29650Sstevel@tonic-gate 	if (compat20) {
29660Sstevel@tonic-gate 		channel_request_start(client_session_id, "x11-req", 0);
29670Sstevel@tonic-gate 		packet_put_char(0);	/* XXX bool single connection */
29680Sstevel@tonic-gate 	} else {
29690Sstevel@tonic-gate 		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
29700Sstevel@tonic-gate 	}
29710Sstevel@tonic-gate 	packet_put_cstring(proto);
29720Sstevel@tonic-gate 	packet_put_cstring(new_data);
29730Sstevel@tonic-gate 	packet_put_int(screen_number);
29740Sstevel@tonic-gate 	packet_send();
29750Sstevel@tonic-gate 	packet_write_wait();
29760Sstevel@tonic-gate 	xfree(new_data);
29770Sstevel@tonic-gate }
29780Sstevel@tonic-gate 
29790Sstevel@tonic-gate 
29800Sstevel@tonic-gate /* -- agent forwarding */
29810Sstevel@tonic-gate 
29820Sstevel@tonic-gate /* Sends a message to the server to request authentication fd forwarding. */
29830Sstevel@tonic-gate 
29840Sstevel@tonic-gate void
auth_request_forwarding(void)29850Sstevel@tonic-gate auth_request_forwarding(void)
29860Sstevel@tonic-gate {
29870Sstevel@tonic-gate 	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
29880Sstevel@tonic-gate 	packet_send();
29890Sstevel@tonic-gate 	packet_write_wait();
29900Sstevel@tonic-gate }
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
29930Sstevel@tonic-gate 
29940Sstevel@tonic-gate void
auth_input_open_request(int type,u_int32_t seq,void * ctxt)29950Sstevel@tonic-gate auth_input_open_request(int type, u_int32_t seq, void *ctxt)
29960Sstevel@tonic-gate {
29970Sstevel@tonic-gate 	Channel *c = NULL;
29980Sstevel@tonic-gate 	int remote_id, sock;
29990Sstevel@tonic-gate 	char *name;
30000Sstevel@tonic-gate 
30010Sstevel@tonic-gate 	/* Read the remote channel number from the message. */
30020Sstevel@tonic-gate 	remote_id = packet_get_int();
30030Sstevel@tonic-gate 	packet_check_eom();
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate 	/*
30060Sstevel@tonic-gate 	 * Get a connection to the local authentication agent (this may again
30070Sstevel@tonic-gate 	 * get forwarded).
30080Sstevel@tonic-gate 	 */
30090Sstevel@tonic-gate 	sock = ssh_get_authentication_socket();
30100Sstevel@tonic-gate 
30110Sstevel@tonic-gate 	/*
30120Sstevel@tonic-gate 	 * If we could not connect the agent, send an error message back to
30130Sstevel@tonic-gate 	 * the server. This should never happen unless the agent dies,
30140Sstevel@tonic-gate 	 * because authentication forwarding is only enabled if we have an
30150Sstevel@tonic-gate 	 * agent.
30160Sstevel@tonic-gate 	 */
30170Sstevel@tonic-gate 	if (sock >= 0) {
30180Sstevel@tonic-gate 		name = xstrdup("authentication agent connection");
30190Sstevel@tonic-gate 		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
30200Sstevel@tonic-gate 		    -1, 0, 0, 0, name, 1);
30210Sstevel@tonic-gate 		c->remote_id = remote_id;
30220Sstevel@tonic-gate 		c->force_drain = 1;
30230Sstevel@tonic-gate 	}
30240Sstevel@tonic-gate 	if (c == NULL) {
30250Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
30260Sstevel@tonic-gate 		packet_put_int(remote_id);
30270Sstevel@tonic-gate 	} else {
30280Sstevel@tonic-gate 		/* Send a confirmation to the remote host. */
30290Sstevel@tonic-gate 		debug("Forwarding authentication connection.");
30300Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
30310Sstevel@tonic-gate 		packet_put_int(remote_id);
30320Sstevel@tonic-gate 		packet_put_int(c->self);
30330Sstevel@tonic-gate 	}
30340Sstevel@tonic-gate 	packet_send();
30350Sstevel@tonic-gate }
3036