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