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