channels.c revision 120489
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" 42113911SdesRCSID("$OpenBSD: channels.c,v 1.187 2003/03/05 22:33:43 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" 5765668Skris 5857429Smarkm 5992559Sdes/* -- channel core */ 6057429Smarkm 6157429Smarkm/* 6257429Smarkm * Pointer to an array containing all allocated channels. The array is 6357429Smarkm * dynamically extended as needed. 6457429Smarkm */ 6592559Sdesstatic Channel **channels = NULL; 6657429Smarkm 6757429Smarkm/* 6857429Smarkm * Size of the channel array. All slots of the array must always be 6992559Sdes * initialized (at least the type field); unused slots set to NULL 7057429Smarkm */ 7157429Smarkmstatic int channels_alloc = 0; 7257429Smarkm 7357429Smarkm/* 7457429Smarkm * Maximum file descriptor value used in any of the channels. This is 7592559Sdes * updated in channel_new. 7657429Smarkm */ 7776262Sgreenstatic int channel_max_fd = 0; 7857429Smarkm 7957429Smarkm 8092559Sdes/* -- tcp forwarding */ 8157429Smarkm 8257429Smarkm/* 8357429Smarkm * Data structure for storing which hosts are permitted for forward requests. 8457429Smarkm * The local sides of any remote forwards are stored in this array to prevent 8557429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 8657429Smarkm * network (which might be behind a firewall). 8757429Smarkm */ 8857429Smarkmtypedef struct { 8960573Skris char *host_to_connect; /* Connect to 'host'. */ 9060573Skris u_short port_to_connect; /* Connect to 'port'. */ 9160573Skris u_short listen_port; /* Remote side should listen port number. */ 9257429Smarkm} ForwardPermission; 9357429Smarkm 9457429Smarkm/* List of all permitted host/port pairs to connect. */ 9557429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 9692559Sdes 9757429Smarkm/* Number of permitted host/port pairs in the array. */ 9857429Smarkmstatic int num_permitted_opens = 0; 9957429Smarkm/* 10057429Smarkm * If this is true, all opens are permitted. This is the case on the server 10157429Smarkm * on which we have to trust the client anyway, and the user could do 10257429Smarkm * anything after logging in anyway. 10357429Smarkm */ 10457429Smarkmstatic int all_opens_permitted = 0; 10557429Smarkm 10657429Smarkm 10792559Sdes/* -- X11 forwarding */ 10892559Sdes 10992559Sdes/* Maximum number of fake X11 displays to try. */ 11092559Sdes#define MAX_DISPLAYS 1000 11192559Sdes 11292559Sdes/* Saved X11 authentication protocol name. */ 11392559Sdesstatic char *x11_saved_proto = NULL; 11492559Sdes 11592559Sdes/* Saved X11 authentication data. This is the real data. */ 11692559Sdesstatic char *x11_saved_data = NULL; 11792559Sdesstatic u_int x11_saved_data_len = 0; 11892559Sdes 11992559Sdes/* 12092559Sdes * Fake X11 authentication data. This is what the server will be sending us; 12192559Sdes * we should replace any occurrences of this by the real data. 12292559Sdes */ 12392559Sdesstatic char *x11_fake_data = NULL; 12492559Sdesstatic u_int x11_fake_data_len; 12592559Sdes 12692559Sdes 12792559Sdes/* -- agent forwarding */ 12892559Sdes 12992559Sdes#define NUM_SOCKS 10 13092559Sdes 13176262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */ 13298941Sdesstatic int IPv4or6 = AF_UNSPEC; 13376262Sgreen 13492559Sdes/* helper */ 13592559Sdesstatic void port_open_helper(Channel *c, char *rtype); 13676262Sgreen 13792559Sdes/* -- channel core */ 13857429Smarkm 13960573SkrisChannel * 14060573Skrischannel_lookup(int id) 14160573Skris{ 14260573Skris Channel *c; 14392559Sdes 14491688Snectar if (id < 0 || id >= channels_alloc) { 14560573Skris log("channel_lookup: %d: bad id", id); 14660573Skris return NULL; 14760573Skris } 14892559Sdes c = channels[id]; 14992559Sdes if (c == NULL) { 15060573Skris log("channel_lookup: %d: bad id: channel free", id); 15160573Skris return NULL; 15260573Skris } 15360573Skris return c; 15460573Skris} 15560573Skris 15657429Smarkm/* 15760573Skris * Register filedescriptors for a channel, used when allocating a channel or 15860573Skris * when the channel consumer/producer is ready, e.g. shell exec'd 15960573Skris */ 16060573Skris 16192559Sdesstatic void 16269587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd, 16369587Sgreen int extusage, int nonblock) 16460573Skris{ 16560573Skris /* Update the maximum file descriptor value. */ 16676262Sgreen channel_max_fd = MAX(channel_max_fd, rfd); 16776262Sgreen channel_max_fd = MAX(channel_max_fd, wfd); 16876262Sgreen channel_max_fd = MAX(channel_max_fd, efd); 16976262Sgreen 17060573Skris /* XXX set close-on-exec -markus */ 17160573Skris 17260573Skris c->rfd = rfd; 17360573Skris c->wfd = wfd; 17460573Skris c->sock = (rfd == wfd) ? rfd : -1; 17560573Skris c->efd = efd; 17660573Skris c->extended_usage = extusage; 17769587Sgreen 17874500Sgreen /* XXX ugly hack: nonblock is only set by the server */ 17974500Sgreen if (nonblock && isatty(c->rfd)) { 18076262Sgreen debug("channel %d: rfd %d isatty", c->self, c->rfd); 18174500Sgreen c->isatty = 1; 18274500Sgreen if (!isatty(c->wfd)) { 18376262Sgreen error("channel %d: wfd %d is not a tty?", 18474500Sgreen c->self, c->wfd); 18574500Sgreen } 18674500Sgreen } else { 18774500Sgreen c->isatty = 0; 18874500Sgreen } 189106130Sdes c->wfd_isatty = isatty(c->wfd); 19074500Sgreen 19169587Sgreen /* enable nonblocking mode */ 19269587Sgreen if (nonblock) { 19369587Sgreen if (rfd != -1) 19469587Sgreen set_nonblock(rfd); 19569587Sgreen if (wfd != -1) 19669587Sgreen set_nonblock(wfd); 19769587Sgreen if (efd != -1) 19869587Sgreen set_nonblock(efd); 19969587Sgreen } 20060573Skris} 20160573Skris 20260573Skris/* 20357429Smarkm * Allocate a new channel object and set its type and socket. This will cause 20457429Smarkm * remote_name to be freed. 20557429Smarkm */ 20657429Smarkm 20792559SdesChannel * 20860573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd, 20999063Sdes u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 21057429Smarkm{ 21157429Smarkm int i, found; 21257429Smarkm Channel *c; 21357429Smarkm 21457429Smarkm /* Do initial allocation if this is the first call. */ 21557429Smarkm if (channels_alloc == 0) { 21657429Smarkm channels_alloc = 10; 21792559Sdes channels = xmalloc(channels_alloc * sizeof(Channel *)); 21857429Smarkm for (i = 0; i < channels_alloc; i++) 21992559Sdes channels[i] = NULL; 22092559Sdes fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); 22157429Smarkm } 22257429Smarkm /* Try to find a free slot where to put the new channel. */ 22357429Smarkm for (found = -1, i = 0; i < channels_alloc; i++) 22492559Sdes if (channels[i] == NULL) { 22557429Smarkm /* Found a free slot. */ 22657429Smarkm found = i; 22757429Smarkm break; 22857429Smarkm } 22957429Smarkm if (found == -1) { 23057429Smarkm /* There are no free slots. Take last+1 slot and expand the array. */ 23157429Smarkm found = channels_alloc; 23299063Sdes if (channels_alloc > 10000) 23399063Sdes fatal("channel_new: internal error: channels_alloc %d " 23499063Sdes "too big.", channels_alloc); 235120489Sjoe channels = xrealloc(channels, 236120489Sjoe (channels_alloc + 10) * sizeof(Channel *)); 237120489Sjoe channels_alloc += 10; 23869587Sgreen debug2("channel: expanding %d", channels_alloc); 23957429Smarkm for (i = found; i < channels_alloc; i++) 24092559Sdes channels[i] = NULL; 24157429Smarkm } 24292559Sdes /* Initialize and return new channel. */ 24392559Sdes c = channels[found] = xmalloc(sizeof(Channel)); 24492559Sdes memset(c, 0, sizeof(Channel)); 24557429Smarkm buffer_init(&c->input); 24657429Smarkm buffer_init(&c->output); 24760573Skris buffer_init(&c->extended); 24892559Sdes c->ostate = CHAN_OUTPUT_OPEN; 24992559Sdes c->istate = CHAN_INPUT_OPEN; 25092559Sdes c->flags = 0; 25169587Sgreen channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 25257429Smarkm c->self = found; 25357429Smarkm c->type = type; 25460573Skris c->ctype = ctype; 25560573Skris c->local_window = window; 25660573Skris c->local_window_max = window; 25760573Skris c->local_consumed = 0; 25860573Skris c->local_maxpacket = maxpack; 25957429Smarkm c->remote_id = -1; 26057429Smarkm c->remote_name = remote_name; 26160573Skris c->remote_window = 0; 26260573Skris c->remote_maxpacket = 0; 26392559Sdes c->force_drain = 0; 26492559Sdes c->single_connection = 0; 26592559Sdes c->detach_user = NULL; 26692559Sdes c->confirm = NULL; 26765668Skris c->input_filter = NULL; 26857429Smarkm debug("channel %d: new [%s]", found, remote_name); 26992559Sdes return c; 27057429Smarkm} 27192559Sdes 27292559Sdesstatic int 27392559Sdeschannel_find_maxfd(void) 27492559Sdes{ 27592559Sdes int i, max = 0; 27692559Sdes Channel *c; 27792559Sdes 27892559Sdes for (i = 0; i < channels_alloc; i++) { 27992559Sdes c = channels[i]; 28092559Sdes if (c != NULL) { 28192559Sdes max = MAX(max, c->rfd); 28292559Sdes max = MAX(max, c->wfd); 28392559Sdes max = MAX(max, c->efd); 28492559Sdes } 28592559Sdes } 28692559Sdes return max; 28792559Sdes} 28892559Sdes 28960573Skrisint 29092559Sdeschannel_close_fd(int *fdp) 29160573Skris{ 29292559Sdes int ret = 0, fd = *fdp; 29392559Sdes 29492559Sdes if (fd != -1) { 29592559Sdes ret = close(fd); 29692559Sdes *fdp = -1; 29792559Sdes if (fd == channel_max_fd) 29892559Sdes channel_max_fd = channel_find_maxfd(); 29992559Sdes } 30092559Sdes return ret; 30160573Skris} 30257429Smarkm 30360573Skris/* Close all channel fd/socket. */ 30460573Skris 30592559Sdesstatic void 30660573Skrischannel_close_fds(Channel *c) 30757429Smarkm{ 30892559Sdes debug3("channel_close_fds: channel %d: r %d w %d e %d", 30992559Sdes c->self, c->rfd, c->wfd, c->efd); 31092559Sdes 31192559Sdes channel_close_fd(&c->sock); 31292559Sdes channel_close_fd(&c->rfd); 31392559Sdes channel_close_fd(&c->wfd); 31492559Sdes channel_close_fd(&c->efd); 31560573Skris} 31657429Smarkm 31760573Skris/* Free the channel and close its fd/socket. */ 31860573Skris 31960573Skrisvoid 32092559Sdeschannel_free(Channel *c) 32160573Skris{ 32292559Sdes char *s; 32392559Sdes int i, n; 32476262Sgreen 32592559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 32692559Sdes if (channels[i]) 32792559Sdes n++; 32892559Sdes debug("channel_free: channel %d: %s, nchannels %d", c->self, 32992559Sdes c->remote_name ? c->remote_name : "???", n); 33092559Sdes 33192559Sdes s = channel_open_message(); 33292559Sdes debug3("channel_free: status: %s", s); 33376262Sgreen xfree(s); 33476262Sgreen 33560573Skris if (c->sock != -1) 33660573Skris shutdown(c->sock, SHUT_RDWR); 33760573Skris channel_close_fds(c); 33860573Skris buffer_free(&c->input); 33960573Skris buffer_free(&c->output); 34060573Skris buffer_free(&c->extended); 34160573Skris if (c->remote_name) { 34260573Skris xfree(c->remote_name); 34360573Skris c->remote_name = NULL; 34460573Skris } 34592559Sdes channels[c->self] = NULL; 34692559Sdes xfree(c); 34757429Smarkm} 34857429Smarkm 34992559Sdesvoid 35092559Sdeschannel_free_all(void) 35192559Sdes{ 35292559Sdes int i; 35392559Sdes 35492559Sdes for (i = 0; i < channels_alloc; i++) 35592559Sdes if (channels[i] != NULL) 35692559Sdes channel_free(channels[i]); 35792559Sdes} 35892559Sdes 35957429Smarkm/* 36092559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 36192559Sdes * descriptors after a fork. 36292559Sdes */ 36392559Sdes 36492559Sdesvoid 36592559Sdeschannel_close_all(void) 36692559Sdes{ 36792559Sdes int i; 36892559Sdes 36992559Sdes for (i = 0; i < channels_alloc; i++) 37092559Sdes if (channels[i] != NULL) 37192559Sdes channel_close_fds(channels[i]); 37292559Sdes} 37392559Sdes 37492559Sdes/* 37592559Sdes * Stop listening to channels. 37692559Sdes */ 37792559Sdes 37892559Sdesvoid 37992559Sdeschannel_stop_listening(void) 38092559Sdes{ 38192559Sdes int i; 38292559Sdes Channel *c; 38392559Sdes 38492559Sdes for (i = 0; i < channels_alloc; i++) { 38592559Sdes c = channels[i]; 38692559Sdes if (c != NULL) { 38792559Sdes switch (c->type) { 38892559Sdes case SSH_CHANNEL_AUTH_SOCKET: 38992559Sdes case SSH_CHANNEL_PORT_LISTENER: 39092559Sdes case SSH_CHANNEL_RPORT_LISTENER: 39192559Sdes case SSH_CHANNEL_X11_LISTENER: 39292559Sdes channel_close_fd(&c->sock); 39392559Sdes channel_free(c); 39492559Sdes break; 39592559Sdes } 39692559Sdes } 39792559Sdes } 39892559Sdes} 39992559Sdes 40092559Sdes/* 40192559Sdes * Returns true if no channel has too much buffered data, and false if one or 40292559Sdes * more channel is overfull. 40392559Sdes */ 40492559Sdes 40592559Sdesint 40692559Sdeschannel_not_very_much_buffered_data(void) 40792559Sdes{ 40892559Sdes u_int i; 40992559Sdes Channel *c; 41092559Sdes 41192559Sdes for (i = 0; i < channels_alloc; i++) { 41292559Sdes c = channels[i]; 41392559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 41492559Sdes#if 0 41592559Sdes if (!compat20 && 41692559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 417113911Sdes debug2("channel %d: big input buffer %d", 41892559Sdes c->self, buffer_len(&c->input)); 41992559Sdes return 0; 42092559Sdes } 42192559Sdes#endif 42292559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 423113911Sdes debug2("channel %d: big output buffer %d > %d", 42492559Sdes c->self, buffer_len(&c->output), 42592559Sdes packet_get_maxsize()); 42692559Sdes return 0; 42792559Sdes } 42892559Sdes } 42992559Sdes } 43092559Sdes return 1; 43192559Sdes} 43292559Sdes 43392559Sdes/* Returns true if any channel is still open. */ 43492559Sdes 43592559Sdesint 43692559Sdeschannel_still_open(void) 43792559Sdes{ 43892559Sdes int i; 43992559Sdes Channel *c; 44092559Sdes 44192559Sdes for (i = 0; i < channels_alloc; i++) { 44292559Sdes c = channels[i]; 44392559Sdes if (c == NULL) 44492559Sdes continue; 44592559Sdes switch (c->type) { 44692559Sdes case SSH_CHANNEL_X11_LISTENER: 44792559Sdes case SSH_CHANNEL_PORT_LISTENER: 44892559Sdes case SSH_CHANNEL_RPORT_LISTENER: 44992559Sdes case SSH_CHANNEL_CLOSED: 45092559Sdes case SSH_CHANNEL_AUTH_SOCKET: 45192559Sdes case SSH_CHANNEL_DYNAMIC: 45292559Sdes case SSH_CHANNEL_CONNECTING: 45392559Sdes case SSH_CHANNEL_ZOMBIE: 45492559Sdes continue; 45592559Sdes case SSH_CHANNEL_LARVAL: 45692559Sdes if (!compat20) 45792559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 45892559Sdes continue; 45992559Sdes case SSH_CHANNEL_OPENING: 46092559Sdes case SSH_CHANNEL_OPEN: 46192559Sdes case SSH_CHANNEL_X11_OPEN: 46292559Sdes return 1; 46392559Sdes case SSH_CHANNEL_INPUT_DRAINING: 46492559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 46592559Sdes if (!compat13) 46692559Sdes fatal("cannot happen: OUT_DRAIN"); 46792559Sdes return 1; 46892559Sdes default: 46992559Sdes fatal("channel_still_open: bad channel type %d", c->type); 47092559Sdes /* NOTREACHED */ 47192559Sdes } 47292559Sdes } 47392559Sdes return 0; 47492559Sdes} 47592559Sdes 47692559Sdes/* Returns the id of an open channel suitable for keepaliving */ 47792559Sdes 47892559Sdesint 47992559Sdeschannel_find_open(void) 48092559Sdes{ 48192559Sdes int i; 48292559Sdes Channel *c; 48392559Sdes 48492559Sdes for (i = 0; i < channels_alloc; i++) { 48592559Sdes c = channels[i]; 48692559Sdes if (c == NULL) 48792559Sdes continue; 48892559Sdes switch (c->type) { 48992559Sdes case SSH_CHANNEL_CLOSED: 49092559Sdes case SSH_CHANNEL_DYNAMIC: 49192559Sdes case SSH_CHANNEL_X11_LISTENER: 49292559Sdes case SSH_CHANNEL_PORT_LISTENER: 49392559Sdes case SSH_CHANNEL_RPORT_LISTENER: 49492559Sdes case SSH_CHANNEL_OPENING: 49592559Sdes case SSH_CHANNEL_CONNECTING: 49692559Sdes case SSH_CHANNEL_ZOMBIE: 49792559Sdes continue; 49892559Sdes case SSH_CHANNEL_LARVAL: 49992559Sdes case SSH_CHANNEL_AUTH_SOCKET: 50092559Sdes case SSH_CHANNEL_OPEN: 50192559Sdes case SSH_CHANNEL_X11_OPEN: 50292559Sdes return i; 50392559Sdes case SSH_CHANNEL_INPUT_DRAINING: 50492559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 50592559Sdes if (!compat13) 50692559Sdes fatal("cannot happen: OUT_DRAIN"); 50792559Sdes return i; 50892559Sdes default: 50992559Sdes fatal("channel_find_open: bad channel type %d", c->type); 51092559Sdes /* NOTREACHED */ 51192559Sdes } 51292559Sdes } 51392559Sdes return -1; 51492559Sdes} 51592559Sdes 51692559Sdes 51792559Sdes/* 51892559Sdes * Returns a message describing the currently open forwarded connections, 51992559Sdes * suitable for sending to the client. The message contains crlf pairs for 52092559Sdes * newlines. 52192559Sdes */ 52292559Sdes 52392559Sdeschar * 52492559Sdeschannel_open_message(void) 52592559Sdes{ 52692559Sdes Buffer buffer; 52792559Sdes Channel *c; 52892559Sdes char buf[1024], *cp; 52992559Sdes int i; 53092559Sdes 53192559Sdes buffer_init(&buffer); 53292559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 53392559Sdes buffer_append(&buffer, buf, strlen(buf)); 53492559Sdes for (i = 0; i < channels_alloc; i++) { 53592559Sdes c = channels[i]; 53692559Sdes if (c == NULL) 53792559Sdes continue; 53892559Sdes switch (c->type) { 53992559Sdes case SSH_CHANNEL_X11_LISTENER: 54092559Sdes case SSH_CHANNEL_PORT_LISTENER: 54192559Sdes case SSH_CHANNEL_RPORT_LISTENER: 54292559Sdes case SSH_CHANNEL_CLOSED: 54392559Sdes case SSH_CHANNEL_AUTH_SOCKET: 54492559Sdes case SSH_CHANNEL_ZOMBIE: 54592559Sdes continue; 54692559Sdes case SSH_CHANNEL_LARVAL: 54792559Sdes case SSH_CHANNEL_OPENING: 54892559Sdes case SSH_CHANNEL_CONNECTING: 54992559Sdes case SSH_CHANNEL_DYNAMIC: 55092559Sdes case SSH_CHANNEL_OPEN: 55192559Sdes case SSH_CHANNEL_X11_OPEN: 55292559Sdes case SSH_CHANNEL_INPUT_DRAINING: 55392559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 55492559Sdes snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 55592559Sdes c->self, c->remote_name, 55692559Sdes c->type, c->remote_id, 55792559Sdes c->istate, buffer_len(&c->input), 55892559Sdes c->ostate, buffer_len(&c->output), 55992559Sdes c->rfd, c->wfd); 56092559Sdes buffer_append(&buffer, buf, strlen(buf)); 56192559Sdes continue; 56292559Sdes default: 56392559Sdes fatal("channel_open_message: bad channel type %d", c->type); 56492559Sdes /* NOTREACHED */ 56592559Sdes } 56692559Sdes } 56792559Sdes buffer_append(&buffer, "\0", 1); 56892559Sdes cp = xstrdup(buffer_ptr(&buffer)); 56992559Sdes buffer_free(&buffer); 57092559Sdes return cp; 57192559Sdes} 57292559Sdes 57392559Sdesvoid 57492559Sdeschannel_send_open(int id) 57592559Sdes{ 57692559Sdes Channel *c = channel_lookup(id); 577106130Sdes 57892559Sdes if (c == NULL) { 57992559Sdes log("channel_send_open: %d: bad id", id); 58092559Sdes return; 58192559Sdes } 582113911Sdes debug2("channel %d: send open", id); 58392559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 58492559Sdes packet_put_cstring(c->ctype); 58592559Sdes packet_put_int(c->self); 58692559Sdes packet_put_int(c->local_window); 58792559Sdes packet_put_int(c->local_maxpacket); 58892559Sdes packet_send(); 58992559Sdes} 59092559Sdes 59192559Sdesvoid 592113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 59392559Sdes{ 594113911Sdes Channel *c = channel_lookup(id); 595106130Sdes 59692559Sdes if (c == NULL) { 597113911Sdes log("channel_request_start: %d: unknown channel id", id); 59892559Sdes return; 59992559Sdes } 600113911Sdes debug("channel %d: request %s", id, service) ; 60192559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 60292559Sdes packet_put_int(c->remote_id); 60392559Sdes packet_put_cstring(service); 60492559Sdes packet_put_char(wantconfirm); 60592559Sdes} 60692559Sdesvoid 60792559Sdeschannel_register_confirm(int id, channel_callback_fn *fn) 60892559Sdes{ 60992559Sdes Channel *c = channel_lookup(id); 610106130Sdes 61192559Sdes if (c == NULL) { 61292559Sdes log("channel_register_comfirm: %d: bad id", id); 61392559Sdes return; 61492559Sdes } 61592559Sdes c->confirm = fn; 61692559Sdes} 61792559Sdesvoid 61892559Sdeschannel_register_cleanup(int id, channel_callback_fn *fn) 61992559Sdes{ 62092559Sdes Channel *c = channel_lookup(id); 621106130Sdes 62292559Sdes if (c == NULL) { 62392559Sdes log("channel_register_cleanup: %d: bad id", id); 62492559Sdes return; 62592559Sdes } 62692559Sdes c->detach_user = fn; 62792559Sdes} 62892559Sdesvoid 62992559Sdeschannel_cancel_cleanup(int id) 63092559Sdes{ 63192559Sdes Channel *c = channel_lookup(id); 632106130Sdes 63392559Sdes if (c == NULL) { 63492559Sdes log("channel_cancel_cleanup: %d: bad id", id); 63592559Sdes return; 63692559Sdes } 63792559Sdes c->detach_user = NULL; 63892559Sdes} 63992559Sdesvoid 64092559Sdeschannel_register_filter(int id, channel_filter_fn *fn) 64192559Sdes{ 64292559Sdes Channel *c = channel_lookup(id); 643106130Sdes 64492559Sdes if (c == NULL) { 64592559Sdes log("channel_register_filter: %d: bad id", id); 64692559Sdes return; 64792559Sdes } 64892559Sdes c->input_filter = fn; 64992559Sdes} 65092559Sdes 65192559Sdesvoid 65292559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 65392559Sdes int extusage, int nonblock, u_int window_max) 65492559Sdes{ 65592559Sdes Channel *c = channel_lookup(id); 656106130Sdes 65792559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 65892559Sdes fatal("channel_activate for non-larval channel %d.", id); 65992559Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 66092559Sdes c->type = SSH_CHANNEL_OPEN; 66192559Sdes c->local_window = c->local_window_max = window_max; 66292559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 66392559Sdes packet_put_int(c->remote_id); 66492559Sdes packet_put_int(c->local_window); 66592559Sdes packet_send(); 66692559Sdes} 66792559Sdes 66892559Sdes/* 66960573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 67060573Skris * channels in the select bitmasks. 67157429Smarkm */ 67260573Skris/* 67360573Skris * 'channel_post*': perform any appropriate operations for channels which 67460573Skris * have events pending. 67560573Skris */ 67660573Skristypedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 67760573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 67860573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 67957429Smarkm 68092559Sdesstatic void 68160573Skrischannel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 68257429Smarkm{ 68360573Skris FD_SET(c->sock, readset); 68460573Skris} 68560573Skris 68692559Sdesstatic void 68776262Sgreenchannel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) 68876262Sgreen{ 68976262Sgreen debug3("channel %d: waiting for connection", c->self); 69076262Sgreen FD_SET(c->sock, writeset); 69176262Sgreen} 69276262Sgreen 69392559Sdesstatic void 69460573Skrischannel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 69560573Skris{ 69660573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 69760573Skris FD_SET(c->sock, readset); 69860573Skris if (buffer_len(&c->output) > 0) 69960573Skris FD_SET(c->sock, writeset); 70060573Skris} 70160573Skris 70292559Sdesstatic void 70392559Sdeschannel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) 70460573Skris{ 70592559Sdes u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 70660573Skris 70760573Skris if (c->istate == CHAN_INPUT_OPEN && 70892559Sdes limit > 0 && 70992559Sdes buffer_len(&c->input) < limit) 71060573Skris FD_SET(c->rfd, readset); 71160573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 71260573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 71360573Skris if (buffer_len(&c->output) > 0) { 71460573Skris FD_SET(c->wfd, writeset); 71560573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 71698684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 71798684Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 71898684Sdes c->self, c->efd, buffer_len(&c->extended)); 71998684Sdes else 72098684Sdes chan_obuf_empty(c); 72160573Skris } 72260573Skris } 72360573Skris /** XXX check close conditions, too */ 72492559Sdes if (compat20 && c->efd != -1) { 72560573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 72660573Skris buffer_len(&c->extended) > 0) 72760573Skris FD_SET(c->efd, writeset); 72898684Sdes else if (!(c->flags & CHAN_EOF_SENT) && 72998684Sdes c->extended_usage == CHAN_EXTENDED_READ && 73060573Skris buffer_len(&c->extended) < c->remote_window) 73160573Skris FD_SET(c->efd, readset); 73260573Skris } 73360573Skris} 73460573Skris 73592559Sdesstatic void 73660573Skrischannel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 73760573Skris{ 73860573Skris if (buffer_len(&c->input) == 0) { 73960573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 74060573Skris packet_put_int(c->remote_id); 74160573Skris packet_send(); 74260573Skris c->type = SSH_CHANNEL_CLOSED; 74376262Sgreen debug("channel %d: closing after input drain.", c->self); 74460573Skris } 74560573Skris} 74660573Skris 74792559Sdesstatic void 74860573Skrischannel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 74960573Skris{ 75060573Skris if (buffer_len(&c->output) == 0) 75192559Sdes chan_mark_dead(c); 75260573Skris else 75360573Skris FD_SET(c->sock, writeset); 75460573Skris} 75560573Skris 75660573Skris/* 75760573Skris * This is a special state for X11 authentication spoofing. An opened X11 75860573Skris * connection (when authentication spoofing is being done) remains in this 75960573Skris * state until the first packet has been completely read. The authentication 76060573Skris * data in that packet is then substituted by the real data if it matches the 76160573Skris * fake data, and the channel is put into normal mode. 76260573Skris * XXX All this happens at the client side. 76392559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 76460573Skris */ 76592559Sdesstatic int 76692559Sdesx11_open_helper(Buffer *b) 76760573Skris{ 76876262Sgreen u_char *ucp; 76976262Sgreen u_int proto_len, data_len; 77057429Smarkm 77160573Skris /* Check if the fixed size part of the packet is in buffer. */ 77292559Sdes if (buffer_len(b) < 12) 77360573Skris return 0; 77457429Smarkm 77560573Skris /* Parse the lengths of variable-length fields. */ 77692559Sdes ucp = buffer_ptr(b); 77760573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 77860573Skris proto_len = 256 * ucp[6] + ucp[7]; 77960573Skris data_len = 256 * ucp[8] + ucp[9]; 78060573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 78160573Skris proto_len = ucp[6] + 256 * ucp[7]; 78260573Skris data_len = ucp[8] + 256 * ucp[9]; 78360573Skris } else { 78460573Skris debug("Initial X11 packet contains bad byte order byte: 0x%x", 78592559Sdes ucp[0]); 78660573Skris return -1; 78760573Skris } 78857429Smarkm 78960573Skris /* Check if the whole packet is in buffer. */ 79092559Sdes if (buffer_len(b) < 79160573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 79260573Skris return 0; 79357429Smarkm 79460573Skris /* Check if authentication protocol matches. */ 79560573Skris if (proto_len != strlen(x11_saved_proto) || 79660573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 79760573Skris debug("X11 connection uses different authentication protocol."); 79860573Skris return -1; 79960573Skris } 80060573Skris /* Check if authentication data matches our fake data. */ 80160573Skris if (data_len != x11_fake_data_len || 80260573Skris memcmp(ucp + 12 + ((proto_len + 3) & ~3), 80360573Skris x11_fake_data, x11_fake_data_len) != 0) { 80460573Skris debug("X11 auth data does not match fake data."); 80560573Skris return -1; 80660573Skris } 80760573Skris /* Check fake data length */ 80860573Skris if (x11_fake_data_len != x11_saved_data_len) { 80960573Skris error("X11 fake_data_len %d != saved_data_len %d", 81060573Skris x11_fake_data_len, x11_saved_data_len); 81160573Skris return -1; 81260573Skris } 81360573Skris /* 81460573Skris * Received authentication protocol and data match 81560573Skris * our fake data. Substitute the fake data with real 81660573Skris * data. 81760573Skris */ 81860573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 81960573Skris x11_saved_data, x11_saved_data_len); 82060573Skris return 1; 82160573Skris} 82257429Smarkm 82392559Sdesstatic void 82460573Skrischannel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 82560573Skris{ 82692559Sdes int ret = x11_open_helper(&c->output); 827106130Sdes 82860573Skris if (ret == 1) { 82960573Skris /* Start normal processing for the channel. */ 83060573Skris c->type = SSH_CHANNEL_OPEN; 83160573Skris channel_pre_open_13(c, readset, writeset); 83260573Skris } else if (ret == -1) { 83360573Skris /* 83460573Skris * We have received an X11 connection that has bad 83560573Skris * authentication information. 83660573Skris */ 83776262Sgreen log("X11 connection rejected because of wrong authentication."); 83860573Skris buffer_clear(&c->input); 83960573Skris buffer_clear(&c->output); 84092559Sdes channel_close_fd(&c->sock); 84160573Skris c->sock = -1; 84260573Skris c->type = SSH_CHANNEL_CLOSED; 84360573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 84460573Skris packet_put_int(c->remote_id); 84560573Skris packet_send(); 84660573Skris } 84760573Skris} 84857429Smarkm 84992559Sdesstatic void 85060573Skrischannel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 85160573Skris{ 85292559Sdes int ret = x11_open_helper(&c->output); 85392559Sdes 85492559Sdes /* c->force_drain = 1; */ 85592559Sdes 85660573Skris if (ret == 1) { 85760573Skris c->type = SSH_CHANNEL_OPEN; 85892559Sdes channel_pre_open(c, readset, writeset); 85992559Sdes } else if (ret == -1) { 86092559Sdes log("X11 connection rejected because of wrong authentication."); 86192559Sdes debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 86292559Sdes chan_read_failed(c); 86392559Sdes buffer_clear(&c->input); 86492559Sdes chan_ibuf_empty(c); 86592559Sdes buffer_clear(&c->output); 86692559Sdes /* for proto v1, the peer will send an IEOF */ 86760573Skris if (compat20) 86892559Sdes chan_write_failed(c); 86960573Skris else 87092559Sdes c->type = SSH_CHANNEL_OPEN; 87160573Skris debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 87260573Skris } 87360573Skris} 87457429Smarkm 87576262Sgreen/* try to decode a socks4 header */ 87692559Sdesstatic int 87776262Sgreenchannel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) 87876262Sgreen{ 879106130Sdes char *p, *host; 88076262Sgreen int len, have, i, found; 88192559Sdes char username[256]; 88276262Sgreen struct { 88376262Sgreen u_int8_t version; 88476262Sgreen u_int8_t command; 88576262Sgreen u_int16_t dest_port; 88676262Sgreen struct in_addr dest_addr; 88776262Sgreen } s4_req, s4_rsp; 88876262Sgreen 88976262Sgreen debug2("channel %d: decode socks4", c->self); 89076262Sgreen 89176262Sgreen have = buffer_len(&c->input); 89276262Sgreen len = sizeof(s4_req); 89376262Sgreen if (have < len) 89476262Sgreen return 0; 89576262Sgreen p = buffer_ptr(&c->input); 89676262Sgreen for (found = 0, i = len; i < have; i++) { 89776262Sgreen if (p[i] == '\0') { 89876262Sgreen found = 1; 89976262Sgreen break; 90076262Sgreen } 90176262Sgreen if (i > 1024) { 90276262Sgreen /* the peer is probably sending garbage */ 90376262Sgreen debug("channel %d: decode socks4: too long", 90476262Sgreen c->self); 90576262Sgreen return -1; 90676262Sgreen } 90776262Sgreen } 90876262Sgreen if (!found) 90976262Sgreen return 0; 91076262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 91176262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 91276262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 91376262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 91476262Sgreen have = buffer_len(&c->input); 91576262Sgreen p = buffer_ptr(&c->input); 91676262Sgreen len = strlen(p); 91776262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 91876262Sgreen if (len > have) 91976262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 92076262Sgreen c->self, len, have); 92176262Sgreen strlcpy(username, p, sizeof(username)); 92276262Sgreen buffer_consume(&c->input, len); 92376262Sgreen buffer_consume(&c->input, 1); /* trailing '\0' */ 92476262Sgreen 92576262Sgreen host = inet_ntoa(s4_req.dest_addr); 92676262Sgreen strlcpy(c->path, host, sizeof(c->path)); 92776262Sgreen c->host_port = ntohs(s4_req.dest_port); 92892559Sdes 92976262Sgreen debug("channel %d: dynamic request: socks4 host %s port %u command %u", 93076262Sgreen c->self, host, c->host_port, s4_req.command); 93176262Sgreen 93276262Sgreen if (s4_req.command != 1) { 93376262Sgreen debug("channel %d: cannot handle: socks4 cn %d", 93476262Sgreen c->self, s4_req.command); 93576262Sgreen return -1; 93676262Sgreen } 93776262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 93876262Sgreen s4_rsp.command = 90; /* cd: req granted */ 93976262Sgreen s4_rsp.dest_port = 0; /* ignored */ 94076262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 94176262Sgreen buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); 94276262Sgreen return 1; 94376262Sgreen} 94476262Sgreen 94576262Sgreen/* dynamic port forwarding */ 94692559Sdesstatic void 94776262Sgreenchannel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 94876262Sgreen{ 94976262Sgreen u_char *p; 95076262Sgreen int have, ret; 95176262Sgreen 95276262Sgreen have = buffer_len(&c->input); 95392559Sdes c->delayed = 0; 95476262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 95576262Sgreen /* buffer_dump(&c->input); */ 95676262Sgreen /* check if the fixed size part of the packet is in buffer. */ 95776262Sgreen if (have < 4) { 95876262Sgreen /* need more */ 95976262Sgreen FD_SET(c->sock, readset); 96076262Sgreen return; 96176262Sgreen } 96276262Sgreen /* try to guess the protocol */ 96376262Sgreen p = buffer_ptr(&c->input); 96476262Sgreen switch (p[0]) { 96576262Sgreen case 0x04: 96676262Sgreen ret = channel_decode_socks4(c, readset, writeset); 96776262Sgreen break; 96876262Sgreen default: 96976262Sgreen ret = -1; 97076262Sgreen break; 97176262Sgreen } 97276262Sgreen if (ret < 0) { 97392559Sdes chan_mark_dead(c); 97476262Sgreen } else if (ret == 0) { 97576262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 97676262Sgreen /* need more */ 97776262Sgreen FD_SET(c->sock, readset); 97876262Sgreen } else { 97976262Sgreen /* switch to the next state */ 98076262Sgreen c->type = SSH_CHANNEL_OPENING; 98176262Sgreen port_open_helper(c, "direct-tcpip"); 98276262Sgreen } 98376262Sgreen} 98476262Sgreen 98560573Skris/* This is our fake X11 server socket. */ 98692559Sdesstatic void 98760573Skrischannel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 98860573Skris{ 98992559Sdes Channel *nc; 99060573Skris struct sockaddr addr; 99192559Sdes int newsock; 99260573Skris socklen_t addrlen; 99376262Sgreen char buf[16384], *remote_ipaddr; 99460573Skris int remote_port; 99557429Smarkm 99660573Skris if (FD_ISSET(c->sock, readset)) { 99760573Skris debug("X11 connection requested."); 99860573Skris addrlen = sizeof(addr); 99960573Skris newsock = accept(c->sock, &addr, &addrlen); 100092559Sdes if (c->single_connection) { 100192559Sdes debug("single_connection: closing X11 listener."); 100292559Sdes channel_close_fd(&c->sock); 100392559Sdes chan_mark_dead(c); 100492559Sdes } 100560573Skris if (newsock < 0) { 100660573Skris error("accept: %.100s", strerror(errno)); 100760573Skris return; 100860573Skris } 100992559Sdes set_nodelay(newsock); 101076262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 101160573Skris remote_port = get_peer_port(newsock); 101260573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 101376262Sgreen remote_ipaddr, remote_port); 101457429Smarkm 101592559Sdes nc = channel_new("accepted x11 socket", 101660573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 101760573Skris c->local_window_max, c->local_maxpacket, 101869587Sgreen 0, xstrdup(buf), 1); 101960573Skris if (compat20) { 102060573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 102160573Skris packet_put_cstring("x11"); 102292559Sdes packet_put_int(nc->self); 102392559Sdes packet_put_int(nc->local_window_max); 102492559Sdes packet_put_int(nc->local_maxpacket); 102576262Sgreen /* originator ipaddr and port */ 102676262Sgreen packet_put_cstring(remote_ipaddr); 102760573Skris if (datafellows & SSH_BUG_X11FWD) { 102860573Skris debug("ssh2 x11 bug compat mode"); 102957429Smarkm } else { 103060573Skris packet_put_int(remote_port); 103157429Smarkm } 103260573Skris packet_send(); 103360573Skris } else { 103460573Skris packet_start(SSH_SMSG_X11_OPEN); 103592559Sdes packet_put_int(nc->self); 103692559Sdes if (packet_get_protocol_flags() & 103792559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 103892559Sdes packet_put_cstring(buf); 103960573Skris packet_send(); 104057429Smarkm } 104176262Sgreen xfree(remote_ipaddr); 104257429Smarkm } 104357429Smarkm} 104457429Smarkm 104592559Sdesstatic void 104676262Sgreenport_open_helper(Channel *c, char *rtype) 104776262Sgreen{ 104876262Sgreen int direct; 104976262Sgreen char buf[1024]; 105076262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 105176262Sgreen u_short remote_port = get_peer_port(c->sock); 105276262Sgreen 105376262Sgreen direct = (strcmp(rtype, "direct-tcpip") == 0); 105476262Sgreen 105576262Sgreen snprintf(buf, sizeof buf, 105676262Sgreen "%s: listening port %d for %.100s port %d, " 105776262Sgreen "connect from %.200s port %d", 105876262Sgreen rtype, c->listening_port, c->path, c->host_port, 105976262Sgreen remote_ipaddr, remote_port); 106076262Sgreen 106176262Sgreen xfree(c->remote_name); 106276262Sgreen c->remote_name = xstrdup(buf); 106376262Sgreen 106476262Sgreen if (compat20) { 106576262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 106676262Sgreen packet_put_cstring(rtype); 106776262Sgreen packet_put_int(c->self); 106876262Sgreen packet_put_int(c->local_window_max); 106976262Sgreen packet_put_int(c->local_maxpacket); 107076262Sgreen if (direct) { 107176262Sgreen /* target host, port */ 107276262Sgreen packet_put_cstring(c->path); 107376262Sgreen packet_put_int(c->host_port); 107476262Sgreen } else { 107576262Sgreen /* listen address, port */ 107676262Sgreen packet_put_cstring(c->path); 107776262Sgreen packet_put_int(c->listening_port); 107876262Sgreen } 107976262Sgreen /* originator host and port */ 108076262Sgreen packet_put_cstring(remote_ipaddr); 108176262Sgreen packet_put_int(remote_port); 108276262Sgreen packet_send(); 108376262Sgreen } else { 108476262Sgreen packet_start(SSH_MSG_PORT_OPEN); 108576262Sgreen packet_put_int(c->self); 108676262Sgreen packet_put_cstring(c->path); 108776262Sgreen packet_put_int(c->host_port); 108892559Sdes if (packet_get_protocol_flags() & 108992559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 109076262Sgreen packet_put_cstring(c->remote_name); 109176262Sgreen packet_send(); 109276262Sgreen } 109376262Sgreen xfree(remote_ipaddr); 109476262Sgreen} 109576262Sgreen 109657429Smarkm/* 109760573Skris * This socket is listening for connections to a forwarded TCP/IP port. 109857429Smarkm */ 109992559Sdesstatic void 110060573Skrischannel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 110157429Smarkm{ 110276262Sgreen Channel *nc; 110357429Smarkm struct sockaddr addr; 110492559Sdes int newsock, nextstate; 110557429Smarkm socklen_t addrlen; 110676262Sgreen char *rtype; 110757429Smarkm 110860573Skris if (FD_ISSET(c->sock, readset)) { 110960573Skris debug("Connection to port %d forwarding " 111060573Skris "to %.100s port %d requested.", 111160573Skris c->listening_port, c->path, c->host_port); 111276262Sgreen 111392559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 111492559Sdes nextstate = SSH_CHANNEL_OPENING; 111592559Sdes rtype = "forwarded-tcpip"; 111692559Sdes } else { 111792559Sdes if (c->host_port == 0) { 111892559Sdes nextstate = SSH_CHANNEL_DYNAMIC; 111992559Sdes rtype = "dynamic-tcpip"; 112092559Sdes } else { 112192559Sdes nextstate = SSH_CHANNEL_OPENING; 112292559Sdes rtype = "direct-tcpip"; 112392559Sdes } 112492559Sdes } 112576262Sgreen 112660573Skris addrlen = sizeof(addr); 112760573Skris newsock = accept(c->sock, &addr, &addrlen); 112860573Skris if (newsock < 0) { 112960573Skris error("accept: %.100s", strerror(errno)); 113060573Skris return; 113160573Skris } 113292559Sdes set_nodelay(newsock); 113392559Sdes nc = channel_new(rtype, 113476262Sgreen nextstate, newsock, newsock, -1, 113560573Skris c->local_window_max, c->local_maxpacket, 113676262Sgreen 0, xstrdup(rtype), 1); 113776262Sgreen nc->listening_port = c->listening_port; 113876262Sgreen nc->host_port = c->host_port; 113976262Sgreen strlcpy(nc->path, c->path, sizeof(nc->path)); 114076262Sgreen 114192559Sdes if (nextstate == SSH_CHANNEL_DYNAMIC) { 114292559Sdes /* 114392559Sdes * do not call the channel_post handler until 114492559Sdes * this flag has been reset by a pre-handler. 114592559Sdes * otherwise the FD_ISSET calls might overflow 114692559Sdes */ 114792559Sdes nc->delayed = 1; 114892559Sdes } else { 114976262Sgreen port_open_helper(nc, rtype); 115092559Sdes } 115160573Skris } 115260573Skris} 115357429Smarkm 115460573Skris/* 115560573Skris * This is the authentication agent socket listening for connections from 115660573Skris * clients. 115760573Skris */ 115892559Sdesstatic void 115960573Skrischannel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 116060573Skris{ 116192559Sdes Channel *nc; 116292559Sdes char *name; 116392559Sdes int newsock; 116460573Skris struct sockaddr addr; 116560573Skris socklen_t addrlen; 116657429Smarkm 116760573Skris if (FD_ISSET(c->sock, readset)) { 116860573Skris addrlen = sizeof(addr); 116960573Skris newsock = accept(c->sock, &addr, &addrlen); 117060573Skris if (newsock < 0) { 117160573Skris error("accept from auth socket: %.100s", strerror(errno)); 117260573Skris return; 117360573Skris } 117492559Sdes name = xstrdup("accepted auth socket"); 117592559Sdes nc = channel_new("accepted auth socket", 117676262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 117776262Sgreen c->local_window_max, c->local_maxpacket, 117892559Sdes 0, name, 1); 117976262Sgreen if (compat20) { 118076262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 118176262Sgreen packet_put_cstring("auth-agent@openssh.com"); 118292559Sdes packet_put_int(nc->self); 118376262Sgreen packet_put_int(c->local_window_max); 118476262Sgreen packet_put_int(c->local_maxpacket); 118576262Sgreen } else { 118676262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 118792559Sdes packet_put_int(nc->self); 118876262Sgreen } 118960573Skris packet_send(); 119060573Skris } 119160573Skris} 119257429Smarkm 119392559Sdesstatic void 119476262Sgreenchannel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 119576262Sgreen{ 119692559Sdes int err = 0; 119792559Sdes socklen_t sz = sizeof(err); 119892559Sdes 119976262Sgreen if (FD_ISSET(c->sock, writeset)) { 120092559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 120192559Sdes err = errno; 120292559Sdes error("getsockopt SO_ERROR failed"); 120392559Sdes } 120492559Sdes if (err == 0) { 120592559Sdes debug("channel %d: connected", c->self); 120692559Sdes c->type = SSH_CHANNEL_OPEN; 120792559Sdes if (compat20) { 120892559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 120992559Sdes packet_put_int(c->remote_id); 121092559Sdes packet_put_int(c->self); 121192559Sdes packet_put_int(c->local_window); 121292559Sdes packet_put_int(c->local_maxpacket); 121392559Sdes } else { 121492559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 121592559Sdes packet_put_int(c->remote_id); 121692559Sdes packet_put_int(c->self); 121792559Sdes } 121876262Sgreen } else { 121992559Sdes debug("channel %d: not connected: %s", 122092559Sdes c->self, strerror(err)); 122192559Sdes if (compat20) { 122292559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 122392559Sdes packet_put_int(c->remote_id); 122492559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 122592559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 122692559Sdes packet_put_cstring(strerror(err)); 122792559Sdes packet_put_cstring(""); 122892559Sdes } 122976262Sgreen } else { 123092559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 123192559Sdes packet_put_int(c->remote_id); 123276262Sgreen } 123392559Sdes chan_mark_dead(c); 123476262Sgreen } 123592559Sdes packet_send(); 123676262Sgreen } 123776262Sgreen} 123876262Sgreen 123992559Sdesstatic int 124060573Skrischannel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 124160573Skris{ 124260573Skris char buf[16*1024]; 124360573Skris int len; 124457429Smarkm 124560573Skris if (c->rfd != -1 && 124660573Skris FD_ISSET(c->rfd, readset)) { 124760573Skris len = read(c->rfd, buf, sizeof(buf)); 124860573Skris if (len < 0 && (errno == EINTR || errno == EAGAIN)) 124960573Skris return 1; 125078827Sgreen if (len <= 0) { 125178827Sgreen debug("channel %d: read<=0 rfd %d len %d", 125260573Skris c->self, c->rfd, len); 125376262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 125476262Sgreen debug("channel %d: not open", c->self); 125592559Sdes chan_mark_dead(c); 125676262Sgreen return -1; 125776262Sgreen } else if (compat13) { 125892559Sdes buffer_clear(&c->output); 125960573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 126092559Sdes debug("channel %d: input draining.", c->self); 126160573Skris } else { 126260573Skris chan_read_failed(c); 126357429Smarkm } 126460573Skris return -1; 126560573Skris } 126692559Sdes if (c->input_filter != NULL) { 126765668Skris if (c->input_filter(c, buf, len) == -1) { 126876262Sgreen debug("channel %d: filter stops", c->self); 126965668Skris chan_read_failed(c); 127065668Skris } 127165668Skris } else { 127265668Skris buffer_append(&c->input, buf, len); 127365668Skris } 127460573Skris } 127560573Skris return 1; 127660573Skris} 127792559Sdesstatic int 127860573Skrischannel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 127960573Skris{ 128076262Sgreen struct termios tio; 128192559Sdes u_char *data; 128292559Sdes u_int dlen; 128360573Skris int len; 128460573Skris 128560573Skris /* Send buffered output data to the socket. */ 128660573Skris if (c->wfd != -1 && 128760573Skris FD_ISSET(c->wfd, writeset) && 128860573Skris buffer_len(&c->output) > 0) { 128992559Sdes data = buffer_ptr(&c->output); 129092559Sdes dlen = buffer_len(&c->output); 1291106130Sdes#ifdef _AIX 1292106130Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1293106130Sdes if (compat20 && c->wfd_isatty && dlen > 8*1024) 1294106130Sdes dlen = 8*1024; 1295106130Sdes#endif 129692559Sdes len = write(c->wfd, data, dlen); 129760573Skris if (len < 0 && (errno == EINTR || errno == EAGAIN)) 129860573Skris return 1; 129960573Skris if (len <= 0) { 130076262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 130176262Sgreen debug("channel %d: not open", c->self); 130292559Sdes chan_mark_dead(c); 130376262Sgreen return -1; 130476262Sgreen } else if (compat13) { 130592559Sdes buffer_clear(&c->output); 130692559Sdes debug("channel %d: input draining.", c->self); 130760573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 130860573Skris } else { 130960573Skris chan_write_failed(c); 131057429Smarkm } 131160573Skris return -1; 131260573Skris } 131392559Sdes if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { 131474500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 131574500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 131674500Sgreen /* 131774500Sgreen * Simulate echo to reduce the impact of 131876262Sgreen * traffic analysis. We need to match the 131976262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 132076262Sgreen * (4 byte channel id + data) 132174500Sgreen */ 132276262Sgreen packet_send_ignore(4 + len); 132374500Sgreen packet_send(); 132474500Sgreen } 132574500Sgreen } 132660573Skris buffer_consume(&c->output, len); 132760573Skris if (compat20 && len > 0) { 132860573Skris c->local_consumed += len; 132960573Skris } 133060573Skris } 133160573Skris return 1; 133260573Skris} 133392559Sdesstatic int 133460573Skrischannel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 133560573Skris{ 133660573Skris char buf[16*1024]; 133760573Skris int len; 133857429Smarkm 133960573Skris/** XXX handle drain efd, too */ 134060573Skris if (c->efd != -1) { 134160573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 134260573Skris FD_ISSET(c->efd, writeset) && 134360573Skris buffer_len(&c->extended) > 0) { 134460573Skris len = write(c->efd, buffer_ptr(&c->extended), 134560573Skris buffer_len(&c->extended)); 134669587Sgreen debug2("channel %d: written %d to efd %d", 134760573Skris c->self, len, c->efd); 134876262Sgreen if (len < 0 && (errno == EINTR || errno == EAGAIN)) 134976262Sgreen return 1; 135076262Sgreen if (len <= 0) { 135176262Sgreen debug2("channel %d: closing write-efd %d", 135276262Sgreen c->self, c->efd); 135392559Sdes channel_close_fd(&c->efd); 135476262Sgreen } else { 135560573Skris buffer_consume(&c->extended, len); 135660573Skris c->local_consumed += len; 135757429Smarkm } 135860573Skris } else if (c->extended_usage == CHAN_EXTENDED_READ && 135960573Skris FD_ISSET(c->efd, readset)) { 136060573Skris len = read(c->efd, buf, sizeof(buf)); 136169587Sgreen debug2("channel %d: read %d from efd %d", 136292559Sdes c->self, len, c->efd); 136376262Sgreen if (len < 0 && (errno == EINTR || errno == EAGAIN)) 136476262Sgreen return 1; 136576262Sgreen if (len <= 0) { 136676262Sgreen debug2("channel %d: closing read-efd %d", 136760573Skris c->self, c->efd); 136892559Sdes channel_close_fd(&c->efd); 136976262Sgreen } else { 137060573Skris buffer_append(&c->extended, buf, len); 137176262Sgreen } 137260573Skris } 137360573Skris } 137460573Skris return 1; 137560573Skris} 137692559Sdesstatic int 137776262Sgreenchannel_check_window(Channel *c) 137860573Skris{ 137976262Sgreen if (c->type == SSH_CHANNEL_OPEN && 138076262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 138160573Skris c->local_window < c->local_window_max/2 && 138260573Skris c->local_consumed > 0) { 138360573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 138460573Skris packet_put_int(c->remote_id); 138560573Skris packet_put_int(c->local_consumed); 138660573Skris packet_send(); 138769587Sgreen debug2("channel %d: window %d sent adjust %d", 138860573Skris c->self, c->local_window, 138960573Skris c->local_consumed); 139060573Skris c->local_window += c->local_consumed; 139160573Skris c->local_consumed = 0; 139260573Skris } 139360573Skris return 1; 139460573Skris} 139557429Smarkm 139692559Sdesstatic void 139792559Sdeschannel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 139860573Skris{ 139992559Sdes if (c->delayed) 140092559Sdes return; 140160573Skris channel_handle_rfd(c, readset, writeset); 140260573Skris channel_handle_wfd(c, readset, writeset); 140392559Sdes if (!compat20) 140492559Sdes return; 140560573Skris channel_handle_efd(c, readset, writeset); 140676262Sgreen channel_check_window(c); 140760573Skris} 140860573Skris 140992559Sdesstatic void 141060573Skrischannel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 141160573Skris{ 141260573Skris int len; 1413106130Sdes 141460573Skris /* Send buffered output data to the socket. */ 141560573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 141660573Skris len = write(c->sock, buffer_ptr(&c->output), 141760573Skris buffer_len(&c->output)); 141860573Skris if (len <= 0) 141992559Sdes buffer_clear(&c->output); 142060573Skris else 142160573Skris buffer_consume(&c->output, len); 142260573Skris } 142360573Skris} 142460573Skris 142592559Sdesstatic void 142660573Skrischannel_handler_init_20(void) 142760573Skris{ 142892559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 142960573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 143060573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 143176262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 143260573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 143376262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 143476262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 143576262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 143660573Skris 143792559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 143860573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 143976262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 144060573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 144176262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 144276262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 144392559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 144460573Skris} 144560573Skris 144692559Sdesstatic void 144760573Skrischannel_handler_init_13(void) 144860573Skris{ 144960573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 145060573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 145160573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 145260573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 145360573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 145460573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 145560573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 145676262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 145776262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 145860573Skris 145992559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 146060573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 146160573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 146260573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 146360573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 146476262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 146592559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 146660573Skris} 146760573Skris 146892559Sdesstatic void 146960573Skrischannel_handler_init_15(void) 147060573Skris{ 147192559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 147260573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 147360573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 147460573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 147560573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 147676262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 147776262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 147860573Skris 147960573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 148060573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 148160573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 148292559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 148376262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 148492559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 148560573Skris} 148660573Skris 148792559Sdesstatic void 148860573Skrischannel_handler_init(void) 148960573Skris{ 149060573Skris int i; 1491106130Sdes 149292559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 149360573Skris channel_pre[i] = NULL; 149460573Skris channel_post[i] = NULL; 149560573Skris } 149660573Skris if (compat20) 149760573Skris channel_handler_init_20(); 149860573Skris else if (compat13) 149960573Skris channel_handler_init_13(); 150060573Skris else 150160573Skris channel_handler_init_15(); 150260573Skris} 150360573Skris 150492559Sdes/* gc dead channels */ 150592559Sdesstatic void 150692559Sdeschannel_garbage_collect(Channel *c) 150792559Sdes{ 150892559Sdes if (c == NULL) 150992559Sdes return; 151092559Sdes if (c->detach_user != NULL) { 151192559Sdes if (!chan_is_dead(c, 0)) 151292559Sdes return; 151392559Sdes debug("channel %d: gc: notify user", c->self); 151492559Sdes c->detach_user(c->self, NULL); 151592559Sdes /* if we still have a callback */ 151692559Sdes if (c->detach_user != NULL) 151792559Sdes return; 151892559Sdes debug("channel %d: gc: user detached", c->self); 151992559Sdes } 152092559Sdes if (!chan_is_dead(c, 1)) 152192559Sdes return; 152292559Sdes debug("channel %d: garbage collecting", c->self); 152392559Sdes channel_free(c); 152492559Sdes} 152592559Sdes 152692559Sdesstatic void 152760573Skrischannel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 152860573Skris{ 152960573Skris static int did_init = 0; 153060573Skris int i; 153160573Skris Channel *c; 153260573Skris 153360573Skris if (!did_init) { 153460573Skris channel_handler_init(); 153560573Skris did_init = 1; 153660573Skris } 153760573Skris for (i = 0; i < channels_alloc; i++) { 153892559Sdes c = channels[i]; 153992559Sdes if (c == NULL) 154057429Smarkm continue; 154192559Sdes if (ftab[c->type] != NULL) 154292559Sdes (*ftab[c->type])(c, readset, writeset); 154392559Sdes channel_garbage_collect(c); 154457429Smarkm } 154557429Smarkm} 154657429Smarkm 154792559Sdes/* 154892559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 154992559Sdes * select bitmasks. 155092559Sdes */ 155160573Skrisvoid 155276262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 155392559Sdes int *nallocp, int rekeying) 155460573Skris{ 155576262Sgreen int n; 155676262Sgreen u_int sz; 155776262Sgreen 155876262Sgreen n = MAX(*maxfdp, channel_max_fd); 155976262Sgreen 156076262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 156192559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 156292559Sdes if (*readsetp == NULL || sz > *nallocp) { 156392559Sdes *readsetp = xrealloc(*readsetp, sz); 156492559Sdes *writesetp = xrealloc(*writesetp, sz); 156592559Sdes *nallocp = sz; 156676262Sgreen } 156792559Sdes *maxfdp = n; 156876262Sgreen memset(*readsetp, 0, sz); 156976262Sgreen memset(*writesetp, 0, sz); 157076262Sgreen 157176262Sgreen if (!rekeying) 157276262Sgreen channel_handler(channel_pre, *readsetp, *writesetp); 157360573Skris} 157460573Skris 157592559Sdes/* 157692559Sdes * After select, perform any appropriate operations for channels which have 157792559Sdes * events pending. 157892559Sdes */ 157960573Skrisvoid 158060573Skrischannel_after_select(fd_set * readset, fd_set * writeset) 158160573Skris{ 158260573Skris channel_handler(channel_post, readset, writeset); 158360573Skris} 158460573Skris 158592559Sdes 158676262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 158757429Smarkm 158860573Skrisvoid 158992559Sdeschannel_output_poll(void) 159057429Smarkm{ 159160573Skris Channel *c; 159299063Sdes int i; 159399063Sdes u_int len; 159457429Smarkm 159557429Smarkm for (i = 0; i < channels_alloc; i++) { 159692559Sdes c = channels[i]; 159792559Sdes if (c == NULL) 159892559Sdes continue; 159957429Smarkm 160092559Sdes /* 160192559Sdes * We are only interested in channels that can have buffered 160292559Sdes * incoming data. 160392559Sdes */ 160457429Smarkm if (compat13) { 160560573Skris if (c->type != SSH_CHANNEL_OPEN && 160660573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 160757429Smarkm continue; 160857429Smarkm } else { 160960573Skris if (c->type != SSH_CHANNEL_OPEN) 161057429Smarkm continue; 161157429Smarkm } 161260573Skris if (compat20 && 161360573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 161476262Sgreen /* XXX is this true? */ 161592559Sdes debug3("channel %d: will not send data after close", c->self); 161660573Skris continue; 161760573Skris } 161857429Smarkm 161957429Smarkm /* Get the amount of buffered data for this channel. */ 162076262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 162176262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 162276262Sgreen (len = buffer_len(&c->input)) > 0) { 162392559Sdes /* 162492559Sdes * Send some data for the other side over the secure 162592559Sdes * connection. 162692559Sdes */ 162760573Skris if (compat20) { 162860573Skris if (len > c->remote_window) 162960573Skris len = c->remote_window; 163060573Skris if (len > c->remote_maxpacket) 163160573Skris len = c->remote_maxpacket; 163257429Smarkm } else { 163360573Skris if (packet_is_interactive()) { 163460573Skris if (len > 1024) 163560573Skris len = 512; 163660573Skris } else { 163760573Skris /* Keep the packets at reasonable size. */ 163860573Skris if (len > packet_get_maxsize()/2) 163960573Skris len = packet_get_maxsize()/2; 164060573Skris } 164157429Smarkm } 164260573Skris if (len > 0) { 164360573Skris packet_start(compat20 ? 164460573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 164560573Skris packet_put_int(c->remote_id); 164660573Skris packet_put_string(buffer_ptr(&c->input), len); 164760573Skris packet_send(); 164860573Skris buffer_consume(&c->input, len); 164960573Skris c->remote_window -= len; 165060573Skris } 165160573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 165257429Smarkm if (compat13) 165357429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 165457429Smarkm /* 165557429Smarkm * input-buffer is empty and read-socket shutdown: 165698684Sdes * tell peer, that we will not send more data: send IEOF. 165798684Sdes * hack for extended data: delay EOF if EFD still in use. 165857429Smarkm */ 165998684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 166098684Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 166198684Sdes c->self, c->efd, buffer_len(&c->extended)); 166298684Sdes else 166398684Sdes chan_ibuf_empty(c); 166457429Smarkm } 166560573Skris /* Send extended data, i.e. stderr */ 166660573Skris if (compat20 && 166798684Sdes !(c->flags & CHAN_EOF_SENT) && 166860573Skris c->remote_window > 0 && 166960573Skris (len = buffer_len(&c->extended)) > 0 && 167060573Skris c->extended_usage == CHAN_EXTENDED_READ) { 167199063Sdes debug2("channel %d: rwin %u elen %u euse %d", 167276262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 167376262Sgreen c->extended_usage); 167460573Skris if (len > c->remote_window) 167560573Skris len = c->remote_window; 167660573Skris if (len > c->remote_maxpacket) 167760573Skris len = c->remote_maxpacket; 167860573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 167960573Skris packet_put_int(c->remote_id); 168060573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 168160573Skris packet_put_string(buffer_ptr(&c->extended), len); 168260573Skris packet_send(); 168360573Skris buffer_consume(&c->extended, len); 168460573Skris c->remote_window -= len; 168576262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 168660573Skris } 168757429Smarkm } 168857429Smarkm} 168957429Smarkm 169057429Smarkm 169192559Sdes/* -- protocol input */ 169292559Sdes 169360573Skrisvoid 169492559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 169557429Smarkm{ 169657429Smarkm int id; 169757429Smarkm char *data; 169876262Sgreen u_int data_len; 169960573Skris Channel *c; 170057429Smarkm 170157429Smarkm /* Get the channel number and verify it. */ 170257429Smarkm id = packet_get_int(); 170360573Skris c = channel_lookup(id); 170460573Skris if (c == NULL) 170557429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 170657429Smarkm 170757429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 170860573Skris if (c->type != SSH_CHANNEL_OPEN && 170960573Skris c->type != SSH_CHANNEL_X11_OPEN) 171057429Smarkm return; 171157429Smarkm 171257429Smarkm /* same for protocol 1.5 if output end is no longer open */ 171360573Skris if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 171457429Smarkm return; 171557429Smarkm 171657429Smarkm /* Get the data. */ 171757429Smarkm data = packet_get_string(&data_len); 171860573Skris 171992559Sdes if (compat20) { 172060573Skris if (data_len > c->local_maxpacket) { 172160573Skris log("channel %d: rcvd big packet %d, maxpack %d", 172260573Skris c->self, data_len, c->local_maxpacket); 172360573Skris } 172460573Skris if (data_len > c->local_window) { 172560573Skris log("channel %d: rcvd too much data %d, win %d", 172660573Skris c->self, data_len, c->local_window); 172760573Skris xfree(data); 172860573Skris return; 172960573Skris } 173060573Skris c->local_window -= data_len; 173160573Skris } 173292559Sdes packet_check_eom(); 173360573Skris buffer_append(&c->output, data, data_len); 173457429Smarkm xfree(data); 173557429Smarkm} 173692559Sdes 173760573Skrisvoid 173892559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 173960573Skris{ 174060573Skris int id; 174160573Skris char *data; 174299063Sdes u_int data_len, tcode; 174360573Skris Channel *c; 174457429Smarkm 174560573Skris /* Get the channel number and verify it. */ 174660573Skris id = packet_get_int(); 174760573Skris c = channel_lookup(id); 174860573Skris 174960573Skris if (c == NULL) 175060573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 175160573Skris if (c->type != SSH_CHANNEL_OPEN) { 175260573Skris log("channel %d: ext data for non open", id); 175360573Skris return; 175460573Skris } 175598684Sdes if (c->flags & CHAN_EOF_RCVD) { 175698684Sdes if (datafellows & SSH_BUG_EXTEOF) 175798684Sdes debug("channel %d: accepting ext data after eof", id); 175898684Sdes else 175998684Sdes packet_disconnect("Received extended_data after EOF " 176098684Sdes "on channel %d.", id); 176198684Sdes } 176260573Skris tcode = packet_get_int(); 176360573Skris if (c->efd == -1 || 176460573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 176560573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 176660573Skris log("channel %d: bad ext data", c->self); 176760573Skris return; 176860573Skris } 176960573Skris data = packet_get_string(&data_len); 177092559Sdes packet_check_eom(); 177160573Skris if (data_len > c->local_window) { 177260573Skris log("channel %d: rcvd too much extended_data %d, win %d", 177360573Skris c->self, data_len, c->local_window); 177460573Skris xfree(data); 177560573Skris return; 177660573Skris } 177769587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 177860573Skris c->local_window -= data_len; 177960573Skris buffer_append(&c->extended, data, data_len); 178060573Skris xfree(data); 178160573Skris} 178260573Skris 178360573Skrisvoid 178492559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 178560573Skris{ 178660573Skris int id; 178760573Skris Channel *c; 178857429Smarkm 178960573Skris id = packet_get_int(); 179092559Sdes packet_check_eom(); 179160573Skris c = channel_lookup(id); 179260573Skris if (c == NULL) 179360573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 179460573Skris chan_rcvd_ieof(c); 179592559Sdes 179692559Sdes /* XXX force input close */ 179792559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 179892559Sdes debug("channel %d: FORCE input drain", c->self); 179992559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 180092559Sdes if (buffer_len(&c->input) == 0) 180192559Sdes chan_ibuf_empty(c); 180292559Sdes } 180392559Sdes 180460573Skris} 180560573Skris 180660573Skrisvoid 180792559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 180857429Smarkm{ 180960573Skris int id; 181060573Skris Channel *c; 181157429Smarkm 181260573Skris id = packet_get_int(); 181392559Sdes packet_check_eom(); 181460573Skris c = channel_lookup(id); 181560573Skris if (c == NULL) 181660573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 181757429Smarkm 181857429Smarkm /* 181957429Smarkm * Send a confirmation that we have closed the channel and no more 182057429Smarkm * data is coming for it. 182157429Smarkm */ 182257429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 182360573Skris packet_put_int(c->remote_id); 182457429Smarkm packet_send(); 182557429Smarkm 182657429Smarkm /* 182757429Smarkm * If the channel is in closed state, we have sent a close request, 182857429Smarkm * and the other side will eventually respond with a confirmation. 182957429Smarkm * Thus, we cannot free the channel here, because then there would be 183057429Smarkm * no-one to receive the confirmation. The channel gets freed when 183157429Smarkm * the confirmation arrives. 183257429Smarkm */ 183360573Skris if (c->type != SSH_CHANNEL_CLOSED) { 183457429Smarkm /* 183557429Smarkm * Not a closed channel - mark it as draining, which will 183657429Smarkm * cause it to be freed later. 183757429Smarkm */ 183892559Sdes buffer_clear(&c->input); 183960573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 184057429Smarkm } 184157429Smarkm} 184257429Smarkm 184360573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 184460573Skrisvoid 184592559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 184660573Skris{ 184760573Skris int id = packet_get_int(); 184860573Skris Channel *c = channel_lookup(id); 184992559Sdes 185092559Sdes packet_check_eom(); 185160573Skris if (c == NULL) 185260573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 185360573Skris chan_rcvd_oclose(c); 185460573Skris} 185557429Smarkm 185660573Skrisvoid 185792559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 185857429Smarkm{ 185960573Skris int id = packet_get_int(); 186060573Skris Channel *c = channel_lookup(id); 186157429Smarkm 186292559Sdes packet_check_eom(); 186360573Skris if (c == NULL) 186460573Skris packet_disconnect("Received close confirmation for " 186560573Skris "out-of-range channel %d.", id); 186660573Skris if (c->type != SSH_CHANNEL_CLOSED) 186760573Skris packet_disconnect("Received close confirmation for " 186860573Skris "non-closed channel %d (type %d).", id, c->type); 186992559Sdes channel_free(c); 187060573Skris} 187157429Smarkm 187260573Skrisvoid 187392559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 187460573Skris{ 187560573Skris int id, remote_id; 187660573Skris Channel *c; 187760573Skris 187860573Skris id = packet_get_int(); 187960573Skris c = channel_lookup(id); 188060573Skris 188160573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 188260573Skris packet_disconnect("Received open confirmation for " 188360573Skris "non-opening channel %d.", id); 188460573Skris remote_id = packet_get_int(); 188560573Skris /* Record the remote channel number and mark that the channel is now open. */ 188660573Skris c->remote_id = remote_id; 188760573Skris c->type = SSH_CHANNEL_OPEN; 188860573Skris 188960573Skris if (compat20) { 189060573Skris c->remote_window = packet_get_int(); 189160573Skris c->remote_maxpacket = packet_get_int(); 189292559Sdes if (c->confirm) { 189369587Sgreen debug2("callback start"); 189492559Sdes c->confirm(c->self, NULL); 189569587Sgreen debug2("callback done"); 189660573Skris } 189799063Sdes debug("channel %d: open confirm rwindow %u rmax %u", c->self, 189860573Skris c->remote_window, c->remote_maxpacket); 189957429Smarkm } 190092559Sdes packet_check_eom(); 190157429Smarkm} 190257429Smarkm 190392559Sdesstatic char * 190492559Sdesreason2txt(int reason) 190592559Sdes{ 190692559Sdes switch (reason) { 190792559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 190892559Sdes return "administratively prohibited"; 190992559Sdes case SSH2_OPEN_CONNECT_FAILED: 191092559Sdes return "connect failed"; 191192559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 191292559Sdes return "unknown channel type"; 191392559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 191492559Sdes return "resource shortage"; 191592559Sdes } 191692559Sdes return "unknown reason"; 191792559Sdes} 191892559Sdes 191960573Skrisvoid 192092559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 192157429Smarkm{ 192276262Sgreen int id, reason; 192376262Sgreen char *msg = NULL, *lang = NULL; 192460573Skris Channel *c; 192557429Smarkm 192660573Skris id = packet_get_int(); 192760573Skris c = channel_lookup(id); 192857429Smarkm 192960573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 193060573Skris packet_disconnect("Received open failure for " 193160573Skris "non-opening channel %d.", id); 193260573Skris if (compat20) { 193376262Sgreen reason = packet_get_int(); 193492559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 193576262Sgreen msg = packet_get_string(NULL); 193676262Sgreen lang = packet_get_string(NULL); 193776262Sgreen } 193892559Sdes log("channel %d: open failed: %s%s%s", id, 193992559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 194076262Sgreen if (msg != NULL) 194176262Sgreen xfree(msg); 194276262Sgreen if (lang != NULL) 194376262Sgreen xfree(lang); 194460573Skris } 194592559Sdes packet_check_eom(); 194660573Skris /* Free the channel. This will also close the socket. */ 194792559Sdes channel_free(c); 194857429Smarkm} 194957429Smarkm 195060573Skrisvoid 195192559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 195260573Skris{ 195360573Skris Channel *c; 195499063Sdes int id; 195599063Sdes u_int adjust; 195657429Smarkm 195760573Skris if (!compat20) 195860573Skris return; 195960573Skris 196057429Smarkm /* Get the channel number and verify it. */ 196160573Skris id = packet_get_int(); 196260573Skris c = channel_lookup(id); 196357429Smarkm 196460573Skris if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 196560573Skris log("Received window adjust for " 196660573Skris "non-open channel %d.", id); 196760573Skris return; 196860573Skris } 196960573Skris adjust = packet_get_int(); 197092559Sdes packet_check_eom(); 197199063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 197260573Skris c->remote_window += adjust; 197357429Smarkm} 197457429Smarkm 197560573Skrisvoid 197692559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 197757429Smarkm{ 197892559Sdes Channel *c = NULL; 197992559Sdes u_short host_port; 198092559Sdes char *host, *originator_string; 198192559Sdes int remote_id, sock = -1; 198257429Smarkm 198392559Sdes remote_id = packet_get_int(); 198492559Sdes host = packet_get_string(NULL); 198592559Sdes host_port = packet_get_int(); 198657429Smarkm 198792559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 198892559Sdes originator_string = packet_get_string(NULL); 198992559Sdes } else { 199092559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 199192559Sdes } 199292559Sdes packet_check_eom(); 199392559Sdes sock = channel_connect_to(host, host_port); 199492559Sdes if (sock != -1) { 199592559Sdes c = channel_new("connected socket", 199692559Sdes SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 199792559Sdes originator_string, 1); 199892559Sdes c->remote_id = remote_id; 199992559Sdes } 200092559Sdes if (c == NULL) { 2001113911Sdes xfree(originator_string); 200292559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 200392559Sdes packet_put_int(remote_id); 200492559Sdes packet_send(); 200592559Sdes } 200692559Sdes xfree(host); 200757429Smarkm} 200857429Smarkm 200957429Smarkm 201092559Sdes/* -- tcp forwarding */ 201157429Smarkm 201292559Sdesvoid 201392559Sdeschannel_set_af(int af) 201476262Sgreen{ 201592559Sdes IPv4or6 = af; 201676262Sgreen} 201776262Sgreen 201892559Sdesstatic int 201992559Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 202092559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 202157429Smarkm{ 202292559Sdes Channel *c; 202392559Sdes int success, sock, on = 1; 202457429Smarkm struct addrinfo hints, *ai, *aitop; 202592559Sdes const char *host; 202657429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 202757429Smarkm 202876262Sgreen success = 0; 202992559Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 203092559Sdes listen_addr : host_to_connect; 203157429Smarkm 203292559Sdes if (host == NULL) { 203392559Sdes error("No forward host name."); 203492559Sdes return success; 203576262Sgreen } 203692559Sdes if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 203776262Sgreen error("Forward host name too long."); 203876262Sgreen return success; 203976262Sgreen } 204076262Sgreen 204157429Smarkm /* 204257429Smarkm * getaddrinfo returns a loopback address if the hostname is 204357429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 204457429Smarkm */ 204557429Smarkm memset(&hints, 0, sizeof(hints)); 204657429Smarkm hints.ai_family = IPv4or6; 204757429Smarkm hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 204857429Smarkm hints.ai_socktype = SOCK_STREAM; 204976262Sgreen snprintf(strport, sizeof strport, "%d", listen_port); 205057429Smarkm if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 205157429Smarkm packet_disconnect("getaddrinfo: fatal error"); 205257429Smarkm 205357429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 205457429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 205557429Smarkm continue; 205657429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 205757429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 205892559Sdes error("channel_setup_fwd_listener: getnameinfo failed"); 205957429Smarkm continue; 206057429Smarkm } 206157429Smarkm /* Create a port to listen for the host. */ 206257429Smarkm sock = socket(ai->ai_family, SOCK_STREAM, 0); 206357429Smarkm if (sock < 0) { 206457429Smarkm /* this is no error since kernel may not support ipv6 */ 206557429Smarkm verbose("socket: %.100s", strerror(errno)); 206657429Smarkm continue; 206757429Smarkm } 206857429Smarkm /* 2069106130Sdes * Set socket options. 2070106130Sdes * Allow local port reuse in TIME_WAIT. 207157429Smarkm */ 2072106130Sdes if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, 2073106130Sdes sizeof(on)) == -1) 2074106130Sdes error("setsockopt SO_REUSEADDR: %s", strerror(errno)); 2075106130Sdes 207657429Smarkm debug("Local forwarding listening on %s port %s.", ntop, strport); 207757429Smarkm 207857429Smarkm /* Bind the socket to the address. */ 207957429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 208057429Smarkm /* address can be in use ipv6 address is already bound */ 208198941Sdes if (!ai->ai_next) 208298941Sdes error("bind: %.100s", strerror(errno)); 208398941Sdes else 208498941Sdes verbose("bind: %.100s", strerror(errno)); 208598941Sdes 208657429Smarkm close(sock); 208757429Smarkm continue; 208857429Smarkm } 208957429Smarkm /* Start listening for connections on the socket. */ 209057429Smarkm if (listen(sock, 5) < 0) { 209157429Smarkm error("listen: %.100s", strerror(errno)); 209257429Smarkm close(sock); 209357429Smarkm continue; 209457429Smarkm } 209557429Smarkm /* Allocate a channel number for the socket. */ 209692559Sdes c = channel_new("port listener", type, sock, sock, -1, 209760573Skris CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 209869587Sgreen 0, xstrdup("port listener"), 1); 209992559Sdes strlcpy(c->path, host, sizeof(c->path)); 210092559Sdes c->host_port = port_to_connect; 210192559Sdes c->listening_port = listen_port; 210257429Smarkm success = 1; 210357429Smarkm } 210457429Smarkm if (success == 0) 210592559Sdes error("channel_setup_fwd_listener: cannot listen to port: %d", 210676262Sgreen listen_port); 210757429Smarkm freeaddrinfo(aitop); 210876262Sgreen return success; 210957429Smarkm} 211057429Smarkm 211192559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 211292559Sdesint 211392559Sdeschannel_setup_local_fwd_listener(u_short listen_port, 211492559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 211592559Sdes{ 211692559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 211792559Sdes NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); 211892559Sdes} 211992559Sdes 212092559Sdes/* protocol v2 remote port fwd, used by sshd */ 212192559Sdesint 212292559Sdeschannel_setup_remote_fwd_listener(const char *listen_address, 212392559Sdes u_short listen_port, int gateway_ports) 212492559Sdes{ 212592559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 212692559Sdes listen_address, listen_port, NULL, 0, gateway_ports); 212792559Sdes} 212892559Sdes 212957429Smarkm/* 213057429Smarkm * Initiate forwarding of connections to port "port" on remote host through 213157429Smarkm * the secure channel to host:port from local side. 213257429Smarkm */ 213357429Smarkm 213460573Skrisvoid 213576262Sgreenchannel_request_remote_forwarding(u_short listen_port, 213676262Sgreen const char *host_to_connect, u_short port_to_connect) 213757429Smarkm{ 213892559Sdes int type, success = 0; 213976262Sgreen 214057429Smarkm /* Record locally that connection to this host/port is permitted. */ 214157429Smarkm if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 214257429Smarkm fatal("channel_request_remote_forwarding: too many forwards"); 214357429Smarkm 214457429Smarkm /* Send the forward request to the remote side. */ 214560573Skris if (compat20) { 214660573Skris const char *address_to_bind = "0.0.0.0"; 214760573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 214860573Skris packet_put_cstring("tcpip-forward"); 214998684Sdes packet_put_char(1); /* boolean: want reply */ 215060573Skris packet_put_cstring(address_to_bind); 215160573Skris packet_put_int(listen_port); 215276262Sgreen packet_send(); 215376262Sgreen packet_write_wait(); 215476262Sgreen /* Assume that server accepts the request */ 215576262Sgreen success = 1; 215660573Skris } else { 215760573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 215860573Skris packet_put_int(listen_port); 215960573Skris packet_put_cstring(host_to_connect); 216060573Skris packet_put_int(port_to_connect); 216160573Skris packet_send(); 216260573Skris packet_write_wait(); 216376262Sgreen 216476262Sgreen /* Wait for response from the remote side. */ 216592559Sdes type = packet_read(); 216676262Sgreen switch (type) { 216776262Sgreen case SSH_SMSG_SUCCESS: 216876262Sgreen success = 1; 216976262Sgreen break; 217076262Sgreen case SSH_SMSG_FAILURE: 217176262Sgreen log("Warning: Server denied remote port forwarding."); 217276262Sgreen break; 217376262Sgreen default: 217476262Sgreen /* Unknown packet */ 217576262Sgreen packet_disconnect("Protocol error for port forward request:" 217676262Sgreen "received packet type %d.", type); 217776262Sgreen } 217860573Skris } 217976262Sgreen if (success) { 218076262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 218176262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 218276262Sgreen permitted_opens[num_permitted_opens].listen_port = listen_port; 218376262Sgreen num_permitted_opens++; 218476262Sgreen } 218557429Smarkm} 218657429Smarkm 218757429Smarkm/* 218857429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 218957429Smarkm * listening for the port, and sends back a success reply (or disconnect 219057429Smarkm * message if there was an error). This never returns if there was an error. 219157429Smarkm */ 219257429Smarkm 219360573Skrisvoid 219460573Skrischannel_input_port_forward_request(int is_root, int gateway_ports) 219557429Smarkm{ 219657429Smarkm u_short port, host_port; 219757429Smarkm char *hostname; 219857429Smarkm 219957429Smarkm /* Get arguments from the packet. */ 220057429Smarkm port = packet_get_int(); 220157429Smarkm hostname = packet_get_string(NULL); 220257429Smarkm host_port = packet_get_int(); 220357429Smarkm 220498941Sdes#ifndef HAVE_CYGWIN 220557429Smarkm /* 220657429Smarkm * Check that an unprivileged user is not trying to forward a 220757429Smarkm * privileged port. 220857429Smarkm */ 220957429Smarkm if (port < IPPORT_RESERVED && !is_root) 221057429Smarkm packet_disconnect("Requested forwarding of port %d but user is not root.", 221157429Smarkm port); 221298941Sdes#endif 221376262Sgreen /* Initiate forwarding */ 221492559Sdes channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); 221557429Smarkm 221657429Smarkm /* Free the argument string. */ 221757429Smarkm xfree(hostname); 221857429Smarkm} 221957429Smarkm 222076262Sgreen/* 222176262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 222276262Sgreen * usually called by the server, because the user could connect to any port 222376262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 222476262Sgreen */ 222576262Sgreenvoid 222692559Sdeschannel_permit_all_opens(void) 222776262Sgreen{ 222876262Sgreen if (num_permitted_opens == 0) 222976262Sgreen all_opens_permitted = 1; 223076262Sgreen} 223176262Sgreen 223276262Sgreenvoid 223376262Sgreenchannel_add_permitted_opens(char *host, int port) 223476262Sgreen{ 223576262Sgreen if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 223676262Sgreen fatal("channel_request_remote_forwarding: too many forwards"); 223776262Sgreen debug("allow port forwarding to host %s port %d", host, port); 223876262Sgreen 223976262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 224076262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 224176262Sgreen num_permitted_opens++; 224276262Sgreen 224376262Sgreen all_opens_permitted = 0; 224476262Sgreen} 224576262Sgreen 224676262Sgreenvoid 224776262Sgreenchannel_clear_permitted_opens(void) 224876262Sgreen{ 224976262Sgreen int i; 225076262Sgreen 225176262Sgreen for (i = 0; i < num_permitted_opens; i++) 225276262Sgreen xfree(permitted_opens[i].host_to_connect); 225376262Sgreen num_permitted_opens = 0; 225476262Sgreen 225576262Sgreen} 225676262Sgreen 225776262Sgreen 225876262Sgreen/* return socket to remote host, port */ 225992559Sdesstatic int 226076262Sgreenconnect_to(const char *host, u_short port) 226160573Skris{ 226260573Skris struct addrinfo hints, *ai, *aitop; 226360573Skris char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 226460573Skris int gaierr; 226560573Skris int sock = -1; 226660573Skris 226760573Skris memset(&hints, 0, sizeof(hints)); 226860573Skris hints.ai_family = IPv4or6; 226960573Skris hints.ai_socktype = SOCK_STREAM; 227076262Sgreen snprintf(strport, sizeof strport, "%d", port); 227160573Skris if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 227276262Sgreen error("connect_to %.100s: unknown host (%s)", host, 227376262Sgreen gai_strerror(gaierr)); 227460573Skris return -1; 227560573Skris } 227660573Skris for (ai = aitop; ai; ai = ai->ai_next) { 227760573Skris if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 227860573Skris continue; 227960573Skris if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 228060573Skris strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 228176262Sgreen error("connect_to: getnameinfo failed"); 228260573Skris continue; 228360573Skris } 228460573Skris sock = socket(ai->ai_family, SOCK_STREAM, 0); 228560573Skris if (sock < 0) { 2286113911Sdes if (ai->ai_next == NULL) 2287113911Sdes error("socket: %.100s", strerror(errno)); 2288113911Sdes else 2289113911Sdes verbose("socket: %.100s", strerror(errno)); 229060573Skris continue; 229160573Skris } 229276262Sgreen if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) 229376262Sgreen fatal("connect_to: F_SETFL: %s", strerror(errno)); 229476262Sgreen if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 229576262Sgreen errno != EINPROGRESS) { 229676262Sgreen error("connect_to %.100s port %s: %.100s", ntop, strport, 229760573Skris strerror(errno)); 229860573Skris close(sock); 229976262Sgreen continue; /* fail -- try next */ 230060573Skris } 230160573Skris break; /* success */ 230260573Skris 230360573Skris } 230460573Skris freeaddrinfo(aitop); 230560573Skris if (!ai) { 230676262Sgreen error("connect_to %.100s port %d: failed.", host, port); 230760573Skris return -1; 230860573Skris } 230960573Skris /* success */ 231092559Sdes set_nodelay(sock); 231160573Skris return sock; 231260573Skris} 231376262Sgreen 231476262Sgreenint 231592559Sdeschannel_connect_by_listen_address(u_short listen_port) 231676262Sgreen{ 231776262Sgreen int i; 231876262Sgreen 231976262Sgreen for (i = 0; i < num_permitted_opens; i++) 232076262Sgreen if (permitted_opens[i].listen_port == listen_port) 232176262Sgreen return connect_to( 232276262Sgreen permitted_opens[i].host_to_connect, 232376262Sgreen permitted_opens[i].port_to_connect); 232476262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 232576262Sgreen listen_port); 232676262Sgreen return -1; 232776262Sgreen} 232876262Sgreen 232976262Sgreen/* Check if connecting to that port is permitted and connect. */ 233076262Sgreenint 233176262Sgreenchannel_connect_to(const char *host, u_short port) 233276262Sgreen{ 233376262Sgreen int i, permit; 233476262Sgreen 233576262Sgreen permit = all_opens_permitted; 233676262Sgreen if (!permit) { 233776262Sgreen for (i = 0; i < num_permitted_opens; i++) 233876262Sgreen if (permitted_opens[i].port_to_connect == port && 233976262Sgreen strcmp(permitted_opens[i].host_to_connect, host) == 0) 234076262Sgreen permit = 1; 234176262Sgreen 234276262Sgreen } 234376262Sgreen if (!permit) { 234476262Sgreen log("Received request to connect to host %.100s port %d, " 234576262Sgreen "but the request was denied.", host, port); 234676262Sgreen return -1; 234776262Sgreen } 234876262Sgreen return connect_to(host, port); 234976262Sgreen} 235076262Sgreen 235192559Sdes/* -- X11 forwarding */ 235257429Smarkm 235357429Smarkm/* 235457429Smarkm * Creates an internet domain socket for listening for X11 connections. 235599063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 235699063Sdes * stored in display_numberp , or -1 if an error occurs. 235757429Smarkm */ 235892559Sdesint 235992559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 236099063Sdes int single_connection, u_int *display_numberp) 236157429Smarkm{ 236292559Sdes Channel *nc = NULL; 236357429Smarkm int display_number, sock; 236457429Smarkm u_short port; 236557429Smarkm struct addrinfo hints, *ai, *aitop; 236657429Smarkm char strport[NI_MAXSERV]; 236757429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 236857429Smarkm 236957429Smarkm for (display_number = x11_display_offset; 237092559Sdes display_number < MAX_DISPLAYS; 237192559Sdes display_number++) { 237257429Smarkm port = 6000 + display_number; 237357429Smarkm memset(&hints, 0, sizeof(hints)); 237457429Smarkm hints.ai_family = IPv4or6; 237592559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 237657429Smarkm hints.ai_socktype = SOCK_STREAM; 237757429Smarkm snprintf(strport, sizeof strport, "%d", port); 237857429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 237957429Smarkm error("getaddrinfo: %.100s", gai_strerror(gaierr)); 238092559Sdes return -1; 238157429Smarkm } 238257429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 238357429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 238457429Smarkm continue; 238557429Smarkm sock = socket(ai->ai_family, SOCK_STREAM, 0); 238657429Smarkm if (sock < 0) { 238798941Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 238898941Sdes error("socket: %.100s", strerror(errno)); 238998941Sdes return -1; 239098941Sdes } else { 239198941Sdes debug("x11_create_display_inet: Socket family %d not supported", 239298941Sdes ai->ai_family); 239398941Sdes continue; 239498941Sdes } 239557429Smarkm } 239698941Sdes#ifdef IPV6_V6ONLY 239798941Sdes if (ai->ai_family == AF_INET6) { 239898941Sdes int on = 1; 239998941Sdes if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 240098941Sdes error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 240198941Sdes } 240298941Sdes#endif 240357429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 240457429Smarkm debug("bind port %d: %.100s", port, strerror(errno)); 240557429Smarkm close(sock); 240698941Sdes 240798941Sdes if (ai->ai_next) 240898941Sdes continue; 240998941Sdes 241057429Smarkm for (n = 0; n < num_socks; n++) { 241157429Smarkm close(socks[n]); 241257429Smarkm } 241357429Smarkm num_socks = 0; 241457429Smarkm break; 241557429Smarkm } 241657429Smarkm socks[num_socks++] = sock; 241798941Sdes#ifndef DONT_TRY_OTHER_AF 241857429Smarkm if (num_socks == NUM_SOCKS) 241957429Smarkm break; 242098941Sdes#else 242198941Sdes if (x11_use_localhost) { 242298941Sdes if (num_socks == NUM_SOCKS) 242398941Sdes break; 242498941Sdes } else { 242598941Sdes break; 242698941Sdes } 242798941Sdes#endif 242857429Smarkm } 242976262Sgreen freeaddrinfo(aitop); 243057429Smarkm if (num_socks > 0) 243157429Smarkm break; 243257429Smarkm } 243357429Smarkm if (display_number >= MAX_DISPLAYS) { 243457429Smarkm error("Failed to allocate internet-domain X11 display socket."); 243592559Sdes return -1; 243657429Smarkm } 243757429Smarkm /* Start listening for connections on the socket. */ 243857429Smarkm for (n = 0; n < num_socks; n++) { 243957429Smarkm sock = socks[n]; 244057429Smarkm if (listen(sock, 5) < 0) { 244157429Smarkm error("listen: %.100s", strerror(errno)); 244257429Smarkm close(sock); 244392559Sdes return -1; 244457429Smarkm } 244557429Smarkm } 244657429Smarkm 244757429Smarkm /* Allocate a channel for each socket. */ 244857429Smarkm for (n = 0; n < num_socks; n++) { 244957429Smarkm sock = socks[n]; 245092559Sdes nc = channel_new("x11 listener", 245160573Skris SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 245260573Skris CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 245369587Sgreen 0, xstrdup("X11 inet listener"), 1); 245492559Sdes nc->single_connection = single_connection; 245557429Smarkm } 245657429Smarkm 245792559Sdes /* Return the display number for the DISPLAY environment variable. */ 245899063Sdes *display_numberp = display_number; 245999063Sdes return (0); 246057429Smarkm} 246157429Smarkm 246292559Sdesstatic int 246376262Sgreenconnect_local_xsocket(u_int dnr) 246457429Smarkm{ 246557429Smarkm int sock; 246657429Smarkm struct sockaddr_un addr; 246757429Smarkm 246892559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 246992559Sdes if (sock < 0) 247092559Sdes error("socket: %.100s", strerror(errno)); 247192559Sdes memset(&addr, 0, sizeof(addr)); 247292559Sdes addr.sun_family = AF_UNIX; 247392559Sdes snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 247492559Sdes if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 247592559Sdes return sock; 247692559Sdes close(sock); 247757429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 247857429Smarkm return -1; 247957429Smarkm} 248057429Smarkm 248160573Skrisint 248260573Skrisx11_connect_display(void) 248357429Smarkm{ 248460573Skris int display_number, sock = 0; 248557429Smarkm const char *display; 248660573Skris char buf[1024], *cp; 248757429Smarkm struct addrinfo hints, *ai, *aitop; 248857429Smarkm char strport[NI_MAXSERV]; 248957429Smarkm int gaierr; 249057429Smarkm 249157429Smarkm /* Try to open a socket for the local X server. */ 249257429Smarkm display = getenv("DISPLAY"); 249357429Smarkm if (!display) { 249457429Smarkm error("DISPLAY not set."); 249560573Skris return -1; 249657429Smarkm } 249757429Smarkm /* 249857429Smarkm * Now we decode the value of the DISPLAY variable and make a 249957429Smarkm * connection to the real X server. 250057429Smarkm */ 250157429Smarkm 250257429Smarkm /* 250357429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 250457429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 250557429Smarkm */ 250657429Smarkm if (strncmp(display, "unix:", 5) == 0 || 250757429Smarkm display[0] == ':') { 250857429Smarkm /* Connect to the unix domain socket. */ 250957429Smarkm if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 251057429Smarkm error("Could not parse display number from DISPLAY: %.100s", 251192559Sdes display); 251260573Skris return -1; 251357429Smarkm } 251457429Smarkm /* Create a socket. */ 251557429Smarkm sock = connect_local_xsocket(display_number); 251657429Smarkm if (sock < 0) 251760573Skris return -1; 251857429Smarkm 251957429Smarkm /* OK, we now have a connection to the display. */ 252060573Skris return sock; 252157429Smarkm } 252257429Smarkm /* 252357429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 252457429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 252557429Smarkm */ 252692559Sdes strlcpy(buf, display, sizeof(buf)); 252757429Smarkm cp = strchr(buf, ':'); 252857429Smarkm if (!cp) { 252957429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 253060573Skris return -1; 253157429Smarkm } 253257429Smarkm *cp = 0; 253357429Smarkm /* buf now contains the host name. But first we parse the display number. */ 253457429Smarkm if (sscanf(cp + 1, "%d", &display_number) != 1) { 253557429Smarkm error("Could not parse display number from DISPLAY: %.100s", 253692559Sdes display); 253760573Skris return -1; 253857429Smarkm } 253957429Smarkm 254057429Smarkm /* Look up the host address */ 254157429Smarkm memset(&hints, 0, sizeof(hints)); 254257429Smarkm hints.ai_family = IPv4or6; 254357429Smarkm hints.ai_socktype = SOCK_STREAM; 254457429Smarkm snprintf(strport, sizeof strport, "%d", 6000 + display_number); 254557429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 254657429Smarkm error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 254760573Skris return -1; 254857429Smarkm } 254957429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 255057429Smarkm /* Create a socket. */ 255157429Smarkm sock = socket(ai->ai_family, SOCK_STREAM, 0); 255257429Smarkm if (sock < 0) { 255357429Smarkm debug("socket: %.100s", strerror(errno)); 255460573Skris continue; 255560573Skris } 255660573Skris /* Connect it to the display. */ 255760573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 255860573Skris debug("connect %.100s port %d: %.100s", buf, 255960573Skris 6000 + display_number, strerror(errno)); 256060573Skris close(sock); 256160573Skris continue; 256260573Skris } 256360573Skris /* Success */ 256460573Skris break; 256557429Smarkm } 256657429Smarkm freeaddrinfo(aitop); 256757429Smarkm if (!ai) { 256860573Skris error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 256957429Smarkm strerror(errno)); 257060573Skris return -1; 257157429Smarkm } 257292559Sdes set_nodelay(sock); 257360573Skris return sock; 257460573Skris} 257557429Smarkm 257660573Skris/* 257760573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 257860573Skris * the remote channel number. We should do whatever we want, and respond 257960573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 258060573Skris */ 258157429Smarkm 258260573Skrisvoid 258392559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 258460573Skris{ 258592559Sdes Channel *c = NULL; 258692559Sdes int remote_id, sock = 0; 258760573Skris char *remote_host; 258857429Smarkm 258992559Sdes debug("Received X11 open request."); 259057429Smarkm 259192559Sdes remote_id = packet_get_int(); 259292559Sdes 259392559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 259492559Sdes remote_host = packet_get_string(NULL); 259560573Skris } else { 259660573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 259760573Skris } 259892559Sdes packet_check_eom(); 259960573Skris 260060573Skris /* Obtain a connection to the real X display. */ 260160573Skris sock = x11_connect_display(); 260292559Sdes if (sock != -1) { 260392559Sdes /* Allocate a channel for this connection. */ 260492559Sdes c = channel_new("connected x11 socket", 260592559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 260692559Sdes remote_host, 1); 260792559Sdes c->remote_id = remote_id; 260892559Sdes c->force_drain = 1; 260992559Sdes } 261092559Sdes if (c == NULL) { 261160573Skris /* Send refusal to the remote host. */ 261260573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 261392559Sdes packet_put_int(remote_id); 2614113911Sdes xfree(remote_host); 261560573Skris } else { 261660573Skris /* Send a confirmation to the remote host. */ 261760573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 261892559Sdes packet_put_int(remote_id); 261992559Sdes packet_put_int(c->self); 262060573Skris } 262192559Sdes packet_send(); 262257429Smarkm} 262357429Smarkm 262469587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 262569587Sgreenvoid 262692559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 262769587Sgreen{ 262869587Sgreen int rchan = packet_get_int(); 2629106130Sdes 263092559Sdes switch (type) { 263169587Sgreen case SSH_SMSG_AGENT_OPEN: 263269587Sgreen error("Warning: ssh server tried agent forwarding."); 263369587Sgreen break; 263469587Sgreen case SSH_SMSG_X11_OPEN: 263569587Sgreen error("Warning: ssh server tried X11 forwarding."); 263669587Sgreen break; 263769587Sgreen default: 263892559Sdes error("deny_input_open: type %d", type); 263969587Sgreen break; 264069587Sgreen } 264169587Sgreen error("Warning: this is probably a break in attempt by a malicious server."); 264269587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 264369587Sgreen packet_put_int(rchan); 264469587Sgreen packet_send(); 264569587Sgreen} 264669587Sgreen 264757429Smarkm/* 264857429Smarkm * Requests forwarding of X11 connections, generates fake authentication 264957429Smarkm * data, and enables authentication spoofing. 265092559Sdes * This should be called in the client only. 265157429Smarkm */ 265260573Skrisvoid 265360573Skrisx11_request_forwarding_with_spoofing(int client_session_id, 265460573Skris const char *proto, const char *data) 265557429Smarkm{ 265676262Sgreen u_int data_len = (u_int) strlen(data) / 2; 265776262Sgreen u_int i, value, len; 265857429Smarkm char *new_data; 265957429Smarkm int screen_number; 266057429Smarkm const char *cp; 266157429Smarkm u_int32_t rand = 0; 266257429Smarkm 266357429Smarkm cp = getenv("DISPLAY"); 266457429Smarkm if (cp) 266557429Smarkm cp = strchr(cp, ':'); 266657429Smarkm if (cp) 266757429Smarkm cp = strchr(cp, '.'); 266857429Smarkm if (cp) 266957429Smarkm screen_number = atoi(cp + 1); 267057429Smarkm else 267157429Smarkm screen_number = 0; 267257429Smarkm 267357429Smarkm /* Save protocol name. */ 267457429Smarkm x11_saved_proto = xstrdup(proto); 267557429Smarkm 267657429Smarkm /* 267757429Smarkm * Extract real authentication data and generate fake data of the 267857429Smarkm * same length. 267957429Smarkm */ 268057429Smarkm x11_saved_data = xmalloc(data_len); 268157429Smarkm x11_fake_data = xmalloc(data_len); 268257429Smarkm for (i = 0; i < data_len; i++) { 268357429Smarkm if (sscanf(data + 2 * i, "%2x", &value) != 1) 268457429Smarkm fatal("x11_request_forwarding: bad authentication data: %.100s", data); 268557429Smarkm if (i % 4 == 0) 268657429Smarkm rand = arc4random(); 268757429Smarkm x11_saved_data[i] = value; 268857429Smarkm x11_fake_data[i] = rand & 0xff; 268957429Smarkm rand >>= 8; 269057429Smarkm } 269157429Smarkm x11_saved_data_len = data_len; 269257429Smarkm x11_fake_data_len = data_len; 269357429Smarkm 269457429Smarkm /* Convert the fake data into hex. */ 269576262Sgreen len = 2 * data_len + 1; 269676262Sgreen new_data = xmalloc(len); 269757429Smarkm for (i = 0; i < data_len; i++) 269876262Sgreen snprintf(new_data + 2 * i, len - 2 * i, 269976262Sgreen "%02x", (u_char) x11_fake_data[i]); 270057429Smarkm 270157429Smarkm /* Send the request packet. */ 270260573Skris if (compat20) { 270360573Skris channel_request_start(client_session_id, "x11-req", 0); 270460573Skris packet_put_char(0); /* XXX bool single connection */ 270560573Skris } else { 270660573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 270760573Skris } 270860573Skris packet_put_cstring(proto); 270960573Skris packet_put_cstring(new_data); 271057429Smarkm packet_put_int(screen_number); 271157429Smarkm packet_send(); 271257429Smarkm packet_write_wait(); 271357429Smarkm xfree(new_data); 271457429Smarkm} 271557429Smarkm 271692559Sdes 271792559Sdes/* -- agent forwarding */ 271892559Sdes 271957429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 272057429Smarkm 272160573Skrisvoid 272292559Sdesauth_request_forwarding(void) 272357429Smarkm{ 272457429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 272557429Smarkm packet_send(); 272657429Smarkm packet_write_wait(); 272757429Smarkm} 272857429Smarkm 272957429Smarkm/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 273057429Smarkm 273160573Skrisvoid 273292559Sdesauth_input_open_request(int type, u_int32_t seq, void *ctxt) 273357429Smarkm{ 273492559Sdes Channel *c = NULL; 273592559Sdes int remote_id, sock; 273692559Sdes char *name; 273757429Smarkm 273857429Smarkm /* Read the remote channel number from the message. */ 273992559Sdes remote_id = packet_get_int(); 274092559Sdes packet_check_eom(); 274157429Smarkm 274257429Smarkm /* 274357429Smarkm * Get a connection to the local authentication agent (this may again 274457429Smarkm * get forwarded). 274557429Smarkm */ 274657429Smarkm sock = ssh_get_authentication_socket(); 274757429Smarkm 274857429Smarkm /* 274957429Smarkm * If we could not connect the agent, send an error message back to 275057429Smarkm * the server. This should never happen unless the agent dies, 275157429Smarkm * because authentication forwarding is only enabled if we have an 275257429Smarkm * agent. 275357429Smarkm */ 275492559Sdes if (sock >= 0) { 275592559Sdes name = xstrdup("authentication agent connection"); 275692559Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 275792559Sdes -1, 0, 0, 0, name, 1); 275892559Sdes c->remote_id = remote_id; 275992559Sdes c->force_drain = 1; 276057429Smarkm } 276160573Skris if (c == NULL) { 276292559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 276392559Sdes packet_put_int(remote_id); 276492559Sdes } else { 276592559Sdes /* Send a confirmation to the remote host. */ 276692559Sdes debug("Forwarding authentication connection."); 276792559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 276892559Sdes packet_put_int(remote_id); 276992559Sdes packet_put_int(c->self); 277060573Skris } 277160573Skris packet_send(); 277260573Skris} 2773