1258343Sdes/* $OpenBSD: channels.c,v 1.327 2013/11/08 00:39:15 djm Exp $ */ 2224638Sbrooks/* $FreeBSD$ */ 357429Smarkm/* 457429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 557429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 657429Smarkm * All rights reserved 757429Smarkm * This file contains functions for generic socket connection forwarding. 857429Smarkm * There is also code for initiating connection forwarding for X11 connections, 957429Smarkm * arbitrary tcp/ip connections, and the authentication agent connection. 1060573Skris * 1165668Skris * As far as I am concerned, the code I have written for this software 1265668Skris * can be used freely for any purpose. Any derived versions of this 1365668Skris * software must be clearly marked as such, and if the derived work is 1465668Skris * incompatible with the protocol description in the RFC file, it must be 1565668Skris * called by a name other than "ssh" or "Secure Shell". 1665668Skris * 1760573Skris * SSH2 support added by Markus Friedl. 1892559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 1965668Skris * Copyright (c) 1999 Dug Song. All rights reserved. 2065668Skris * Copyright (c) 1999 Theo de Raadt. All rights reserved. 2165668Skris * 2265668Skris * Redistribution and use in source and binary forms, with or without 2365668Skris * modification, are permitted provided that the following conditions 2465668Skris * are met: 2565668Skris * 1. Redistributions of source code must retain the above copyright 2665668Skris * notice, this list of conditions and the following disclaimer. 2765668Skris * 2. Redistributions in binary form must reproduce the above copyright 2865668Skris * notice, this list of conditions and the following disclaimer in the 2965668Skris * documentation and/or other materials provided with the distribution. 3065668Skris * 3165668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 3265668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3365668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3465668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3565668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3665668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3765668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3865668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3965668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 4065668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4157429Smarkm */ 4257429Smarkm 4357429Smarkm#include "includes.h" 4457429Smarkm 45162856Sdes#include <sys/types.h> 46162856Sdes#include <sys/ioctl.h> 47162856Sdes#include <sys/un.h> 48162856Sdes#include <sys/socket.h> 49162856Sdes#ifdef HAVE_SYS_TIME_H 50162856Sdes# include <sys/time.h> 51162856Sdes#endif 52162856Sdes 53162856Sdes#include <netinet/in.h> 54162856Sdes#include <arpa/inet.h> 55162856Sdes 56162856Sdes#include <errno.h> 57204917Sdes#include <fcntl.h> 58162856Sdes#include <netdb.h> 59162856Sdes#include <stdio.h> 60162856Sdes#include <stdlib.h> 61162856Sdes#include <string.h> 62162856Sdes#include <termios.h> 63162856Sdes#include <unistd.h> 64162856Sdes#include <stdarg.h> 65162856Sdes 66181111Sdes#include "openbsd-compat/sys-queue.h" 67162856Sdes#include "xmalloc.h" 6857429Smarkm#include "ssh.h" 6976262Sgreen#include "ssh1.h" 7076262Sgreen#include "ssh2.h" 7157429Smarkm#include "packet.h" 7276262Sgreen#include "log.h" 7376262Sgreen#include "misc.h" 74162856Sdes#include "buffer.h" 7557429Smarkm#include "channels.h" 7657429Smarkm#include "compat.h" 7776262Sgreen#include "canohost.h" 7865668Skris#include "key.h" 7965668Skris#include "authfd.h" 8092559Sdes#include "pathnames.h" 8165668Skris 8292559Sdes/* -- channel core */ 8357429Smarkm 8457429Smarkm/* 8557429Smarkm * Pointer to an array containing all allocated channels. The array is 8657429Smarkm * dynamically extended as needed. 8757429Smarkm */ 8892559Sdesstatic Channel **channels = NULL; 8957429Smarkm 9057429Smarkm/* 9157429Smarkm * Size of the channel array. All slots of the array must always be 9292559Sdes * initialized (at least the type field); unused slots set to NULL 9357429Smarkm */ 94137019Sdesstatic u_int channels_alloc = 0; 9557429Smarkm 9657429Smarkm/* 9757429Smarkm * Maximum file descriptor value used in any of the channels. This is 9892559Sdes * updated in channel_new. 9957429Smarkm */ 10076262Sgreenstatic int channel_max_fd = 0; 10157429Smarkm 10257429Smarkm 10392559Sdes/* -- tcp forwarding */ 10457429Smarkm 10557429Smarkm/* 10657429Smarkm * Data structure for storing which hosts are permitted for forward requests. 10757429Smarkm * The local sides of any remote forwards are stored in this array to prevent 10857429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 10957429Smarkm * network (which might be behind a firewall). 11057429Smarkm */ 11157429Smarkmtypedef struct { 11260573Skris char *host_to_connect; /* Connect to 'host'. */ 11360573Skris u_short port_to_connect; /* Connect to 'port'. */ 11460573Skris u_short listen_port; /* Remote side should listen port number. */ 11557429Smarkm} ForwardPermission; 11657429Smarkm 117162856Sdes/* List of all permitted host/port pairs to connect by the user. */ 118215116Sdesstatic ForwardPermission *permitted_opens = NULL; 11992559Sdes 120162856Sdes/* List of all permitted host/port pairs to connect by the admin. */ 121215116Sdesstatic ForwardPermission *permitted_adm_opens = NULL; 122162856Sdes 123162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */ 12457429Smarkmstatic int num_permitted_opens = 0; 125162856Sdes 126162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */ 127162856Sdesstatic int num_adm_permitted_opens = 0; 128162856Sdes 129240075Sdes/* special-case port number meaning allow any port */ 130240075Sdes#define FWD_PERMIT_ANY_PORT 0 131240075Sdes 13257429Smarkm/* 13357429Smarkm * If this is true, all opens are permitted. This is the case on the server 13457429Smarkm * on which we have to trust the client anyway, and the user could do 13557429Smarkm * anything after logging in anyway. 13657429Smarkm */ 13757429Smarkmstatic int all_opens_permitted = 0; 13857429Smarkm 13957429Smarkm 14092559Sdes/* -- X11 forwarding */ 14192559Sdes 14292559Sdes/* Maximum number of fake X11 displays to try. */ 14392559Sdes#define MAX_DISPLAYS 1000 14492559Sdes 145149753Sdes/* Saved X11 local (client) display. */ 146149753Sdesstatic char *x11_saved_display = NULL; 147149753Sdes 14892559Sdes/* Saved X11 authentication protocol name. */ 14992559Sdesstatic char *x11_saved_proto = NULL; 15092559Sdes 15192559Sdes/* Saved X11 authentication data. This is the real data. */ 15292559Sdesstatic char *x11_saved_data = NULL; 15392559Sdesstatic u_int x11_saved_data_len = 0; 15492559Sdes 15592559Sdes/* 15692559Sdes * Fake X11 authentication data. This is what the server will be sending us; 15792559Sdes * we should replace any occurrences of this by the real data. 15892559Sdes */ 159162856Sdesstatic u_char *x11_fake_data = NULL; 16092559Sdesstatic u_int x11_fake_data_len; 16192559Sdes 16292559Sdes 16392559Sdes/* -- agent forwarding */ 16492559Sdes 16592559Sdes#define NUM_SOCKS 10 16692559Sdes 16776262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */ 16898941Sdesstatic int IPv4or6 = AF_UNSPEC; 16976262Sgreen 17092559Sdes/* helper */ 17192559Sdesstatic void port_open_helper(Channel *c, char *rtype); 17276262Sgreen 173181111Sdes/* non-blocking connect helpers */ 174181111Sdesstatic int connect_next(struct channel_connect *); 175181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *); 176181111Sdes 177224638Sbrooks/* -- HPN */ 178224638Sbrooks 179224638Sbrooksstatic int hpn_disabled = 0; 180224638Sbrooksstatic u_int buffer_size = CHAN_HPN_MIN_WINDOW_DEFAULT; 181224638Sbrooks 18292559Sdes/* -- channel core */ 18357429Smarkm 18460573SkrisChannel * 185157019Sdeschannel_by_id(int id) 18660573Skris{ 18760573Skris Channel *c; 18892559Sdes 189137019Sdes if (id < 0 || (u_int)id >= channels_alloc) { 190157019Sdes logit("channel_by_id: %d: bad id", id); 19160573Skris return NULL; 19260573Skris } 19392559Sdes c = channels[id]; 19492559Sdes if (c == NULL) { 195157019Sdes logit("channel_by_id: %d: bad id: channel free", id); 19660573Skris return NULL; 19760573Skris } 19860573Skris return c; 19960573Skris} 20060573Skris 20157429Smarkm/* 202157019Sdes * Returns the channel if it is allowed to receive protocol messages. 203157019Sdes * Private channels, like listening sockets, may not receive messages. 204157019Sdes */ 205157019SdesChannel * 206157019Sdeschannel_lookup(int id) 207157019Sdes{ 208157019Sdes Channel *c; 209157019Sdes 210157019Sdes if ((c = channel_by_id(id)) == NULL) 211157019Sdes return (NULL); 212157019Sdes 213162856Sdes switch (c->type) { 214157019Sdes case SSH_CHANNEL_X11_OPEN: 215157019Sdes case SSH_CHANNEL_LARVAL: 216157019Sdes case SSH_CHANNEL_CONNECTING: 217157019Sdes case SSH_CHANNEL_DYNAMIC: 218157019Sdes case SSH_CHANNEL_OPENING: 219157019Sdes case SSH_CHANNEL_OPEN: 220157019Sdes case SSH_CHANNEL_INPUT_DRAINING: 221157019Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 222255767Sdes case SSH_CHANNEL_ABANDONED: 223157019Sdes return (c); 224157019Sdes } 225157019Sdes logit("Non-public channel %d, type %d.", id, c->type); 226157019Sdes return (NULL); 227157019Sdes} 228157019Sdes 229157019Sdes/* 23060573Skris * Register filedescriptors for a channel, used when allocating a channel or 23160573Skris * when the channel consumer/producer is ready, e.g. shell exec'd 23260573Skris */ 23392559Sdesstatic void 23469587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd, 235181111Sdes int extusage, int nonblock, int is_tty) 23660573Skris{ 23760573Skris /* Update the maximum file descriptor value. */ 23876262Sgreen channel_max_fd = MAX(channel_max_fd, rfd); 23976262Sgreen channel_max_fd = MAX(channel_max_fd, wfd); 24076262Sgreen channel_max_fd = MAX(channel_max_fd, efd); 24176262Sgreen 242204917Sdes if (rfd != -1) 243204917Sdes fcntl(rfd, F_SETFD, FD_CLOEXEC); 244204917Sdes if (wfd != -1 && wfd != rfd) 245204917Sdes fcntl(wfd, F_SETFD, FD_CLOEXEC); 246204917Sdes if (efd != -1 && efd != rfd && efd != wfd) 247204917Sdes fcntl(efd, F_SETFD, FD_CLOEXEC); 24860573Skris 24960573Skris c->rfd = rfd; 25060573Skris c->wfd = wfd; 25160573Skris c->sock = (rfd == wfd) ? rfd : -1; 25260573Skris c->efd = efd; 25360573Skris c->extended_usage = extusage; 25469587Sgreen 255181111Sdes if ((c->isatty = is_tty) != 0) 256124207Sdes debug2("channel %d: rfd %d isatty", c->self, c->rfd); 257255767Sdes#ifdef _AIX 258255767Sdes /* XXX: Later AIX versions can't push as much data to tty */ 259181111Sdes c->wfd_isatty = is_tty || isatty(c->wfd); 260255767Sdes#endif 26174500Sgreen 26269587Sgreen /* enable nonblocking mode */ 26369587Sgreen if (nonblock) { 26469587Sgreen if (rfd != -1) 26569587Sgreen set_nonblock(rfd); 26669587Sgreen if (wfd != -1) 26769587Sgreen set_nonblock(wfd); 26869587Sgreen if (efd != -1) 26969587Sgreen set_nonblock(efd); 27069587Sgreen } 27160573Skris} 27260573Skris 27360573Skris/* 27457429Smarkm * Allocate a new channel object and set its type and socket. This will cause 27557429Smarkm * remote_name to be freed. 27657429Smarkm */ 27792559SdesChannel * 27860573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd, 27999063Sdes u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 28057429Smarkm{ 281137019Sdes int found; 282137019Sdes u_int i; 28357429Smarkm Channel *c; 28457429Smarkm 28557429Smarkm /* Do initial allocation if this is the first call. */ 28657429Smarkm if (channels_alloc == 0) { 28757429Smarkm channels_alloc = 10; 288162856Sdes channels = xcalloc(channels_alloc, sizeof(Channel *)); 28957429Smarkm for (i = 0; i < channels_alloc; i++) 29092559Sdes channels[i] = NULL; 29157429Smarkm } 29257429Smarkm /* Try to find a free slot where to put the new channel. */ 29357429Smarkm for (found = -1, i = 0; i < channels_alloc; i++) 29492559Sdes if (channels[i] == NULL) { 29557429Smarkm /* Found a free slot. */ 296137019Sdes found = (int)i; 29757429Smarkm break; 29857429Smarkm } 299137019Sdes if (found < 0) { 30057429Smarkm /* There are no free slots. Take last+1 slot and expand the array. */ 30157429Smarkm found = channels_alloc; 30299063Sdes if (channels_alloc > 10000) 30399063Sdes fatal("channel_new: internal error: channels_alloc %d " 30499063Sdes "too big.", channels_alloc); 305162856Sdes channels = xrealloc(channels, channels_alloc + 10, 306162856Sdes sizeof(Channel *)); 307120489Sjoe channels_alloc += 10; 30869587Sgreen debug2("channel: expanding %d", channels_alloc); 30957429Smarkm for (i = found; i < channels_alloc; i++) 31092559Sdes channels[i] = NULL; 31157429Smarkm } 31292559Sdes /* Initialize and return new channel. */ 313162856Sdes c = channels[found] = xcalloc(1, sizeof(Channel)); 31457429Smarkm buffer_init(&c->input); 31557429Smarkm buffer_init(&c->output); 31660573Skris buffer_init(&c->extended); 317192595Sdes c->path = NULL; 318240075Sdes c->listening_addr = NULL; 319240075Sdes c->listening_port = 0; 32092559Sdes c->ostate = CHAN_OUTPUT_OPEN; 32192559Sdes c->istate = CHAN_INPUT_OPEN; 32292559Sdes c->flags = 0; 323181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 324240075Sdes c->notbefore = 0; 32557429Smarkm c->self = found; 32657429Smarkm c->type = type; 32760573Skris c->ctype = ctype; 328224638Sbrooks c->dynamic_window = 0; 32960573Skris c->local_window = window; 33060573Skris c->local_window_max = window; 33160573Skris c->local_consumed = 0; 33260573Skris c->local_maxpacket = maxpack; 33357429Smarkm c->remote_id = -1; 334124207Sdes c->remote_name = xstrdup(remote_name); 33560573Skris c->remote_window = 0; 33660573Skris c->remote_maxpacket = 0; 33792559Sdes c->force_drain = 0; 33892559Sdes c->single_connection = 0; 33992559Sdes c->detach_user = NULL; 340157019Sdes c->detach_close = 0; 341181111Sdes c->open_confirm = NULL; 342181111Sdes c->open_confirm_ctx = NULL; 34365668Skris c->input_filter = NULL; 344157019Sdes c->output_filter = NULL; 345181111Sdes c->filter_ctx = NULL; 346181111Sdes c->filter_cleanup = NULL; 347204917Sdes c->ctl_chan = -1; 348204917Sdes c->mux_rcb = NULL; 349204917Sdes c->mux_ctx = NULL; 350215116Sdes c->mux_pause = 0; 351204917Sdes c->delayed = 1; /* prevent call to channel_post handler */ 352181111Sdes TAILQ_INIT(&c->status_confirms); 35357429Smarkm debug("channel %d: new [%s]", found, remote_name); 35492559Sdes return c; 35557429Smarkm} 35692559Sdes 35792559Sdesstatic int 35892559Sdeschannel_find_maxfd(void) 35992559Sdes{ 360137019Sdes u_int i; 361137019Sdes int max = 0; 36292559Sdes Channel *c; 36392559Sdes 36492559Sdes for (i = 0; i < channels_alloc; i++) { 36592559Sdes c = channels[i]; 36692559Sdes if (c != NULL) { 36792559Sdes max = MAX(max, c->rfd); 36892559Sdes max = MAX(max, c->wfd); 36992559Sdes max = MAX(max, c->efd); 37092559Sdes } 37192559Sdes } 37292559Sdes return max; 37392559Sdes} 37492559Sdes 37560573Skrisint 37692559Sdeschannel_close_fd(int *fdp) 37760573Skris{ 37892559Sdes int ret = 0, fd = *fdp; 37992559Sdes 38092559Sdes if (fd != -1) { 38192559Sdes ret = close(fd); 38292559Sdes *fdp = -1; 38392559Sdes if (fd == channel_max_fd) 38492559Sdes channel_max_fd = channel_find_maxfd(); 38592559Sdes } 38692559Sdes return ret; 38760573Skris} 38857429Smarkm 38960573Skris/* Close all channel fd/socket. */ 39092559Sdesstatic void 39160573Skrischannel_close_fds(Channel *c) 39257429Smarkm{ 39392559Sdes channel_close_fd(&c->sock); 39492559Sdes channel_close_fd(&c->rfd); 39592559Sdes channel_close_fd(&c->wfd); 39692559Sdes channel_close_fd(&c->efd); 39760573Skris} 39857429Smarkm 39960573Skris/* Free the channel and close its fd/socket. */ 40060573Skrisvoid 40192559Sdeschannel_free(Channel *c) 40260573Skris{ 40392559Sdes char *s; 404137019Sdes u_int i, n; 405181111Sdes struct channel_confirm *cc; 40676262Sgreen 40792559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 40892559Sdes if (channels[i]) 40992559Sdes n++; 410137019Sdes debug("channel %d: free: %s, nchannels %u", c->self, 41192559Sdes c->remote_name ? c->remote_name : "???", n); 41292559Sdes 41392559Sdes s = channel_open_message(); 414124207Sdes debug3("channel %d: status: %s", c->self, s); 415255767Sdes free(s); 41676262Sgreen 41760573Skris if (c->sock != -1) 41860573Skris shutdown(c->sock, SHUT_RDWR); 41960573Skris channel_close_fds(c); 42060573Skris buffer_free(&c->input); 42160573Skris buffer_free(&c->output); 42260573Skris buffer_free(&c->extended); 423255767Sdes free(c->remote_name); 424255767Sdes c->remote_name = NULL; 425255767Sdes free(c->path); 426255767Sdes c->path = NULL; 427255767Sdes free(c->listening_addr); 428255767Sdes c->listening_addr = NULL; 429181111Sdes while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 430181111Sdes if (cc->abandon_cb != NULL) 431181111Sdes cc->abandon_cb(c, cc->ctx); 432181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 433181111Sdes bzero(cc, sizeof(*cc)); 434255767Sdes free(cc); 435181111Sdes } 436181111Sdes if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 437181111Sdes c->filter_cleanup(c->self, c->filter_ctx); 43892559Sdes channels[c->self] = NULL; 439255767Sdes free(c); 44057429Smarkm} 44157429Smarkm 44292559Sdesvoid 44392559Sdeschannel_free_all(void) 44492559Sdes{ 445137019Sdes u_int i; 44692559Sdes 44792559Sdes for (i = 0; i < channels_alloc; i++) 44892559Sdes if (channels[i] != NULL) 44992559Sdes channel_free(channels[i]); 45092559Sdes} 45192559Sdes 45257429Smarkm/* 45392559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 45492559Sdes * descriptors after a fork. 45592559Sdes */ 45692559Sdesvoid 45792559Sdeschannel_close_all(void) 45892559Sdes{ 459137019Sdes u_int i; 46092559Sdes 46192559Sdes for (i = 0; i < channels_alloc; i++) 46292559Sdes if (channels[i] != NULL) 46392559Sdes channel_close_fds(channels[i]); 46492559Sdes} 46592559Sdes 46692559Sdes/* 46792559Sdes * Stop listening to channels. 46892559Sdes */ 46992559Sdesvoid 47092559Sdeschannel_stop_listening(void) 47192559Sdes{ 472137019Sdes u_int i; 47392559Sdes Channel *c; 47492559Sdes 47592559Sdes for (i = 0; i < channels_alloc; i++) { 47692559Sdes c = channels[i]; 47792559Sdes if (c != NULL) { 47892559Sdes switch (c->type) { 47992559Sdes case SSH_CHANNEL_AUTH_SOCKET: 48092559Sdes case SSH_CHANNEL_PORT_LISTENER: 48192559Sdes case SSH_CHANNEL_RPORT_LISTENER: 48292559Sdes case SSH_CHANNEL_X11_LISTENER: 48392559Sdes channel_close_fd(&c->sock); 48492559Sdes channel_free(c); 48592559Sdes break; 48692559Sdes } 48792559Sdes } 48892559Sdes } 48992559Sdes} 49092559Sdes 49192559Sdes/* 49292559Sdes * Returns true if no channel has too much buffered data, and false if one or 49392559Sdes * more channel is overfull. 49492559Sdes */ 49592559Sdesint 49692559Sdeschannel_not_very_much_buffered_data(void) 49792559Sdes{ 49892559Sdes u_int i; 49992559Sdes Channel *c; 50092559Sdes 50192559Sdes for (i = 0; i < channels_alloc; i++) { 50292559Sdes c = channels[i]; 50392559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 50492559Sdes#if 0 50592559Sdes if (!compat20 && 50692559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 507113911Sdes debug2("channel %d: big input buffer %d", 50892559Sdes c->self, buffer_len(&c->input)); 50992559Sdes return 0; 51092559Sdes } 51192559Sdes#endif 51292559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 513124207Sdes debug2("channel %d: big output buffer %u > %u", 51492559Sdes c->self, buffer_len(&c->output), 51592559Sdes packet_get_maxsize()); 51692559Sdes return 0; 51792559Sdes } 51892559Sdes } 51992559Sdes } 52092559Sdes return 1; 52192559Sdes} 52292559Sdes 52392559Sdes/* Returns true if any channel is still open. */ 52492559Sdesint 52592559Sdeschannel_still_open(void) 52692559Sdes{ 527137019Sdes u_int i; 52892559Sdes Channel *c; 52992559Sdes 53092559Sdes for (i = 0; i < channels_alloc; i++) { 53192559Sdes c = channels[i]; 53292559Sdes if (c == NULL) 53392559Sdes continue; 53492559Sdes switch (c->type) { 53592559Sdes case SSH_CHANNEL_X11_LISTENER: 53692559Sdes case SSH_CHANNEL_PORT_LISTENER: 53792559Sdes case SSH_CHANNEL_RPORT_LISTENER: 538204917Sdes case SSH_CHANNEL_MUX_LISTENER: 53992559Sdes case SSH_CHANNEL_CLOSED: 54092559Sdes case SSH_CHANNEL_AUTH_SOCKET: 54192559Sdes case SSH_CHANNEL_DYNAMIC: 54292559Sdes case SSH_CHANNEL_CONNECTING: 54392559Sdes case SSH_CHANNEL_ZOMBIE: 544255767Sdes case SSH_CHANNEL_ABANDONED: 54592559Sdes continue; 54692559Sdes case SSH_CHANNEL_LARVAL: 54792559Sdes if (!compat20) 54892559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 54992559Sdes continue; 55092559Sdes case SSH_CHANNEL_OPENING: 55192559Sdes case SSH_CHANNEL_OPEN: 55292559Sdes case SSH_CHANNEL_X11_OPEN: 553204917Sdes case SSH_CHANNEL_MUX_CLIENT: 55492559Sdes return 1; 55592559Sdes case SSH_CHANNEL_INPUT_DRAINING: 55692559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 55792559Sdes if (!compat13) 55892559Sdes fatal("cannot happen: OUT_DRAIN"); 55992559Sdes return 1; 56092559Sdes default: 56192559Sdes fatal("channel_still_open: bad channel type %d", c->type); 56292559Sdes /* NOTREACHED */ 56392559Sdes } 56492559Sdes } 56592559Sdes return 0; 56692559Sdes} 56792559Sdes 56892559Sdes/* Returns the id of an open channel suitable for keepaliving */ 56992559Sdesint 57092559Sdeschannel_find_open(void) 57192559Sdes{ 572137019Sdes u_int i; 57392559Sdes Channel *c; 57492559Sdes 57592559Sdes for (i = 0; i < channels_alloc; i++) { 57692559Sdes c = channels[i]; 577137019Sdes if (c == NULL || c->remote_id < 0) 57892559Sdes continue; 57992559Sdes switch (c->type) { 58092559Sdes case SSH_CHANNEL_CLOSED: 58192559Sdes case SSH_CHANNEL_DYNAMIC: 58292559Sdes case SSH_CHANNEL_X11_LISTENER: 58392559Sdes case SSH_CHANNEL_PORT_LISTENER: 58492559Sdes case SSH_CHANNEL_RPORT_LISTENER: 585204917Sdes case SSH_CHANNEL_MUX_LISTENER: 586204917Sdes case SSH_CHANNEL_MUX_CLIENT: 58792559Sdes case SSH_CHANNEL_OPENING: 58892559Sdes case SSH_CHANNEL_CONNECTING: 58992559Sdes case SSH_CHANNEL_ZOMBIE: 590255767Sdes case SSH_CHANNEL_ABANDONED: 59192559Sdes continue; 59292559Sdes case SSH_CHANNEL_LARVAL: 59392559Sdes case SSH_CHANNEL_AUTH_SOCKET: 59492559Sdes case SSH_CHANNEL_OPEN: 59592559Sdes case SSH_CHANNEL_X11_OPEN: 59692559Sdes return i; 59792559Sdes case SSH_CHANNEL_INPUT_DRAINING: 59892559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 59992559Sdes if (!compat13) 60092559Sdes fatal("cannot happen: OUT_DRAIN"); 60192559Sdes return i; 60292559Sdes default: 60392559Sdes fatal("channel_find_open: bad channel type %d", c->type); 60492559Sdes /* NOTREACHED */ 60592559Sdes } 60692559Sdes } 60792559Sdes return -1; 60892559Sdes} 60992559Sdes 61092559Sdes 61192559Sdes/* 61292559Sdes * Returns a message describing the currently open forwarded connections, 61392559Sdes * suitable for sending to the client. The message contains crlf pairs for 61492559Sdes * newlines. 61592559Sdes */ 61692559Sdeschar * 61792559Sdeschannel_open_message(void) 61892559Sdes{ 61992559Sdes Buffer buffer; 62092559Sdes Channel *c; 62192559Sdes char buf[1024], *cp; 622137019Sdes u_int i; 62392559Sdes 62492559Sdes buffer_init(&buffer); 62592559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 62692559Sdes buffer_append(&buffer, buf, strlen(buf)); 62792559Sdes for (i = 0; i < channels_alloc; i++) { 62892559Sdes c = channels[i]; 62992559Sdes if (c == NULL) 63092559Sdes continue; 63192559Sdes switch (c->type) { 63292559Sdes case SSH_CHANNEL_X11_LISTENER: 63392559Sdes case SSH_CHANNEL_PORT_LISTENER: 63492559Sdes case SSH_CHANNEL_RPORT_LISTENER: 63592559Sdes case SSH_CHANNEL_CLOSED: 63692559Sdes case SSH_CHANNEL_AUTH_SOCKET: 63792559Sdes case SSH_CHANNEL_ZOMBIE: 638255767Sdes case SSH_CHANNEL_ABANDONED: 639204917Sdes case SSH_CHANNEL_MUX_CLIENT: 640204917Sdes case SSH_CHANNEL_MUX_LISTENER: 64192559Sdes continue; 64292559Sdes case SSH_CHANNEL_LARVAL: 64392559Sdes case SSH_CHANNEL_OPENING: 64492559Sdes case SSH_CHANNEL_CONNECTING: 64592559Sdes case SSH_CHANNEL_DYNAMIC: 64692559Sdes case SSH_CHANNEL_OPEN: 64792559Sdes case SSH_CHANNEL_X11_OPEN: 64892559Sdes case SSH_CHANNEL_INPUT_DRAINING: 64992559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 650137019Sdes snprintf(buf, sizeof buf, 651204917Sdes " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n", 65292559Sdes c->self, c->remote_name, 65392559Sdes c->type, c->remote_id, 65492559Sdes c->istate, buffer_len(&c->input), 65592559Sdes c->ostate, buffer_len(&c->output), 656204917Sdes c->rfd, c->wfd, c->ctl_chan); 65792559Sdes buffer_append(&buffer, buf, strlen(buf)); 65892559Sdes continue; 65992559Sdes default: 66092559Sdes fatal("channel_open_message: bad channel type %d", c->type); 66192559Sdes /* NOTREACHED */ 66292559Sdes } 66392559Sdes } 66492559Sdes buffer_append(&buffer, "\0", 1); 66592559Sdes cp = xstrdup(buffer_ptr(&buffer)); 66692559Sdes buffer_free(&buffer); 66792559Sdes return cp; 66892559Sdes} 66992559Sdes 67092559Sdesvoid 67192559Sdeschannel_send_open(int id) 67292559Sdes{ 67392559Sdes Channel *c = channel_lookup(id); 674106130Sdes 67592559Sdes if (c == NULL) { 676124207Sdes logit("channel_send_open: %d: bad id", id); 67792559Sdes return; 67892559Sdes } 679113911Sdes debug2("channel %d: send open", id); 68092559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 68192559Sdes packet_put_cstring(c->ctype); 68292559Sdes packet_put_int(c->self); 68392559Sdes packet_put_int(c->local_window); 68492559Sdes packet_put_int(c->local_maxpacket); 68592559Sdes packet_send(); 68692559Sdes} 68792559Sdes 68892559Sdesvoid 689113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 69092559Sdes{ 691113911Sdes Channel *c = channel_lookup(id); 692106130Sdes 69392559Sdes if (c == NULL) { 694124207Sdes logit("channel_request_start: %d: unknown channel id", id); 69592559Sdes return; 69692559Sdes } 697137019Sdes debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 69892559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 69992559Sdes packet_put_int(c->remote_id); 70092559Sdes packet_put_cstring(service); 70192559Sdes packet_put_char(wantconfirm); 70292559Sdes} 703162856Sdes 70492559Sdesvoid 705181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb, 706181111Sdes channel_confirm_abandon_cb *abandon_cb, void *ctx) 70792559Sdes{ 708181111Sdes struct channel_confirm *cc; 709181111Sdes Channel *c; 710181111Sdes 711181111Sdes if ((c = channel_lookup(id)) == NULL) 712181111Sdes fatal("channel_register_expect: %d: bad id", id); 713181111Sdes 714258343Sdes cc = xcalloc(1, sizeof(*cc)); 715181111Sdes cc->cb = cb; 716181111Sdes cc->abandon_cb = abandon_cb; 717181111Sdes cc->ctx = ctx; 718181111Sdes TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 719181111Sdes} 720181111Sdes 721181111Sdesvoid 722215116Sdeschannel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) 723181111Sdes{ 72492559Sdes Channel *c = channel_lookup(id); 725106130Sdes 72692559Sdes if (c == NULL) { 727192595Sdes logit("channel_register_open_confirm: %d: bad id", id); 72892559Sdes return; 72992559Sdes } 730181111Sdes c->open_confirm = fn; 731181111Sdes c->open_confirm_ctx = ctx; 73292559Sdes} 733162856Sdes 73492559Sdesvoid 735157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 73692559Sdes{ 737157019Sdes Channel *c = channel_by_id(id); 738106130Sdes 73992559Sdes if (c == NULL) { 740124207Sdes logit("channel_register_cleanup: %d: bad id", id); 74192559Sdes return; 74292559Sdes } 74392559Sdes c->detach_user = fn; 744157019Sdes c->detach_close = do_close; 74592559Sdes} 746162856Sdes 74792559Sdesvoid 74892559Sdeschannel_cancel_cleanup(int id) 74992559Sdes{ 750157019Sdes Channel *c = channel_by_id(id); 751106130Sdes 75292559Sdes if (c == NULL) { 753124207Sdes logit("channel_cancel_cleanup: %d: bad id", id); 75492559Sdes return; 75592559Sdes } 75692559Sdes c->detach_user = NULL; 757157019Sdes c->detach_close = 0; 75892559Sdes} 759162856Sdes 76092559Sdesvoid 761157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn, 762181111Sdes channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 76392559Sdes{ 76492559Sdes Channel *c = channel_lookup(id); 765106130Sdes 76692559Sdes if (c == NULL) { 767124207Sdes logit("channel_register_filter: %d: bad id", id); 76892559Sdes return; 76992559Sdes } 770157019Sdes c->input_filter = ifn; 771157019Sdes c->output_filter = ofn; 772181111Sdes c->filter_ctx = ctx; 773181111Sdes c->filter_cleanup = cfn; 77492559Sdes} 77592559Sdes 77692559Sdesvoid 77792559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 778181111Sdes int extusage, int nonblock, int is_tty, u_int window_max) 77992559Sdes{ 78092559Sdes Channel *c = channel_lookup(id); 781106130Sdes 78292559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 78392559Sdes fatal("channel_activate for non-larval channel %d.", id); 784181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 78592559Sdes c->type = SSH_CHANNEL_OPEN; 78692559Sdes c->local_window = c->local_window_max = window_max; 78792559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 78892559Sdes packet_put_int(c->remote_id); 78992559Sdes packet_put_int(c->local_window); 79092559Sdes packet_send(); 79192559Sdes} 79292559Sdes 79392559Sdes/* 79460573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 79560573Skris * channels in the select bitmasks. 79657429Smarkm */ 79760573Skris/* 79860573Skris * 'channel_post*': perform any appropriate operations for channels which 79960573Skris * have events pending. 80060573Skris */ 801162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 80260573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 80360573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 80457429Smarkm 805162856Sdes/* ARGSUSED */ 80692559Sdesstatic void 807162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 80857429Smarkm{ 80960573Skris FD_SET(c->sock, readset); 81060573Skris} 81160573Skris 812162856Sdes/* ARGSUSED */ 81392559Sdesstatic void 814162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 81576262Sgreen{ 81676262Sgreen debug3("channel %d: waiting for connection", c->self); 81776262Sgreen FD_SET(c->sock, writeset); 81876262Sgreen} 81976262Sgreen 82092559Sdesstatic void 821162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 82260573Skris{ 82360573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 82460573Skris FD_SET(c->sock, readset); 82560573Skris if (buffer_len(&c->output) > 0) 82660573Skris FD_SET(c->sock, writeset); 82760573Skris} 82860573Skris 829224638Sbrooksstatic u_int 830224638Sbrookschannel_tcpwinsz(void) 831224638Sbrooks{ 832224638Sbrooks u_int32_t tcpwinsz; 833224638Sbrooks socklen_t optsz; 834224638Sbrooks int ret, sd; 835224638Sbrooks u_int maxlen; 836224638Sbrooks 837224638Sbrooks /* If we are not on a socket return 128KB. */ 838231584Sed if (!packet_connection_is_on_socket()) 839224638Sbrooks return (128 * 1024); 840224638Sbrooks 841224638Sbrooks tcpwinsz = 0; 842224638Sbrooks optsz = sizeof(tcpwinsz); 843224638Sbrooks sd = packet_get_connection_in(); 844224638Sbrooks ret = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz); 845224638Sbrooks 846224638Sbrooks /* Return no more than the maximum buffer size. */ 847224638Sbrooks maxlen = buffer_get_max_len(); 848224638Sbrooks if ((ret == 0) && tcpwinsz > maxlen) 849224638Sbrooks tcpwinsz = maxlen; 850224638Sbrooks /* In case getsockopt() failed return a minimum. */ 851224638Sbrooks if (tcpwinsz == 0) 852224638Sbrooks tcpwinsz = CHAN_TCP_WINDOW_DEFAULT; 853224638Sbrooks debug2("tcpwinsz: %d for connection: %d", tcpwinsz, sd); 854224638Sbrooks return (tcpwinsz); 855224638Sbrooks} 856224638Sbrooks 85792559Sdesstatic void 858162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 85960573Skris{ 860224638Sbrooks u_int limit; 86160573Skris 862224638Sbrooks /* Check buffer limits. */ 863224638Sbrooks if (!c->tcpwinsz || c->dynamic_window > 0) 864224638Sbrooks c->tcpwinsz = channel_tcpwinsz(); 865224638Sbrooks 866224638Sbrooks limit = MIN(compat20 ? c->remote_window : packet_get_maxsize(), 867224638Sbrooks 2 * c->tcpwinsz); 868231584Sed 86960573Skris if (c->istate == CHAN_INPUT_OPEN && 87092559Sdes limit > 0 && 871162856Sdes buffer_len(&c->input) < limit && 872162856Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 87360573Skris FD_SET(c->rfd, readset); 87460573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 87560573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 87660573Skris if (buffer_len(&c->output) > 0) { 87760573Skris FD_SET(c->wfd, writeset); 87860573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 87998684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 880149753Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 881149753Sdes c->self, c->efd, buffer_len(&c->extended)); 88298684Sdes else 88398684Sdes chan_obuf_empty(c); 88460573Skris } 88560573Skris } 88660573Skris /** XXX check close conditions, too */ 887181111Sdes if (compat20 && c->efd != -1 && 888181111Sdes !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 88960573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 89060573Skris buffer_len(&c->extended) > 0) 89160573Skris FD_SET(c->efd, writeset); 892215116Sdes else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 893215116Sdes (c->extended_usage == CHAN_EXTENDED_READ || 894215116Sdes c->extended_usage == CHAN_EXTENDED_IGNORE) && 89560573Skris buffer_len(&c->extended) < c->remote_window) 89660573Skris FD_SET(c->efd, readset); 89760573Skris } 898137019Sdes /* XXX: What about efd? races? */ 89960573Skris} 90060573Skris 901162856Sdes/* ARGSUSED */ 90292559Sdesstatic void 903162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 90460573Skris{ 90560573Skris if (buffer_len(&c->input) == 0) { 90660573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 90760573Skris packet_put_int(c->remote_id); 90860573Skris packet_send(); 90960573Skris c->type = SSH_CHANNEL_CLOSED; 910124207Sdes debug2("channel %d: closing after input drain.", c->self); 91160573Skris } 91260573Skris} 91360573Skris 914162856Sdes/* ARGSUSED */ 91592559Sdesstatic void 916162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 91760573Skris{ 91860573Skris if (buffer_len(&c->output) == 0) 91992559Sdes chan_mark_dead(c); 92060573Skris else 92160573Skris FD_SET(c->sock, writeset); 92260573Skris} 92360573Skris 92460573Skris/* 92560573Skris * This is a special state for X11 authentication spoofing. An opened X11 92660573Skris * connection (when authentication spoofing is being done) remains in this 92760573Skris * state until the first packet has been completely read. The authentication 92860573Skris * data in that packet is then substituted by the real data if it matches the 92960573Skris * fake data, and the channel is put into normal mode. 93060573Skris * XXX All this happens at the client side. 93192559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 93260573Skris */ 93392559Sdesstatic int 93492559Sdesx11_open_helper(Buffer *b) 93560573Skris{ 93676262Sgreen u_char *ucp; 93776262Sgreen u_int proto_len, data_len; 93857429Smarkm 93960573Skris /* Check if the fixed size part of the packet is in buffer. */ 94092559Sdes if (buffer_len(b) < 12) 94160573Skris return 0; 94257429Smarkm 94360573Skris /* Parse the lengths of variable-length fields. */ 94492559Sdes ucp = buffer_ptr(b); 94560573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 94660573Skris proto_len = 256 * ucp[6] + ucp[7]; 94760573Skris data_len = 256 * ucp[8] + ucp[9]; 94860573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 94960573Skris proto_len = ucp[6] + 256 * ucp[7]; 95060573Skris data_len = ucp[8] + 256 * ucp[9]; 95160573Skris } else { 952124207Sdes debug2("Initial X11 packet contains bad byte order byte: 0x%x", 95392559Sdes ucp[0]); 95460573Skris return -1; 95560573Skris } 95657429Smarkm 95760573Skris /* Check if the whole packet is in buffer. */ 95892559Sdes if (buffer_len(b) < 95960573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 96060573Skris return 0; 96157429Smarkm 96260573Skris /* Check if authentication protocol matches. */ 96360573Skris if (proto_len != strlen(x11_saved_proto) || 96460573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 965124207Sdes debug2("X11 connection uses different authentication protocol."); 96660573Skris return -1; 96760573Skris } 96860573Skris /* Check if authentication data matches our fake data. */ 96960573Skris if (data_len != x11_fake_data_len || 970215116Sdes timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 97160573Skris x11_fake_data, x11_fake_data_len) != 0) { 972124207Sdes debug2("X11 auth data does not match fake data."); 97360573Skris return -1; 97460573Skris } 97560573Skris /* Check fake data length */ 97660573Skris if (x11_fake_data_len != x11_saved_data_len) { 97760573Skris error("X11 fake_data_len %d != saved_data_len %d", 97860573Skris x11_fake_data_len, x11_saved_data_len); 97960573Skris return -1; 98060573Skris } 98160573Skris /* 98260573Skris * Received authentication protocol and data match 98360573Skris * our fake data. Substitute the fake data with real 98460573Skris * data. 98560573Skris */ 98660573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 98760573Skris x11_saved_data, x11_saved_data_len); 98860573Skris return 1; 98960573Skris} 99057429Smarkm 99192559Sdesstatic void 992162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 99360573Skris{ 99492559Sdes int ret = x11_open_helper(&c->output); 995106130Sdes 99660573Skris if (ret == 1) { 99760573Skris /* Start normal processing for the channel. */ 99860573Skris c->type = SSH_CHANNEL_OPEN; 99960573Skris channel_pre_open_13(c, readset, writeset); 100060573Skris } else if (ret == -1) { 100160573Skris /* 100260573Skris * We have received an X11 connection that has bad 100360573Skris * authentication information. 100460573Skris */ 1005124207Sdes logit("X11 connection rejected because of wrong authentication."); 100660573Skris buffer_clear(&c->input); 100760573Skris buffer_clear(&c->output); 100892559Sdes channel_close_fd(&c->sock); 100960573Skris c->sock = -1; 101060573Skris c->type = SSH_CHANNEL_CLOSED; 101160573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 101260573Skris packet_put_int(c->remote_id); 101360573Skris packet_send(); 101460573Skris } 101560573Skris} 101657429Smarkm 101792559Sdesstatic void 1018162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 101960573Skris{ 102092559Sdes int ret = x11_open_helper(&c->output); 102192559Sdes 102292559Sdes /* c->force_drain = 1; */ 102392559Sdes 102460573Skris if (ret == 1) { 102560573Skris c->type = SSH_CHANNEL_OPEN; 102692559Sdes channel_pre_open(c, readset, writeset); 102792559Sdes } else if (ret == -1) { 1028124207Sdes logit("X11 connection rejected because of wrong authentication."); 1029124207Sdes debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 103092559Sdes chan_read_failed(c); 103192559Sdes buffer_clear(&c->input); 103292559Sdes chan_ibuf_empty(c); 103392559Sdes buffer_clear(&c->output); 103492559Sdes /* for proto v1, the peer will send an IEOF */ 103560573Skris if (compat20) 103692559Sdes chan_write_failed(c); 103760573Skris else 103892559Sdes c->type = SSH_CHANNEL_OPEN; 1039124207Sdes debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 104060573Skris } 104160573Skris} 104257429Smarkm 1043204917Sdesstatic void 1044204917Sdeschannel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1045204917Sdes{ 1046215116Sdes if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 1047204917Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 1048204917Sdes FD_SET(c->rfd, readset); 1049204917Sdes if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1050204917Sdes /* clear buffer immediately (discard any partial packet) */ 1051204917Sdes buffer_clear(&c->input); 1052204917Sdes chan_ibuf_empty(c); 1053204917Sdes /* Start output drain. XXX just kill chan? */ 1054204917Sdes chan_rcvd_oclose(c); 1055204917Sdes } 1056204917Sdes if (c->ostate == CHAN_OUTPUT_OPEN || 1057204917Sdes c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1058204917Sdes if (buffer_len(&c->output) > 0) 1059204917Sdes FD_SET(c->wfd, writeset); 1060204917Sdes else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 1061204917Sdes chan_obuf_empty(c); 1062204917Sdes } 1063204917Sdes} 1064204917Sdes 106576262Sgreen/* try to decode a socks4 header */ 1066162856Sdes/* ARGSUSED */ 106792559Sdesstatic int 1068162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 106976262Sgreen{ 1070106130Sdes char *p, *host; 1071192595Sdes u_int len, have, i, found, need; 107292559Sdes char username[256]; 107376262Sgreen struct { 107476262Sgreen u_int8_t version; 107576262Sgreen u_int8_t command; 107676262Sgreen u_int16_t dest_port; 107776262Sgreen struct in_addr dest_addr; 107876262Sgreen } s4_req, s4_rsp; 107976262Sgreen 108076262Sgreen debug2("channel %d: decode socks4", c->self); 108176262Sgreen 108276262Sgreen have = buffer_len(&c->input); 108376262Sgreen len = sizeof(s4_req); 108476262Sgreen if (have < len) 108576262Sgreen return 0; 108676262Sgreen p = buffer_ptr(&c->input); 1087192595Sdes 1088192595Sdes need = 1; 1089192595Sdes /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1090192595Sdes if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1091192595Sdes debug2("channel %d: socks4a request", c->self); 1092192595Sdes /* ... and needs an extra string (the hostname) */ 1093192595Sdes need = 2; 1094192595Sdes } 1095192595Sdes /* Check for terminating NUL on the string(s) */ 109676262Sgreen for (found = 0, i = len; i < have; i++) { 109776262Sgreen if (p[i] == '\0') { 1098192595Sdes found++; 1099192595Sdes if (found == need) 1100192595Sdes break; 110176262Sgreen } 110276262Sgreen if (i > 1024) { 110376262Sgreen /* the peer is probably sending garbage */ 110476262Sgreen debug("channel %d: decode socks4: too long", 110576262Sgreen c->self); 110676262Sgreen return -1; 110776262Sgreen } 110876262Sgreen } 1109192595Sdes if (found < need) 111076262Sgreen return 0; 111176262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 111276262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 111376262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 111476262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 111576262Sgreen have = buffer_len(&c->input); 111676262Sgreen p = buffer_ptr(&c->input); 111776262Sgreen len = strlen(p); 111876262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1119192595Sdes len++; /* trailing '\0' */ 112076262Sgreen if (len > have) 112176262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 112276262Sgreen c->self, len, have); 112376262Sgreen strlcpy(username, p, sizeof(username)); 112476262Sgreen buffer_consume(&c->input, len); 112576262Sgreen 1126255767Sdes free(c->path); 1127255767Sdes c->path = NULL; 1128192595Sdes if (need == 1) { /* SOCKS4: one string */ 1129192595Sdes host = inet_ntoa(s4_req.dest_addr); 1130192595Sdes c->path = xstrdup(host); 1131192595Sdes } else { /* SOCKS4A: two strings */ 1132192595Sdes have = buffer_len(&c->input); 1133192595Sdes p = buffer_ptr(&c->input); 1134192595Sdes len = strlen(p); 1135192595Sdes debug2("channel %d: decode socks4a: host %s/%d", 1136192595Sdes c->self, p, len); 1137192595Sdes len++; /* trailing '\0' */ 1138192595Sdes if (len > have) 1139192595Sdes fatal("channel %d: decode socks4a: len %d > have %d", 1140192595Sdes c->self, len, have); 1141192595Sdes if (len > NI_MAXHOST) { 1142192595Sdes error("channel %d: hostname \"%.100s\" too long", 1143192595Sdes c->self, p); 1144192595Sdes return -1; 1145192595Sdes } 1146192595Sdes c->path = xstrdup(p); 1147192595Sdes buffer_consume(&c->input, len); 1148192595Sdes } 114976262Sgreen c->host_port = ntohs(s4_req.dest_port); 115092559Sdes 1151124207Sdes debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1152192595Sdes c->self, c->path, c->host_port, s4_req.command); 115376262Sgreen 115476262Sgreen if (s4_req.command != 1) { 1155192595Sdes debug("channel %d: cannot handle: %s cn %d", 1156192595Sdes c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 115776262Sgreen return -1; 115876262Sgreen } 115976262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 116076262Sgreen s4_rsp.command = 90; /* cd: req granted */ 116176262Sgreen s4_rsp.dest_port = 0; /* ignored */ 116276262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1163162856Sdes buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 116476262Sgreen return 1; 116576262Sgreen} 116676262Sgreen 1167124207Sdes/* try to decode a socks5 header */ 1168124207Sdes#define SSH_SOCKS5_AUTHDONE 0x1000 1169124207Sdes#define SSH_SOCKS5_NOAUTH 0x00 1170124207Sdes#define SSH_SOCKS5_IPV4 0x01 1171124207Sdes#define SSH_SOCKS5_DOMAIN 0x03 1172124207Sdes#define SSH_SOCKS5_IPV6 0x04 1173124207Sdes#define SSH_SOCKS5_CONNECT 0x01 1174124207Sdes#define SSH_SOCKS5_SUCCESS 0x00 1175124207Sdes 1176162856Sdes/* ARGSUSED */ 1177124207Sdesstatic int 1178162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1179124207Sdes{ 1180124207Sdes struct { 1181124207Sdes u_int8_t version; 1182124207Sdes u_int8_t command; 1183124207Sdes u_int8_t reserved; 1184124207Sdes u_int8_t atyp; 1185124207Sdes } s5_req, s5_rsp; 1186124207Sdes u_int16_t dest_port; 1187255767Sdes char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1188255767Sdes u_char *p; 1189162856Sdes u_int have, need, i, found, nmethods, addrlen, af; 1190124207Sdes 1191124207Sdes debug2("channel %d: decode socks5", c->self); 1192124207Sdes p = buffer_ptr(&c->input); 1193124207Sdes if (p[0] != 0x05) 1194124207Sdes return -1; 1195124207Sdes have = buffer_len(&c->input); 1196124207Sdes if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1197124207Sdes /* format: ver | nmethods | methods */ 1198126273Sdes if (have < 2) 1199124207Sdes return 0; 1200124207Sdes nmethods = p[1]; 1201124207Sdes if (have < nmethods + 2) 1202124207Sdes return 0; 1203124207Sdes /* look for method: "NO AUTHENTICATION REQUIRED" */ 1204181111Sdes for (found = 0, i = 2; i < nmethods + 2; i++) { 1205162856Sdes if (p[i] == SSH_SOCKS5_NOAUTH) { 1206124207Sdes found = 1; 1207124207Sdes break; 1208124207Sdes } 1209124207Sdes } 1210124207Sdes if (!found) { 1211124207Sdes debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1212124207Sdes c->self); 1213124207Sdes return -1; 1214124207Sdes } 1215124207Sdes buffer_consume(&c->input, nmethods + 2); 1216124207Sdes buffer_put_char(&c->output, 0x05); /* version */ 1217124207Sdes buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1218124207Sdes FD_SET(c->sock, writeset); 1219124207Sdes c->flags |= SSH_SOCKS5_AUTHDONE; 1220124207Sdes debug2("channel %d: socks5 auth done", c->self); 1221124207Sdes return 0; /* need more */ 1222124207Sdes } 1223124207Sdes debug2("channel %d: socks5 post auth", c->self); 1224124207Sdes if (have < sizeof(s5_req)+1) 1225124207Sdes return 0; /* need more */ 1226162856Sdes memcpy(&s5_req, p, sizeof(s5_req)); 1227124207Sdes if (s5_req.version != 0x05 || 1228124207Sdes s5_req.command != SSH_SOCKS5_CONNECT || 1229124207Sdes s5_req.reserved != 0x00) { 1230124207Sdes debug2("channel %d: only socks5 connect supported", c->self); 1231124207Sdes return -1; 1232124207Sdes } 1233147005Sdes switch (s5_req.atyp){ 1234124207Sdes case SSH_SOCKS5_IPV4: 1235124207Sdes addrlen = 4; 1236124207Sdes af = AF_INET; 1237124207Sdes break; 1238124207Sdes case SSH_SOCKS5_DOMAIN: 1239124207Sdes addrlen = p[sizeof(s5_req)]; 1240124207Sdes af = -1; 1241124207Sdes break; 1242124207Sdes case SSH_SOCKS5_IPV6: 1243124207Sdes addrlen = 16; 1244124207Sdes af = AF_INET6; 1245124207Sdes break; 1246124207Sdes default: 1247124207Sdes debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1248124207Sdes return -1; 1249124207Sdes } 1250162856Sdes need = sizeof(s5_req) + addrlen + 2; 1251162856Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1252162856Sdes need++; 1253162856Sdes if (have < need) 1254124207Sdes return 0; 1255124207Sdes buffer_consume(&c->input, sizeof(s5_req)); 1256124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1257124207Sdes buffer_consume(&c->input, 1); /* host string length */ 1258255767Sdes buffer_get(&c->input, &dest_addr, addrlen); 1259124207Sdes buffer_get(&c->input, (char *)&dest_port, 2); 1260124207Sdes dest_addr[addrlen] = '\0'; 1261255767Sdes free(c->path); 1262255767Sdes c->path = NULL; 1263192595Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1264192595Sdes if (addrlen >= NI_MAXHOST) { 1265192595Sdes error("channel %d: dynamic request: socks5 hostname " 1266192595Sdes "\"%.100s\" too long", c->self, dest_addr); 1267192595Sdes return -1; 1268192595Sdes } 1269192595Sdes c->path = xstrdup(dest_addr); 1270192595Sdes } else { 1271192595Sdes if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1272192595Sdes return -1; 1273192595Sdes c->path = xstrdup(ntop); 1274192595Sdes } 1275124207Sdes c->host_port = ntohs(dest_port); 1276126273Sdes 1277124207Sdes debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1278124207Sdes c->self, c->path, c->host_port, s5_req.command); 1279124207Sdes 1280124207Sdes s5_rsp.version = 0x05; 1281124207Sdes s5_rsp.command = SSH_SOCKS5_SUCCESS; 1282124207Sdes s5_rsp.reserved = 0; /* ignored */ 1283124207Sdes s5_rsp.atyp = SSH_SOCKS5_IPV4; 1284124207Sdes dest_port = 0; /* ignored */ 1285124207Sdes 1286162856Sdes buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1287255767Sdes buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ 1288162856Sdes buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1289124207Sdes return 1; 1290124207Sdes} 1291124207Sdes 1292204917SdesChannel * 1293204917Sdeschannel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, 1294204917Sdes int in, int out) 1295204917Sdes{ 1296204917Sdes Channel *c; 1297204917Sdes 1298204917Sdes debug("channel_connect_stdio_fwd %s:%d", host_to_connect, 1299204917Sdes port_to_connect); 1300204917Sdes 1301204917Sdes c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, 1302204917Sdes -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1303204917Sdes 0, "stdio-forward", /*nonblock*/0); 1304204917Sdes 1305204917Sdes c->path = xstrdup(host_to_connect); 1306204917Sdes c->host_port = port_to_connect; 1307204917Sdes c->listening_port = 0; 1308204917Sdes c->force_drain = 1; 1309204917Sdes 1310204917Sdes channel_register_fds(c, in, out, -1, 0, 1, 0); 1311204917Sdes port_open_helper(c, "direct-tcpip"); 1312204917Sdes 1313204917Sdes return c; 1314204917Sdes} 1315204917Sdes 131676262Sgreen/* dynamic port forwarding */ 131792559Sdesstatic void 1318162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 131976262Sgreen{ 132076262Sgreen u_char *p; 1321149753Sdes u_int have; 1322149753Sdes int ret; 132376262Sgreen 132476262Sgreen have = buffer_len(&c->input); 132576262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 132676262Sgreen /* buffer_dump(&c->input); */ 132776262Sgreen /* check if the fixed size part of the packet is in buffer. */ 1328124207Sdes if (have < 3) { 132976262Sgreen /* need more */ 133076262Sgreen FD_SET(c->sock, readset); 133176262Sgreen return; 133276262Sgreen } 133376262Sgreen /* try to guess the protocol */ 133476262Sgreen p = buffer_ptr(&c->input); 133576262Sgreen switch (p[0]) { 133676262Sgreen case 0x04: 133776262Sgreen ret = channel_decode_socks4(c, readset, writeset); 133876262Sgreen break; 1339124207Sdes case 0x05: 1340124207Sdes ret = channel_decode_socks5(c, readset, writeset); 1341124207Sdes break; 134276262Sgreen default: 134376262Sgreen ret = -1; 134476262Sgreen break; 134576262Sgreen } 134676262Sgreen if (ret < 0) { 134792559Sdes chan_mark_dead(c); 134876262Sgreen } else if (ret == 0) { 134976262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 135076262Sgreen /* need more */ 135176262Sgreen FD_SET(c->sock, readset); 135276262Sgreen } else { 135376262Sgreen /* switch to the next state */ 135476262Sgreen c->type = SSH_CHANNEL_OPENING; 135576262Sgreen port_open_helper(c, "direct-tcpip"); 135676262Sgreen } 135776262Sgreen} 135876262Sgreen 135960573Skris/* This is our fake X11 server socket. */ 1360162856Sdes/* ARGSUSED */ 136192559Sdesstatic void 1362162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 136360573Skris{ 136492559Sdes Channel *nc; 1365181111Sdes struct sockaddr_storage addr; 1366255767Sdes int newsock, oerrno; 136760573Skris socklen_t addrlen; 136876262Sgreen char buf[16384], *remote_ipaddr; 136960573Skris int remote_port; 137057429Smarkm 137160573Skris if (FD_ISSET(c->sock, readset)) { 137260573Skris debug("X11 connection requested."); 137360573Skris addrlen = sizeof(addr); 1374181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 137592559Sdes if (c->single_connection) { 1376255767Sdes oerrno = errno; 1377124207Sdes debug2("single_connection: closing X11 listener."); 137892559Sdes channel_close_fd(&c->sock); 137992559Sdes chan_mark_dead(c); 1380255767Sdes errno = oerrno; 138192559Sdes } 138260573Skris if (newsock < 0) { 1383255767Sdes if (errno != EINTR && errno != EWOULDBLOCK && 1384255767Sdes errno != ECONNABORTED) 1385255767Sdes error("accept: %.100s", strerror(errno)); 1386240075Sdes if (errno == EMFILE || errno == ENFILE) 1387255767Sdes c->notbefore = monotime() + 1; 138860573Skris return; 138960573Skris } 139092559Sdes set_nodelay(newsock); 139176262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 139260573Skris remote_port = get_peer_port(newsock); 139360573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 139476262Sgreen remote_ipaddr, remote_port); 139557429Smarkm 139692559Sdes nc = channel_new("accepted x11 socket", 139760573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 1398124207Sdes c->local_window_max, c->local_maxpacket, 0, buf, 1); 139960573Skris if (compat20) { 140060573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 140160573Skris packet_put_cstring("x11"); 140292559Sdes packet_put_int(nc->self); 140392559Sdes packet_put_int(nc->local_window_max); 140492559Sdes packet_put_int(nc->local_maxpacket); 140576262Sgreen /* originator ipaddr and port */ 140676262Sgreen packet_put_cstring(remote_ipaddr); 140760573Skris if (datafellows & SSH_BUG_X11FWD) { 1408124207Sdes debug2("ssh2 x11 bug compat mode"); 140957429Smarkm } else { 141060573Skris packet_put_int(remote_port); 141157429Smarkm } 141260573Skris packet_send(); 141360573Skris } else { 141460573Skris packet_start(SSH_SMSG_X11_OPEN); 141592559Sdes packet_put_int(nc->self); 141692559Sdes if (packet_get_protocol_flags() & 141792559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 141892559Sdes packet_put_cstring(buf); 141960573Skris packet_send(); 142057429Smarkm } 1421255767Sdes free(remote_ipaddr); 142257429Smarkm } 142357429Smarkm} 142457429Smarkm 142592559Sdesstatic void 142676262Sgreenport_open_helper(Channel *c, char *rtype) 142776262Sgreen{ 142876262Sgreen int direct; 142976262Sgreen char buf[1024]; 143076262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 1431149753Sdes int remote_port = get_peer_port(c->sock); 143276262Sgreen 1433204917Sdes if (remote_port == -1) { 1434204917Sdes /* Fake addr/port to appease peers that validate it (Tectia) */ 1435255767Sdes free(remote_ipaddr); 1436204917Sdes remote_ipaddr = xstrdup("127.0.0.1"); 1437204917Sdes remote_port = 65535; 1438204917Sdes } 1439204917Sdes 144076262Sgreen direct = (strcmp(rtype, "direct-tcpip") == 0); 144176262Sgreen 144276262Sgreen snprintf(buf, sizeof buf, 144376262Sgreen "%s: listening port %d for %.100s port %d, " 144476262Sgreen "connect from %.200s port %d", 144576262Sgreen rtype, c->listening_port, c->path, c->host_port, 144676262Sgreen remote_ipaddr, remote_port); 144776262Sgreen 1448255767Sdes free(c->remote_name); 144976262Sgreen c->remote_name = xstrdup(buf); 145076262Sgreen 145176262Sgreen if (compat20) { 145276262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 145376262Sgreen packet_put_cstring(rtype); 145476262Sgreen packet_put_int(c->self); 145576262Sgreen packet_put_int(c->local_window_max); 145676262Sgreen packet_put_int(c->local_maxpacket); 145776262Sgreen if (direct) { 145876262Sgreen /* target host, port */ 145976262Sgreen packet_put_cstring(c->path); 146076262Sgreen packet_put_int(c->host_port); 146176262Sgreen } else { 146276262Sgreen /* listen address, port */ 146376262Sgreen packet_put_cstring(c->path); 146476262Sgreen packet_put_int(c->listening_port); 146576262Sgreen } 146676262Sgreen /* originator host and port */ 146776262Sgreen packet_put_cstring(remote_ipaddr); 1468149753Sdes packet_put_int((u_int)remote_port); 146976262Sgreen packet_send(); 147076262Sgreen } else { 147176262Sgreen packet_start(SSH_MSG_PORT_OPEN); 147276262Sgreen packet_put_int(c->self); 147376262Sgreen packet_put_cstring(c->path); 147476262Sgreen packet_put_int(c->host_port); 147592559Sdes if (packet_get_protocol_flags() & 147692559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 147776262Sgreen packet_put_cstring(c->remote_name); 147876262Sgreen packet_send(); 147976262Sgreen } 1480255767Sdes free(remote_ipaddr); 148176262Sgreen} 148276262Sgreen 1483157019Sdesstatic void 1484157019Sdeschannel_set_reuseaddr(int fd) 1485157019Sdes{ 1486157019Sdes int on = 1; 1487157019Sdes 1488157019Sdes /* 1489157019Sdes * Set socket options. 1490157019Sdes * Allow local port reuse in TIME_WAIT. 1491157019Sdes */ 1492157019Sdes if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1493157019Sdes error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1494157019Sdes} 1495157019Sdes 149657429Smarkm/* 149760573Skris * This socket is listening for connections to a forwarded TCP/IP port. 149857429Smarkm */ 1499162856Sdes/* ARGSUSED */ 150092559Sdesstatic void 1501162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 150257429Smarkm{ 150376262Sgreen Channel *nc; 1504181111Sdes struct sockaddr_storage addr; 150592559Sdes int newsock, nextstate; 150657429Smarkm socklen_t addrlen; 150776262Sgreen char *rtype; 150857429Smarkm 150960573Skris if (FD_ISSET(c->sock, readset)) { 151060573Skris debug("Connection to port %d forwarding " 151160573Skris "to %.100s port %d requested.", 151260573Skris c->listening_port, c->path, c->host_port); 151376262Sgreen 151492559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 151592559Sdes nextstate = SSH_CHANNEL_OPENING; 151692559Sdes rtype = "forwarded-tcpip"; 151792559Sdes } else { 151892559Sdes if (c->host_port == 0) { 151992559Sdes nextstate = SSH_CHANNEL_DYNAMIC; 152092559Sdes rtype = "dynamic-tcpip"; 152192559Sdes } else { 152292559Sdes nextstate = SSH_CHANNEL_OPENING; 152392559Sdes rtype = "direct-tcpip"; 152492559Sdes } 152592559Sdes } 152676262Sgreen 152760573Skris addrlen = sizeof(addr); 1528181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 152960573Skris if (newsock < 0) { 1530255767Sdes if (errno != EINTR && errno != EWOULDBLOCK && 1531255767Sdes errno != ECONNABORTED) 1532255767Sdes error("accept: %.100s", strerror(errno)); 1533240075Sdes if (errno == EMFILE || errno == ENFILE) 1534255767Sdes c->notbefore = monotime() + 1; 153560573Skris return; 153660573Skris } 153792559Sdes set_nodelay(newsock); 1538124207Sdes nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1539124207Sdes c->local_window_max, c->local_maxpacket, 0, rtype, 1); 154076262Sgreen nc->listening_port = c->listening_port; 154176262Sgreen nc->host_port = c->host_port; 1542192595Sdes if (c->path != NULL) 1543192595Sdes nc->path = xstrdup(c->path); 154476262Sgreen 1545204917Sdes if (nextstate != SSH_CHANNEL_DYNAMIC) 154676262Sgreen port_open_helper(nc, rtype); 154760573Skris } 154860573Skris} 154957429Smarkm 155060573Skris/* 155160573Skris * This is the authentication agent socket listening for connections from 155260573Skris * clients. 155360573Skris */ 1554162856Sdes/* ARGSUSED */ 155592559Sdesstatic void 1556162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 155760573Skris{ 155892559Sdes Channel *nc; 155992559Sdes int newsock; 1560181111Sdes struct sockaddr_storage addr; 156160573Skris socklen_t addrlen; 156257429Smarkm 156360573Skris if (FD_ISSET(c->sock, readset)) { 156460573Skris addrlen = sizeof(addr); 1565181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 156660573Skris if (newsock < 0) { 1567240075Sdes error("accept from auth socket: %.100s", 1568240075Sdes strerror(errno)); 1569240075Sdes if (errno == EMFILE || errno == ENFILE) 1570255767Sdes c->notbefore = monotime() + 1; 157160573Skris return; 157260573Skris } 157392559Sdes nc = channel_new("accepted auth socket", 157476262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 157576262Sgreen c->local_window_max, c->local_maxpacket, 1576124207Sdes 0, "accepted auth socket", 1); 157776262Sgreen if (compat20) { 157876262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 157976262Sgreen packet_put_cstring("auth-agent@openssh.com"); 158092559Sdes packet_put_int(nc->self); 158176262Sgreen packet_put_int(c->local_window_max); 158276262Sgreen packet_put_int(c->local_maxpacket); 158376262Sgreen } else { 158476262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 158592559Sdes packet_put_int(nc->self); 158676262Sgreen } 158760573Skris packet_send(); 158860573Skris } 158960573Skris} 159057429Smarkm 1591162856Sdes/* ARGSUSED */ 159292559Sdesstatic void 1593162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 159476262Sgreen{ 1595181111Sdes int err = 0, sock; 159692559Sdes socklen_t sz = sizeof(err); 159792559Sdes 159876262Sgreen if (FD_ISSET(c->sock, writeset)) { 159992559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 160092559Sdes err = errno; 160192559Sdes error("getsockopt SO_ERROR failed"); 160292559Sdes } 160392559Sdes if (err == 0) { 1604181111Sdes debug("channel %d: connected to %s port %d", 1605181111Sdes c->self, c->connect_ctx.host, c->connect_ctx.port); 1606181111Sdes channel_connect_ctx_free(&c->connect_ctx); 160792559Sdes c->type = SSH_CHANNEL_OPEN; 160892559Sdes if (compat20) { 160992559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 161092559Sdes packet_put_int(c->remote_id); 161192559Sdes packet_put_int(c->self); 161292559Sdes packet_put_int(c->local_window); 161392559Sdes packet_put_int(c->local_maxpacket); 161492559Sdes } else { 161592559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 161692559Sdes packet_put_int(c->remote_id); 161792559Sdes packet_put_int(c->self); 161892559Sdes } 161976262Sgreen } else { 1620181111Sdes debug("channel %d: connection failed: %s", 162192559Sdes c->self, strerror(err)); 1622181111Sdes /* Try next address, if any */ 1623181111Sdes if ((sock = connect_next(&c->connect_ctx)) > 0) { 1624181111Sdes close(c->sock); 1625181111Sdes c->sock = c->rfd = c->wfd = sock; 1626181111Sdes channel_max_fd = channel_find_maxfd(); 1627181111Sdes return; 1628181111Sdes } 1629181111Sdes /* Exhausted all addresses */ 1630181111Sdes error("connect_to %.100s port %d: failed.", 1631181111Sdes c->connect_ctx.host, c->connect_ctx.port); 1632181111Sdes channel_connect_ctx_free(&c->connect_ctx); 163392559Sdes if (compat20) { 163492559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 163592559Sdes packet_put_int(c->remote_id); 163692559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 163792559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 163892559Sdes packet_put_cstring(strerror(err)); 163992559Sdes packet_put_cstring(""); 164092559Sdes } 164176262Sgreen } else { 164292559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 164392559Sdes packet_put_int(c->remote_id); 164476262Sgreen } 164592559Sdes chan_mark_dead(c); 164676262Sgreen } 164792559Sdes packet_send(); 164876262Sgreen } 164976262Sgreen} 165076262Sgreen 1651162856Sdes/* ARGSUSED */ 165292559Sdesstatic int 1653162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 165460573Skris{ 1655147005Sdes char buf[CHAN_RBUF]; 1656181111Sdes int len, force; 165757429Smarkm 1658181111Sdes force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1659181111Sdes if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1660162856Sdes errno = 0; 166160573Skris len = read(c->rfd, buf, sizeof(buf)); 1662181111Sdes if (len < 0 && (errno == EINTR || 1663181111Sdes ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 166460573Skris return 1; 1665162856Sdes#ifndef PTY_ZEROREAD 166678827Sgreen if (len <= 0) { 1667162856Sdes#else 1668162856Sdes if ((!c->isatty && len <= 0) || 1669162856Sdes (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1670162856Sdes#endif 1671124207Sdes debug2("channel %d: read<=0 rfd %d len %d", 167260573Skris c->self, c->rfd, len); 167376262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1674124207Sdes debug2("channel %d: not open", c->self); 167592559Sdes chan_mark_dead(c); 167676262Sgreen return -1; 167776262Sgreen } else if (compat13) { 167892559Sdes buffer_clear(&c->output); 167960573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 1680124207Sdes debug2("channel %d: input draining.", c->self); 168160573Skris } else { 168260573Skris chan_read_failed(c); 168357429Smarkm } 168460573Skris return -1; 168560573Skris } 168692559Sdes if (c->input_filter != NULL) { 168765668Skris if (c->input_filter(c, buf, len) == -1) { 1688124207Sdes debug2("channel %d: filter stops", c->self); 168965668Skris chan_read_failed(c); 169065668Skris } 1691157019Sdes } else if (c->datagram) { 1692157019Sdes buffer_put_string(&c->input, buf, len); 169365668Skris } else { 169465668Skris buffer_append(&c->input, buf, len); 169565668Skris } 169660573Skris } 169760573Skris return 1; 169860573Skris} 1699162856Sdes 1700162856Sdes/* ARGSUSED */ 170192559Sdesstatic int 1702162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 170360573Skris{ 170476262Sgreen struct termios tio; 1705157019Sdes u_char *data = NULL, *buf; 1706215116Sdes u_int dlen, olen = 0; 170760573Skris int len; 170860573Skris 170960573Skris /* Send buffered output data to the socket. */ 171060573Skris if (c->wfd != -1 && 171160573Skris FD_ISSET(c->wfd, writeset) && 171260573Skris buffer_len(&c->output) > 0) { 1713215116Sdes olen = buffer_len(&c->output); 1714157019Sdes if (c->output_filter != NULL) { 1715157019Sdes if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1716157019Sdes debug2("channel %d: filter stops", c->self); 1717157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1718157019Sdes chan_mark_dead(c); 1719157019Sdes else 1720157019Sdes chan_write_failed(c); 1721157019Sdes return -1; 1722157019Sdes } 1723157019Sdes } else if (c->datagram) { 1724157019Sdes buf = data = buffer_get_string(&c->output, &dlen); 1725157019Sdes } else { 1726157019Sdes buf = data = buffer_ptr(&c->output); 1727157019Sdes dlen = buffer_len(&c->output); 1728157019Sdes } 1729157019Sdes 1730157019Sdes if (c->datagram) { 1731157019Sdes /* ignore truncated writes, datagrams might get lost */ 1732157019Sdes len = write(c->wfd, buf, dlen); 1733255767Sdes free(data); 1734181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1735181111Sdes errno == EWOULDBLOCK)) 1736157019Sdes return 1; 1737157019Sdes if (len <= 0) { 1738157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1739157019Sdes chan_mark_dead(c); 1740157019Sdes else 1741157019Sdes chan_write_failed(c); 1742157019Sdes return -1; 1743157019Sdes } 1744215116Sdes goto out; 1745157019Sdes } 1746106130Sdes#ifdef _AIX 1747126273Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1748126273Sdes if (compat20 && c->wfd_isatty) 1749126273Sdes dlen = MIN(dlen, 8*1024); 1750106130Sdes#endif 1751157019Sdes 1752157019Sdes len = write(c->wfd, buf, dlen); 1753181111Sdes if (len < 0 && 1754181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 175560573Skris return 1; 175660573Skris if (len <= 0) { 175776262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1758124207Sdes debug2("channel %d: not open", c->self); 175992559Sdes chan_mark_dead(c); 176076262Sgreen return -1; 176176262Sgreen } else if (compat13) { 176292559Sdes buffer_clear(&c->output); 1763124207Sdes debug2("channel %d: input draining.", c->self); 176460573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 176560573Skris } else { 176660573Skris chan_write_failed(c); 176757429Smarkm } 176860573Skris return -1; 176960573Skris } 1770197679Sdes#ifndef BROKEN_TCGETATTR_ICANON 1771157019Sdes if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 177274500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 177374500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 177474500Sgreen /* 177574500Sgreen * Simulate echo to reduce the impact of 177676262Sgreen * traffic analysis. We need to match the 177776262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 1778157019Sdes * (4 byte channel id + buf) 177974500Sgreen */ 178076262Sgreen packet_send_ignore(4 + len); 178174500Sgreen packet_send(); 178274500Sgreen } 178374500Sgreen } 1784197679Sdes#endif 178560573Skris buffer_consume(&c->output, len); 178660573Skris } 1787215116Sdes out: 1788215116Sdes if (compat20 && olen > 0) 1789215116Sdes c->local_consumed += olen - buffer_len(&c->output); 179060573Skris return 1; 179160573Skris} 1792162856Sdes 179392559Sdesstatic int 1794162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 179560573Skris{ 1796147005Sdes char buf[CHAN_RBUF]; 179760573Skris int len; 179857429Smarkm 179960573Skris/** XXX handle drain efd, too */ 180060573Skris if (c->efd != -1) { 180160573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 180260573Skris FD_ISSET(c->efd, writeset) && 180360573Skris buffer_len(&c->extended) > 0) { 180460573Skris len = write(c->efd, buffer_ptr(&c->extended), 180560573Skris buffer_len(&c->extended)); 180669587Sgreen debug2("channel %d: written %d to efd %d", 180760573Skris c->self, len, c->efd); 1808181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1809181111Sdes errno == EWOULDBLOCK)) 181076262Sgreen return 1; 181176262Sgreen if (len <= 0) { 181276262Sgreen debug2("channel %d: closing write-efd %d", 181376262Sgreen c->self, c->efd); 181492559Sdes channel_close_fd(&c->efd); 181576262Sgreen } else { 181660573Skris buffer_consume(&c->extended, len); 181760573Skris c->local_consumed += len; 181857429Smarkm } 1819215116Sdes } else if (c->efd != -1 && 1820215116Sdes (c->extended_usage == CHAN_EXTENDED_READ || 1821215116Sdes c->extended_usage == CHAN_EXTENDED_IGNORE) && 1822181111Sdes (c->detach_close || FD_ISSET(c->efd, readset))) { 182360573Skris len = read(c->efd, buf, sizeof(buf)); 182469587Sgreen debug2("channel %d: read %d from efd %d", 182592559Sdes c->self, len, c->efd); 1826181111Sdes if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1827181111Sdes errno == EWOULDBLOCK) && !c->detach_close))) 182876262Sgreen return 1; 182976262Sgreen if (len <= 0) { 183076262Sgreen debug2("channel %d: closing read-efd %d", 183160573Skris c->self, c->efd); 183292559Sdes channel_close_fd(&c->efd); 183376262Sgreen } else { 1834215116Sdes if (c->extended_usage == CHAN_EXTENDED_IGNORE) { 1835215116Sdes debug3("channel %d: discard efd", 1836215116Sdes c->self); 1837215116Sdes } else 1838215116Sdes buffer_append(&c->extended, buf, len); 183976262Sgreen } 184060573Skris } 184160573Skris } 184260573Skris return 1; 184360573Skris} 1844162856Sdes 184592559Sdesstatic int 184676262Sgreenchannel_check_window(Channel *c) 184760573Skris{ 184876262Sgreen if (c->type == SSH_CHANNEL_OPEN && 184976262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1850181111Sdes ((c->local_window_max - c->local_window > 1851181111Sdes c->local_maxpacket*3) || 1852181111Sdes c->local_window < c->local_window_max/2) && 185360573Skris c->local_consumed > 0) { 1854224638Sbrooks u_int addition = 0; 1855224638Sbrooks 1856224638Sbrooks /* Adjust max window size if we are in a dynamic environment. */ 1857224638Sbrooks if (c->dynamic_window && c->tcpwinsz > c->local_window_max) { 1858224638Sbrooks /* 1859224638Sbrooks * Grow the window somewhat aggressively to maintain 1860224638Sbrooks * pressure. 1861224638Sbrooks */ 1862224638Sbrooks addition = 1.5 * (c->tcpwinsz - c->local_window_max); 1863224638Sbrooks c->local_window_max += addition; 1864224638Sbrooks } 186560573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 186660573Skris packet_put_int(c->remote_id); 1867224638Sbrooks packet_put_int(c->local_consumed + addition); 186860573Skris packet_send(); 186969587Sgreen debug2("channel %d: window %d sent adjust %d", 187060573Skris c->self, c->local_window, 187160573Skris c->local_consumed); 1872224638Sbrooks c->local_window += c->local_consumed + addition; 187360573Skris c->local_consumed = 0; 187460573Skris } 187560573Skris return 1; 187660573Skris} 187757429Smarkm 187892559Sdesstatic void 1879162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 188060573Skris{ 188160573Skris channel_handle_rfd(c, readset, writeset); 188260573Skris channel_handle_wfd(c, readset, writeset); 188392559Sdes if (!compat20) 188492559Sdes return; 188560573Skris channel_handle_efd(c, readset, writeset); 188676262Sgreen channel_check_window(c); 188760573Skris} 188860573Skris 1889204917Sdesstatic u_int 1890204917Sdesread_mux(Channel *c, u_int need) 1891204917Sdes{ 1892204917Sdes char buf[CHAN_RBUF]; 1893204917Sdes int len; 1894204917Sdes u_int rlen; 1895204917Sdes 1896204917Sdes if (buffer_len(&c->input) < need) { 1897204917Sdes rlen = need - buffer_len(&c->input); 1898204917Sdes len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); 1899204917Sdes if (len <= 0) { 1900204917Sdes if (errno != EINTR && errno != EAGAIN) { 1901204917Sdes debug2("channel %d: ctl read<=0 rfd %d len %d", 1902204917Sdes c->self, c->rfd, len); 1903204917Sdes chan_read_failed(c); 1904204917Sdes return 0; 1905204917Sdes } 1906204917Sdes } else 1907204917Sdes buffer_append(&c->input, buf, len); 1908204917Sdes } 1909204917Sdes return buffer_len(&c->input); 1910204917Sdes} 1911204917Sdes 1912204917Sdesstatic void 1913204917Sdeschannel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1914204917Sdes{ 1915204917Sdes u_int need; 1916204917Sdes ssize_t len; 1917204917Sdes 1918204917Sdes if (!compat20) 1919204917Sdes fatal("%s: entered with !compat20", __func__); 1920204917Sdes 1921215116Sdes if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && 1922204917Sdes (c->istate == CHAN_INPUT_OPEN || 1923204917Sdes c->istate == CHAN_INPUT_WAIT_DRAIN)) { 1924204917Sdes /* 1925204917Sdes * Don't not read past the precise end of packets to 1926204917Sdes * avoid disrupting fd passing. 1927204917Sdes */ 1928204917Sdes if (read_mux(c, 4) < 4) /* read header */ 1929204917Sdes return; 1930204917Sdes need = get_u32(buffer_ptr(&c->input)); 1931204917Sdes#define CHANNEL_MUX_MAX_PACKET (256 * 1024) 1932204917Sdes if (need > CHANNEL_MUX_MAX_PACKET) { 1933204917Sdes debug2("channel %d: packet too big %u > %u", 1934204917Sdes c->self, CHANNEL_MUX_MAX_PACKET, need); 1935204917Sdes chan_rcvd_oclose(c); 1936204917Sdes return; 1937204917Sdes } 1938204917Sdes if (read_mux(c, need + 4) < need + 4) /* read body */ 1939204917Sdes return; 1940204917Sdes if (c->mux_rcb(c) != 0) { 1941204917Sdes debug("channel %d: mux_rcb failed", c->self); 1942204917Sdes chan_mark_dead(c); 1943204917Sdes return; 1944204917Sdes } 1945204917Sdes } 1946204917Sdes 1947204917Sdes if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && 1948204917Sdes buffer_len(&c->output) > 0) { 1949204917Sdes len = write(c->wfd, buffer_ptr(&c->output), 1950204917Sdes buffer_len(&c->output)); 1951204917Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1952204917Sdes return; 1953204917Sdes if (len <= 0) { 1954204917Sdes chan_mark_dead(c); 1955204917Sdes return; 1956204917Sdes } 1957204917Sdes buffer_consume(&c->output, len); 1958204917Sdes } 1959204917Sdes} 1960204917Sdes 1961204917Sdesstatic void 1962204917Sdeschannel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) 1963204917Sdes{ 1964204917Sdes Channel *nc; 1965204917Sdes struct sockaddr_storage addr; 1966204917Sdes socklen_t addrlen; 1967204917Sdes int newsock; 1968204917Sdes uid_t euid; 1969204917Sdes gid_t egid; 1970204917Sdes 1971204917Sdes if (!FD_ISSET(c->sock, readset)) 1972204917Sdes return; 1973204917Sdes 1974204917Sdes debug("multiplexing control connection"); 1975204917Sdes 1976204917Sdes /* 1977204917Sdes * Accept connection on control socket 1978204917Sdes */ 1979204917Sdes memset(&addr, 0, sizeof(addr)); 1980204917Sdes addrlen = sizeof(addr); 1981204917Sdes if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 1982204917Sdes &addrlen)) == -1) { 1983204917Sdes error("%s accept: %s", __func__, strerror(errno)); 1984240075Sdes if (errno == EMFILE || errno == ENFILE) 1985255767Sdes c->notbefore = monotime() + 1; 1986204917Sdes return; 1987204917Sdes } 1988204917Sdes 1989204917Sdes if (getpeereid(newsock, &euid, &egid) < 0) { 1990204917Sdes error("%s getpeereid failed: %s", __func__, 1991204917Sdes strerror(errno)); 1992204917Sdes close(newsock); 1993204917Sdes return; 1994204917Sdes } 1995204917Sdes if ((euid != 0) && (getuid() != euid)) { 1996204917Sdes error("multiplex uid mismatch: peer euid %u != uid %u", 1997204917Sdes (u_int)euid, (u_int)getuid()); 1998204917Sdes close(newsock); 1999204917Sdes return; 2000204917Sdes } 2001204917Sdes nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, 2002204917Sdes newsock, newsock, -1, c->local_window_max, 2003204917Sdes c->local_maxpacket, 0, "mux-control", 1); 2004204917Sdes nc->mux_rcb = c->mux_rcb; 2005204917Sdes debug3("%s: new mux channel %d fd %d", __func__, 2006204917Sdes nc->self, nc->sock); 2007204917Sdes /* establish state */ 2008204917Sdes nc->mux_rcb(nc); 2009204917Sdes /* mux state transitions must not elicit protocol messages */ 2010204917Sdes nc->flags |= CHAN_LOCAL; 2011204917Sdes} 2012204917Sdes 2013162856Sdes/* ARGSUSED */ 201492559Sdesstatic void 2015162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 201660573Skris{ 201760573Skris int len; 2018106130Sdes 201960573Skris /* Send buffered output data to the socket. */ 202060573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 202160573Skris len = write(c->sock, buffer_ptr(&c->output), 202260573Skris buffer_len(&c->output)); 202360573Skris if (len <= 0) 202492559Sdes buffer_clear(&c->output); 202560573Skris else 202660573Skris buffer_consume(&c->output, len); 202760573Skris } 202860573Skris} 202960573Skris 203092559Sdesstatic void 203160573Skrischannel_handler_init_20(void) 203260573Skris{ 203392559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 203460573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 203560573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 203676262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 203760573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 203876262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 203976262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 204076262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2041204917Sdes channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 2042204917Sdes channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 204360573Skris 204492559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 204560573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 204676262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 204760573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 204876262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 204976262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 205092559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2051204917Sdes channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 2052204917Sdes channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 205360573Skris} 205460573Skris 205592559Sdesstatic void 205660573Skrischannel_handler_init_13(void) 205760573Skris{ 205860573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 205960573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 206060573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 206160573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 206260573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 206360573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 206460573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 206576262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 206676262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 206760573Skris 206892559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 206960573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 207060573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 207160573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 207260573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 207376262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 207492559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 207560573Skris} 207660573Skris 207792559Sdesstatic void 207860573Skrischannel_handler_init_15(void) 207960573Skris{ 208092559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 208160573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 208260573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 208360573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 208460573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 208576262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 208676262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 208760573Skris 208860573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 208960573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 209060573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 209192559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 209276262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 209392559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 209460573Skris} 209560573Skris 209692559Sdesstatic void 209760573Skrischannel_handler_init(void) 209860573Skris{ 209960573Skris int i; 2100106130Sdes 210192559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 210260573Skris channel_pre[i] = NULL; 210360573Skris channel_post[i] = NULL; 210460573Skris } 210560573Skris if (compat20) 210660573Skris channel_handler_init_20(); 210760573Skris else if (compat13) 210860573Skris channel_handler_init_13(); 210960573Skris else 211060573Skris channel_handler_init_15(); 211160573Skris} 211260573Skris 211392559Sdes/* gc dead channels */ 211492559Sdesstatic void 211592559Sdeschannel_garbage_collect(Channel *c) 211692559Sdes{ 211792559Sdes if (c == NULL) 211892559Sdes return; 211992559Sdes if (c->detach_user != NULL) { 2120157019Sdes if (!chan_is_dead(c, c->detach_close)) 212192559Sdes return; 2122124207Sdes debug2("channel %d: gc: notify user", c->self); 212392559Sdes c->detach_user(c->self, NULL); 212492559Sdes /* if we still have a callback */ 212592559Sdes if (c->detach_user != NULL) 212692559Sdes return; 2127124207Sdes debug2("channel %d: gc: user detached", c->self); 212892559Sdes } 212992559Sdes if (!chan_is_dead(c, 1)) 213092559Sdes return; 2131124207Sdes debug2("channel %d: garbage collecting", c->self); 213292559Sdes channel_free(c); 213392559Sdes} 213492559Sdes 213592559Sdesstatic void 2136240075Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, 2137240075Sdes time_t *unpause_secs) 213860573Skris{ 213960573Skris static int did_init = 0; 2140204917Sdes u_int i, oalloc; 214160573Skris Channel *c; 2142240075Sdes time_t now; 214360573Skris 214460573Skris if (!did_init) { 214560573Skris channel_handler_init(); 214660573Skris did_init = 1; 214760573Skris } 2148255767Sdes now = monotime(); 2149240075Sdes if (unpause_secs != NULL) 2150240075Sdes *unpause_secs = 0; 2151204917Sdes for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { 215292559Sdes c = channels[i]; 215392559Sdes if (c == NULL) 215457429Smarkm continue; 2155204917Sdes if (c->delayed) { 2156204917Sdes if (ftab == channel_pre) 2157204917Sdes c->delayed = 0; 2158204917Sdes else 2159204917Sdes continue; 2160204917Sdes } 2161240075Sdes if (ftab[c->type] != NULL) { 2162240075Sdes /* 2163240075Sdes * Run handlers that are not paused. 2164240075Sdes */ 2165240075Sdes if (c->notbefore <= now) 2166240075Sdes (*ftab[c->type])(c, readset, writeset); 2167240075Sdes else if (unpause_secs != NULL) { 2168240075Sdes /* 2169240075Sdes * Collect the time that the earliest 2170240075Sdes * channel comes off pause. 2171240075Sdes */ 2172240075Sdes debug3("%s: chan %d: skip for %d more seconds", 2173240075Sdes __func__, c->self, 2174240075Sdes (int)(c->notbefore - now)); 2175240075Sdes if (*unpause_secs == 0 || 2176240075Sdes (c->notbefore - now) < *unpause_secs) 2177240075Sdes *unpause_secs = c->notbefore - now; 2178240075Sdes } 2179240075Sdes } 218092559Sdes channel_garbage_collect(c); 218157429Smarkm } 2182240075Sdes if (unpause_secs != NULL && *unpause_secs != 0) 2183240075Sdes debug3("%s: first channel unpauses in %d seconds", 2184240075Sdes __func__, (int)*unpause_secs); 218557429Smarkm} 218657429Smarkm 218792559Sdes/* 218892559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 218992559Sdes * select bitmasks. 219092559Sdes */ 219160573Skrisvoid 219276262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2193240075Sdes u_int *nallocp, time_t *minwait_secs, int rekeying) 219460573Skris{ 2195162856Sdes u_int n, sz, nfdset; 219676262Sgreen 219776262Sgreen n = MAX(*maxfdp, channel_max_fd); 219876262Sgreen 2199162856Sdes nfdset = howmany(n+1, NFDBITS); 2200162856Sdes /* Explicitly test here, because xrealloc isn't always called */ 2201162856Sdes if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) 2202162856Sdes fatal("channel_prepare_select: max_fd (%d) is too large", n); 2203162856Sdes sz = nfdset * sizeof(fd_mask); 2204162856Sdes 220592559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 220692559Sdes if (*readsetp == NULL || sz > *nallocp) { 2207162856Sdes *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); 2208162856Sdes *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); 220992559Sdes *nallocp = sz; 221076262Sgreen } 221192559Sdes *maxfdp = n; 221276262Sgreen memset(*readsetp, 0, sz); 221376262Sgreen memset(*writesetp, 0, sz); 221476262Sgreen 221576262Sgreen if (!rekeying) 2216240075Sdes channel_handler(channel_pre, *readsetp, *writesetp, 2217240075Sdes minwait_secs); 221860573Skris} 221960573Skris 222092559Sdes/* 222192559Sdes * After select, perform any appropriate operations for channels which have 222292559Sdes * events pending. 222392559Sdes */ 222460573Skrisvoid 2225162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset) 222660573Skris{ 2227240075Sdes channel_handler(channel_post, readset, writeset, NULL); 222860573Skris} 222960573Skris 223092559Sdes 223176262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 223260573Skrisvoid 223392559Sdeschannel_output_poll(void) 223457429Smarkm{ 223560573Skris Channel *c; 2236137019Sdes u_int i, len; 223757429Smarkm 223857429Smarkm for (i = 0; i < channels_alloc; i++) { 223992559Sdes c = channels[i]; 224092559Sdes if (c == NULL) 224192559Sdes continue; 224257429Smarkm 224392559Sdes /* 224492559Sdes * We are only interested in channels that can have buffered 224592559Sdes * incoming data. 224692559Sdes */ 224757429Smarkm if (compat13) { 224860573Skris if (c->type != SSH_CHANNEL_OPEN && 224960573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 225057429Smarkm continue; 225157429Smarkm } else { 225260573Skris if (c->type != SSH_CHANNEL_OPEN) 225357429Smarkm continue; 225457429Smarkm } 225560573Skris if (compat20 && 225660573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 225776262Sgreen /* XXX is this true? */ 225892559Sdes debug3("channel %d: will not send data after close", c->self); 225960573Skris continue; 226060573Skris } 226157429Smarkm 226257429Smarkm /* Get the amount of buffered data for this channel. */ 226376262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 226476262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 226576262Sgreen (len = buffer_len(&c->input)) > 0) { 2266157019Sdes if (c->datagram) { 2267157019Sdes if (len > 0) { 2268157019Sdes u_char *data; 2269157019Sdes u_int dlen; 2270157019Sdes 2271157019Sdes data = buffer_get_string(&c->input, 2272157019Sdes &dlen); 2273215116Sdes if (dlen > c->remote_window || 2274215116Sdes dlen > c->remote_maxpacket) { 2275215116Sdes debug("channel %d: datagram " 2276215116Sdes "too big for channel", 2277215116Sdes c->self); 2278255767Sdes free(data); 2279215116Sdes continue; 2280215116Sdes } 2281157019Sdes packet_start(SSH2_MSG_CHANNEL_DATA); 2282157019Sdes packet_put_int(c->remote_id); 2283157019Sdes packet_put_string(data, dlen); 2284157019Sdes packet_send(); 2285157019Sdes c->remote_window -= dlen + 4; 2286255767Sdes free(data); 2287157019Sdes } 2288157019Sdes continue; 2289157019Sdes } 229092559Sdes /* 229192559Sdes * Send some data for the other side over the secure 229292559Sdes * connection. 229392559Sdes */ 229460573Skris if (compat20) { 229560573Skris if (len > c->remote_window) 229660573Skris len = c->remote_window; 229760573Skris if (len > c->remote_maxpacket) 229860573Skris len = c->remote_maxpacket; 229957429Smarkm } else { 230060573Skris if (packet_is_interactive()) { 230160573Skris if (len > 1024) 230260573Skris len = 512; 230360573Skris } else { 230460573Skris /* Keep the packets at reasonable size. */ 230560573Skris if (len > packet_get_maxsize()/2) 230660573Skris len = packet_get_maxsize()/2; 230760573Skris } 230857429Smarkm } 230960573Skris if (len > 0) { 231060573Skris packet_start(compat20 ? 231160573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 231260573Skris packet_put_int(c->remote_id); 231360573Skris packet_put_string(buffer_ptr(&c->input), len); 231460573Skris packet_send(); 231560573Skris buffer_consume(&c->input, len); 231660573Skris c->remote_window -= len; 231760573Skris } 231860573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 231957429Smarkm if (compat13) 232057429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 232157429Smarkm /* 232257429Smarkm * input-buffer is empty and read-socket shutdown: 232398684Sdes * tell peer, that we will not send more data: send IEOF. 232498684Sdes * hack for extended data: delay EOF if EFD still in use. 232557429Smarkm */ 232698684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 2327149753Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 2328149753Sdes c->self, c->efd, buffer_len(&c->extended)); 232998684Sdes else 233098684Sdes chan_ibuf_empty(c); 233157429Smarkm } 233260573Skris /* Send extended data, i.e. stderr */ 233360573Skris if (compat20 && 233498684Sdes !(c->flags & CHAN_EOF_SENT) && 233560573Skris c->remote_window > 0 && 233660573Skris (len = buffer_len(&c->extended)) > 0 && 233760573Skris c->extended_usage == CHAN_EXTENDED_READ) { 233899063Sdes debug2("channel %d: rwin %u elen %u euse %d", 233976262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 234076262Sgreen c->extended_usage); 234160573Skris if (len > c->remote_window) 234260573Skris len = c->remote_window; 234360573Skris if (len > c->remote_maxpacket) 234460573Skris len = c->remote_maxpacket; 234560573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 234660573Skris packet_put_int(c->remote_id); 234760573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 234860573Skris packet_put_string(buffer_ptr(&c->extended), len); 234960573Skris packet_send(); 235060573Skris buffer_consume(&c->extended, len); 235160573Skris c->remote_window -= len; 235276262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 235360573Skris } 235457429Smarkm } 235557429Smarkm} 235657429Smarkm 235757429Smarkm 235892559Sdes/* -- protocol input */ 235992559Sdes 2360162856Sdes/* ARGSUSED */ 236160573Skrisvoid 236292559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 236357429Smarkm{ 236457429Smarkm int id; 236557429Smarkm char *data; 2366215116Sdes u_int data_len, win_len; 236760573Skris Channel *c; 236857429Smarkm 236957429Smarkm /* Get the channel number and verify it. */ 237057429Smarkm id = packet_get_int(); 237160573Skris c = channel_lookup(id); 237260573Skris if (c == NULL) 237357429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 237457429Smarkm 237557429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 237660573Skris if (c->type != SSH_CHANNEL_OPEN && 237760573Skris c->type != SSH_CHANNEL_X11_OPEN) 237857429Smarkm return; 237957429Smarkm 238057429Smarkm /* Get the data. */ 2381181111Sdes data = packet_get_string_ptr(&data_len); 2382215116Sdes win_len = data_len; 2383215116Sdes if (c->datagram) 2384215116Sdes win_len += 4; /* string length header */ 238560573Skris 2386126273Sdes /* 2387126273Sdes * Ignore data for protocol > 1.3 if output end is no longer open. 2388126273Sdes * For protocol 2 the sending side is reducing its window as it sends 2389126273Sdes * data, so we must 'fake' consumption of the data in order to ensure 2390126273Sdes * that window updates are sent back. Otherwise the connection might 2391126273Sdes * deadlock. 2392126273Sdes */ 2393126273Sdes if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2394126273Sdes if (compat20) { 2395215116Sdes c->local_window -= win_len; 2396215116Sdes c->local_consumed += win_len; 2397126273Sdes } 2398126273Sdes return; 2399126273Sdes } 2400126273Sdes 240192559Sdes if (compat20) { 2402215116Sdes if (win_len > c->local_maxpacket) { 2403124207Sdes logit("channel %d: rcvd big packet %d, maxpack %d", 2404215116Sdes c->self, win_len, c->local_maxpacket); 240560573Skris } 2406215116Sdes if (win_len > c->local_window) { 2407124207Sdes logit("channel %d: rcvd too much data %d, win %d", 2408215116Sdes c->self, win_len, c->local_window); 240960573Skris return; 241060573Skris } 2411215116Sdes c->local_window -= win_len; 241260573Skris } 2413157019Sdes if (c->datagram) 2414157019Sdes buffer_put_string(&c->output, data, data_len); 2415157019Sdes else 2416157019Sdes buffer_append(&c->output, data, data_len); 2417181111Sdes packet_check_eom(); 241857429Smarkm} 241992559Sdes 2420162856Sdes/* ARGSUSED */ 242160573Skrisvoid 242292559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 242360573Skris{ 242460573Skris int id; 242560573Skris char *data; 242699063Sdes u_int data_len, tcode; 242760573Skris Channel *c; 242857429Smarkm 242960573Skris /* Get the channel number and verify it. */ 243060573Skris id = packet_get_int(); 243160573Skris c = channel_lookup(id); 243260573Skris 243360573Skris if (c == NULL) 243460573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 243560573Skris if (c->type != SSH_CHANNEL_OPEN) { 2436124207Sdes logit("channel %d: ext data for non open", id); 243760573Skris return; 243860573Skris } 243998684Sdes if (c->flags & CHAN_EOF_RCVD) { 244098684Sdes if (datafellows & SSH_BUG_EXTEOF) 244198684Sdes debug("channel %d: accepting ext data after eof", id); 244298684Sdes else 244398684Sdes packet_disconnect("Received extended_data after EOF " 244498684Sdes "on channel %d.", id); 244598684Sdes } 244660573Skris tcode = packet_get_int(); 244760573Skris if (c->efd == -1 || 244860573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 244960573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 2450124207Sdes logit("channel %d: bad ext data", c->self); 245160573Skris return; 245260573Skris } 245360573Skris data = packet_get_string(&data_len); 245492559Sdes packet_check_eom(); 245560573Skris if (data_len > c->local_window) { 2456124207Sdes logit("channel %d: rcvd too much extended_data %d, win %d", 245760573Skris c->self, data_len, c->local_window); 2458255767Sdes free(data); 245960573Skris return; 246060573Skris } 246169587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 246260573Skris c->local_window -= data_len; 246360573Skris buffer_append(&c->extended, data, data_len); 2464255767Sdes free(data); 246560573Skris} 246660573Skris 2467162856Sdes/* ARGSUSED */ 246860573Skrisvoid 246992559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 247060573Skris{ 247160573Skris int id; 247260573Skris Channel *c; 247357429Smarkm 247460573Skris id = packet_get_int(); 247592559Sdes packet_check_eom(); 247660573Skris c = channel_lookup(id); 247760573Skris if (c == NULL) 247860573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 247960573Skris chan_rcvd_ieof(c); 248092559Sdes 248192559Sdes /* XXX force input close */ 248292559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 248392559Sdes debug("channel %d: FORCE input drain", c->self); 248492559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 248592559Sdes if (buffer_len(&c->input) == 0) 248692559Sdes chan_ibuf_empty(c); 248792559Sdes } 248892559Sdes 248960573Skris} 249060573Skris 2491162856Sdes/* ARGSUSED */ 249260573Skrisvoid 249392559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 249457429Smarkm{ 249560573Skris int id; 249660573Skris Channel *c; 249757429Smarkm 249860573Skris id = packet_get_int(); 249992559Sdes packet_check_eom(); 250060573Skris c = channel_lookup(id); 250160573Skris if (c == NULL) 250260573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 250357429Smarkm 250457429Smarkm /* 250557429Smarkm * Send a confirmation that we have closed the channel and no more 250657429Smarkm * data is coming for it. 250757429Smarkm */ 250857429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 250960573Skris packet_put_int(c->remote_id); 251057429Smarkm packet_send(); 251157429Smarkm 251257429Smarkm /* 251357429Smarkm * If the channel is in closed state, we have sent a close request, 251457429Smarkm * and the other side will eventually respond with a confirmation. 251557429Smarkm * Thus, we cannot free the channel here, because then there would be 251657429Smarkm * no-one to receive the confirmation. The channel gets freed when 251757429Smarkm * the confirmation arrives. 251857429Smarkm */ 251960573Skris if (c->type != SSH_CHANNEL_CLOSED) { 252057429Smarkm /* 252157429Smarkm * Not a closed channel - mark it as draining, which will 252257429Smarkm * cause it to be freed later. 252357429Smarkm */ 252492559Sdes buffer_clear(&c->input); 252560573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 252657429Smarkm } 252757429Smarkm} 252857429Smarkm 252960573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2530162856Sdes/* ARGSUSED */ 253160573Skrisvoid 253292559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 253360573Skris{ 253460573Skris int id = packet_get_int(); 253560573Skris Channel *c = channel_lookup(id); 253692559Sdes 253792559Sdes packet_check_eom(); 253860573Skris if (c == NULL) 253960573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 254060573Skris chan_rcvd_oclose(c); 254160573Skris} 254257429Smarkm 2543162856Sdes/* ARGSUSED */ 254460573Skrisvoid 254592559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 254657429Smarkm{ 254760573Skris int id = packet_get_int(); 254860573Skris Channel *c = channel_lookup(id); 254957429Smarkm 255092559Sdes packet_check_eom(); 255160573Skris if (c == NULL) 255260573Skris packet_disconnect("Received close confirmation for " 255360573Skris "out-of-range channel %d.", id); 2554255767Sdes if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) 255560573Skris packet_disconnect("Received close confirmation for " 255660573Skris "non-closed channel %d (type %d).", id, c->type); 255792559Sdes channel_free(c); 255860573Skris} 255957429Smarkm 2560162856Sdes/* ARGSUSED */ 256160573Skrisvoid 256292559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 256360573Skris{ 256460573Skris int id, remote_id; 256560573Skris Channel *c; 256660573Skris 256760573Skris id = packet_get_int(); 256860573Skris c = channel_lookup(id); 256960573Skris 257060573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 257160573Skris packet_disconnect("Received open confirmation for " 257260573Skris "non-opening channel %d.", id); 257360573Skris remote_id = packet_get_int(); 257460573Skris /* Record the remote channel number and mark that the channel is now open. */ 257560573Skris c->remote_id = remote_id; 257660573Skris c->type = SSH_CHANNEL_OPEN; 257760573Skris 257860573Skris if (compat20) { 257960573Skris c->remote_window = packet_get_int(); 258060573Skris c->remote_maxpacket = packet_get_int(); 2581181111Sdes if (c->open_confirm) { 258269587Sgreen debug2("callback start"); 2583215116Sdes c->open_confirm(c->self, 1, c->open_confirm_ctx); 258469587Sgreen debug2("callback done"); 258560573Skris } 2586124207Sdes debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 258760573Skris c->remote_window, c->remote_maxpacket); 258857429Smarkm } 258992559Sdes packet_check_eom(); 259057429Smarkm} 259157429Smarkm 259292559Sdesstatic char * 259392559Sdesreason2txt(int reason) 259492559Sdes{ 259592559Sdes switch (reason) { 259692559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 259792559Sdes return "administratively prohibited"; 259892559Sdes case SSH2_OPEN_CONNECT_FAILED: 259992559Sdes return "connect failed"; 260092559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 260192559Sdes return "unknown channel type"; 260292559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 260392559Sdes return "resource shortage"; 260492559Sdes } 260592559Sdes return "unknown reason"; 260692559Sdes} 260792559Sdes 2608162856Sdes/* ARGSUSED */ 260960573Skrisvoid 261092559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 261157429Smarkm{ 261276262Sgreen int id, reason; 261376262Sgreen char *msg = NULL, *lang = NULL; 261460573Skris Channel *c; 261557429Smarkm 261660573Skris id = packet_get_int(); 261760573Skris c = channel_lookup(id); 261857429Smarkm 261960573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 262060573Skris packet_disconnect("Received open failure for " 262160573Skris "non-opening channel %d.", id); 262260573Skris if (compat20) { 262376262Sgreen reason = packet_get_int(); 262492559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 262576262Sgreen msg = packet_get_string(NULL); 262676262Sgreen lang = packet_get_string(NULL); 262776262Sgreen } 2628124207Sdes logit("channel %d: open failed: %s%s%s", id, 262992559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 2630255767Sdes free(msg); 2631255767Sdes free(lang); 2632215116Sdes if (c->open_confirm) { 2633215116Sdes debug2("callback start"); 2634215116Sdes c->open_confirm(c->self, 0, c->open_confirm_ctx); 2635215116Sdes debug2("callback done"); 2636215116Sdes } 263760573Skris } 263892559Sdes packet_check_eom(); 2639192595Sdes /* Schedule the channel for cleanup/deletion. */ 2640192595Sdes chan_mark_dead(c); 264157429Smarkm} 264257429Smarkm 2643162856Sdes/* ARGSUSED */ 264460573Skrisvoid 264592559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 264660573Skris{ 264760573Skris Channel *c; 264899063Sdes int id; 264999063Sdes u_int adjust; 265057429Smarkm 265160573Skris if (!compat20) 265260573Skris return; 265360573Skris 265457429Smarkm /* Get the channel number and verify it. */ 265560573Skris id = packet_get_int(); 265660573Skris c = channel_lookup(id); 265757429Smarkm 2658157019Sdes if (c == NULL) { 2659157019Sdes logit("Received window adjust for non-open channel %d.", id); 266060573Skris return; 266160573Skris } 266260573Skris adjust = packet_get_int(); 266392559Sdes packet_check_eom(); 266499063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 266560573Skris c->remote_window += adjust; 266657429Smarkm} 266757429Smarkm 2668162856Sdes/* ARGSUSED */ 266960573Skrisvoid 267092559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 267157429Smarkm{ 267292559Sdes Channel *c = NULL; 267392559Sdes u_short host_port; 267492559Sdes char *host, *originator_string; 2675181111Sdes int remote_id; 267657429Smarkm 267792559Sdes remote_id = packet_get_int(); 267892559Sdes host = packet_get_string(NULL); 267992559Sdes host_port = packet_get_int(); 268057429Smarkm 268192559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 268292559Sdes originator_string = packet_get_string(NULL); 268392559Sdes } else { 268492559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 268592559Sdes } 268692559Sdes packet_check_eom(); 2687181111Sdes c = channel_connect_to(host, host_port, 2688181111Sdes "connected socket", originator_string); 2689255767Sdes free(originator_string); 2690255767Sdes free(host); 269192559Sdes if (c == NULL) { 269292559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 269392559Sdes packet_put_int(remote_id); 269492559Sdes packet_send(); 2695181111Sdes } else 2696181111Sdes c->remote_id = remote_id; 269757429Smarkm} 269857429Smarkm 2699181111Sdes/* ARGSUSED */ 2700181111Sdesvoid 2701181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2702181111Sdes{ 2703181111Sdes Channel *c; 2704181111Sdes struct channel_confirm *cc; 2705192595Sdes int id; 270657429Smarkm 2707181111Sdes /* Reset keepalive timeout */ 2708197679Sdes packet_set_alive_timeouts(0); 2709181111Sdes 2710192595Sdes id = packet_get_int(); 2711181111Sdes packet_check_eom(); 2712181111Sdes 2713192595Sdes debug2("channel_input_status_confirm: type %d id %d", type, id); 2714181111Sdes 2715192595Sdes if ((c = channel_lookup(id)) == NULL) { 2716192595Sdes logit("channel_input_status_confirm: %d: unknown", id); 2717181111Sdes return; 2718181111Sdes } 2719181111Sdes ; 2720181111Sdes if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2721181111Sdes return; 2722181111Sdes cc->cb(type, c, cc->ctx); 2723181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 2724181111Sdes bzero(cc, sizeof(*cc)); 2725255767Sdes free(cc); 2726181111Sdes} 2727181111Sdes 272892559Sdes/* -- tcp forwarding */ 272957429Smarkm 273092559Sdesvoid 273192559Sdeschannel_set_af(int af) 273276262Sgreen{ 273392559Sdes IPv4or6 = af; 273476262Sgreen} 273576262Sgreen 2736231584Sedvoid 2737224638Sbrookschannel_set_hpn(int disabled, u_int buf_size) 2738224638Sbrooks{ 2739231584Sed hpn_disabled = disabled; 2740224638Sbrooks buffer_size = buf_size; 2741224638Sbrooks debug("HPN Disabled: %d, HPN Buffer Size: %d", 2742224638Sbrooks hpn_disabled, buffer_size); 2743224638Sbrooks} 2744224638Sbrooks 2745240075Sdes/* 2746240075Sdes * Determine whether or not a port forward listens to loopback, the 2747240075Sdes * specified address or wildcard. On the client, a specified bind 2748240075Sdes * address will always override gateway_ports. On the server, a 2749240075Sdes * gateway_ports of 1 (``yes'') will override the client's specification 2750240075Sdes * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 2751240075Sdes * will bind to whatever address the client asked for. 2752240075Sdes * 2753240075Sdes * Special-case listen_addrs are: 2754240075Sdes * 2755240075Sdes * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2756240075Sdes * "" (empty string), "*" -> wildcard v4/v6 2757240075Sdes * "localhost" -> loopback v4/v6 2758240075Sdes */ 2759240075Sdesstatic const char * 2760240075Sdeschannel_fwd_bind_addr(const char *listen_addr, int *wildcardp, 2761240075Sdes int is_client, int gateway_ports) 2762240075Sdes{ 2763240075Sdes const char *addr = NULL; 2764240075Sdes int wildcard = 0; 2765240075Sdes 2766240075Sdes if (listen_addr == NULL) { 2767240075Sdes /* No address specified: default to gateway_ports setting */ 2768240075Sdes if (gateway_ports) 2769240075Sdes wildcard = 1; 2770240075Sdes } else if (gateway_ports || is_client) { 2771240075Sdes if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2772240075Sdes strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2773240075Sdes *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2774240075Sdes (!is_client && gateway_ports == 1)) 2775240075Sdes wildcard = 1; 2776240075Sdes else if (strcmp(listen_addr, "localhost") != 0) 2777240075Sdes addr = listen_addr; 2778240075Sdes } 2779240075Sdes if (wildcardp != NULL) 2780240075Sdes *wildcardp = wildcard; 2781240075Sdes return addr; 2782240075Sdes} 2783240075Sdes 278492559Sdesstatic int 2785192595Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, 2786192595Sdes u_short listen_port, int *allocated_listen_port, 278792559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 278857429Smarkm{ 278992559Sdes Channel *c; 2790157019Sdes int sock, r, success = 0, wildcard = 0, is_client; 279157429Smarkm struct addrinfo hints, *ai, *aitop; 2792147005Sdes const char *host, *addr; 279357429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2794192595Sdes in_port_t *lport_p; 279557429Smarkm 279692559Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 279792559Sdes listen_addr : host_to_connect; 2798147005Sdes is_client = (type == SSH_CHANNEL_PORT_LISTENER); 279957429Smarkm 280092559Sdes if (host == NULL) { 280192559Sdes error("No forward host name."); 2802149753Sdes return 0; 280376262Sgreen } 2804192595Sdes if (strlen(host) >= NI_MAXHOST) { 280576262Sgreen error("Forward host name too long."); 2806149753Sdes return 0; 280776262Sgreen } 280876262Sgreen 2809240075Sdes /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 2810240075Sdes addr = channel_fwd_bind_addr(listen_addr, &wildcard, 2811240075Sdes is_client, gateway_ports); 2812147005Sdes debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", 2813147005Sdes type, wildcard, (addr == NULL) ? "NULL" : addr); 2814147005Sdes 2815147005Sdes /* 281657429Smarkm * getaddrinfo returns a loopback address if the hostname is 281757429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 281857429Smarkm */ 281957429Smarkm memset(&hints, 0, sizeof(hints)); 282057429Smarkm hints.ai_family = IPv4or6; 2821147005Sdes hints.ai_flags = wildcard ? AI_PASSIVE : 0; 282257429Smarkm hints.ai_socktype = SOCK_STREAM; 282376262Sgreen snprintf(strport, sizeof strport, "%d", listen_port); 2824147005Sdes if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2825147005Sdes if (addr == NULL) { 2826147005Sdes /* This really shouldn't happen */ 2827147005Sdes packet_disconnect("getaddrinfo: fatal error: %s", 2828181111Sdes ssh_gai_strerror(r)); 2829147005Sdes } else { 2830149753Sdes error("channel_setup_fwd_listener: " 2831181111Sdes "getaddrinfo(%.64s): %s", addr, 2832181111Sdes ssh_gai_strerror(r)); 2833147005Sdes } 2834149753Sdes return 0; 2835147005Sdes } 2836192595Sdes if (allocated_listen_port != NULL) 2837192595Sdes *allocated_listen_port = 0; 283857429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 2839192595Sdes switch (ai->ai_family) { 2840192595Sdes case AF_INET: 2841192595Sdes lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 2842192595Sdes sin_port; 2843192595Sdes break; 2844192595Sdes case AF_INET6: 2845192595Sdes lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 2846192595Sdes sin6_port; 2847192595Sdes break; 2848192595Sdes default: 284957429Smarkm continue; 2850192595Sdes } 2851192595Sdes /* 2852192595Sdes * If allocating a port for -R forwards, then use the 2853192595Sdes * same port for all address families. 2854192595Sdes */ 2855192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2856192595Sdes allocated_listen_port != NULL && *allocated_listen_port > 0) 2857192595Sdes *lport_p = htons(*allocated_listen_port); 2858192595Sdes 285957429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 286057429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 286192559Sdes error("channel_setup_fwd_listener: getnameinfo failed"); 286257429Smarkm continue; 286357429Smarkm } 286457429Smarkm /* Create a port to listen for the host. */ 2865124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 286657429Smarkm if (sock < 0) { 286757429Smarkm /* this is no error since kernel may not support ipv6 */ 286857429Smarkm verbose("socket: %.100s", strerror(errno)); 286957429Smarkm continue; 287057429Smarkm } 2871106130Sdes 2872157019Sdes channel_set_reuseaddr(sock); 2873204917Sdes if (ai->ai_family == AF_INET6) 2874204917Sdes sock_set_v6only(sock); 2875157019Sdes 2876192595Sdes debug("Local forwarding listening on %s port %s.", 2877192595Sdes ntop, strport); 287857429Smarkm 287957429Smarkm /* Bind the socket to the address. */ 288057429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 288157429Smarkm /* address can be in use ipv6 address is already bound */ 288298941Sdes if (!ai->ai_next) 288398941Sdes error("bind: %.100s", strerror(errno)); 288498941Sdes else 288598941Sdes verbose("bind: %.100s", strerror(errno)); 288698941Sdes 288757429Smarkm close(sock); 288857429Smarkm continue; 288957429Smarkm } 289057429Smarkm /* Start listening for connections on the socket. */ 2891126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 289257429Smarkm error("listen: %.100s", strerror(errno)); 289357429Smarkm close(sock); 289457429Smarkm continue; 289557429Smarkm } 2896192595Sdes 2897192595Sdes /* 2898192595Sdes * listen_port == 0 requests a dynamically allocated port - 2899192595Sdes * record what we got. 2900192595Sdes */ 2901192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2902192595Sdes allocated_listen_port != NULL && 2903192595Sdes *allocated_listen_port == 0) { 2904192595Sdes *allocated_listen_port = get_sock_port(sock, 1); 2905192595Sdes debug("Allocated listen port %d", 2906192595Sdes *allocated_listen_port); 2907192595Sdes } 2908192595Sdes 2909224638Sbrooks /* 2910224638Sbrooks * Allocate a channel number for the socket. Explicitly test 2911224638Sbrooks * for hpn disabled option. If true use smaller window size. 2912224638Sbrooks */ 2913224638Sbrooks if (hpn_disabled) 2914224638Sbrooks c = channel_new("port listener", type, sock, sock, -1, 2915224638Sbrooks CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2916224638Sbrooks 0, "port listener", 1); 2917231584Sed else 2918231584Sed c = channel_new("port listener", type, sock, sock, -1, 2919231584Sed buffer_size, CHAN_TCP_PACKET_DEFAULT, 2920231584Sed 0, "port listener", 1); 2921192595Sdes c->path = xstrdup(host); 292292559Sdes c->host_port = port_to_connect; 2923240075Sdes c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 2924240075Sdes if (listen_port == 0 && allocated_listen_port != NULL && 2925240075Sdes !(datafellows & SSH_BUG_DYNAMIC_RPORT)) 2926240075Sdes c->listening_port = *allocated_listen_port; 2927240075Sdes else 2928240075Sdes c->listening_port = listen_port; 292957429Smarkm success = 1; 293057429Smarkm } 293157429Smarkm if (success == 0) 293292559Sdes error("channel_setup_fwd_listener: cannot listen to port: %d", 293376262Sgreen listen_port); 293457429Smarkm freeaddrinfo(aitop); 293576262Sgreen return success; 293657429Smarkm} 293757429Smarkm 2938137019Sdesint 2939137019Sdeschannel_cancel_rport_listener(const char *host, u_short port) 2940137019Sdes{ 2941137019Sdes u_int i; 2942137019Sdes int found = 0; 2943137019Sdes 2944147005Sdes for (i = 0; i < channels_alloc; i++) { 2945137019Sdes Channel *c = channels[i]; 2946240075Sdes if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 2947240075Sdes continue; 2948240075Sdes if (strcmp(c->path, host) == 0 && c->listening_port == port) { 2949240075Sdes debug2("%s: close channel %d", __func__, i); 2950240075Sdes channel_free(c); 2951240075Sdes found = 1; 2952240075Sdes } 2953240075Sdes } 2954137019Sdes 2955240075Sdes return (found); 2956240075Sdes} 2957240075Sdes 2958240075Sdesint 2959240075Sdeschannel_cancel_lport_listener(const char *lhost, u_short lport, 2960240075Sdes int cport, int gateway_ports) 2961240075Sdes{ 2962240075Sdes u_int i; 2963240075Sdes int found = 0; 2964240075Sdes const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports); 2965240075Sdes 2966240075Sdes for (i = 0; i < channels_alloc; i++) { 2967240075Sdes Channel *c = channels[i]; 2968240075Sdes if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 2969240075Sdes continue; 2970240075Sdes if (c->listening_port != lport) 2971240075Sdes continue; 2972240075Sdes if (cport == CHANNEL_CANCEL_PORT_STATIC) { 2973240075Sdes /* skip dynamic forwardings */ 2974240075Sdes if (c->host_port == 0) 2975240075Sdes continue; 2976240075Sdes } else { 2977240075Sdes if (c->host_port != cport) 2978240075Sdes continue; 2979240075Sdes } 2980240075Sdes if ((c->listening_addr == NULL && addr != NULL) || 2981240075Sdes (c->listening_addr != NULL && addr == NULL)) 2982240075Sdes continue; 2983240075Sdes if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 2984147005Sdes debug2("%s: close channel %d", __func__, i); 2985137019Sdes channel_free(c); 2986137019Sdes found = 1; 2987137019Sdes } 2988137019Sdes } 2989137019Sdes 2990137019Sdes return (found); 2991137019Sdes} 2992137019Sdes 299392559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 299492559Sdesint 2995147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, 299692559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 299792559Sdes{ 299892559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2999192595Sdes listen_host, listen_port, NULL, host_to_connect, port_to_connect, 3000147005Sdes gateway_ports); 300192559Sdes} 300292559Sdes 300392559Sdes/* protocol v2 remote port fwd, used by sshd */ 300492559Sdesint 300592559Sdeschannel_setup_remote_fwd_listener(const char *listen_address, 3006192595Sdes u_short listen_port, int *allocated_listen_port, int gateway_ports) 300792559Sdes{ 300892559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 3009192595Sdes listen_address, listen_port, allocated_listen_port, 3010192595Sdes NULL, 0, gateway_ports); 301192559Sdes} 301292559Sdes 301357429Smarkm/* 3014240075Sdes * Translate the requested rfwd listen host to something usable for 3015240075Sdes * this server. 3016240075Sdes */ 3017240075Sdesstatic const char * 3018240075Sdeschannel_rfwd_bind_host(const char *listen_host) 3019240075Sdes{ 3020240075Sdes if (listen_host == NULL) { 3021240075Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 3022240075Sdes return "127.0.0.1"; 3023240075Sdes else 3024240075Sdes return "localhost"; 3025240075Sdes } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 3026240075Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 3027240075Sdes return "0.0.0.0"; 3028240075Sdes else 3029240075Sdes return ""; 3030240075Sdes } else 3031240075Sdes return listen_host; 3032240075Sdes} 3033240075Sdes 3034240075Sdes/* 303557429Smarkm * Initiate forwarding of connections to port "port" on remote host through 303657429Smarkm * the secure channel to host:port from local side. 3037240075Sdes * Returns handle (index) for updating the dynamic listen port with 3038240075Sdes * channel_update_permitted_opens(). 303957429Smarkm */ 3040162856Sdesint 3041147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port, 304276262Sgreen const char *host_to_connect, u_short port_to_connect) 304357429Smarkm{ 3044240075Sdes int type, success = 0, idx = -1; 304576262Sgreen 304657429Smarkm /* Send the forward request to the remote side. */ 304760573Skris if (compat20) { 304860573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 304960573Skris packet_put_cstring("tcpip-forward"); 3050240075Sdes packet_put_char(1); /* boolean: want reply */ 3051240075Sdes packet_put_cstring(channel_rfwd_bind_host(listen_host)); 305260573Skris packet_put_int(listen_port); 305376262Sgreen packet_send(); 305476262Sgreen packet_write_wait(); 305576262Sgreen /* Assume that server accepts the request */ 305676262Sgreen success = 1; 305760573Skris } else { 305860573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 305960573Skris packet_put_int(listen_port); 306060573Skris packet_put_cstring(host_to_connect); 306160573Skris packet_put_int(port_to_connect); 306260573Skris packet_send(); 306360573Skris packet_write_wait(); 306476262Sgreen 306576262Sgreen /* Wait for response from the remote side. */ 306692559Sdes type = packet_read(); 306776262Sgreen switch (type) { 306876262Sgreen case SSH_SMSG_SUCCESS: 306976262Sgreen success = 1; 307076262Sgreen break; 307176262Sgreen case SSH_SMSG_FAILURE: 307276262Sgreen break; 307376262Sgreen default: 307476262Sgreen /* Unknown packet */ 307576262Sgreen packet_disconnect("Protocol error for port forward request:" 307676262Sgreen "received packet type %d.", type); 307776262Sgreen } 307860573Skris } 307976262Sgreen if (success) { 3080215116Sdes /* Record that connection to this host/port is permitted. */ 3081215116Sdes permitted_opens = xrealloc(permitted_opens, 3082215116Sdes num_permitted_opens + 1, sizeof(*permitted_opens)); 3083240075Sdes idx = num_permitted_opens++; 3084240075Sdes permitted_opens[idx].host_to_connect = xstrdup(host_to_connect); 3085240075Sdes permitted_opens[idx].port_to_connect = port_to_connect; 3086240075Sdes permitted_opens[idx].listen_port = listen_port; 308776262Sgreen } 3088240075Sdes return (idx); 308957429Smarkm} 309057429Smarkm 309157429Smarkm/* 3092137019Sdes * Request cancellation of remote forwarding of connection host:port from 3093137019Sdes * local side. 3094137019Sdes */ 3095240075Sdesint 3096147005Sdeschannel_request_rforward_cancel(const char *host, u_short port) 3097137019Sdes{ 3098137019Sdes int i; 3099137019Sdes 3100137019Sdes if (!compat20) 3101240075Sdes return -1; 3102137019Sdes 3103137019Sdes for (i = 0; i < num_permitted_opens; i++) { 3104137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3105137019Sdes permitted_opens[i].listen_port == port) 3106137019Sdes break; 3107137019Sdes } 3108137019Sdes if (i >= num_permitted_opens) { 3109137019Sdes debug("%s: requested forward not found", __func__); 3110240075Sdes return -1; 3111137019Sdes } 3112137019Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 3113137019Sdes packet_put_cstring("cancel-tcpip-forward"); 3114137019Sdes packet_put_char(0); 3115240075Sdes packet_put_cstring(channel_rfwd_bind_host(host)); 3116137019Sdes packet_put_int(port); 3117137019Sdes packet_send(); 3118137019Sdes 3119137019Sdes permitted_opens[i].listen_port = 0; 3120137019Sdes permitted_opens[i].port_to_connect = 0; 3121255767Sdes free(permitted_opens[i].host_to_connect); 3122137019Sdes permitted_opens[i].host_to_connect = NULL; 3123240075Sdes 3124240075Sdes return 0; 3125137019Sdes} 3126137019Sdes 3127137019Sdes/* 312857429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 312957429Smarkm * listening for the port, and sends back a success reply (or disconnect 3130162856Sdes * message if there was an error). 313157429Smarkm */ 3132162856Sdesint 313360573Skrischannel_input_port_forward_request(int is_root, int gateway_ports) 313457429Smarkm{ 313557429Smarkm u_short port, host_port; 3136162856Sdes int success = 0; 313757429Smarkm char *hostname; 313857429Smarkm 313957429Smarkm /* Get arguments from the packet. */ 314057429Smarkm port = packet_get_int(); 314157429Smarkm hostname = packet_get_string(NULL); 314257429Smarkm host_port = packet_get_int(); 314357429Smarkm 314498941Sdes#ifndef HAVE_CYGWIN 314557429Smarkm /* 314657429Smarkm * Check that an unprivileged user is not trying to forward a 314757429Smarkm * privileged port. 314857429Smarkm */ 314957429Smarkm if (port < IPPORT_RESERVED && !is_root) 3150124207Sdes packet_disconnect( 3151124207Sdes "Requested forwarding of port %d but user is not root.", 3152124207Sdes port); 3153124207Sdes if (host_port == 0) 3154124207Sdes packet_disconnect("Dynamic forwarding denied."); 315598941Sdes#endif 3156124207Sdes 315776262Sgreen /* Initiate forwarding */ 3158162856Sdes success = channel_setup_local_fwd_listener(NULL, port, hostname, 3159147005Sdes host_port, gateway_ports); 316057429Smarkm 316157429Smarkm /* Free the argument string. */ 3162255767Sdes free(hostname); 3163162856Sdes 3164162856Sdes return (success ? 0 : -1); 316557429Smarkm} 316657429Smarkm 316776262Sgreen/* 316876262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 316976262Sgreen * usually called by the server, because the user could connect to any port 317076262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 317176262Sgreen */ 317276262Sgreenvoid 317392559Sdeschannel_permit_all_opens(void) 317476262Sgreen{ 317576262Sgreen if (num_permitted_opens == 0) 317676262Sgreen all_opens_permitted = 1; 317776262Sgreen} 317876262Sgreen 317976262Sgreenvoid 318076262Sgreenchannel_add_permitted_opens(char *host, int port) 318176262Sgreen{ 318276262Sgreen debug("allow port forwarding to host %s port %d", host, port); 318376262Sgreen 3184215116Sdes permitted_opens = xrealloc(permitted_opens, 3185215116Sdes num_permitted_opens + 1, sizeof(*permitted_opens)); 318676262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 318776262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 318876262Sgreen num_permitted_opens++; 318976262Sgreen 319076262Sgreen all_opens_permitted = 0; 319176262Sgreen} 319276262Sgreen 3193240075Sdes/* 3194240075Sdes * Update the listen port for a dynamic remote forward, after 3195240075Sdes * the actual 'newport' has been allocated. If 'newport' < 0 is 3196240075Sdes * passed then they entry will be invalidated. 3197240075Sdes */ 3198240075Sdesvoid 3199240075Sdeschannel_update_permitted_opens(int idx, int newport) 3200240075Sdes{ 3201240075Sdes if (idx < 0 || idx >= num_permitted_opens) { 3202240075Sdes debug("channel_update_permitted_opens: index out of range:" 3203240075Sdes " %d num_permitted_opens %d", idx, num_permitted_opens); 3204240075Sdes return; 3205240075Sdes } 3206240075Sdes debug("%s allowed port %d for forwarding to host %s port %d", 3207240075Sdes newport > 0 ? "Updating" : "Removing", 3208240075Sdes newport, 3209240075Sdes permitted_opens[idx].host_to_connect, 3210240075Sdes permitted_opens[idx].port_to_connect); 3211240075Sdes if (newport >= 0) { 3212240075Sdes permitted_opens[idx].listen_port = 3213240075Sdes (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 3214240075Sdes } else { 3215240075Sdes permitted_opens[idx].listen_port = 0; 3216240075Sdes permitted_opens[idx].port_to_connect = 0; 3217255767Sdes free(permitted_opens[idx].host_to_connect); 3218240075Sdes permitted_opens[idx].host_to_connect = NULL; 3219240075Sdes } 3220240075Sdes} 3221240075Sdes 3222162856Sdesint 3223162856Sdeschannel_add_adm_permitted_opens(char *host, int port) 3224162856Sdes{ 3225162856Sdes debug("config allows port forwarding to host %s port %d", host, port); 3226162856Sdes 3227215116Sdes permitted_adm_opens = xrealloc(permitted_adm_opens, 3228215116Sdes num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); 3229162856Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect 3230162856Sdes = xstrdup(host); 3231162856Sdes permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 3232162856Sdes return ++num_adm_permitted_opens; 3233162856Sdes} 3234162856Sdes 323576262Sgreenvoid 3236240075Sdeschannel_disable_adm_local_opens(void) 3237240075Sdes{ 3238248619Sdes channel_clear_adm_permitted_opens(); 3239248619Sdes permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens)); 3240248619Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; 3241248619Sdes num_adm_permitted_opens = 1; 3242240075Sdes} 3243240075Sdes 3244240075Sdesvoid 324576262Sgreenchannel_clear_permitted_opens(void) 324676262Sgreen{ 324776262Sgreen int i; 324876262Sgreen 324976262Sgreen for (i = 0; i < num_permitted_opens; i++) 3250255767Sdes free(permitted_opens[i].host_to_connect); 3251255767Sdes free(permitted_opens); 3252255767Sdes permitted_opens = NULL; 325376262Sgreen num_permitted_opens = 0; 3254162856Sdes} 325576262Sgreen 3256162856Sdesvoid 3257162856Sdeschannel_clear_adm_permitted_opens(void) 3258162856Sdes{ 3259162856Sdes int i; 3260162856Sdes 3261162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3262255767Sdes free(permitted_adm_opens[i].host_to_connect); 3263255767Sdes free(permitted_adm_opens); 3264255767Sdes permitted_adm_opens = NULL; 3265162856Sdes num_adm_permitted_opens = 0; 326676262Sgreen} 326776262Sgreen 3268181111Sdesvoid 3269181111Sdeschannel_print_adm_permitted_opens(void) 3270181111Sdes{ 3271181111Sdes int i; 3272181111Sdes 3273192595Sdes printf("permitopen"); 3274192595Sdes if (num_adm_permitted_opens == 0) { 3275192595Sdes printf(" any\n"); 3276192595Sdes return; 3277192595Sdes } 3278181111Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3279240075Sdes if (permitted_adm_opens[i].host_to_connect == NULL) 3280240075Sdes printf(" none"); 3281240075Sdes else 3282181111Sdes printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 3283181111Sdes permitted_adm_opens[i].port_to_connect); 3284192595Sdes printf("\n"); 3285181111Sdes} 3286181111Sdes 3287240075Sdes/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 3288240075Sdesint 3289240075Sdespermitopen_port(const char *p) 3290240075Sdes{ 3291240075Sdes int port; 3292240075Sdes 3293240075Sdes if (strcmp(p, "*") == 0) 3294240075Sdes return FWD_PERMIT_ANY_PORT; 3295240075Sdes if ((port = a2port(p)) > 0) 3296240075Sdes return port; 3297240075Sdes return -1; 3298240075Sdes} 3299240075Sdes 3300240075Sdesstatic int 3301240075Sdesport_match(u_short allowedport, u_short requestedport) 3302240075Sdes{ 3303240075Sdes if (allowedport == FWD_PERMIT_ANY_PORT || 3304240075Sdes allowedport == requestedport) 3305240075Sdes return 1; 3306240075Sdes return 0; 3307240075Sdes} 3308240075Sdes 3309181111Sdes/* Try to start non-blocking connect to next host in cctx list */ 331092559Sdesstatic int 3311181111Sdesconnect_next(struct channel_connect *cctx) 331260573Skris{ 3313181111Sdes int sock, saved_errno; 331460573Skris char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 331560573Skris 3316181111Sdes for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 3317181111Sdes if (cctx->ai->ai_family != AF_INET && 3318181111Sdes cctx->ai->ai_family != AF_INET6) 331960573Skris continue; 3320181111Sdes if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 3321181111Sdes ntop, sizeof(ntop), strport, sizeof(strport), 3322181111Sdes NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3323181111Sdes error("connect_next: getnameinfo failed"); 332460573Skris continue; 332560573Skris } 3326181111Sdes if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 3327181111Sdes cctx->ai->ai_protocol)) == -1) { 3328181111Sdes if (cctx->ai->ai_next == NULL) 3329113911Sdes error("socket: %.100s", strerror(errno)); 3330113911Sdes else 3331113911Sdes verbose("socket: %.100s", strerror(errno)); 333260573Skris continue; 333360573Skris } 3334137019Sdes if (set_nonblock(sock) == -1) 3335137019Sdes fatal("%s: set_nonblock(%d)", __func__, sock); 3336181111Sdes if (connect(sock, cctx->ai->ai_addr, 3337181111Sdes cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 3338181111Sdes debug("connect_next: host %.100s ([%.100s]:%s): " 3339181111Sdes "%.100s", cctx->host, ntop, strport, 334060573Skris strerror(errno)); 3341181111Sdes saved_errno = errno; 334260573Skris close(sock); 3343181111Sdes errno = saved_errno; 334476262Sgreen continue; /* fail -- try next */ 334560573Skris } 3346181111Sdes debug("connect_next: host %.100s ([%.100s]:%s) " 3347181111Sdes "in progress, fd=%d", cctx->host, ntop, strport, sock); 3348181111Sdes cctx->ai = cctx->ai->ai_next; 3349181111Sdes set_nodelay(sock); 3350181111Sdes return sock; 3351181111Sdes } 3352181111Sdes return -1; 3353181111Sdes} 335460573Skris 3355181111Sdesstatic void 3356181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx) 3357181111Sdes{ 3358255767Sdes free(cctx->host); 3359181111Sdes if (cctx->aitop) 3360181111Sdes freeaddrinfo(cctx->aitop); 3361181111Sdes bzero(cctx, sizeof(*cctx)); 3362181111Sdes cctx->host = NULL; 3363181111Sdes cctx->ai = cctx->aitop = NULL; 3364181111Sdes} 3365181111Sdes 3366181111Sdes/* Return CONNECTING channel to remote host, port */ 3367181111Sdesstatic Channel * 3368181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname) 3369181111Sdes{ 3370181111Sdes struct addrinfo hints; 3371181111Sdes int gaierr; 3372181111Sdes int sock = -1; 3373181111Sdes char strport[NI_MAXSERV]; 3374181111Sdes struct channel_connect cctx; 3375181111Sdes Channel *c; 3376181111Sdes 3377181111Sdes memset(&cctx, 0, sizeof(cctx)); 3378181111Sdes memset(&hints, 0, sizeof(hints)); 3379181111Sdes hints.ai_family = IPv4or6; 3380181111Sdes hints.ai_socktype = SOCK_STREAM; 3381181111Sdes snprintf(strport, sizeof strport, "%d", port); 3382181111Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { 3383181111Sdes error("connect_to %.100s: unknown host (%s)", host, 3384181111Sdes ssh_gai_strerror(gaierr)); 3385181111Sdes return NULL; 338660573Skris } 3387181111Sdes 3388181111Sdes cctx.host = xstrdup(host); 3389181111Sdes cctx.port = port; 3390181111Sdes cctx.ai = cctx.aitop; 3391181111Sdes 3392181111Sdes if ((sock = connect_next(&cctx)) == -1) { 3393181111Sdes error("connect to %.100s port %d failed: %s", 3394181111Sdes host, port, strerror(errno)); 3395181111Sdes channel_connect_ctx_free(&cctx); 3396181111Sdes return NULL; 339760573Skris } 3398181111Sdes c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 3399181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 3400181111Sdes c->connect_ctx = cctx; 3401181111Sdes return c; 340260573Skris} 340376262Sgreen 3404181111SdesChannel * 3405181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) 340676262Sgreen{ 340776262Sgreen int i; 340876262Sgreen 3409181111Sdes for (i = 0; i < num_permitted_opens; i++) { 3410137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3411240075Sdes port_match(permitted_opens[i].listen_port, listen_port)) { 341276262Sgreen return connect_to( 341376262Sgreen permitted_opens[i].host_to_connect, 3414181111Sdes permitted_opens[i].port_to_connect, ctype, rname); 3415181111Sdes } 3416181111Sdes } 341776262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 341876262Sgreen listen_port); 3419181111Sdes return NULL; 342076262Sgreen} 342176262Sgreen 342276262Sgreen/* Check if connecting to that port is permitted and connect. */ 3423181111SdesChannel * 3424181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname) 342576262Sgreen{ 3426162856Sdes int i, permit, permit_adm = 1; 342776262Sgreen 342876262Sgreen permit = all_opens_permitted; 342976262Sgreen if (!permit) { 343076262Sgreen for (i = 0; i < num_permitted_opens; i++) 3431137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3432240075Sdes port_match(permitted_opens[i].port_to_connect, port) && 343376262Sgreen strcmp(permitted_opens[i].host_to_connect, host) == 0) 343476262Sgreen permit = 1; 3435162856Sdes } 343676262Sgreen 3437162856Sdes if (num_adm_permitted_opens > 0) { 3438162856Sdes permit_adm = 0; 3439162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3440162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL && 3441240075Sdes port_match(permitted_adm_opens[i].port_to_connect, port) && 3442162856Sdes strcmp(permitted_adm_opens[i].host_to_connect, host) 3443162856Sdes == 0) 3444162856Sdes permit_adm = 1; 344576262Sgreen } 3446162856Sdes 3447162856Sdes if (!permit || !permit_adm) { 3448124207Sdes logit("Received request to connect to host %.100s port %d, " 344976262Sgreen "but the request was denied.", host, port); 3450181111Sdes return NULL; 345176262Sgreen } 3452181111Sdes return connect_to(host, port, ctype, rname); 345376262Sgreen} 345476262Sgreen 3455137019Sdesvoid 3456137019Sdeschannel_send_window_changes(void) 3457137019Sdes{ 3458137019Sdes u_int i; 3459137019Sdes struct winsize ws; 3460137019Sdes 3461137019Sdes for (i = 0; i < channels_alloc; i++) { 3462147005Sdes if (channels[i] == NULL || !channels[i]->client_tty || 3463137019Sdes channels[i]->type != SSH_CHANNEL_OPEN) 3464137019Sdes continue; 3465137019Sdes if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 3466137019Sdes continue; 3467137019Sdes channel_request_start(i, "window-change", 0); 3468162856Sdes packet_put_int((u_int)ws.ws_col); 3469162856Sdes packet_put_int((u_int)ws.ws_row); 3470162856Sdes packet_put_int((u_int)ws.ws_xpixel); 3471162856Sdes packet_put_int((u_int)ws.ws_ypixel); 3472137019Sdes packet_send(); 3473137019Sdes } 3474137019Sdes} 3475137019Sdes 347692559Sdes/* -- X11 forwarding */ 347757429Smarkm 347857429Smarkm/* 347957429Smarkm * Creates an internet domain socket for listening for X11 connections. 348099063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 348199063Sdes * stored in display_numberp , or -1 if an error occurs. 348257429Smarkm */ 348392559Sdesint 348492559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 3485149753Sdes int single_connection, u_int *display_numberp, int **chanids) 348657429Smarkm{ 348792559Sdes Channel *nc = NULL; 348857429Smarkm int display_number, sock; 348957429Smarkm u_short port; 349057429Smarkm struct addrinfo hints, *ai, *aitop; 349157429Smarkm char strport[NI_MAXSERV]; 349257429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 349357429Smarkm 3494157019Sdes if (chanids == NULL) 3495157019Sdes return -1; 3496157019Sdes 349757429Smarkm for (display_number = x11_display_offset; 349892559Sdes display_number < MAX_DISPLAYS; 349992559Sdes display_number++) { 350057429Smarkm port = 6000 + display_number; 350157429Smarkm memset(&hints, 0, sizeof(hints)); 350257429Smarkm hints.ai_family = IPv4or6; 350392559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 350457429Smarkm hints.ai_socktype = SOCK_STREAM; 350557429Smarkm snprintf(strport, sizeof strport, "%d", port); 350657429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 3507181111Sdes error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 350892559Sdes return -1; 350957429Smarkm } 351057429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 351157429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 351257429Smarkm continue; 3513124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, 3514124207Sdes ai->ai_protocol); 351557429Smarkm if (sock < 0) { 3516207319Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 3517207319Sdes#ifdef EPFNOSUPPORT 3518207319Sdes && (errno != EPFNOSUPPORT) 3519207319Sdes#endif 3520207319Sdes ) { 352198941Sdes error("socket: %.100s", strerror(errno)); 3522137019Sdes freeaddrinfo(aitop); 352398941Sdes return -1; 352498941Sdes } else { 352598941Sdes debug("x11_create_display_inet: Socket family %d not supported", 352698941Sdes ai->ai_family); 352798941Sdes continue; 352898941Sdes } 352957429Smarkm } 3530204917Sdes if (ai->ai_family == AF_INET6) 3531204917Sdes sock_set_v6only(sock); 3532181111Sdes if (x11_use_localhost) 3533181111Sdes channel_set_reuseaddr(sock); 353457429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3535124207Sdes debug2("bind port %d: %.100s", port, strerror(errno)); 353657429Smarkm close(sock); 353798941Sdes 353857429Smarkm for (n = 0; n < num_socks; n++) { 353957429Smarkm close(socks[n]); 354057429Smarkm } 354157429Smarkm num_socks = 0; 354257429Smarkm break; 354357429Smarkm } 354457429Smarkm socks[num_socks++] = sock; 354557429Smarkm if (num_socks == NUM_SOCKS) 354657429Smarkm break; 354757429Smarkm } 354876262Sgreen freeaddrinfo(aitop); 354957429Smarkm if (num_socks > 0) 355057429Smarkm break; 355157429Smarkm } 355257429Smarkm if (display_number >= MAX_DISPLAYS) { 355357429Smarkm error("Failed to allocate internet-domain X11 display socket."); 355492559Sdes return -1; 355557429Smarkm } 355657429Smarkm /* Start listening for connections on the socket. */ 355757429Smarkm for (n = 0; n < num_socks; n++) { 355857429Smarkm sock = socks[n]; 3559126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 356057429Smarkm error("listen: %.100s", strerror(errno)); 356157429Smarkm close(sock); 356292559Sdes return -1; 356357429Smarkm } 356457429Smarkm } 356557429Smarkm 356657429Smarkm /* Allocate a channel for each socket. */ 3567162856Sdes *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 356857429Smarkm for (n = 0; n < num_socks; n++) { 356957429Smarkm sock = socks[n]; 3570224638Sbrooks if (hpn_disabled) 3571224638Sbrooks nc = channel_new("x11 listener", 3572224638Sbrooks SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 3573224638Sbrooks CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3574224638Sbrooks 0, "X11 inet listener", 1); 3575224638Sbrooks else 3576224638Sbrooks nc = channel_new("x11 listener", 3577224638Sbrooks SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 3578224638Sbrooks buffer_size, CHAN_X11_PACKET_DEFAULT, 3579224638Sbrooks 0, "X11 inet listener", 1); 358092559Sdes nc->single_connection = single_connection; 3581157019Sdes (*chanids)[n] = nc->self; 358257429Smarkm } 3583157019Sdes (*chanids)[n] = -1; 358457429Smarkm 358592559Sdes /* Return the display number for the DISPLAY environment variable. */ 358699063Sdes *display_numberp = display_number; 358799063Sdes return (0); 358857429Smarkm} 358957429Smarkm 359092559Sdesstatic int 3591192595Sdesconnect_local_xsocket_path(const char *pathname) 359257429Smarkm{ 359357429Smarkm int sock; 359457429Smarkm struct sockaddr_un addr; 359557429Smarkm 359692559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 359792559Sdes if (sock < 0) 359892559Sdes error("socket: %.100s", strerror(errno)); 359992559Sdes memset(&addr, 0, sizeof(addr)); 360092559Sdes addr.sun_family = AF_UNIX; 3601192595Sdes strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 3602162856Sdes if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 360392559Sdes return sock; 360492559Sdes close(sock); 360557429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 360657429Smarkm return -1; 360757429Smarkm} 360857429Smarkm 3609192595Sdesstatic int 3610192595Sdesconnect_local_xsocket(u_int dnr) 3611192595Sdes{ 3612192595Sdes char buf[1024]; 3613192595Sdes snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 3614192595Sdes return connect_local_xsocket_path(buf); 3615192595Sdes} 3616192595Sdes 361760573Skrisint 361860573Skrisx11_connect_display(void) 361957429Smarkm{ 3620162856Sdes u_int display_number; 362157429Smarkm const char *display; 362260573Skris char buf[1024], *cp; 362357429Smarkm struct addrinfo hints, *ai, *aitop; 362457429Smarkm char strport[NI_MAXSERV]; 3625162856Sdes int gaierr, sock = 0; 362657429Smarkm 362757429Smarkm /* Try to open a socket for the local X server. */ 362857429Smarkm display = getenv("DISPLAY"); 362957429Smarkm if (!display) { 363057429Smarkm error("DISPLAY not set."); 363160573Skris return -1; 363257429Smarkm } 363357429Smarkm /* 363457429Smarkm * Now we decode the value of the DISPLAY variable and make a 363557429Smarkm * connection to the real X server. 363657429Smarkm */ 363757429Smarkm 3638192595Sdes /* Check if the display is from launchd. */ 3639192595Sdes#ifdef __APPLE__ 3640192595Sdes if (strncmp(display, "/tmp/launch", 11) == 0) { 3641192595Sdes sock = connect_local_xsocket_path(display); 3642192595Sdes if (sock < 0) 3643192595Sdes return -1; 3644192595Sdes 3645192595Sdes /* OK, we now have a connection to the display. */ 3646192595Sdes return sock; 3647192595Sdes } 3648192595Sdes#endif 364957429Smarkm /* 365057429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 365157429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 365257429Smarkm */ 365357429Smarkm if (strncmp(display, "unix:", 5) == 0 || 365457429Smarkm display[0] == ':') { 365557429Smarkm /* Connect to the unix domain socket. */ 3656162856Sdes if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 365757429Smarkm error("Could not parse display number from DISPLAY: %.100s", 365892559Sdes display); 365960573Skris return -1; 366057429Smarkm } 366157429Smarkm /* Create a socket. */ 366257429Smarkm sock = connect_local_xsocket(display_number); 366357429Smarkm if (sock < 0) 366460573Skris return -1; 366557429Smarkm 366657429Smarkm /* OK, we now have a connection to the display. */ 366760573Skris return sock; 366857429Smarkm } 366957429Smarkm /* 367057429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 367157429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 367257429Smarkm */ 367392559Sdes strlcpy(buf, display, sizeof(buf)); 367457429Smarkm cp = strchr(buf, ':'); 367557429Smarkm if (!cp) { 367657429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 367760573Skris return -1; 367857429Smarkm } 367957429Smarkm *cp = 0; 368057429Smarkm /* buf now contains the host name. But first we parse the display number. */ 3681162856Sdes if (sscanf(cp + 1, "%u", &display_number) != 1) { 368257429Smarkm error("Could not parse display number from DISPLAY: %.100s", 368392559Sdes display); 368460573Skris return -1; 368557429Smarkm } 368657429Smarkm 368757429Smarkm /* Look up the host address */ 368857429Smarkm memset(&hints, 0, sizeof(hints)); 368957429Smarkm hints.ai_family = IPv4or6; 369057429Smarkm hints.ai_socktype = SOCK_STREAM; 3691162856Sdes snprintf(strport, sizeof strport, "%u", 6000 + display_number); 369257429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 3693181111Sdes error("%.100s: unknown host. (%s)", buf, 3694181111Sdes ssh_gai_strerror(gaierr)); 369560573Skris return -1; 369657429Smarkm } 369757429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 369857429Smarkm /* Create a socket. */ 3699124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 370057429Smarkm if (sock < 0) { 3701124207Sdes debug2("socket: %.100s", strerror(errno)); 370260573Skris continue; 370360573Skris } 370460573Skris /* Connect it to the display. */ 370560573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3706162856Sdes debug2("connect %.100s port %u: %.100s", buf, 370760573Skris 6000 + display_number, strerror(errno)); 370860573Skris close(sock); 370960573Skris continue; 371060573Skris } 371160573Skris /* Success */ 371260573Skris break; 371357429Smarkm } 371457429Smarkm freeaddrinfo(aitop); 371557429Smarkm if (!ai) { 3716162856Sdes error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 371757429Smarkm strerror(errno)); 371860573Skris return -1; 371957429Smarkm } 372092559Sdes set_nodelay(sock); 372160573Skris return sock; 372260573Skris} 372357429Smarkm 372460573Skris/* 372560573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 372660573Skris * the remote channel number. We should do whatever we want, and respond 372760573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 372860573Skris */ 372957429Smarkm 3730162856Sdes/* ARGSUSED */ 373160573Skrisvoid 373292559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 373360573Skris{ 373492559Sdes Channel *c = NULL; 373592559Sdes int remote_id, sock = 0; 373660573Skris char *remote_host; 373757429Smarkm 373892559Sdes debug("Received X11 open request."); 373957429Smarkm 374092559Sdes remote_id = packet_get_int(); 374192559Sdes 374292559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 374392559Sdes remote_host = packet_get_string(NULL); 374460573Skris } else { 374560573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 374660573Skris } 374792559Sdes packet_check_eom(); 374860573Skris 374960573Skris /* Obtain a connection to the real X display. */ 375060573Skris sock = x11_connect_display(); 375192559Sdes if (sock != -1) { 375292559Sdes /* Allocate a channel for this connection. */ 375392559Sdes c = channel_new("connected x11 socket", 375492559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 375592559Sdes remote_host, 1); 375692559Sdes c->remote_id = remote_id; 375792559Sdes c->force_drain = 1; 375892559Sdes } 3759255767Sdes free(remote_host); 376092559Sdes if (c == NULL) { 376160573Skris /* Send refusal to the remote host. */ 376260573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 376392559Sdes packet_put_int(remote_id); 376460573Skris } else { 376560573Skris /* Send a confirmation to the remote host. */ 376660573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 376792559Sdes packet_put_int(remote_id); 376892559Sdes packet_put_int(c->self); 376960573Skris } 377092559Sdes packet_send(); 377157429Smarkm} 377257429Smarkm 377369587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 3774162856Sdes/* ARGSUSED */ 377569587Sgreenvoid 377692559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 377769587Sgreen{ 377869587Sgreen int rchan = packet_get_int(); 3779106130Sdes 378092559Sdes switch (type) { 378169587Sgreen case SSH_SMSG_AGENT_OPEN: 378269587Sgreen error("Warning: ssh server tried agent forwarding."); 378369587Sgreen break; 378469587Sgreen case SSH_SMSG_X11_OPEN: 378569587Sgreen error("Warning: ssh server tried X11 forwarding."); 378669587Sgreen break; 378769587Sgreen default: 378892559Sdes error("deny_input_open: type %d", type); 378969587Sgreen break; 379069587Sgreen } 3791157019Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 379269587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 379369587Sgreen packet_put_int(rchan); 379469587Sgreen packet_send(); 379569587Sgreen} 379669587Sgreen 379757429Smarkm/* 379857429Smarkm * Requests forwarding of X11 connections, generates fake authentication 379957429Smarkm * data, and enables authentication spoofing. 380092559Sdes * This should be called in the client only. 380157429Smarkm */ 380260573Skrisvoid 3803149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 3804226046Sdes const char *proto, const char *data, int want_reply) 380557429Smarkm{ 380676262Sgreen u_int data_len = (u_int) strlen(data) / 2; 3807149753Sdes u_int i, value; 380857429Smarkm char *new_data; 380957429Smarkm int screen_number; 381057429Smarkm const char *cp; 3811137019Sdes u_int32_t rnd = 0; 381257429Smarkm 3813149753Sdes if (x11_saved_display == NULL) 3814149753Sdes x11_saved_display = xstrdup(disp); 3815149753Sdes else if (strcmp(disp, x11_saved_display) != 0) { 3816149753Sdes error("x11_request_forwarding_with_spoofing: different " 3817149753Sdes "$DISPLAY already forwarded"); 3818149753Sdes return; 3819149753Sdes } 3820149753Sdes 3821162856Sdes cp = strchr(disp, ':'); 382257429Smarkm if (cp) 382357429Smarkm cp = strchr(cp, '.'); 382457429Smarkm if (cp) 3825162856Sdes screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 382657429Smarkm else 382757429Smarkm screen_number = 0; 382857429Smarkm 3829149753Sdes if (x11_saved_proto == NULL) { 3830149753Sdes /* Save protocol name. */ 3831149753Sdes x11_saved_proto = xstrdup(proto); 3832149753Sdes /* 3833149753Sdes * Extract real authentication data and generate fake data 3834149753Sdes * of the same length. 3835149753Sdes */ 3836149753Sdes x11_saved_data = xmalloc(data_len); 3837149753Sdes x11_fake_data = xmalloc(data_len); 3838149753Sdes for (i = 0; i < data_len; i++) { 3839149753Sdes if (sscanf(data + 2 * i, "%2x", &value) != 1) 3840149753Sdes fatal("x11_request_forwarding: bad " 3841149753Sdes "authentication data: %.100s", data); 3842149753Sdes if (i % 4 == 0) 3843149753Sdes rnd = arc4random(); 3844149753Sdes x11_saved_data[i] = value; 3845149753Sdes x11_fake_data[i] = rnd & 0xff; 3846149753Sdes rnd >>= 8; 3847149753Sdes } 3848149753Sdes x11_saved_data_len = data_len; 3849149753Sdes x11_fake_data_len = data_len; 385057429Smarkm } 385157429Smarkm 385257429Smarkm /* Convert the fake data into hex. */ 3853149753Sdes new_data = tohex(x11_fake_data, data_len); 385457429Smarkm 385557429Smarkm /* Send the request packet. */ 385660573Skris if (compat20) { 3857226046Sdes channel_request_start(client_session_id, "x11-req", want_reply); 385860573Skris packet_put_char(0); /* XXX bool single connection */ 385960573Skris } else { 386060573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 386160573Skris } 386260573Skris packet_put_cstring(proto); 386360573Skris packet_put_cstring(new_data); 386457429Smarkm packet_put_int(screen_number); 386557429Smarkm packet_send(); 386657429Smarkm packet_write_wait(); 3867255767Sdes free(new_data); 386857429Smarkm} 386957429Smarkm 387092559Sdes 387192559Sdes/* -- agent forwarding */ 387292559Sdes 387357429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 387457429Smarkm 387560573Skrisvoid 387692559Sdesauth_request_forwarding(void) 387757429Smarkm{ 387857429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 387957429Smarkm packet_send(); 388057429Smarkm packet_write_wait(); 388157429Smarkm} 3882