channels.c revision 181111
1181111Sdes/* $OpenBSD: channels.c,v 1.286 2008/07/16 11:52:19 djm Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * This file contains functions for generic socket connection forwarding. 757429Smarkm * There is also code for initiating connection forwarding for X11 connections, 857429Smarkm * arbitrary tcp/ip connections, and the authentication agent connection. 960573Skris * 1065668Skris * As far as I am concerned, the code I have written for this software 1165668Skris * can be used freely for any purpose. Any derived versions of this 1265668Skris * software must be clearly marked as such, and if the derived work is 1365668Skris * incompatible with the protocol description in the RFC file, it must be 1465668Skris * called by a name other than "ssh" or "Secure Shell". 1565668Skris * 1660573Skris * SSH2 support added by Markus Friedl. 1792559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 1865668Skris * Copyright (c) 1999 Dug Song. All rights reserved. 1965668Skris * Copyright (c) 1999 Theo de Raadt. All rights reserved. 2065668Skris * 2165668Skris * Redistribution and use in source and binary forms, with or without 2265668Skris * modification, are permitted provided that the following conditions 2365668Skris * are met: 2465668Skris * 1. Redistributions of source code must retain the above copyright 2565668Skris * notice, this list of conditions and the following disclaimer. 2665668Skris * 2. Redistributions in binary form must reproduce the above copyright 2765668Skris * notice, this list of conditions and the following disclaimer in the 2865668Skris * documentation and/or other materials provided with the distribution. 2965668Skris * 3065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 3165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4057429Smarkm */ 4157429Smarkm 4257429Smarkm#include "includes.h" 4357429Smarkm 44162856Sdes#include <sys/types.h> 45162856Sdes#include <sys/ioctl.h> 46162856Sdes#include <sys/un.h> 47162856Sdes#include <sys/socket.h> 48162856Sdes#ifdef HAVE_SYS_TIME_H 49162856Sdes# include <sys/time.h> 50162856Sdes#endif 51162856Sdes 52162856Sdes#include <netinet/in.h> 53162856Sdes#include <arpa/inet.h> 54162856Sdes 55162856Sdes#include <errno.h> 56162856Sdes#include <netdb.h> 57162856Sdes#include <stdio.h> 58162856Sdes#include <stdlib.h> 59162856Sdes#include <string.h> 60162856Sdes#include <termios.h> 61162856Sdes#include <unistd.h> 62162856Sdes#include <stdarg.h> 63162856Sdes 64181111Sdes#include "openbsd-compat/sys-queue.h" 65162856Sdes#include "xmalloc.h" 6657429Smarkm#include "ssh.h" 6776262Sgreen#include "ssh1.h" 6876262Sgreen#include "ssh2.h" 6957429Smarkm#include "packet.h" 7076262Sgreen#include "log.h" 7176262Sgreen#include "misc.h" 72162856Sdes#include "buffer.h" 7357429Smarkm#include "channels.h" 7457429Smarkm#include "compat.h" 7576262Sgreen#include "canohost.h" 7665668Skris#include "key.h" 7765668Skris#include "authfd.h" 7892559Sdes#include "pathnames.h" 7965668Skris 8092559Sdes/* -- channel core */ 8157429Smarkm 8257429Smarkm/* 8357429Smarkm * Pointer to an array containing all allocated channels. The array is 8457429Smarkm * dynamically extended as needed. 8557429Smarkm */ 8692559Sdesstatic Channel **channels = NULL; 8757429Smarkm 8857429Smarkm/* 8957429Smarkm * Size of the channel array. All slots of the array must always be 9092559Sdes * initialized (at least the type field); unused slots set to NULL 9157429Smarkm */ 92137019Sdesstatic u_int channels_alloc = 0; 9357429Smarkm 9457429Smarkm/* 9557429Smarkm * Maximum file descriptor value used in any of the channels. This is 9692559Sdes * updated in channel_new. 9757429Smarkm */ 9876262Sgreenstatic int channel_max_fd = 0; 9957429Smarkm 10057429Smarkm 10192559Sdes/* -- tcp forwarding */ 10257429Smarkm 10357429Smarkm/* 10457429Smarkm * Data structure for storing which hosts are permitted for forward requests. 10557429Smarkm * The local sides of any remote forwards are stored in this array to prevent 10657429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 10757429Smarkm * network (which might be behind a firewall). 10857429Smarkm */ 10957429Smarkmtypedef struct { 11060573Skris char *host_to_connect; /* Connect to 'host'. */ 11160573Skris u_short port_to_connect; /* Connect to 'port'. */ 11260573Skris u_short listen_port; /* Remote side should listen port number. */ 11357429Smarkm} ForwardPermission; 11457429Smarkm 115162856Sdes/* List of all permitted host/port pairs to connect by the user. */ 11657429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 11792559Sdes 118162856Sdes/* List of all permitted host/port pairs to connect by the admin. */ 119162856Sdesstatic ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 120162856Sdes 121162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */ 12257429Smarkmstatic int num_permitted_opens = 0; 123162856Sdes 124162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */ 125162856Sdesstatic int num_adm_permitted_opens = 0; 126162856Sdes 12757429Smarkm/* 12857429Smarkm * If this is true, all opens are permitted. This is the case on the server 12957429Smarkm * on which we have to trust the client anyway, and the user could do 13057429Smarkm * anything after logging in anyway. 13157429Smarkm */ 13257429Smarkmstatic int all_opens_permitted = 0; 13357429Smarkm 13457429Smarkm 13592559Sdes/* -- X11 forwarding */ 13692559Sdes 13792559Sdes/* Maximum number of fake X11 displays to try. */ 13892559Sdes#define MAX_DISPLAYS 1000 13992559Sdes 140149753Sdes/* Saved X11 local (client) display. */ 141149753Sdesstatic char *x11_saved_display = NULL; 142149753Sdes 14392559Sdes/* Saved X11 authentication protocol name. */ 14492559Sdesstatic char *x11_saved_proto = NULL; 14592559Sdes 14692559Sdes/* Saved X11 authentication data. This is the real data. */ 14792559Sdesstatic char *x11_saved_data = NULL; 14892559Sdesstatic u_int x11_saved_data_len = 0; 14992559Sdes 15092559Sdes/* 15192559Sdes * Fake X11 authentication data. This is what the server will be sending us; 15292559Sdes * we should replace any occurrences of this by the real data. 15392559Sdes */ 154162856Sdesstatic u_char *x11_fake_data = NULL; 15592559Sdesstatic u_int x11_fake_data_len; 15692559Sdes 15792559Sdes 15892559Sdes/* -- agent forwarding */ 15992559Sdes 16092559Sdes#define NUM_SOCKS 10 16192559Sdes 16276262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */ 16398941Sdesstatic int IPv4or6 = AF_UNSPEC; 16476262Sgreen 16592559Sdes/* helper */ 16692559Sdesstatic void port_open_helper(Channel *c, char *rtype); 16776262Sgreen 168181111Sdes/* non-blocking connect helpers */ 169181111Sdesstatic int connect_next(struct channel_connect *); 170181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *); 171181111Sdes 17292559Sdes/* -- channel core */ 17357429Smarkm 17460573SkrisChannel * 175157019Sdeschannel_by_id(int id) 17660573Skris{ 17760573Skris Channel *c; 17892559Sdes 179137019Sdes if (id < 0 || (u_int)id >= channels_alloc) { 180157019Sdes logit("channel_by_id: %d: bad id", id); 18160573Skris return NULL; 18260573Skris } 18392559Sdes c = channels[id]; 18492559Sdes if (c == NULL) { 185157019Sdes logit("channel_by_id: %d: bad id: channel free", id); 18660573Skris return NULL; 18760573Skris } 18860573Skris return c; 18960573Skris} 19060573Skris 19157429Smarkm/* 192157019Sdes * Returns the channel if it is allowed to receive protocol messages. 193157019Sdes * Private channels, like listening sockets, may not receive messages. 194157019Sdes */ 195157019SdesChannel * 196157019Sdeschannel_lookup(int id) 197157019Sdes{ 198157019Sdes Channel *c; 199157019Sdes 200157019Sdes if ((c = channel_by_id(id)) == NULL) 201157019Sdes return (NULL); 202157019Sdes 203162856Sdes switch (c->type) { 204157019Sdes case SSH_CHANNEL_X11_OPEN: 205157019Sdes case SSH_CHANNEL_LARVAL: 206157019Sdes case SSH_CHANNEL_CONNECTING: 207157019Sdes case SSH_CHANNEL_DYNAMIC: 208157019Sdes case SSH_CHANNEL_OPENING: 209157019Sdes case SSH_CHANNEL_OPEN: 210157019Sdes case SSH_CHANNEL_INPUT_DRAINING: 211157019Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 212157019Sdes return (c); 213157019Sdes } 214157019Sdes logit("Non-public channel %d, type %d.", id, c->type); 215157019Sdes return (NULL); 216157019Sdes} 217157019Sdes 218157019Sdes/* 21960573Skris * Register filedescriptors for a channel, used when allocating a channel or 22060573Skris * when the channel consumer/producer is ready, e.g. shell exec'd 22160573Skris */ 22292559Sdesstatic void 22369587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd, 224181111Sdes int extusage, int nonblock, int is_tty) 22560573Skris{ 22660573Skris /* Update the maximum file descriptor value. */ 22776262Sgreen channel_max_fd = MAX(channel_max_fd, rfd); 22876262Sgreen channel_max_fd = MAX(channel_max_fd, wfd); 22976262Sgreen channel_max_fd = MAX(channel_max_fd, efd); 23076262Sgreen 23160573Skris /* XXX set close-on-exec -markus */ 23260573Skris 23360573Skris c->rfd = rfd; 23460573Skris c->wfd = wfd; 23560573Skris c->sock = (rfd == wfd) ? rfd : -1; 236137019Sdes c->ctl_fd = -1; /* XXX: set elsewhere */ 23760573Skris c->efd = efd; 23860573Skris c->extended_usage = extusage; 23969587Sgreen 240181111Sdes if ((c->isatty = is_tty) != 0) 241124207Sdes debug2("channel %d: rfd %d isatty", c->self, c->rfd); 242181111Sdes c->wfd_isatty = is_tty || isatty(c->wfd); 24374500Sgreen 24469587Sgreen /* enable nonblocking mode */ 24569587Sgreen if (nonblock) { 24669587Sgreen if (rfd != -1) 24769587Sgreen set_nonblock(rfd); 24869587Sgreen if (wfd != -1) 24969587Sgreen set_nonblock(wfd); 25069587Sgreen if (efd != -1) 25169587Sgreen set_nonblock(efd); 25269587Sgreen } 25360573Skris} 25460573Skris 25560573Skris/* 25657429Smarkm * Allocate a new channel object and set its type and socket. This will cause 25757429Smarkm * remote_name to be freed. 25857429Smarkm */ 25992559SdesChannel * 26060573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd, 26199063Sdes u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 26257429Smarkm{ 263137019Sdes int found; 264137019Sdes u_int i; 26557429Smarkm Channel *c; 26657429Smarkm 26757429Smarkm /* Do initial allocation if this is the first call. */ 26857429Smarkm if (channels_alloc == 0) { 26957429Smarkm channels_alloc = 10; 270162856Sdes channels = xcalloc(channels_alloc, sizeof(Channel *)); 27157429Smarkm for (i = 0; i < channels_alloc; i++) 27292559Sdes channels[i] = NULL; 27357429Smarkm } 27457429Smarkm /* Try to find a free slot where to put the new channel. */ 27557429Smarkm for (found = -1, i = 0; i < channels_alloc; i++) 27692559Sdes if (channels[i] == NULL) { 27757429Smarkm /* Found a free slot. */ 278137019Sdes found = (int)i; 27957429Smarkm break; 28057429Smarkm } 281137019Sdes if (found < 0) { 28257429Smarkm /* There are no free slots. Take last+1 slot and expand the array. */ 28357429Smarkm found = channels_alloc; 28499063Sdes if (channels_alloc > 10000) 28599063Sdes fatal("channel_new: internal error: channels_alloc %d " 28699063Sdes "too big.", channels_alloc); 287162856Sdes channels = xrealloc(channels, channels_alloc + 10, 288162856Sdes sizeof(Channel *)); 289120489Sjoe channels_alloc += 10; 29069587Sgreen debug2("channel: expanding %d", channels_alloc); 29157429Smarkm for (i = found; i < channels_alloc; i++) 29292559Sdes channels[i] = NULL; 29357429Smarkm } 29492559Sdes /* Initialize and return new channel. */ 295162856Sdes c = channels[found] = xcalloc(1, sizeof(Channel)); 29657429Smarkm buffer_init(&c->input); 29757429Smarkm buffer_init(&c->output); 29860573Skris buffer_init(&c->extended); 29992559Sdes c->ostate = CHAN_OUTPUT_OPEN; 30092559Sdes c->istate = CHAN_INPUT_OPEN; 30192559Sdes c->flags = 0; 302181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 30357429Smarkm c->self = found; 30457429Smarkm c->type = type; 30560573Skris c->ctype = ctype; 30660573Skris c->local_window = window; 30760573Skris c->local_window_max = window; 30860573Skris c->local_consumed = 0; 30960573Skris c->local_maxpacket = maxpack; 31057429Smarkm c->remote_id = -1; 311124207Sdes c->remote_name = xstrdup(remote_name); 31260573Skris c->remote_window = 0; 31360573Skris c->remote_maxpacket = 0; 31492559Sdes c->force_drain = 0; 31592559Sdes c->single_connection = 0; 31692559Sdes c->detach_user = NULL; 317157019Sdes c->detach_close = 0; 318181111Sdes c->open_confirm = NULL; 319181111Sdes c->open_confirm_ctx = NULL; 32065668Skris c->input_filter = NULL; 321157019Sdes c->output_filter = NULL; 322181111Sdes c->filter_ctx = NULL; 323181111Sdes c->filter_cleanup = NULL; 324181111Sdes TAILQ_INIT(&c->status_confirms); 32557429Smarkm debug("channel %d: new [%s]", found, remote_name); 32692559Sdes return c; 32757429Smarkm} 32892559Sdes 32992559Sdesstatic int 33092559Sdeschannel_find_maxfd(void) 33192559Sdes{ 332137019Sdes u_int i; 333137019Sdes int max = 0; 33492559Sdes Channel *c; 33592559Sdes 33692559Sdes for (i = 0; i < channels_alloc; i++) { 33792559Sdes c = channels[i]; 33892559Sdes if (c != NULL) { 33992559Sdes max = MAX(max, c->rfd); 34092559Sdes max = MAX(max, c->wfd); 34192559Sdes max = MAX(max, c->efd); 34292559Sdes } 34392559Sdes } 34492559Sdes return max; 34592559Sdes} 34692559Sdes 34760573Skrisint 34892559Sdeschannel_close_fd(int *fdp) 34960573Skris{ 35092559Sdes int ret = 0, fd = *fdp; 35192559Sdes 35292559Sdes if (fd != -1) { 35392559Sdes ret = close(fd); 35492559Sdes *fdp = -1; 35592559Sdes if (fd == channel_max_fd) 35692559Sdes channel_max_fd = channel_find_maxfd(); 35792559Sdes } 35892559Sdes return ret; 35960573Skris} 36057429Smarkm 36160573Skris/* Close all channel fd/socket. */ 36292559Sdesstatic void 36360573Skrischannel_close_fds(Channel *c) 36457429Smarkm{ 365137019Sdes debug3("channel %d: close_fds r %d w %d e %d c %d", 366137019Sdes c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); 36792559Sdes 36892559Sdes channel_close_fd(&c->sock); 369137019Sdes channel_close_fd(&c->ctl_fd); 37092559Sdes channel_close_fd(&c->rfd); 37192559Sdes channel_close_fd(&c->wfd); 37292559Sdes channel_close_fd(&c->efd); 37360573Skris} 37457429Smarkm 37560573Skris/* Free the channel and close its fd/socket. */ 37660573Skrisvoid 37792559Sdeschannel_free(Channel *c) 37860573Skris{ 37992559Sdes char *s; 380137019Sdes u_int i, n; 381181111Sdes struct channel_confirm *cc; 38276262Sgreen 38392559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 38492559Sdes if (channels[i]) 38592559Sdes n++; 386137019Sdes debug("channel %d: free: %s, nchannels %u", c->self, 38792559Sdes c->remote_name ? c->remote_name : "???", n); 38892559Sdes 38992559Sdes s = channel_open_message(); 390124207Sdes debug3("channel %d: status: %s", c->self, s); 39176262Sgreen xfree(s); 39276262Sgreen 39360573Skris if (c->sock != -1) 39460573Skris shutdown(c->sock, SHUT_RDWR); 395137019Sdes if (c->ctl_fd != -1) 396137019Sdes shutdown(c->ctl_fd, SHUT_RDWR); 39760573Skris channel_close_fds(c); 39860573Skris buffer_free(&c->input); 39960573Skris buffer_free(&c->output); 40060573Skris buffer_free(&c->extended); 40160573Skris if (c->remote_name) { 40260573Skris xfree(c->remote_name); 40360573Skris c->remote_name = NULL; 40460573Skris } 405181111Sdes while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 406181111Sdes if (cc->abandon_cb != NULL) 407181111Sdes cc->abandon_cb(c, cc->ctx); 408181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 409181111Sdes bzero(cc, sizeof(*cc)); 410181111Sdes xfree(cc); 411181111Sdes } 412181111Sdes if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 413181111Sdes c->filter_cleanup(c->self, c->filter_ctx); 41492559Sdes channels[c->self] = NULL; 41592559Sdes xfree(c); 41657429Smarkm} 41757429Smarkm 41892559Sdesvoid 41992559Sdeschannel_free_all(void) 42092559Sdes{ 421137019Sdes u_int i; 42292559Sdes 42392559Sdes for (i = 0; i < channels_alloc; i++) 42492559Sdes if (channels[i] != NULL) 42592559Sdes channel_free(channels[i]); 42692559Sdes} 42792559Sdes 42857429Smarkm/* 42992559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 43092559Sdes * descriptors after a fork. 43192559Sdes */ 43292559Sdesvoid 43392559Sdeschannel_close_all(void) 43492559Sdes{ 435137019Sdes u_int i; 43692559Sdes 43792559Sdes for (i = 0; i < channels_alloc; i++) 43892559Sdes if (channels[i] != NULL) 43992559Sdes channel_close_fds(channels[i]); 44092559Sdes} 44192559Sdes 44292559Sdes/* 44392559Sdes * Stop listening to channels. 44492559Sdes */ 44592559Sdesvoid 44692559Sdeschannel_stop_listening(void) 44792559Sdes{ 448137019Sdes u_int i; 44992559Sdes Channel *c; 45092559Sdes 45192559Sdes for (i = 0; i < channels_alloc; i++) { 45292559Sdes c = channels[i]; 45392559Sdes if (c != NULL) { 45492559Sdes switch (c->type) { 45592559Sdes case SSH_CHANNEL_AUTH_SOCKET: 45692559Sdes case SSH_CHANNEL_PORT_LISTENER: 45792559Sdes case SSH_CHANNEL_RPORT_LISTENER: 45892559Sdes case SSH_CHANNEL_X11_LISTENER: 45992559Sdes channel_close_fd(&c->sock); 46092559Sdes channel_free(c); 46192559Sdes break; 46292559Sdes } 46392559Sdes } 46492559Sdes } 46592559Sdes} 46692559Sdes 46792559Sdes/* 46892559Sdes * Returns true if no channel has too much buffered data, and false if one or 46992559Sdes * more channel is overfull. 47092559Sdes */ 47192559Sdesint 47292559Sdeschannel_not_very_much_buffered_data(void) 47392559Sdes{ 47492559Sdes u_int i; 47592559Sdes Channel *c; 47692559Sdes 47792559Sdes for (i = 0; i < channels_alloc; i++) { 47892559Sdes c = channels[i]; 47992559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 48092559Sdes#if 0 48192559Sdes if (!compat20 && 48292559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 483113911Sdes debug2("channel %d: big input buffer %d", 48492559Sdes c->self, buffer_len(&c->input)); 48592559Sdes return 0; 48692559Sdes } 48792559Sdes#endif 48892559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 489124207Sdes debug2("channel %d: big output buffer %u > %u", 49092559Sdes c->self, buffer_len(&c->output), 49192559Sdes packet_get_maxsize()); 49292559Sdes return 0; 49392559Sdes } 49492559Sdes } 49592559Sdes } 49692559Sdes return 1; 49792559Sdes} 49892559Sdes 49992559Sdes/* Returns true if any channel is still open. */ 50092559Sdesint 50192559Sdeschannel_still_open(void) 50292559Sdes{ 503137019Sdes u_int i; 50492559Sdes Channel *c; 50592559Sdes 50692559Sdes for (i = 0; i < channels_alloc; i++) { 50792559Sdes c = channels[i]; 50892559Sdes if (c == NULL) 50992559Sdes continue; 51092559Sdes switch (c->type) { 51192559Sdes case SSH_CHANNEL_X11_LISTENER: 51292559Sdes case SSH_CHANNEL_PORT_LISTENER: 51392559Sdes case SSH_CHANNEL_RPORT_LISTENER: 51492559Sdes case SSH_CHANNEL_CLOSED: 51592559Sdes case SSH_CHANNEL_AUTH_SOCKET: 51692559Sdes case SSH_CHANNEL_DYNAMIC: 51792559Sdes case SSH_CHANNEL_CONNECTING: 51892559Sdes case SSH_CHANNEL_ZOMBIE: 51992559Sdes continue; 52092559Sdes case SSH_CHANNEL_LARVAL: 52192559Sdes if (!compat20) 52292559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 52392559Sdes continue; 52492559Sdes case SSH_CHANNEL_OPENING: 52592559Sdes case SSH_CHANNEL_OPEN: 52692559Sdes case SSH_CHANNEL_X11_OPEN: 52792559Sdes return 1; 52892559Sdes case SSH_CHANNEL_INPUT_DRAINING: 52992559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 53092559Sdes if (!compat13) 53192559Sdes fatal("cannot happen: OUT_DRAIN"); 53292559Sdes return 1; 53392559Sdes default: 53492559Sdes fatal("channel_still_open: bad channel type %d", c->type); 53592559Sdes /* NOTREACHED */ 53692559Sdes } 53792559Sdes } 53892559Sdes return 0; 53992559Sdes} 54092559Sdes 54192559Sdes/* Returns the id of an open channel suitable for keepaliving */ 54292559Sdesint 54392559Sdeschannel_find_open(void) 54492559Sdes{ 545137019Sdes u_int i; 54692559Sdes Channel *c; 54792559Sdes 54892559Sdes for (i = 0; i < channels_alloc; i++) { 54992559Sdes c = channels[i]; 550137019Sdes if (c == NULL || c->remote_id < 0) 55192559Sdes continue; 55292559Sdes switch (c->type) { 55392559Sdes case SSH_CHANNEL_CLOSED: 55492559Sdes case SSH_CHANNEL_DYNAMIC: 55592559Sdes case SSH_CHANNEL_X11_LISTENER: 55692559Sdes case SSH_CHANNEL_PORT_LISTENER: 55792559Sdes case SSH_CHANNEL_RPORT_LISTENER: 55892559Sdes case SSH_CHANNEL_OPENING: 55992559Sdes case SSH_CHANNEL_CONNECTING: 56092559Sdes case SSH_CHANNEL_ZOMBIE: 56192559Sdes continue; 56292559Sdes case SSH_CHANNEL_LARVAL: 56392559Sdes case SSH_CHANNEL_AUTH_SOCKET: 56492559Sdes case SSH_CHANNEL_OPEN: 56592559Sdes case SSH_CHANNEL_X11_OPEN: 56692559Sdes return i; 56792559Sdes case SSH_CHANNEL_INPUT_DRAINING: 56892559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 56992559Sdes if (!compat13) 57092559Sdes fatal("cannot happen: OUT_DRAIN"); 57192559Sdes return i; 57292559Sdes default: 57392559Sdes fatal("channel_find_open: bad channel type %d", c->type); 57492559Sdes /* NOTREACHED */ 57592559Sdes } 57692559Sdes } 57792559Sdes return -1; 57892559Sdes} 57992559Sdes 58092559Sdes 58192559Sdes/* 58292559Sdes * Returns a message describing the currently open forwarded connections, 58392559Sdes * suitable for sending to the client. The message contains crlf pairs for 58492559Sdes * newlines. 58592559Sdes */ 58692559Sdeschar * 58792559Sdeschannel_open_message(void) 58892559Sdes{ 58992559Sdes Buffer buffer; 59092559Sdes Channel *c; 59192559Sdes char buf[1024], *cp; 592137019Sdes u_int i; 59392559Sdes 59492559Sdes buffer_init(&buffer); 59592559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 59692559Sdes buffer_append(&buffer, buf, strlen(buf)); 59792559Sdes for (i = 0; i < channels_alloc; i++) { 59892559Sdes c = channels[i]; 59992559Sdes if (c == NULL) 60092559Sdes continue; 60192559Sdes switch (c->type) { 60292559Sdes case SSH_CHANNEL_X11_LISTENER: 60392559Sdes case SSH_CHANNEL_PORT_LISTENER: 60492559Sdes case SSH_CHANNEL_RPORT_LISTENER: 60592559Sdes case SSH_CHANNEL_CLOSED: 60692559Sdes case SSH_CHANNEL_AUTH_SOCKET: 60792559Sdes case SSH_CHANNEL_ZOMBIE: 60892559Sdes continue; 60992559Sdes case SSH_CHANNEL_LARVAL: 61092559Sdes case SSH_CHANNEL_OPENING: 61192559Sdes case SSH_CHANNEL_CONNECTING: 61292559Sdes case SSH_CHANNEL_DYNAMIC: 61392559Sdes case SSH_CHANNEL_OPEN: 61492559Sdes case SSH_CHANNEL_X11_OPEN: 61592559Sdes case SSH_CHANNEL_INPUT_DRAINING: 61692559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 617137019Sdes snprintf(buf, sizeof buf, 618137019Sdes " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", 61992559Sdes c->self, c->remote_name, 62092559Sdes c->type, c->remote_id, 62192559Sdes c->istate, buffer_len(&c->input), 62292559Sdes c->ostate, buffer_len(&c->output), 623137019Sdes c->rfd, c->wfd, c->ctl_fd); 62492559Sdes buffer_append(&buffer, buf, strlen(buf)); 62592559Sdes continue; 62692559Sdes default: 62792559Sdes fatal("channel_open_message: bad channel type %d", c->type); 62892559Sdes /* NOTREACHED */ 62992559Sdes } 63092559Sdes } 63192559Sdes buffer_append(&buffer, "\0", 1); 63292559Sdes cp = xstrdup(buffer_ptr(&buffer)); 63392559Sdes buffer_free(&buffer); 63492559Sdes return cp; 63592559Sdes} 63692559Sdes 63792559Sdesvoid 63892559Sdeschannel_send_open(int id) 63992559Sdes{ 64092559Sdes Channel *c = channel_lookup(id); 641106130Sdes 64292559Sdes if (c == NULL) { 643124207Sdes logit("channel_send_open: %d: bad id", id); 64492559Sdes return; 64592559Sdes } 646113911Sdes debug2("channel %d: send open", id); 64792559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 64892559Sdes packet_put_cstring(c->ctype); 64992559Sdes packet_put_int(c->self); 65092559Sdes packet_put_int(c->local_window); 65192559Sdes packet_put_int(c->local_maxpacket); 65292559Sdes packet_send(); 65392559Sdes} 65492559Sdes 65592559Sdesvoid 656113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 65792559Sdes{ 658113911Sdes Channel *c = channel_lookup(id); 659106130Sdes 66092559Sdes if (c == NULL) { 661124207Sdes logit("channel_request_start: %d: unknown channel id", id); 66292559Sdes return; 66392559Sdes } 664137019Sdes debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 66592559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 66692559Sdes packet_put_int(c->remote_id); 66792559Sdes packet_put_cstring(service); 66892559Sdes packet_put_char(wantconfirm); 66992559Sdes} 670162856Sdes 67192559Sdesvoid 672181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb, 673181111Sdes channel_confirm_abandon_cb *abandon_cb, void *ctx) 67492559Sdes{ 675181111Sdes struct channel_confirm *cc; 676181111Sdes Channel *c; 677181111Sdes 678181111Sdes if ((c = channel_lookup(id)) == NULL) 679181111Sdes fatal("channel_register_expect: %d: bad id", id); 680181111Sdes 681181111Sdes cc = xmalloc(sizeof(*cc)); 682181111Sdes cc->cb = cb; 683181111Sdes cc->abandon_cb = abandon_cb; 684181111Sdes cc->ctx = ctx; 685181111Sdes TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 686181111Sdes} 687181111Sdes 688181111Sdesvoid 689181111Sdeschannel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) 690181111Sdes{ 69192559Sdes Channel *c = channel_lookup(id); 692106130Sdes 69392559Sdes if (c == NULL) { 694181111Sdes logit("channel_register_open_comfirm: %d: bad id", id); 69592559Sdes return; 69692559Sdes } 697181111Sdes c->open_confirm = fn; 698181111Sdes c->open_confirm_ctx = ctx; 69992559Sdes} 700162856Sdes 70192559Sdesvoid 702157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 70392559Sdes{ 704157019Sdes Channel *c = channel_by_id(id); 705106130Sdes 70692559Sdes if (c == NULL) { 707124207Sdes logit("channel_register_cleanup: %d: bad id", id); 70892559Sdes return; 70992559Sdes } 71092559Sdes c->detach_user = fn; 711157019Sdes c->detach_close = do_close; 71292559Sdes} 713162856Sdes 71492559Sdesvoid 71592559Sdeschannel_cancel_cleanup(int id) 71692559Sdes{ 717157019Sdes Channel *c = channel_by_id(id); 718106130Sdes 71992559Sdes if (c == NULL) { 720124207Sdes logit("channel_cancel_cleanup: %d: bad id", id); 72192559Sdes return; 72292559Sdes } 72392559Sdes c->detach_user = NULL; 724157019Sdes c->detach_close = 0; 72592559Sdes} 726162856Sdes 72792559Sdesvoid 728157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn, 729181111Sdes channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 73092559Sdes{ 73192559Sdes Channel *c = channel_lookup(id); 732106130Sdes 73392559Sdes if (c == NULL) { 734124207Sdes logit("channel_register_filter: %d: bad id", id); 73592559Sdes return; 73692559Sdes } 737157019Sdes c->input_filter = ifn; 738157019Sdes c->output_filter = ofn; 739181111Sdes c->filter_ctx = ctx; 740181111Sdes c->filter_cleanup = cfn; 74192559Sdes} 74292559Sdes 74392559Sdesvoid 74492559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 745181111Sdes int extusage, int nonblock, int is_tty, u_int window_max) 74692559Sdes{ 74792559Sdes Channel *c = channel_lookup(id); 748106130Sdes 74992559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 75092559Sdes fatal("channel_activate for non-larval channel %d.", id); 751181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 75292559Sdes c->type = SSH_CHANNEL_OPEN; 75392559Sdes c->local_window = c->local_window_max = window_max; 75492559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 75592559Sdes packet_put_int(c->remote_id); 75692559Sdes packet_put_int(c->local_window); 75792559Sdes packet_send(); 75892559Sdes} 75992559Sdes 76092559Sdes/* 76160573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 76260573Skris * channels in the select bitmasks. 76357429Smarkm */ 76460573Skris/* 76560573Skris * 'channel_post*': perform any appropriate operations for channels which 76660573Skris * have events pending. 76760573Skris */ 768162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 76960573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 77060573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 77157429Smarkm 772162856Sdes/* ARGSUSED */ 77392559Sdesstatic void 774162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 77557429Smarkm{ 77660573Skris FD_SET(c->sock, readset); 77760573Skris} 77860573Skris 779162856Sdes/* ARGSUSED */ 78092559Sdesstatic void 781162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 78276262Sgreen{ 78376262Sgreen debug3("channel %d: waiting for connection", c->self); 78476262Sgreen FD_SET(c->sock, writeset); 78576262Sgreen} 78676262Sgreen 78792559Sdesstatic void 788162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 78960573Skris{ 79060573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 79160573Skris FD_SET(c->sock, readset); 79260573Skris if (buffer_len(&c->output) > 0) 79360573Skris FD_SET(c->sock, writeset); 79460573Skris} 79560573Skris 79692559Sdesstatic void 797162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 79860573Skris{ 79992559Sdes u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 80060573Skris 80160573Skris if (c->istate == CHAN_INPUT_OPEN && 80292559Sdes limit > 0 && 803162856Sdes buffer_len(&c->input) < limit && 804162856Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 80560573Skris FD_SET(c->rfd, readset); 80660573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 80760573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 80860573Skris if (buffer_len(&c->output) > 0) { 80960573Skris FD_SET(c->wfd, writeset); 81060573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 81198684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 812149753Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 813149753Sdes c->self, c->efd, buffer_len(&c->extended)); 81498684Sdes else 81598684Sdes chan_obuf_empty(c); 81660573Skris } 81760573Skris } 81860573Skris /** XXX check close conditions, too */ 819181111Sdes if (compat20 && c->efd != -1 && 820181111Sdes !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 82160573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 82260573Skris buffer_len(&c->extended) > 0) 82360573Skris FD_SET(c->efd, writeset); 82498684Sdes else if (!(c->flags & CHAN_EOF_SENT) && 82598684Sdes c->extended_usage == CHAN_EXTENDED_READ && 82660573Skris buffer_len(&c->extended) < c->remote_window) 82760573Skris FD_SET(c->efd, readset); 82860573Skris } 829137019Sdes /* XXX: What about efd? races? */ 830137019Sdes if (compat20 && c->ctl_fd != -1 && 831137019Sdes c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) 832137019Sdes FD_SET(c->ctl_fd, readset); 83360573Skris} 83460573Skris 835162856Sdes/* ARGSUSED */ 83692559Sdesstatic void 837162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 83860573Skris{ 83960573Skris if (buffer_len(&c->input) == 0) { 84060573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 84160573Skris packet_put_int(c->remote_id); 84260573Skris packet_send(); 84360573Skris c->type = SSH_CHANNEL_CLOSED; 844124207Sdes debug2("channel %d: closing after input drain.", c->self); 84560573Skris } 84660573Skris} 84760573Skris 848162856Sdes/* ARGSUSED */ 84992559Sdesstatic void 850162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 85160573Skris{ 85260573Skris if (buffer_len(&c->output) == 0) 85392559Sdes chan_mark_dead(c); 85460573Skris else 85560573Skris FD_SET(c->sock, writeset); 85660573Skris} 85760573Skris 85860573Skris/* 85960573Skris * This is a special state for X11 authentication spoofing. An opened X11 86060573Skris * connection (when authentication spoofing is being done) remains in this 86160573Skris * state until the first packet has been completely read. The authentication 86260573Skris * data in that packet is then substituted by the real data if it matches the 86360573Skris * fake data, and the channel is put into normal mode. 86460573Skris * XXX All this happens at the client side. 86592559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 86660573Skris */ 86792559Sdesstatic int 86892559Sdesx11_open_helper(Buffer *b) 86960573Skris{ 87076262Sgreen u_char *ucp; 87176262Sgreen u_int proto_len, data_len; 87257429Smarkm 87360573Skris /* Check if the fixed size part of the packet is in buffer. */ 87492559Sdes if (buffer_len(b) < 12) 87560573Skris return 0; 87657429Smarkm 87760573Skris /* Parse the lengths of variable-length fields. */ 87892559Sdes ucp = buffer_ptr(b); 87960573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 88060573Skris proto_len = 256 * ucp[6] + ucp[7]; 88160573Skris data_len = 256 * ucp[8] + ucp[9]; 88260573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 88360573Skris proto_len = ucp[6] + 256 * ucp[7]; 88460573Skris data_len = ucp[8] + 256 * ucp[9]; 88560573Skris } else { 886124207Sdes debug2("Initial X11 packet contains bad byte order byte: 0x%x", 88792559Sdes ucp[0]); 88860573Skris return -1; 88960573Skris } 89057429Smarkm 89160573Skris /* Check if the whole packet is in buffer. */ 89292559Sdes if (buffer_len(b) < 89360573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 89460573Skris return 0; 89557429Smarkm 89660573Skris /* Check if authentication protocol matches. */ 89760573Skris if (proto_len != strlen(x11_saved_proto) || 89860573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 899124207Sdes debug2("X11 connection uses different authentication protocol."); 90060573Skris return -1; 90160573Skris } 90260573Skris /* Check if authentication data matches our fake data. */ 90360573Skris if (data_len != x11_fake_data_len || 90460573Skris memcmp(ucp + 12 + ((proto_len + 3) & ~3), 90560573Skris x11_fake_data, x11_fake_data_len) != 0) { 906124207Sdes debug2("X11 auth data does not match fake data."); 90760573Skris return -1; 90860573Skris } 90960573Skris /* Check fake data length */ 91060573Skris if (x11_fake_data_len != x11_saved_data_len) { 91160573Skris error("X11 fake_data_len %d != saved_data_len %d", 91260573Skris x11_fake_data_len, x11_saved_data_len); 91360573Skris return -1; 91460573Skris } 91560573Skris /* 91660573Skris * Received authentication protocol and data match 91760573Skris * our fake data. Substitute the fake data with real 91860573Skris * data. 91960573Skris */ 92060573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 92160573Skris x11_saved_data, x11_saved_data_len); 92260573Skris return 1; 92360573Skris} 92457429Smarkm 92592559Sdesstatic void 926162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 92760573Skris{ 92892559Sdes int ret = x11_open_helper(&c->output); 929106130Sdes 93060573Skris if (ret == 1) { 93160573Skris /* Start normal processing for the channel. */ 93260573Skris c->type = SSH_CHANNEL_OPEN; 93360573Skris channel_pre_open_13(c, readset, writeset); 93460573Skris } else if (ret == -1) { 93560573Skris /* 93660573Skris * We have received an X11 connection that has bad 93760573Skris * authentication information. 93860573Skris */ 939124207Sdes logit("X11 connection rejected because of wrong authentication."); 94060573Skris buffer_clear(&c->input); 94160573Skris buffer_clear(&c->output); 94292559Sdes channel_close_fd(&c->sock); 94360573Skris c->sock = -1; 94460573Skris c->type = SSH_CHANNEL_CLOSED; 94560573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 94660573Skris packet_put_int(c->remote_id); 94760573Skris packet_send(); 94860573Skris } 94960573Skris} 95057429Smarkm 95192559Sdesstatic void 952162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 95360573Skris{ 95492559Sdes int ret = x11_open_helper(&c->output); 95592559Sdes 95692559Sdes /* c->force_drain = 1; */ 95792559Sdes 95860573Skris if (ret == 1) { 95960573Skris c->type = SSH_CHANNEL_OPEN; 96092559Sdes channel_pre_open(c, readset, writeset); 96192559Sdes } else if (ret == -1) { 962124207Sdes logit("X11 connection rejected because of wrong authentication."); 963124207Sdes debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 96492559Sdes chan_read_failed(c); 96592559Sdes buffer_clear(&c->input); 96692559Sdes chan_ibuf_empty(c); 96792559Sdes buffer_clear(&c->output); 96892559Sdes /* for proto v1, the peer will send an IEOF */ 96960573Skris if (compat20) 97092559Sdes chan_write_failed(c); 97160573Skris else 97292559Sdes c->type = SSH_CHANNEL_OPEN; 973124207Sdes debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 97460573Skris } 97560573Skris} 97657429Smarkm 97776262Sgreen/* try to decode a socks4 header */ 978162856Sdes/* ARGSUSED */ 97992559Sdesstatic int 980162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 98176262Sgreen{ 982106130Sdes char *p, *host; 983149753Sdes u_int len, have, i, found; 98492559Sdes char username[256]; 98576262Sgreen struct { 98676262Sgreen u_int8_t version; 98776262Sgreen u_int8_t command; 98876262Sgreen u_int16_t dest_port; 98976262Sgreen struct in_addr dest_addr; 99076262Sgreen } s4_req, s4_rsp; 99176262Sgreen 99276262Sgreen debug2("channel %d: decode socks4", c->self); 99376262Sgreen 99476262Sgreen have = buffer_len(&c->input); 99576262Sgreen len = sizeof(s4_req); 99676262Sgreen if (have < len) 99776262Sgreen return 0; 99876262Sgreen p = buffer_ptr(&c->input); 99976262Sgreen for (found = 0, i = len; i < have; i++) { 100076262Sgreen if (p[i] == '\0') { 100176262Sgreen found = 1; 100276262Sgreen break; 100376262Sgreen } 100476262Sgreen if (i > 1024) { 100576262Sgreen /* the peer is probably sending garbage */ 100676262Sgreen debug("channel %d: decode socks4: too long", 100776262Sgreen c->self); 100876262Sgreen return -1; 100976262Sgreen } 101076262Sgreen } 101176262Sgreen if (!found) 101276262Sgreen return 0; 101376262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 101476262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 101576262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 101676262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 101776262Sgreen have = buffer_len(&c->input); 101876262Sgreen p = buffer_ptr(&c->input); 101976262Sgreen len = strlen(p); 102076262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 102176262Sgreen if (len > have) 102276262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 102376262Sgreen c->self, len, have); 102476262Sgreen strlcpy(username, p, sizeof(username)); 102576262Sgreen buffer_consume(&c->input, len); 102676262Sgreen buffer_consume(&c->input, 1); /* trailing '\0' */ 102776262Sgreen 102876262Sgreen host = inet_ntoa(s4_req.dest_addr); 102976262Sgreen strlcpy(c->path, host, sizeof(c->path)); 103076262Sgreen c->host_port = ntohs(s4_req.dest_port); 103192559Sdes 1032124207Sdes debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 103376262Sgreen c->self, host, c->host_port, s4_req.command); 103476262Sgreen 103576262Sgreen if (s4_req.command != 1) { 103676262Sgreen debug("channel %d: cannot handle: socks4 cn %d", 103776262Sgreen c->self, s4_req.command); 103876262Sgreen return -1; 103976262Sgreen } 104076262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 104176262Sgreen s4_rsp.command = 90; /* cd: req granted */ 104276262Sgreen s4_rsp.dest_port = 0; /* ignored */ 104376262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1044162856Sdes buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 104576262Sgreen return 1; 104676262Sgreen} 104776262Sgreen 1048124207Sdes/* try to decode a socks5 header */ 1049124207Sdes#define SSH_SOCKS5_AUTHDONE 0x1000 1050124207Sdes#define SSH_SOCKS5_NOAUTH 0x00 1051124207Sdes#define SSH_SOCKS5_IPV4 0x01 1052124207Sdes#define SSH_SOCKS5_DOMAIN 0x03 1053124207Sdes#define SSH_SOCKS5_IPV6 0x04 1054124207Sdes#define SSH_SOCKS5_CONNECT 0x01 1055124207Sdes#define SSH_SOCKS5_SUCCESS 0x00 1056124207Sdes 1057162856Sdes/* ARGSUSED */ 1058124207Sdesstatic int 1059162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1060124207Sdes{ 1061124207Sdes struct { 1062124207Sdes u_int8_t version; 1063124207Sdes u_int8_t command; 1064124207Sdes u_int8_t reserved; 1065124207Sdes u_int8_t atyp; 1066124207Sdes } s5_req, s5_rsp; 1067124207Sdes u_int16_t dest_port; 1068124207Sdes u_char *p, dest_addr[255+1]; 1069162856Sdes u_int have, need, i, found, nmethods, addrlen, af; 1070124207Sdes 1071124207Sdes debug2("channel %d: decode socks5", c->self); 1072124207Sdes p = buffer_ptr(&c->input); 1073124207Sdes if (p[0] != 0x05) 1074124207Sdes return -1; 1075124207Sdes have = buffer_len(&c->input); 1076124207Sdes if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1077124207Sdes /* format: ver | nmethods | methods */ 1078126273Sdes if (have < 2) 1079124207Sdes return 0; 1080124207Sdes nmethods = p[1]; 1081124207Sdes if (have < nmethods + 2) 1082124207Sdes return 0; 1083124207Sdes /* look for method: "NO AUTHENTICATION REQUIRED" */ 1084181111Sdes for (found = 0, i = 2; i < nmethods + 2; i++) { 1085162856Sdes if (p[i] == SSH_SOCKS5_NOAUTH) { 1086124207Sdes found = 1; 1087124207Sdes break; 1088124207Sdes } 1089124207Sdes } 1090124207Sdes if (!found) { 1091124207Sdes debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1092124207Sdes c->self); 1093124207Sdes return -1; 1094124207Sdes } 1095124207Sdes buffer_consume(&c->input, nmethods + 2); 1096124207Sdes buffer_put_char(&c->output, 0x05); /* version */ 1097124207Sdes buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1098124207Sdes FD_SET(c->sock, writeset); 1099124207Sdes c->flags |= SSH_SOCKS5_AUTHDONE; 1100124207Sdes debug2("channel %d: socks5 auth done", c->self); 1101124207Sdes return 0; /* need more */ 1102124207Sdes } 1103124207Sdes debug2("channel %d: socks5 post auth", c->self); 1104124207Sdes if (have < sizeof(s5_req)+1) 1105124207Sdes return 0; /* need more */ 1106162856Sdes memcpy(&s5_req, p, sizeof(s5_req)); 1107124207Sdes if (s5_req.version != 0x05 || 1108124207Sdes s5_req.command != SSH_SOCKS5_CONNECT || 1109124207Sdes s5_req.reserved != 0x00) { 1110124207Sdes debug2("channel %d: only socks5 connect supported", c->self); 1111124207Sdes return -1; 1112124207Sdes } 1113147005Sdes switch (s5_req.atyp){ 1114124207Sdes case SSH_SOCKS5_IPV4: 1115124207Sdes addrlen = 4; 1116124207Sdes af = AF_INET; 1117124207Sdes break; 1118124207Sdes case SSH_SOCKS5_DOMAIN: 1119124207Sdes addrlen = p[sizeof(s5_req)]; 1120124207Sdes af = -1; 1121124207Sdes break; 1122124207Sdes case SSH_SOCKS5_IPV6: 1123124207Sdes addrlen = 16; 1124124207Sdes af = AF_INET6; 1125124207Sdes break; 1126124207Sdes default: 1127124207Sdes debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1128124207Sdes return -1; 1129124207Sdes } 1130162856Sdes need = sizeof(s5_req) + addrlen + 2; 1131162856Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1132162856Sdes need++; 1133162856Sdes if (have < need) 1134124207Sdes return 0; 1135124207Sdes buffer_consume(&c->input, sizeof(s5_req)); 1136124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1137124207Sdes buffer_consume(&c->input, 1); /* host string length */ 1138124207Sdes buffer_get(&c->input, (char *)&dest_addr, addrlen); 1139124207Sdes buffer_get(&c->input, (char *)&dest_port, 2); 1140124207Sdes dest_addr[addrlen] = '\0'; 1141124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1142137019Sdes strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); 1143124207Sdes else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) 1144124207Sdes return -1; 1145124207Sdes c->host_port = ntohs(dest_port); 1146126273Sdes 1147124207Sdes debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1148124207Sdes c->self, c->path, c->host_port, s5_req.command); 1149124207Sdes 1150124207Sdes s5_rsp.version = 0x05; 1151124207Sdes s5_rsp.command = SSH_SOCKS5_SUCCESS; 1152124207Sdes s5_rsp.reserved = 0; /* ignored */ 1153124207Sdes s5_rsp.atyp = SSH_SOCKS5_IPV4; 1154124207Sdes ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; 1155124207Sdes dest_port = 0; /* ignored */ 1156124207Sdes 1157162856Sdes buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1158162856Sdes buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); 1159162856Sdes buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1160124207Sdes return 1; 1161124207Sdes} 1162124207Sdes 116376262Sgreen/* dynamic port forwarding */ 116492559Sdesstatic void 1165162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 116676262Sgreen{ 116776262Sgreen u_char *p; 1168149753Sdes u_int have; 1169149753Sdes int ret; 117076262Sgreen 117176262Sgreen have = buffer_len(&c->input); 117292559Sdes c->delayed = 0; 117376262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 117476262Sgreen /* buffer_dump(&c->input); */ 117576262Sgreen /* check if the fixed size part of the packet is in buffer. */ 1176124207Sdes if (have < 3) { 117776262Sgreen /* need more */ 117876262Sgreen FD_SET(c->sock, readset); 117976262Sgreen return; 118076262Sgreen } 118176262Sgreen /* try to guess the protocol */ 118276262Sgreen p = buffer_ptr(&c->input); 118376262Sgreen switch (p[0]) { 118476262Sgreen case 0x04: 118576262Sgreen ret = channel_decode_socks4(c, readset, writeset); 118676262Sgreen break; 1187124207Sdes case 0x05: 1188124207Sdes ret = channel_decode_socks5(c, readset, writeset); 1189124207Sdes break; 119076262Sgreen default: 119176262Sgreen ret = -1; 119276262Sgreen break; 119376262Sgreen } 119476262Sgreen if (ret < 0) { 119592559Sdes chan_mark_dead(c); 119676262Sgreen } else if (ret == 0) { 119776262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 119876262Sgreen /* need more */ 119976262Sgreen FD_SET(c->sock, readset); 120076262Sgreen } else { 120176262Sgreen /* switch to the next state */ 120276262Sgreen c->type = SSH_CHANNEL_OPENING; 120376262Sgreen port_open_helper(c, "direct-tcpip"); 120476262Sgreen } 120576262Sgreen} 120676262Sgreen 120760573Skris/* This is our fake X11 server socket. */ 1208162856Sdes/* ARGSUSED */ 120992559Sdesstatic void 1210162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 121160573Skris{ 121292559Sdes Channel *nc; 1213181111Sdes struct sockaddr_storage addr; 121492559Sdes int newsock; 121560573Skris socklen_t addrlen; 121676262Sgreen char buf[16384], *remote_ipaddr; 121760573Skris int remote_port; 121857429Smarkm 121960573Skris if (FD_ISSET(c->sock, readset)) { 122060573Skris debug("X11 connection requested."); 122160573Skris addrlen = sizeof(addr); 1222181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 122392559Sdes if (c->single_connection) { 1224124207Sdes debug2("single_connection: closing X11 listener."); 122592559Sdes channel_close_fd(&c->sock); 122692559Sdes chan_mark_dead(c); 122792559Sdes } 122860573Skris if (newsock < 0) { 122960573Skris error("accept: %.100s", strerror(errno)); 123060573Skris return; 123160573Skris } 123292559Sdes set_nodelay(newsock); 123376262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 123460573Skris remote_port = get_peer_port(newsock); 123560573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 123676262Sgreen remote_ipaddr, remote_port); 123757429Smarkm 123892559Sdes nc = channel_new("accepted x11 socket", 123960573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 1240124207Sdes c->local_window_max, c->local_maxpacket, 0, buf, 1); 124160573Skris if (compat20) { 124260573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 124360573Skris packet_put_cstring("x11"); 124492559Sdes packet_put_int(nc->self); 124592559Sdes packet_put_int(nc->local_window_max); 124692559Sdes packet_put_int(nc->local_maxpacket); 124776262Sgreen /* originator ipaddr and port */ 124876262Sgreen packet_put_cstring(remote_ipaddr); 124960573Skris if (datafellows & SSH_BUG_X11FWD) { 1250124207Sdes debug2("ssh2 x11 bug compat mode"); 125157429Smarkm } else { 125260573Skris packet_put_int(remote_port); 125357429Smarkm } 125460573Skris packet_send(); 125560573Skris } else { 125660573Skris packet_start(SSH_SMSG_X11_OPEN); 125792559Sdes packet_put_int(nc->self); 125892559Sdes if (packet_get_protocol_flags() & 125992559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 126092559Sdes packet_put_cstring(buf); 126160573Skris packet_send(); 126257429Smarkm } 126376262Sgreen xfree(remote_ipaddr); 126457429Smarkm } 126557429Smarkm} 126657429Smarkm 126792559Sdesstatic void 126876262Sgreenport_open_helper(Channel *c, char *rtype) 126976262Sgreen{ 127076262Sgreen int direct; 127176262Sgreen char buf[1024]; 127276262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 1273149753Sdes int remote_port = get_peer_port(c->sock); 127476262Sgreen 127576262Sgreen direct = (strcmp(rtype, "direct-tcpip") == 0); 127676262Sgreen 127776262Sgreen snprintf(buf, sizeof buf, 127876262Sgreen "%s: listening port %d for %.100s port %d, " 127976262Sgreen "connect from %.200s port %d", 128076262Sgreen rtype, c->listening_port, c->path, c->host_port, 128176262Sgreen remote_ipaddr, remote_port); 128276262Sgreen 128376262Sgreen xfree(c->remote_name); 128476262Sgreen c->remote_name = xstrdup(buf); 128576262Sgreen 128676262Sgreen if (compat20) { 128776262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 128876262Sgreen packet_put_cstring(rtype); 128976262Sgreen packet_put_int(c->self); 129076262Sgreen packet_put_int(c->local_window_max); 129176262Sgreen packet_put_int(c->local_maxpacket); 129276262Sgreen if (direct) { 129376262Sgreen /* target host, port */ 129476262Sgreen packet_put_cstring(c->path); 129576262Sgreen packet_put_int(c->host_port); 129676262Sgreen } else { 129776262Sgreen /* listen address, port */ 129876262Sgreen packet_put_cstring(c->path); 129976262Sgreen packet_put_int(c->listening_port); 130076262Sgreen } 130176262Sgreen /* originator host and port */ 130276262Sgreen packet_put_cstring(remote_ipaddr); 1303149753Sdes packet_put_int((u_int)remote_port); 130476262Sgreen packet_send(); 130576262Sgreen } else { 130676262Sgreen packet_start(SSH_MSG_PORT_OPEN); 130776262Sgreen packet_put_int(c->self); 130876262Sgreen packet_put_cstring(c->path); 130976262Sgreen packet_put_int(c->host_port); 131092559Sdes if (packet_get_protocol_flags() & 131192559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 131276262Sgreen packet_put_cstring(c->remote_name); 131376262Sgreen packet_send(); 131476262Sgreen } 131576262Sgreen xfree(remote_ipaddr); 131676262Sgreen} 131776262Sgreen 1318157019Sdesstatic void 1319157019Sdeschannel_set_reuseaddr(int fd) 1320157019Sdes{ 1321157019Sdes int on = 1; 1322157019Sdes 1323157019Sdes /* 1324157019Sdes * Set socket options. 1325157019Sdes * Allow local port reuse in TIME_WAIT. 1326157019Sdes */ 1327157019Sdes if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1328157019Sdes error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1329157019Sdes} 1330157019Sdes 133157429Smarkm/* 133260573Skris * This socket is listening for connections to a forwarded TCP/IP port. 133357429Smarkm */ 1334162856Sdes/* ARGSUSED */ 133592559Sdesstatic void 1336162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 133757429Smarkm{ 133876262Sgreen Channel *nc; 1339181111Sdes struct sockaddr_storage addr; 134092559Sdes int newsock, nextstate; 134157429Smarkm socklen_t addrlen; 134276262Sgreen char *rtype; 134357429Smarkm 134460573Skris if (FD_ISSET(c->sock, readset)) { 134560573Skris debug("Connection to port %d forwarding " 134660573Skris "to %.100s port %d requested.", 134760573Skris c->listening_port, c->path, c->host_port); 134876262Sgreen 134992559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 135092559Sdes nextstate = SSH_CHANNEL_OPENING; 135192559Sdes rtype = "forwarded-tcpip"; 135292559Sdes } else { 135392559Sdes if (c->host_port == 0) { 135492559Sdes nextstate = SSH_CHANNEL_DYNAMIC; 135592559Sdes rtype = "dynamic-tcpip"; 135692559Sdes } else { 135792559Sdes nextstate = SSH_CHANNEL_OPENING; 135892559Sdes rtype = "direct-tcpip"; 135992559Sdes } 136092559Sdes } 136176262Sgreen 136260573Skris addrlen = sizeof(addr); 1363181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 136460573Skris if (newsock < 0) { 136560573Skris error("accept: %.100s", strerror(errno)); 136660573Skris return; 136760573Skris } 136892559Sdes set_nodelay(newsock); 1369124207Sdes nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1370124207Sdes c->local_window_max, c->local_maxpacket, 0, rtype, 1); 137176262Sgreen nc->listening_port = c->listening_port; 137276262Sgreen nc->host_port = c->host_port; 137376262Sgreen strlcpy(nc->path, c->path, sizeof(nc->path)); 137476262Sgreen 137592559Sdes if (nextstate == SSH_CHANNEL_DYNAMIC) { 137692559Sdes /* 137792559Sdes * do not call the channel_post handler until 137892559Sdes * this flag has been reset by a pre-handler. 137992559Sdes * otherwise the FD_ISSET calls might overflow 138092559Sdes */ 138192559Sdes nc->delayed = 1; 138292559Sdes } else { 138376262Sgreen port_open_helper(nc, rtype); 138492559Sdes } 138560573Skris } 138660573Skris} 138757429Smarkm 138860573Skris/* 138960573Skris * This is the authentication agent socket listening for connections from 139060573Skris * clients. 139160573Skris */ 1392162856Sdes/* ARGSUSED */ 139392559Sdesstatic void 1394162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 139560573Skris{ 139692559Sdes Channel *nc; 139792559Sdes int newsock; 1398181111Sdes struct sockaddr_storage addr; 139960573Skris socklen_t addrlen; 140057429Smarkm 140160573Skris if (FD_ISSET(c->sock, readset)) { 140260573Skris addrlen = sizeof(addr); 1403181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 140460573Skris if (newsock < 0) { 140560573Skris error("accept from auth socket: %.100s", strerror(errno)); 140660573Skris return; 140760573Skris } 140892559Sdes nc = channel_new("accepted auth socket", 140976262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 141076262Sgreen c->local_window_max, c->local_maxpacket, 1411124207Sdes 0, "accepted auth socket", 1); 141276262Sgreen if (compat20) { 141376262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 141476262Sgreen packet_put_cstring("auth-agent@openssh.com"); 141592559Sdes packet_put_int(nc->self); 141676262Sgreen packet_put_int(c->local_window_max); 141776262Sgreen packet_put_int(c->local_maxpacket); 141876262Sgreen } else { 141976262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 142092559Sdes packet_put_int(nc->self); 142176262Sgreen } 142260573Skris packet_send(); 142360573Skris } 142460573Skris} 142557429Smarkm 1426162856Sdes/* ARGSUSED */ 142792559Sdesstatic void 1428162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 142976262Sgreen{ 1430181111Sdes int err = 0, sock; 143192559Sdes socklen_t sz = sizeof(err); 143292559Sdes 143376262Sgreen if (FD_ISSET(c->sock, writeset)) { 143492559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 143592559Sdes err = errno; 143692559Sdes error("getsockopt SO_ERROR failed"); 143792559Sdes } 143892559Sdes if (err == 0) { 1439181111Sdes debug("channel %d: connected to %s port %d", 1440181111Sdes c->self, c->connect_ctx.host, c->connect_ctx.port); 1441181111Sdes channel_connect_ctx_free(&c->connect_ctx); 144292559Sdes c->type = SSH_CHANNEL_OPEN; 144392559Sdes if (compat20) { 144492559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 144592559Sdes packet_put_int(c->remote_id); 144692559Sdes packet_put_int(c->self); 144792559Sdes packet_put_int(c->local_window); 144892559Sdes packet_put_int(c->local_maxpacket); 144992559Sdes } else { 145092559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 145192559Sdes packet_put_int(c->remote_id); 145292559Sdes packet_put_int(c->self); 145392559Sdes } 145476262Sgreen } else { 1455181111Sdes debug("channel %d: connection failed: %s", 145692559Sdes c->self, strerror(err)); 1457181111Sdes /* Try next address, if any */ 1458181111Sdes if ((sock = connect_next(&c->connect_ctx)) > 0) { 1459181111Sdes close(c->sock); 1460181111Sdes c->sock = c->rfd = c->wfd = sock; 1461181111Sdes channel_max_fd = channel_find_maxfd(); 1462181111Sdes return; 1463181111Sdes } 1464181111Sdes /* Exhausted all addresses */ 1465181111Sdes error("connect_to %.100s port %d: failed.", 1466181111Sdes c->connect_ctx.host, c->connect_ctx.port); 1467181111Sdes channel_connect_ctx_free(&c->connect_ctx); 146892559Sdes if (compat20) { 146992559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 147092559Sdes packet_put_int(c->remote_id); 147192559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 147292559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 147392559Sdes packet_put_cstring(strerror(err)); 147492559Sdes packet_put_cstring(""); 147592559Sdes } 147676262Sgreen } else { 147792559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 147892559Sdes packet_put_int(c->remote_id); 147976262Sgreen } 148092559Sdes chan_mark_dead(c); 148176262Sgreen } 148292559Sdes packet_send(); 148376262Sgreen } 148476262Sgreen} 148576262Sgreen 1486162856Sdes/* ARGSUSED */ 148792559Sdesstatic int 1488162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 148960573Skris{ 1490147005Sdes char buf[CHAN_RBUF]; 1491181111Sdes int len, force; 149257429Smarkm 1493181111Sdes force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1494181111Sdes if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1495162856Sdes errno = 0; 149660573Skris len = read(c->rfd, buf, sizeof(buf)); 1497181111Sdes if (len < 0 && (errno == EINTR || 1498181111Sdes ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 149960573Skris return 1; 1500162856Sdes#ifndef PTY_ZEROREAD 150178827Sgreen if (len <= 0) { 1502162856Sdes#else 1503162856Sdes if ((!c->isatty && len <= 0) || 1504162856Sdes (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1505162856Sdes#endif 1506124207Sdes debug2("channel %d: read<=0 rfd %d len %d", 150760573Skris c->self, c->rfd, len); 150876262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1509124207Sdes debug2("channel %d: not open", c->self); 151092559Sdes chan_mark_dead(c); 151176262Sgreen return -1; 151276262Sgreen } else if (compat13) { 151392559Sdes buffer_clear(&c->output); 151460573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 1515124207Sdes debug2("channel %d: input draining.", c->self); 151660573Skris } else { 151760573Skris chan_read_failed(c); 151857429Smarkm } 151960573Skris return -1; 152060573Skris } 152192559Sdes if (c->input_filter != NULL) { 152265668Skris if (c->input_filter(c, buf, len) == -1) { 1523124207Sdes debug2("channel %d: filter stops", c->self); 152465668Skris chan_read_failed(c); 152565668Skris } 1526157019Sdes } else if (c->datagram) { 1527157019Sdes buffer_put_string(&c->input, buf, len); 152865668Skris } else { 152965668Skris buffer_append(&c->input, buf, len); 153065668Skris } 153160573Skris } 153260573Skris return 1; 153360573Skris} 1534162856Sdes 1535162856Sdes/* ARGSUSED */ 153692559Sdesstatic int 1537162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 153860573Skris{ 153976262Sgreen struct termios tio; 1540157019Sdes u_char *data = NULL, *buf; 154192559Sdes u_int dlen; 154260573Skris int len; 154360573Skris 154460573Skris /* Send buffered output data to the socket. */ 154560573Skris if (c->wfd != -1 && 154660573Skris FD_ISSET(c->wfd, writeset) && 154760573Skris buffer_len(&c->output) > 0) { 1548157019Sdes if (c->output_filter != NULL) { 1549157019Sdes if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1550157019Sdes debug2("channel %d: filter stops", c->self); 1551157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1552157019Sdes chan_mark_dead(c); 1553157019Sdes else 1554157019Sdes chan_write_failed(c); 1555157019Sdes return -1; 1556157019Sdes } 1557157019Sdes } else if (c->datagram) { 1558157019Sdes buf = data = buffer_get_string(&c->output, &dlen); 1559157019Sdes } else { 1560157019Sdes buf = data = buffer_ptr(&c->output); 1561157019Sdes dlen = buffer_len(&c->output); 1562157019Sdes } 1563157019Sdes 1564157019Sdes if (c->datagram) { 1565157019Sdes /* ignore truncated writes, datagrams might get lost */ 1566157019Sdes c->local_consumed += dlen + 4; 1567157019Sdes len = write(c->wfd, buf, dlen); 1568157019Sdes xfree(data); 1569181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1570181111Sdes errno == EWOULDBLOCK)) 1571157019Sdes return 1; 1572157019Sdes if (len <= 0) { 1573157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1574157019Sdes chan_mark_dead(c); 1575157019Sdes else 1576157019Sdes chan_write_failed(c); 1577157019Sdes return -1; 1578157019Sdes } 1579157019Sdes return 1; 1580157019Sdes } 1581106130Sdes#ifdef _AIX 1582126273Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1583126273Sdes if (compat20 && c->wfd_isatty) 1584126273Sdes dlen = MIN(dlen, 8*1024); 1585106130Sdes#endif 1586157019Sdes 1587157019Sdes len = write(c->wfd, buf, dlen); 1588181111Sdes if (len < 0 && 1589181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 159060573Skris return 1; 159160573Skris if (len <= 0) { 159276262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1593124207Sdes debug2("channel %d: not open", c->self); 159492559Sdes chan_mark_dead(c); 159576262Sgreen return -1; 159676262Sgreen } else if (compat13) { 159792559Sdes buffer_clear(&c->output); 1598124207Sdes debug2("channel %d: input draining.", c->self); 159960573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 160060573Skris } else { 160160573Skris chan_write_failed(c); 160257429Smarkm } 160360573Skris return -1; 160460573Skris } 1605157019Sdes if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 160674500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 160774500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 160874500Sgreen /* 160974500Sgreen * Simulate echo to reduce the impact of 161076262Sgreen * traffic analysis. We need to match the 161176262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 1612157019Sdes * (4 byte channel id + buf) 161374500Sgreen */ 161476262Sgreen packet_send_ignore(4 + len); 161574500Sgreen packet_send(); 161674500Sgreen } 161774500Sgreen } 161860573Skris buffer_consume(&c->output, len); 161960573Skris if (compat20 && len > 0) { 162060573Skris c->local_consumed += len; 162160573Skris } 162260573Skris } 162360573Skris return 1; 162460573Skris} 1625162856Sdes 162692559Sdesstatic int 1627162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 162860573Skris{ 1629147005Sdes char buf[CHAN_RBUF]; 163060573Skris int len; 163157429Smarkm 163260573Skris/** XXX handle drain efd, too */ 163360573Skris if (c->efd != -1) { 163460573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 163560573Skris FD_ISSET(c->efd, writeset) && 163660573Skris buffer_len(&c->extended) > 0) { 163760573Skris len = write(c->efd, buffer_ptr(&c->extended), 163860573Skris buffer_len(&c->extended)); 163969587Sgreen debug2("channel %d: written %d to efd %d", 164060573Skris c->self, len, c->efd); 1641181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1642181111Sdes errno == EWOULDBLOCK)) 164376262Sgreen return 1; 164476262Sgreen if (len <= 0) { 164576262Sgreen debug2("channel %d: closing write-efd %d", 164676262Sgreen c->self, c->efd); 164792559Sdes channel_close_fd(&c->efd); 164876262Sgreen } else { 164960573Skris buffer_consume(&c->extended, len); 165060573Skris c->local_consumed += len; 165157429Smarkm } 165260573Skris } else if (c->extended_usage == CHAN_EXTENDED_READ && 1653181111Sdes (c->detach_close || FD_ISSET(c->efd, readset))) { 165460573Skris len = read(c->efd, buf, sizeof(buf)); 165569587Sgreen debug2("channel %d: read %d from efd %d", 165692559Sdes c->self, len, c->efd); 1657181111Sdes if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1658181111Sdes errno == EWOULDBLOCK) && !c->detach_close))) 165976262Sgreen return 1; 166076262Sgreen if (len <= 0) { 166176262Sgreen debug2("channel %d: closing read-efd %d", 166260573Skris c->self, c->efd); 166392559Sdes channel_close_fd(&c->efd); 166476262Sgreen } else { 166560573Skris buffer_append(&c->extended, buf, len); 166676262Sgreen } 166760573Skris } 166860573Skris } 166960573Skris return 1; 167060573Skris} 1671162856Sdes 1672162856Sdes/* ARGSUSED */ 167392559Sdesstatic int 1674162856Sdeschannel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) 1675137019Sdes{ 1676137019Sdes char buf[16]; 1677137019Sdes int len; 1678137019Sdes 1679137019Sdes /* Monitor control fd to detect if the slave client exits */ 1680137019Sdes if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { 1681137019Sdes len = read(c->ctl_fd, buf, sizeof(buf)); 1682181111Sdes if (len < 0 && 1683181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 1684137019Sdes return 1; 1685137019Sdes if (len <= 0) { 1686137019Sdes debug2("channel %d: ctl read<=0", c->self); 1687137019Sdes if (c->type != SSH_CHANNEL_OPEN) { 1688137019Sdes debug2("channel %d: not open", c->self); 1689137019Sdes chan_mark_dead(c); 1690137019Sdes return -1; 1691137019Sdes } else { 1692137019Sdes chan_read_failed(c); 1693137019Sdes chan_write_failed(c); 1694137019Sdes } 1695137019Sdes return -1; 1696137019Sdes } else 1697137019Sdes fatal("%s: unexpected data on ctl fd", __func__); 1698137019Sdes } 1699137019Sdes return 1; 1700137019Sdes} 1701162856Sdes 1702137019Sdesstatic int 170376262Sgreenchannel_check_window(Channel *c) 170460573Skris{ 170576262Sgreen if (c->type == SSH_CHANNEL_OPEN && 170676262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1707181111Sdes ((c->local_window_max - c->local_window > 1708181111Sdes c->local_maxpacket*3) || 1709181111Sdes c->local_window < c->local_window_max/2) && 171060573Skris c->local_consumed > 0) { 171160573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 171260573Skris packet_put_int(c->remote_id); 171360573Skris packet_put_int(c->local_consumed); 171460573Skris packet_send(); 171569587Sgreen debug2("channel %d: window %d sent adjust %d", 171660573Skris c->self, c->local_window, 171760573Skris c->local_consumed); 171860573Skris c->local_window += c->local_consumed; 171960573Skris c->local_consumed = 0; 172060573Skris } 172160573Skris return 1; 172260573Skris} 172357429Smarkm 172492559Sdesstatic void 1725162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 172660573Skris{ 172792559Sdes if (c->delayed) 172892559Sdes return; 172960573Skris channel_handle_rfd(c, readset, writeset); 173060573Skris channel_handle_wfd(c, readset, writeset); 173192559Sdes if (!compat20) 173292559Sdes return; 173360573Skris channel_handle_efd(c, readset, writeset); 1734137019Sdes channel_handle_ctl(c, readset, writeset); 173576262Sgreen channel_check_window(c); 173660573Skris} 173760573Skris 1738162856Sdes/* ARGSUSED */ 173992559Sdesstatic void 1740162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 174160573Skris{ 174260573Skris int len; 1743106130Sdes 174460573Skris /* Send buffered output data to the socket. */ 174560573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 174660573Skris len = write(c->sock, buffer_ptr(&c->output), 174760573Skris buffer_len(&c->output)); 174860573Skris if (len <= 0) 174992559Sdes buffer_clear(&c->output); 175060573Skris else 175160573Skris buffer_consume(&c->output, len); 175260573Skris } 175360573Skris} 175460573Skris 175592559Sdesstatic void 175660573Skrischannel_handler_init_20(void) 175760573Skris{ 175892559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 175960573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 176060573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 176176262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 176260573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 176376262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 176476262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 176576262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 176660573Skris 176792559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 176860573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 176976262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 177060573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 177176262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 177276262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 177392559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 177460573Skris} 177560573Skris 177692559Sdesstatic void 177760573Skrischannel_handler_init_13(void) 177860573Skris{ 177960573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 178060573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 178160573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 178260573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 178360573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 178460573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 178560573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 178676262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 178776262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 178860573Skris 178992559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 179060573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 179160573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 179260573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 179360573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 179476262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 179592559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 179660573Skris} 179760573Skris 179892559Sdesstatic void 179960573Skrischannel_handler_init_15(void) 180060573Skris{ 180192559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 180260573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 180360573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 180460573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 180560573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 180676262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 180776262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 180860573Skris 180960573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 181060573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 181160573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 181292559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 181376262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 181492559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 181560573Skris} 181660573Skris 181792559Sdesstatic void 181860573Skrischannel_handler_init(void) 181960573Skris{ 182060573Skris int i; 1821106130Sdes 182292559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 182360573Skris channel_pre[i] = NULL; 182460573Skris channel_post[i] = NULL; 182560573Skris } 182660573Skris if (compat20) 182760573Skris channel_handler_init_20(); 182860573Skris else if (compat13) 182960573Skris channel_handler_init_13(); 183060573Skris else 183160573Skris channel_handler_init_15(); 183260573Skris} 183360573Skris 183492559Sdes/* gc dead channels */ 183592559Sdesstatic void 183692559Sdeschannel_garbage_collect(Channel *c) 183792559Sdes{ 183892559Sdes if (c == NULL) 183992559Sdes return; 184092559Sdes if (c->detach_user != NULL) { 1841157019Sdes if (!chan_is_dead(c, c->detach_close)) 184292559Sdes return; 1843124207Sdes debug2("channel %d: gc: notify user", c->self); 184492559Sdes c->detach_user(c->self, NULL); 184592559Sdes /* if we still have a callback */ 184692559Sdes if (c->detach_user != NULL) 184792559Sdes return; 1848124207Sdes debug2("channel %d: gc: user detached", c->self); 184992559Sdes } 185092559Sdes if (!chan_is_dead(c, 1)) 185192559Sdes return; 1852124207Sdes debug2("channel %d: garbage collecting", c->self); 185392559Sdes channel_free(c); 185492559Sdes} 185592559Sdes 185692559Sdesstatic void 1857162856Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) 185860573Skris{ 185960573Skris static int did_init = 0; 1860137019Sdes u_int i; 186160573Skris Channel *c; 186260573Skris 186360573Skris if (!did_init) { 186460573Skris channel_handler_init(); 186560573Skris did_init = 1; 186660573Skris } 186760573Skris for (i = 0; i < channels_alloc; i++) { 186892559Sdes c = channels[i]; 186992559Sdes if (c == NULL) 187057429Smarkm continue; 187192559Sdes if (ftab[c->type] != NULL) 187292559Sdes (*ftab[c->type])(c, readset, writeset); 187392559Sdes channel_garbage_collect(c); 187457429Smarkm } 187557429Smarkm} 187657429Smarkm 187792559Sdes/* 187892559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 187992559Sdes * select bitmasks. 188092559Sdes */ 188160573Skrisvoid 188276262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1883137019Sdes u_int *nallocp, int rekeying) 188460573Skris{ 1885162856Sdes u_int n, sz, nfdset; 188676262Sgreen 188776262Sgreen n = MAX(*maxfdp, channel_max_fd); 188876262Sgreen 1889162856Sdes nfdset = howmany(n+1, NFDBITS); 1890162856Sdes /* Explicitly test here, because xrealloc isn't always called */ 1891162856Sdes if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) 1892162856Sdes fatal("channel_prepare_select: max_fd (%d) is too large", n); 1893162856Sdes sz = nfdset * sizeof(fd_mask); 1894162856Sdes 189592559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 189692559Sdes if (*readsetp == NULL || sz > *nallocp) { 1897162856Sdes *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); 1898162856Sdes *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); 189992559Sdes *nallocp = sz; 190076262Sgreen } 190192559Sdes *maxfdp = n; 190276262Sgreen memset(*readsetp, 0, sz); 190376262Sgreen memset(*writesetp, 0, sz); 190476262Sgreen 190576262Sgreen if (!rekeying) 190676262Sgreen channel_handler(channel_pre, *readsetp, *writesetp); 190760573Skris} 190860573Skris 190992559Sdes/* 191092559Sdes * After select, perform any appropriate operations for channels which have 191192559Sdes * events pending. 191292559Sdes */ 191360573Skrisvoid 1914162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset) 191560573Skris{ 191660573Skris channel_handler(channel_post, readset, writeset); 191760573Skris} 191860573Skris 191992559Sdes 192076262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 192160573Skrisvoid 192292559Sdeschannel_output_poll(void) 192357429Smarkm{ 192460573Skris Channel *c; 1925137019Sdes u_int i, len; 192657429Smarkm 192757429Smarkm for (i = 0; i < channels_alloc; i++) { 192892559Sdes c = channels[i]; 192992559Sdes if (c == NULL) 193092559Sdes continue; 193157429Smarkm 193292559Sdes /* 193392559Sdes * We are only interested in channels that can have buffered 193492559Sdes * incoming data. 193592559Sdes */ 193657429Smarkm if (compat13) { 193760573Skris if (c->type != SSH_CHANNEL_OPEN && 193860573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 193957429Smarkm continue; 194057429Smarkm } else { 194160573Skris if (c->type != SSH_CHANNEL_OPEN) 194257429Smarkm continue; 194357429Smarkm } 194460573Skris if (compat20 && 194560573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 194676262Sgreen /* XXX is this true? */ 194792559Sdes debug3("channel %d: will not send data after close", c->self); 194860573Skris continue; 194960573Skris } 195057429Smarkm 195157429Smarkm /* Get the amount of buffered data for this channel. */ 195276262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 195376262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 195476262Sgreen (len = buffer_len(&c->input)) > 0) { 1955157019Sdes if (c->datagram) { 1956157019Sdes if (len > 0) { 1957157019Sdes u_char *data; 1958157019Sdes u_int dlen; 1959157019Sdes 1960157019Sdes data = buffer_get_string(&c->input, 1961157019Sdes &dlen); 1962157019Sdes packet_start(SSH2_MSG_CHANNEL_DATA); 1963157019Sdes packet_put_int(c->remote_id); 1964157019Sdes packet_put_string(data, dlen); 1965157019Sdes packet_send(); 1966157019Sdes c->remote_window -= dlen + 4; 1967157019Sdes xfree(data); 1968157019Sdes } 1969157019Sdes continue; 1970157019Sdes } 197192559Sdes /* 197292559Sdes * Send some data for the other side over the secure 197392559Sdes * connection. 197492559Sdes */ 197560573Skris if (compat20) { 197660573Skris if (len > c->remote_window) 197760573Skris len = c->remote_window; 197860573Skris if (len > c->remote_maxpacket) 197960573Skris len = c->remote_maxpacket; 198057429Smarkm } else { 198160573Skris if (packet_is_interactive()) { 198260573Skris if (len > 1024) 198360573Skris len = 512; 198460573Skris } else { 198560573Skris /* Keep the packets at reasonable size. */ 198660573Skris if (len > packet_get_maxsize()/2) 198760573Skris len = packet_get_maxsize()/2; 198860573Skris } 198957429Smarkm } 199060573Skris if (len > 0) { 199160573Skris packet_start(compat20 ? 199260573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 199360573Skris packet_put_int(c->remote_id); 199460573Skris packet_put_string(buffer_ptr(&c->input), len); 199560573Skris packet_send(); 199660573Skris buffer_consume(&c->input, len); 199760573Skris c->remote_window -= len; 199860573Skris } 199960573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 200057429Smarkm if (compat13) 200157429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 200257429Smarkm /* 200357429Smarkm * input-buffer is empty and read-socket shutdown: 200498684Sdes * tell peer, that we will not send more data: send IEOF. 200598684Sdes * hack for extended data: delay EOF if EFD still in use. 200657429Smarkm */ 200798684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 2008149753Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 2009149753Sdes c->self, c->efd, buffer_len(&c->extended)); 201098684Sdes else 201198684Sdes chan_ibuf_empty(c); 201257429Smarkm } 201360573Skris /* Send extended data, i.e. stderr */ 201460573Skris if (compat20 && 201598684Sdes !(c->flags & CHAN_EOF_SENT) && 201660573Skris c->remote_window > 0 && 201760573Skris (len = buffer_len(&c->extended)) > 0 && 201860573Skris c->extended_usage == CHAN_EXTENDED_READ) { 201999063Sdes debug2("channel %d: rwin %u elen %u euse %d", 202076262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 202176262Sgreen c->extended_usage); 202260573Skris if (len > c->remote_window) 202360573Skris len = c->remote_window; 202460573Skris if (len > c->remote_maxpacket) 202560573Skris len = c->remote_maxpacket; 202660573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 202760573Skris packet_put_int(c->remote_id); 202860573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 202960573Skris packet_put_string(buffer_ptr(&c->extended), len); 203060573Skris packet_send(); 203160573Skris buffer_consume(&c->extended, len); 203260573Skris c->remote_window -= len; 203376262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 203460573Skris } 203557429Smarkm } 203657429Smarkm} 203757429Smarkm 203857429Smarkm 203992559Sdes/* -- protocol input */ 204092559Sdes 2041162856Sdes/* ARGSUSED */ 204260573Skrisvoid 204392559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 204457429Smarkm{ 204557429Smarkm int id; 204657429Smarkm char *data; 204776262Sgreen u_int data_len; 204860573Skris Channel *c; 204957429Smarkm 205057429Smarkm /* Get the channel number and verify it. */ 205157429Smarkm id = packet_get_int(); 205260573Skris c = channel_lookup(id); 205360573Skris if (c == NULL) 205457429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 205557429Smarkm 205657429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 205760573Skris if (c->type != SSH_CHANNEL_OPEN && 205860573Skris c->type != SSH_CHANNEL_X11_OPEN) 205957429Smarkm return; 206057429Smarkm 206157429Smarkm /* Get the data. */ 2062181111Sdes data = packet_get_string_ptr(&data_len); 206360573Skris 2064126273Sdes /* 2065126273Sdes * Ignore data for protocol > 1.3 if output end is no longer open. 2066126273Sdes * For protocol 2 the sending side is reducing its window as it sends 2067126273Sdes * data, so we must 'fake' consumption of the data in order to ensure 2068126273Sdes * that window updates are sent back. Otherwise the connection might 2069126273Sdes * deadlock. 2070126273Sdes */ 2071126273Sdes if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2072126273Sdes if (compat20) { 2073126273Sdes c->local_window -= data_len; 2074126273Sdes c->local_consumed += data_len; 2075126273Sdes } 2076126273Sdes return; 2077126273Sdes } 2078126273Sdes 207992559Sdes if (compat20) { 208060573Skris if (data_len > c->local_maxpacket) { 2081124207Sdes logit("channel %d: rcvd big packet %d, maxpack %d", 208260573Skris c->self, data_len, c->local_maxpacket); 208360573Skris } 208460573Skris if (data_len > c->local_window) { 2085124207Sdes logit("channel %d: rcvd too much data %d, win %d", 208660573Skris c->self, data_len, c->local_window); 208760573Skris return; 208860573Skris } 208960573Skris c->local_window -= data_len; 209060573Skris } 2091157019Sdes if (c->datagram) 2092157019Sdes buffer_put_string(&c->output, data, data_len); 2093157019Sdes else 2094157019Sdes buffer_append(&c->output, data, data_len); 2095181111Sdes packet_check_eom(); 209657429Smarkm} 209792559Sdes 2098162856Sdes/* ARGSUSED */ 209960573Skrisvoid 210092559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 210160573Skris{ 210260573Skris int id; 210360573Skris char *data; 210499063Sdes u_int data_len, tcode; 210560573Skris Channel *c; 210657429Smarkm 210760573Skris /* Get the channel number and verify it. */ 210860573Skris id = packet_get_int(); 210960573Skris c = channel_lookup(id); 211060573Skris 211160573Skris if (c == NULL) 211260573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 211360573Skris if (c->type != SSH_CHANNEL_OPEN) { 2114124207Sdes logit("channel %d: ext data for non open", id); 211560573Skris return; 211660573Skris } 211798684Sdes if (c->flags & CHAN_EOF_RCVD) { 211898684Sdes if (datafellows & SSH_BUG_EXTEOF) 211998684Sdes debug("channel %d: accepting ext data after eof", id); 212098684Sdes else 212198684Sdes packet_disconnect("Received extended_data after EOF " 212298684Sdes "on channel %d.", id); 212398684Sdes } 212460573Skris tcode = packet_get_int(); 212560573Skris if (c->efd == -1 || 212660573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 212760573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 2128124207Sdes logit("channel %d: bad ext data", c->self); 212960573Skris return; 213060573Skris } 213160573Skris data = packet_get_string(&data_len); 213292559Sdes packet_check_eom(); 213360573Skris if (data_len > c->local_window) { 2134124207Sdes logit("channel %d: rcvd too much extended_data %d, win %d", 213560573Skris c->self, data_len, c->local_window); 213660573Skris xfree(data); 213760573Skris return; 213860573Skris } 213969587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 214060573Skris c->local_window -= data_len; 214160573Skris buffer_append(&c->extended, data, data_len); 214260573Skris xfree(data); 214360573Skris} 214460573Skris 2145162856Sdes/* ARGSUSED */ 214660573Skrisvoid 214792559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 214860573Skris{ 214960573Skris int id; 215060573Skris Channel *c; 215157429Smarkm 215260573Skris id = packet_get_int(); 215392559Sdes packet_check_eom(); 215460573Skris c = channel_lookup(id); 215560573Skris if (c == NULL) 215660573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 215760573Skris chan_rcvd_ieof(c); 215892559Sdes 215992559Sdes /* XXX force input close */ 216092559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 216192559Sdes debug("channel %d: FORCE input drain", c->self); 216292559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 216392559Sdes if (buffer_len(&c->input) == 0) 216492559Sdes chan_ibuf_empty(c); 216592559Sdes } 216692559Sdes 216760573Skris} 216860573Skris 2169162856Sdes/* ARGSUSED */ 217060573Skrisvoid 217192559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 217257429Smarkm{ 217360573Skris int id; 217460573Skris Channel *c; 217557429Smarkm 217660573Skris id = packet_get_int(); 217792559Sdes packet_check_eom(); 217860573Skris c = channel_lookup(id); 217960573Skris if (c == NULL) 218060573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 218157429Smarkm 218257429Smarkm /* 218357429Smarkm * Send a confirmation that we have closed the channel and no more 218457429Smarkm * data is coming for it. 218557429Smarkm */ 218657429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 218760573Skris packet_put_int(c->remote_id); 218857429Smarkm packet_send(); 218957429Smarkm 219057429Smarkm /* 219157429Smarkm * If the channel is in closed state, we have sent a close request, 219257429Smarkm * and the other side will eventually respond with a confirmation. 219357429Smarkm * Thus, we cannot free the channel here, because then there would be 219457429Smarkm * no-one to receive the confirmation. The channel gets freed when 219557429Smarkm * the confirmation arrives. 219657429Smarkm */ 219760573Skris if (c->type != SSH_CHANNEL_CLOSED) { 219857429Smarkm /* 219957429Smarkm * Not a closed channel - mark it as draining, which will 220057429Smarkm * cause it to be freed later. 220157429Smarkm */ 220292559Sdes buffer_clear(&c->input); 220360573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 220457429Smarkm } 220557429Smarkm} 220657429Smarkm 220760573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2208162856Sdes/* ARGSUSED */ 220960573Skrisvoid 221092559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 221160573Skris{ 221260573Skris int id = packet_get_int(); 221360573Skris Channel *c = channel_lookup(id); 221492559Sdes 221592559Sdes packet_check_eom(); 221660573Skris if (c == NULL) 221760573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 221860573Skris chan_rcvd_oclose(c); 221960573Skris} 222057429Smarkm 2221162856Sdes/* ARGSUSED */ 222260573Skrisvoid 222392559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 222457429Smarkm{ 222560573Skris int id = packet_get_int(); 222660573Skris Channel *c = channel_lookup(id); 222757429Smarkm 222892559Sdes packet_check_eom(); 222960573Skris if (c == NULL) 223060573Skris packet_disconnect("Received close confirmation for " 223160573Skris "out-of-range channel %d.", id); 223260573Skris if (c->type != SSH_CHANNEL_CLOSED) 223360573Skris packet_disconnect("Received close confirmation for " 223460573Skris "non-closed channel %d (type %d).", id, c->type); 223592559Sdes channel_free(c); 223660573Skris} 223757429Smarkm 2238162856Sdes/* ARGSUSED */ 223960573Skrisvoid 224092559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 224160573Skris{ 224260573Skris int id, remote_id; 224360573Skris Channel *c; 224460573Skris 224560573Skris id = packet_get_int(); 224660573Skris c = channel_lookup(id); 224760573Skris 224860573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 224960573Skris packet_disconnect("Received open confirmation for " 225060573Skris "non-opening channel %d.", id); 225160573Skris remote_id = packet_get_int(); 225260573Skris /* Record the remote channel number and mark that the channel is now open. */ 225360573Skris c->remote_id = remote_id; 225460573Skris c->type = SSH_CHANNEL_OPEN; 225560573Skris 225660573Skris if (compat20) { 225760573Skris c->remote_window = packet_get_int(); 225860573Skris c->remote_maxpacket = packet_get_int(); 2259181111Sdes if (c->open_confirm) { 226069587Sgreen debug2("callback start"); 2261181111Sdes c->open_confirm(c->self, c->open_confirm_ctx); 226269587Sgreen debug2("callback done"); 226360573Skris } 2264124207Sdes debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 226560573Skris c->remote_window, c->remote_maxpacket); 226657429Smarkm } 226792559Sdes packet_check_eom(); 226857429Smarkm} 226957429Smarkm 227092559Sdesstatic char * 227192559Sdesreason2txt(int reason) 227292559Sdes{ 227392559Sdes switch (reason) { 227492559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 227592559Sdes return "administratively prohibited"; 227692559Sdes case SSH2_OPEN_CONNECT_FAILED: 227792559Sdes return "connect failed"; 227892559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 227992559Sdes return "unknown channel type"; 228092559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 228192559Sdes return "resource shortage"; 228292559Sdes } 228392559Sdes return "unknown reason"; 228492559Sdes} 228592559Sdes 2286162856Sdes/* ARGSUSED */ 228760573Skrisvoid 228892559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 228957429Smarkm{ 229076262Sgreen int id, reason; 229176262Sgreen char *msg = NULL, *lang = NULL; 229260573Skris Channel *c; 229357429Smarkm 229460573Skris id = packet_get_int(); 229560573Skris c = channel_lookup(id); 229657429Smarkm 229760573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 229860573Skris packet_disconnect("Received open failure for " 229960573Skris "non-opening channel %d.", id); 230060573Skris if (compat20) { 230176262Sgreen reason = packet_get_int(); 230292559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 230376262Sgreen msg = packet_get_string(NULL); 230476262Sgreen lang = packet_get_string(NULL); 230576262Sgreen } 2306124207Sdes logit("channel %d: open failed: %s%s%s", id, 230792559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 230876262Sgreen if (msg != NULL) 230976262Sgreen xfree(msg); 231076262Sgreen if (lang != NULL) 231176262Sgreen xfree(lang); 231260573Skris } 231392559Sdes packet_check_eom(); 231460573Skris /* Free the channel. This will also close the socket. */ 231592559Sdes channel_free(c); 231657429Smarkm} 231757429Smarkm 2318162856Sdes/* ARGSUSED */ 231960573Skrisvoid 232092559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 232160573Skris{ 232260573Skris Channel *c; 232399063Sdes int id; 232499063Sdes u_int adjust; 232557429Smarkm 232660573Skris if (!compat20) 232760573Skris return; 232860573Skris 232957429Smarkm /* Get the channel number and verify it. */ 233060573Skris id = packet_get_int(); 233160573Skris c = channel_lookup(id); 233257429Smarkm 2333157019Sdes if (c == NULL) { 2334157019Sdes logit("Received window adjust for non-open channel %d.", id); 233560573Skris return; 233660573Skris } 233760573Skris adjust = packet_get_int(); 233892559Sdes packet_check_eom(); 233999063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 234060573Skris c->remote_window += adjust; 234157429Smarkm} 234257429Smarkm 2343162856Sdes/* ARGSUSED */ 234460573Skrisvoid 234592559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 234657429Smarkm{ 234792559Sdes Channel *c = NULL; 234892559Sdes u_short host_port; 234992559Sdes char *host, *originator_string; 2350181111Sdes int remote_id; 235157429Smarkm 235292559Sdes remote_id = packet_get_int(); 235392559Sdes host = packet_get_string(NULL); 235492559Sdes host_port = packet_get_int(); 235557429Smarkm 235692559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 235792559Sdes originator_string = packet_get_string(NULL); 235892559Sdes } else { 235992559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 236092559Sdes } 236192559Sdes packet_check_eom(); 2362181111Sdes c = channel_connect_to(host, host_port, 2363181111Sdes "connected socket", originator_string); 2364124207Sdes xfree(originator_string); 2365181111Sdes xfree(host); 236692559Sdes if (c == NULL) { 236792559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 236892559Sdes packet_put_int(remote_id); 236992559Sdes packet_send(); 2370181111Sdes } else 2371181111Sdes c->remote_id = remote_id; 237257429Smarkm} 237357429Smarkm 2374181111Sdes/* ARGSUSED */ 2375181111Sdesvoid 2376181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2377181111Sdes{ 2378181111Sdes Channel *c; 2379181111Sdes struct channel_confirm *cc; 2380181111Sdes int remote_id; 238157429Smarkm 2382181111Sdes /* Reset keepalive timeout */ 2383181111Sdes keep_alive_timeouts = 0; 2384181111Sdes 2385181111Sdes remote_id = packet_get_int(); 2386181111Sdes packet_check_eom(); 2387181111Sdes 2388181111Sdes debug2("channel_input_confirm: type %d id %d", type, remote_id); 2389181111Sdes 2390181111Sdes if ((c = channel_lookup(remote_id)) == NULL) { 2391181111Sdes logit("channel_input_success_failure: %d: unknown", remote_id); 2392181111Sdes return; 2393181111Sdes } 2394181111Sdes ; 2395181111Sdes if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2396181111Sdes return; 2397181111Sdes cc->cb(type, c, cc->ctx); 2398181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 2399181111Sdes bzero(cc, sizeof(*cc)); 2400181111Sdes xfree(cc); 2401181111Sdes} 2402181111Sdes 240392559Sdes/* -- tcp forwarding */ 240457429Smarkm 240592559Sdesvoid 240692559Sdeschannel_set_af(int af) 240776262Sgreen{ 240892559Sdes IPv4or6 = af; 240976262Sgreen} 241076262Sgreen 241192559Sdesstatic int 241292559Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 241392559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 241457429Smarkm{ 241592559Sdes Channel *c; 2416157019Sdes int sock, r, success = 0, wildcard = 0, is_client; 241757429Smarkm struct addrinfo hints, *ai, *aitop; 2418147005Sdes const char *host, *addr; 241957429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 242057429Smarkm 242192559Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 242292559Sdes listen_addr : host_to_connect; 2423147005Sdes is_client = (type == SSH_CHANNEL_PORT_LISTENER); 242457429Smarkm 242592559Sdes if (host == NULL) { 242692559Sdes error("No forward host name."); 2427149753Sdes return 0; 242876262Sgreen } 242992559Sdes if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 243076262Sgreen error("Forward host name too long."); 2431149753Sdes return 0; 243276262Sgreen } 243376262Sgreen 243457429Smarkm /* 2435147005Sdes * Determine whether or not a port forward listens to loopback, 2436147005Sdes * specified address or wildcard. On the client, a specified bind 2437147005Sdes * address will always override gateway_ports. On the server, a 2438147005Sdes * gateway_ports of 1 (``yes'') will override the client's 2439147005Sdes * specification and force a wildcard bind, whereas a value of 2 2440147005Sdes * (``clientspecified'') will bind to whatever address the client 2441147005Sdes * asked for. 2442147005Sdes * 2443147005Sdes * Special-case listen_addrs are: 2444147005Sdes * 2445147005Sdes * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2446147005Sdes * "" (empty string), "*" -> wildcard v4/v6 2447147005Sdes * "localhost" -> loopback v4/v6 2448147005Sdes */ 2449147005Sdes addr = NULL; 2450147005Sdes if (listen_addr == NULL) { 2451147005Sdes /* No address specified: default to gateway_ports setting */ 2452147005Sdes if (gateway_ports) 2453147005Sdes wildcard = 1; 2454147005Sdes } else if (gateway_ports || is_client) { 2455147005Sdes if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2456181111Sdes strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2457147005Sdes *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2458147005Sdes (!is_client && gateway_ports == 1)) 2459147005Sdes wildcard = 1; 2460147005Sdes else if (strcmp(listen_addr, "localhost") != 0) 2461147005Sdes addr = listen_addr; 2462147005Sdes } 2463147005Sdes 2464147005Sdes debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", 2465147005Sdes type, wildcard, (addr == NULL) ? "NULL" : addr); 2466147005Sdes 2467147005Sdes /* 246857429Smarkm * getaddrinfo returns a loopback address if the hostname is 246957429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 247057429Smarkm */ 247157429Smarkm memset(&hints, 0, sizeof(hints)); 247257429Smarkm hints.ai_family = IPv4or6; 2473147005Sdes hints.ai_flags = wildcard ? AI_PASSIVE : 0; 247457429Smarkm hints.ai_socktype = SOCK_STREAM; 247576262Sgreen snprintf(strport, sizeof strport, "%d", listen_port); 2476147005Sdes if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2477147005Sdes if (addr == NULL) { 2478147005Sdes /* This really shouldn't happen */ 2479147005Sdes packet_disconnect("getaddrinfo: fatal error: %s", 2480181111Sdes ssh_gai_strerror(r)); 2481147005Sdes } else { 2482149753Sdes error("channel_setup_fwd_listener: " 2483181111Sdes "getaddrinfo(%.64s): %s", addr, 2484181111Sdes ssh_gai_strerror(r)); 2485147005Sdes } 2486149753Sdes return 0; 2487147005Sdes } 248857429Smarkm 248957429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 249057429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 249157429Smarkm continue; 249257429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 249357429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 249492559Sdes error("channel_setup_fwd_listener: getnameinfo failed"); 249557429Smarkm continue; 249657429Smarkm } 249757429Smarkm /* Create a port to listen for the host. */ 2498124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 249957429Smarkm if (sock < 0) { 250057429Smarkm /* this is no error since kernel may not support ipv6 */ 250157429Smarkm verbose("socket: %.100s", strerror(errno)); 250257429Smarkm continue; 250357429Smarkm } 2504106130Sdes 2505157019Sdes channel_set_reuseaddr(sock); 2506157019Sdes 250757429Smarkm debug("Local forwarding listening on %s port %s.", ntop, strport); 250857429Smarkm 250957429Smarkm /* Bind the socket to the address. */ 251057429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 251157429Smarkm /* address can be in use ipv6 address is already bound */ 251298941Sdes if (!ai->ai_next) 251398941Sdes error("bind: %.100s", strerror(errno)); 251498941Sdes else 251598941Sdes verbose("bind: %.100s", strerror(errno)); 251698941Sdes 251757429Smarkm close(sock); 251857429Smarkm continue; 251957429Smarkm } 252057429Smarkm /* Start listening for connections on the socket. */ 2521126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 252257429Smarkm error("listen: %.100s", strerror(errno)); 252357429Smarkm close(sock); 252457429Smarkm continue; 252557429Smarkm } 252657429Smarkm /* Allocate a channel number for the socket. */ 252792559Sdes c = channel_new("port listener", type, sock, sock, -1, 252860573Skris CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2529124207Sdes 0, "port listener", 1); 253092559Sdes strlcpy(c->path, host, sizeof(c->path)); 253192559Sdes c->host_port = port_to_connect; 253292559Sdes c->listening_port = listen_port; 253357429Smarkm success = 1; 253457429Smarkm } 253557429Smarkm if (success == 0) 253692559Sdes error("channel_setup_fwd_listener: cannot listen to port: %d", 253776262Sgreen listen_port); 253857429Smarkm freeaddrinfo(aitop); 253976262Sgreen return success; 254057429Smarkm} 254157429Smarkm 2542137019Sdesint 2543137019Sdeschannel_cancel_rport_listener(const char *host, u_short port) 2544137019Sdes{ 2545137019Sdes u_int i; 2546137019Sdes int found = 0; 2547137019Sdes 2548147005Sdes for (i = 0; i < channels_alloc; i++) { 2549137019Sdes Channel *c = channels[i]; 2550137019Sdes 2551137019Sdes if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && 2552137019Sdes strncmp(c->path, host, sizeof(c->path)) == 0 && 2553137019Sdes c->listening_port == port) { 2554147005Sdes debug2("%s: close channel %d", __func__, i); 2555137019Sdes channel_free(c); 2556137019Sdes found = 1; 2557137019Sdes } 2558137019Sdes } 2559137019Sdes 2560137019Sdes return (found); 2561137019Sdes} 2562137019Sdes 256392559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 256492559Sdesint 2565147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, 256692559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 256792559Sdes{ 256892559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2569147005Sdes listen_host, listen_port, host_to_connect, port_to_connect, 2570147005Sdes gateway_ports); 257192559Sdes} 257292559Sdes 257392559Sdes/* protocol v2 remote port fwd, used by sshd */ 257492559Sdesint 257592559Sdeschannel_setup_remote_fwd_listener(const char *listen_address, 257692559Sdes u_short listen_port, int gateway_ports) 257792559Sdes{ 257892559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 257992559Sdes listen_address, listen_port, NULL, 0, gateway_ports); 258092559Sdes} 258192559Sdes 258257429Smarkm/* 258357429Smarkm * Initiate forwarding of connections to port "port" on remote host through 258457429Smarkm * the secure channel to host:port from local side. 258557429Smarkm */ 258657429Smarkm 2587162856Sdesint 2588147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port, 258976262Sgreen const char *host_to_connect, u_short port_to_connect) 259057429Smarkm{ 259192559Sdes int type, success = 0; 259276262Sgreen 259357429Smarkm /* Record locally that connection to this host/port is permitted. */ 259457429Smarkm if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 259557429Smarkm fatal("channel_request_remote_forwarding: too many forwards"); 259657429Smarkm 259757429Smarkm /* Send the forward request to the remote side. */ 259860573Skris if (compat20) { 2599147005Sdes const char *address_to_bind; 2600181111Sdes if (listen_host == NULL) { 2601181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2602181111Sdes address_to_bind = "127.0.0.1"; 2603181111Sdes else 2604181111Sdes address_to_bind = "localhost"; 2605181111Sdes } else if (*listen_host == '\0' || 2606181111Sdes strcmp(listen_host, "*") == 0) { 2607181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2608181111Sdes address_to_bind = "0.0.0.0"; 2609181111Sdes else 2610181111Sdes address_to_bind = ""; 2611181111Sdes } else 2612147005Sdes address_to_bind = listen_host; 2613147005Sdes 261460573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 261560573Skris packet_put_cstring("tcpip-forward"); 261698684Sdes packet_put_char(1); /* boolean: want reply */ 261760573Skris packet_put_cstring(address_to_bind); 261860573Skris packet_put_int(listen_port); 261976262Sgreen packet_send(); 262076262Sgreen packet_write_wait(); 262176262Sgreen /* Assume that server accepts the request */ 262276262Sgreen success = 1; 262360573Skris } else { 262460573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 262560573Skris packet_put_int(listen_port); 262660573Skris packet_put_cstring(host_to_connect); 262760573Skris packet_put_int(port_to_connect); 262860573Skris packet_send(); 262960573Skris packet_write_wait(); 263076262Sgreen 263176262Sgreen /* Wait for response from the remote side. */ 263292559Sdes type = packet_read(); 263376262Sgreen switch (type) { 263476262Sgreen case SSH_SMSG_SUCCESS: 263576262Sgreen success = 1; 263676262Sgreen break; 263776262Sgreen case SSH_SMSG_FAILURE: 263876262Sgreen break; 263976262Sgreen default: 264076262Sgreen /* Unknown packet */ 264176262Sgreen packet_disconnect("Protocol error for port forward request:" 264276262Sgreen "received packet type %d.", type); 264376262Sgreen } 264460573Skris } 264576262Sgreen if (success) { 264676262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 264776262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 264876262Sgreen permitted_opens[num_permitted_opens].listen_port = listen_port; 264976262Sgreen num_permitted_opens++; 265076262Sgreen } 2651162856Sdes return (success ? 0 : -1); 265257429Smarkm} 265357429Smarkm 265457429Smarkm/* 2655137019Sdes * Request cancellation of remote forwarding of connection host:port from 2656137019Sdes * local side. 2657137019Sdes */ 2658137019Sdesvoid 2659147005Sdeschannel_request_rforward_cancel(const char *host, u_short port) 2660137019Sdes{ 2661137019Sdes int i; 2662137019Sdes 2663137019Sdes if (!compat20) 2664137019Sdes return; 2665137019Sdes 2666137019Sdes for (i = 0; i < num_permitted_opens; i++) { 2667137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2668137019Sdes permitted_opens[i].listen_port == port) 2669137019Sdes break; 2670137019Sdes } 2671137019Sdes if (i >= num_permitted_opens) { 2672137019Sdes debug("%s: requested forward not found", __func__); 2673137019Sdes return; 2674137019Sdes } 2675137019Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 2676137019Sdes packet_put_cstring("cancel-tcpip-forward"); 2677137019Sdes packet_put_char(0); 2678147005Sdes packet_put_cstring(host == NULL ? "" : host); 2679137019Sdes packet_put_int(port); 2680137019Sdes packet_send(); 2681137019Sdes 2682137019Sdes permitted_opens[i].listen_port = 0; 2683137019Sdes permitted_opens[i].port_to_connect = 0; 2684157019Sdes xfree(permitted_opens[i].host_to_connect); 2685137019Sdes permitted_opens[i].host_to_connect = NULL; 2686137019Sdes} 2687137019Sdes 2688137019Sdes/* 268957429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 269057429Smarkm * listening for the port, and sends back a success reply (or disconnect 2691162856Sdes * message if there was an error). 269257429Smarkm */ 2693162856Sdesint 269460573Skrischannel_input_port_forward_request(int is_root, int gateway_ports) 269557429Smarkm{ 269657429Smarkm u_short port, host_port; 2697162856Sdes int success = 0; 269857429Smarkm char *hostname; 269957429Smarkm 270057429Smarkm /* Get arguments from the packet. */ 270157429Smarkm port = packet_get_int(); 270257429Smarkm hostname = packet_get_string(NULL); 270357429Smarkm host_port = packet_get_int(); 270457429Smarkm 270598941Sdes#ifndef HAVE_CYGWIN 270657429Smarkm /* 270757429Smarkm * Check that an unprivileged user is not trying to forward a 270857429Smarkm * privileged port. 270957429Smarkm */ 271057429Smarkm if (port < IPPORT_RESERVED && !is_root) 2711124207Sdes packet_disconnect( 2712124207Sdes "Requested forwarding of port %d but user is not root.", 2713124207Sdes port); 2714124207Sdes if (host_port == 0) 2715124207Sdes packet_disconnect("Dynamic forwarding denied."); 271698941Sdes#endif 2717124207Sdes 271876262Sgreen /* Initiate forwarding */ 2719162856Sdes success = channel_setup_local_fwd_listener(NULL, port, hostname, 2720147005Sdes host_port, gateway_ports); 272157429Smarkm 272257429Smarkm /* Free the argument string. */ 272357429Smarkm xfree(hostname); 2724162856Sdes 2725162856Sdes return (success ? 0 : -1); 272657429Smarkm} 272757429Smarkm 272876262Sgreen/* 272976262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 273076262Sgreen * usually called by the server, because the user could connect to any port 273176262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 273276262Sgreen */ 273376262Sgreenvoid 273492559Sdeschannel_permit_all_opens(void) 273576262Sgreen{ 273676262Sgreen if (num_permitted_opens == 0) 273776262Sgreen all_opens_permitted = 1; 273876262Sgreen} 273976262Sgreen 274076262Sgreenvoid 274176262Sgreenchannel_add_permitted_opens(char *host, int port) 274276262Sgreen{ 274376262Sgreen if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2744162856Sdes fatal("channel_add_permitted_opens: too many forwards"); 274576262Sgreen debug("allow port forwarding to host %s port %d", host, port); 274676262Sgreen 274776262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 274876262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 274976262Sgreen num_permitted_opens++; 275076262Sgreen 275176262Sgreen all_opens_permitted = 0; 275276262Sgreen} 275376262Sgreen 2754162856Sdesint 2755162856Sdeschannel_add_adm_permitted_opens(char *host, int port) 2756162856Sdes{ 2757162856Sdes if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2758162856Sdes fatal("channel_add_adm_permitted_opens: too many forwards"); 2759162856Sdes debug("config allows port forwarding to host %s port %d", host, port); 2760162856Sdes 2761162856Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect 2762162856Sdes = xstrdup(host); 2763162856Sdes permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 2764162856Sdes return ++num_adm_permitted_opens; 2765162856Sdes} 2766162856Sdes 276776262Sgreenvoid 276876262Sgreenchannel_clear_permitted_opens(void) 276976262Sgreen{ 277076262Sgreen int i; 277176262Sgreen 277276262Sgreen for (i = 0; i < num_permitted_opens; i++) 2773137019Sdes if (permitted_opens[i].host_to_connect != NULL) 2774137019Sdes xfree(permitted_opens[i].host_to_connect); 277576262Sgreen num_permitted_opens = 0; 2776162856Sdes} 277776262Sgreen 2778162856Sdesvoid 2779162856Sdeschannel_clear_adm_permitted_opens(void) 2780162856Sdes{ 2781162856Sdes int i; 2782162856Sdes 2783162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 2784162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 2785162856Sdes xfree(permitted_adm_opens[i].host_to_connect); 2786162856Sdes num_adm_permitted_opens = 0; 278776262Sgreen} 278876262Sgreen 2789181111Sdesvoid 2790181111Sdeschannel_print_adm_permitted_opens(void) 2791181111Sdes{ 2792181111Sdes int i; 2793181111Sdes 2794181111Sdes for (i = 0; i < num_adm_permitted_opens; i++) 2795181111Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 2796181111Sdes printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 2797181111Sdes permitted_adm_opens[i].port_to_connect); 2798181111Sdes} 2799181111Sdes 2800181111Sdes/* Try to start non-blocking connect to next host in cctx list */ 280192559Sdesstatic int 2802181111Sdesconnect_next(struct channel_connect *cctx) 280360573Skris{ 2804181111Sdes int sock, saved_errno; 280560573Skris char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 280660573Skris 2807181111Sdes for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 2808181111Sdes if (cctx->ai->ai_family != AF_INET && 2809181111Sdes cctx->ai->ai_family != AF_INET6) 281060573Skris continue; 2811181111Sdes if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 2812181111Sdes ntop, sizeof(ntop), strport, sizeof(strport), 2813181111Sdes NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2814181111Sdes error("connect_next: getnameinfo failed"); 281560573Skris continue; 281660573Skris } 2817181111Sdes if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 2818181111Sdes cctx->ai->ai_protocol)) == -1) { 2819181111Sdes if (cctx->ai->ai_next == NULL) 2820113911Sdes error("socket: %.100s", strerror(errno)); 2821113911Sdes else 2822113911Sdes verbose("socket: %.100s", strerror(errno)); 282360573Skris continue; 282460573Skris } 2825137019Sdes if (set_nonblock(sock) == -1) 2826137019Sdes fatal("%s: set_nonblock(%d)", __func__, sock); 2827181111Sdes if (connect(sock, cctx->ai->ai_addr, 2828181111Sdes cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 2829181111Sdes debug("connect_next: host %.100s ([%.100s]:%s): " 2830181111Sdes "%.100s", cctx->host, ntop, strport, 283160573Skris strerror(errno)); 2832181111Sdes saved_errno = errno; 283360573Skris close(sock); 2834181111Sdes errno = saved_errno; 283576262Sgreen continue; /* fail -- try next */ 283660573Skris } 2837181111Sdes debug("connect_next: host %.100s ([%.100s]:%s) " 2838181111Sdes "in progress, fd=%d", cctx->host, ntop, strport, sock); 2839181111Sdes cctx->ai = cctx->ai->ai_next; 2840181111Sdes set_nodelay(sock); 2841181111Sdes return sock; 2842181111Sdes } 2843181111Sdes return -1; 2844181111Sdes} 284560573Skris 2846181111Sdesstatic void 2847181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx) 2848181111Sdes{ 2849181111Sdes xfree(cctx->host); 2850181111Sdes if (cctx->aitop) 2851181111Sdes freeaddrinfo(cctx->aitop); 2852181111Sdes bzero(cctx, sizeof(*cctx)); 2853181111Sdes cctx->host = NULL; 2854181111Sdes cctx->ai = cctx->aitop = NULL; 2855181111Sdes} 2856181111Sdes 2857181111Sdes/* Return CONNECTING channel to remote host, port */ 2858181111Sdesstatic Channel * 2859181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname) 2860181111Sdes{ 2861181111Sdes struct addrinfo hints; 2862181111Sdes int gaierr; 2863181111Sdes int sock = -1; 2864181111Sdes char strport[NI_MAXSERV]; 2865181111Sdes struct channel_connect cctx; 2866181111Sdes Channel *c; 2867181111Sdes 2868181111Sdes memset(&cctx, 0, sizeof(cctx)); 2869181111Sdes memset(&hints, 0, sizeof(hints)); 2870181111Sdes hints.ai_family = IPv4or6; 2871181111Sdes hints.ai_socktype = SOCK_STREAM; 2872181111Sdes snprintf(strport, sizeof strport, "%d", port); 2873181111Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { 2874181111Sdes error("connect_to %.100s: unknown host (%s)", host, 2875181111Sdes ssh_gai_strerror(gaierr)); 2876181111Sdes return NULL; 287760573Skris } 2878181111Sdes 2879181111Sdes cctx.host = xstrdup(host); 2880181111Sdes cctx.port = port; 2881181111Sdes cctx.ai = cctx.aitop; 2882181111Sdes 2883181111Sdes if ((sock = connect_next(&cctx)) == -1) { 2884181111Sdes error("connect to %.100s port %d failed: %s", 2885181111Sdes host, port, strerror(errno)); 2886181111Sdes channel_connect_ctx_free(&cctx); 2887181111Sdes return NULL; 288860573Skris } 2889181111Sdes c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 2890181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 2891181111Sdes c->connect_ctx = cctx; 2892181111Sdes return c; 289360573Skris} 289476262Sgreen 2895181111SdesChannel * 2896181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) 289776262Sgreen{ 289876262Sgreen int i; 289976262Sgreen 2900181111Sdes for (i = 0; i < num_permitted_opens; i++) { 2901137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2902181111Sdes permitted_opens[i].listen_port == listen_port) { 290376262Sgreen return connect_to( 290476262Sgreen permitted_opens[i].host_to_connect, 2905181111Sdes permitted_opens[i].port_to_connect, ctype, rname); 2906181111Sdes } 2907181111Sdes } 290876262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 290976262Sgreen listen_port); 2910181111Sdes return NULL; 291176262Sgreen} 291276262Sgreen 291376262Sgreen/* Check if connecting to that port is permitted and connect. */ 2914181111SdesChannel * 2915181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname) 291676262Sgreen{ 2917162856Sdes int i, permit, permit_adm = 1; 291876262Sgreen 291976262Sgreen permit = all_opens_permitted; 292076262Sgreen if (!permit) { 292176262Sgreen for (i = 0; i < num_permitted_opens; i++) 2922137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2923137019Sdes permitted_opens[i].port_to_connect == port && 292476262Sgreen strcmp(permitted_opens[i].host_to_connect, host) == 0) 292576262Sgreen permit = 1; 2926162856Sdes } 292776262Sgreen 2928162856Sdes if (num_adm_permitted_opens > 0) { 2929162856Sdes permit_adm = 0; 2930162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 2931162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL && 2932162856Sdes permitted_adm_opens[i].port_to_connect == port && 2933162856Sdes strcmp(permitted_adm_opens[i].host_to_connect, host) 2934162856Sdes == 0) 2935162856Sdes permit_adm = 1; 293676262Sgreen } 2937162856Sdes 2938162856Sdes if (!permit || !permit_adm) { 2939124207Sdes logit("Received request to connect to host %.100s port %d, " 294076262Sgreen "but the request was denied.", host, port); 2941181111Sdes return NULL; 294276262Sgreen } 2943181111Sdes return connect_to(host, port, ctype, rname); 294476262Sgreen} 294576262Sgreen 2946137019Sdesvoid 2947137019Sdeschannel_send_window_changes(void) 2948137019Sdes{ 2949137019Sdes u_int i; 2950137019Sdes struct winsize ws; 2951137019Sdes 2952137019Sdes for (i = 0; i < channels_alloc; i++) { 2953147005Sdes if (channels[i] == NULL || !channels[i]->client_tty || 2954137019Sdes channels[i]->type != SSH_CHANNEL_OPEN) 2955137019Sdes continue; 2956137019Sdes if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 2957137019Sdes continue; 2958137019Sdes channel_request_start(i, "window-change", 0); 2959162856Sdes packet_put_int((u_int)ws.ws_col); 2960162856Sdes packet_put_int((u_int)ws.ws_row); 2961162856Sdes packet_put_int((u_int)ws.ws_xpixel); 2962162856Sdes packet_put_int((u_int)ws.ws_ypixel); 2963137019Sdes packet_send(); 2964137019Sdes } 2965137019Sdes} 2966137019Sdes 296792559Sdes/* -- X11 forwarding */ 296857429Smarkm 296957429Smarkm/* 297057429Smarkm * Creates an internet domain socket for listening for X11 connections. 297199063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 297299063Sdes * stored in display_numberp , or -1 if an error occurs. 297357429Smarkm */ 297492559Sdesint 297592559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2976149753Sdes int single_connection, u_int *display_numberp, int **chanids) 297757429Smarkm{ 297892559Sdes Channel *nc = NULL; 297957429Smarkm int display_number, sock; 298057429Smarkm u_short port; 298157429Smarkm struct addrinfo hints, *ai, *aitop; 298257429Smarkm char strport[NI_MAXSERV]; 298357429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 298457429Smarkm 2985157019Sdes if (chanids == NULL) 2986157019Sdes return -1; 2987157019Sdes 298857429Smarkm for (display_number = x11_display_offset; 298992559Sdes display_number < MAX_DISPLAYS; 299092559Sdes display_number++) { 299157429Smarkm port = 6000 + display_number; 299257429Smarkm memset(&hints, 0, sizeof(hints)); 299357429Smarkm hints.ai_family = IPv4or6; 299492559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 299557429Smarkm hints.ai_socktype = SOCK_STREAM; 299657429Smarkm snprintf(strport, sizeof strport, "%d", port); 299757429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2998181111Sdes error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 299992559Sdes return -1; 300057429Smarkm } 300157429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 300257429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 300357429Smarkm continue; 3004124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, 3005124207Sdes ai->ai_protocol); 300657429Smarkm if (sock < 0) { 300798941Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 300898941Sdes error("socket: %.100s", strerror(errno)); 3009137019Sdes freeaddrinfo(aitop); 301098941Sdes return -1; 301198941Sdes } else { 301298941Sdes debug("x11_create_display_inet: Socket family %d not supported", 301398941Sdes ai->ai_family); 301498941Sdes continue; 301598941Sdes } 301657429Smarkm } 301798941Sdes#ifdef IPV6_V6ONLY 301898941Sdes if (ai->ai_family == AF_INET6) { 301998941Sdes int on = 1; 302098941Sdes if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 302198941Sdes error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 302298941Sdes } 302398941Sdes#endif 3024181111Sdes if (x11_use_localhost) 3025181111Sdes channel_set_reuseaddr(sock); 302657429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3027124207Sdes debug2("bind port %d: %.100s", port, strerror(errno)); 302857429Smarkm close(sock); 302998941Sdes 303057429Smarkm for (n = 0; n < num_socks; n++) { 303157429Smarkm close(socks[n]); 303257429Smarkm } 303357429Smarkm num_socks = 0; 303457429Smarkm break; 303557429Smarkm } 303657429Smarkm socks[num_socks++] = sock; 303757429Smarkm if (num_socks == NUM_SOCKS) 303857429Smarkm break; 303957429Smarkm } 304076262Sgreen freeaddrinfo(aitop); 304157429Smarkm if (num_socks > 0) 304257429Smarkm break; 304357429Smarkm } 304457429Smarkm if (display_number >= MAX_DISPLAYS) { 304557429Smarkm error("Failed to allocate internet-domain X11 display socket."); 304692559Sdes return -1; 304757429Smarkm } 304857429Smarkm /* Start listening for connections on the socket. */ 304957429Smarkm for (n = 0; n < num_socks; n++) { 305057429Smarkm sock = socks[n]; 3051126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 305257429Smarkm error("listen: %.100s", strerror(errno)); 305357429Smarkm close(sock); 305492559Sdes return -1; 305557429Smarkm } 305657429Smarkm } 305757429Smarkm 305857429Smarkm /* Allocate a channel for each socket. */ 3059162856Sdes *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 306057429Smarkm for (n = 0; n < num_socks; n++) { 306157429Smarkm sock = socks[n]; 306292559Sdes nc = channel_new("x11 listener", 306360573Skris SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 306460573Skris CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3065124207Sdes 0, "X11 inet listener", 1); 306692559Sdes nc->single_connection = single_connection; 3067157019Sdes (*chanids)[n] = nc->self; 306857429Smarkm } 3069157019Sdes (*chanids)[n] = -1; 307057429Smarkm 307192559Sdes /* Return the display number for the DISPLAY environment variable. */ 307299063Sdes *display_numberp = display_number; 307399063Sdes return (0); 307457429Smarkm} 307557429Smarkm 307692559Sdesstatic int 307776262Sgreenconnect_local_xsocket(u_int dnr) 307857429Smarkm{ 307957429Smarkm int sock; 308057429Smarkm struct sockaddr_un addr; 308157429Smarkm 308292559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 308392559Sdes if (sock < 0) 308492559Sdes error("socket: %.100s", strerror(errno)); 308592559Sdes memset(&addr, 0, sizeof(addr)); 308692559Sdes addr.sun_family = AF_UNIX; 308792559Sdes snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 3088162856Sdes if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 308992559Sdes return sock; 309092559Sdes close(sock); 309157429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 309257429Smarkm return -1; 309357429Smarkm} 309457429Smarkm 309560573Skrisint 309660573Skrisx11_connect_display(void) 309757429Smarkm{ 3098162856Sdes u_int display_number; 309957429Smarkm const char *display; 310060573Skris char buf[1024], *cp; 310157429Smarkm struct addrinfo hints, *ai, *aitop; 310257429Smarkm char strport[NI_MAXSERV]; 3103162856Sdes int gaierr, sock = 0; 310457429Smarkm 310557429Smarkm /* Try to open a socket for the local X server. */ 310657429Smarkm display = getenv("DISPLAY"); 310757429Smarkm if (!display) { 310857429Smarkm error("DISPLAY not set."); 310960573Skris return -1; 311057429Smarkm } 311157429Smarkm /* 311257429Smarkm * Now we decode the value of the DISPLAY variable and make a 311357429Smarkm * connection to the real X server. 311457429Smarkm */ 311557429Smarkm 311657429Smarkm /* 311757429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 311857429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 311957429Smarkm */ 312057429Smarkm if (strncmp(display, "unix:", 5) == 0 || 312157429Smarkm display[0] == ':') { 312257429Smarkm /* Connect to the unix domain socket. */ 3123162856Sdes if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 312457429Smarkm error("Could not parse display number from DISPLAY: %.100s", 312592559Sdes display); 312660573Skris return -1; 312757429Smarkm } 312857429Smarkm /* Create a socket. */ 312957429Smarkm sock = connect_local_xsocket(display_number); 313057429Smarkm if (sock < 0) 313160573Skris return -1; 313257429Smarkm 313357429Smarkm /* OK, we now have a connection to the display. */ 313460573Skris return sock; 313557429Smarkm } 313657429Smarkm /* 313757429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 313857429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 313957429Smarkm */ 314092559Sdes strlcpy(buf, display, sizeof(buf)); 314157429Smarkm cp = strchr(buf, ':'); 314257429Smarkm if (!cp) { 314357429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 314460573Skris return -1; 314557429Smarkm } 314657429Smarkm *cp = 0; 314757429Smarkm /* buf now contains the host name. But first we parse the display number. */ 3148162856Sdes if (sscanf(cp + 1, "%u", &display_number) != 1) { 314957429Smarkm error("Could not parse display number from DISPLAY: %.100s", 315092559Sdes display); 315160573Skris return -1; 315257429Smarkm } 315357429Smarkm 315457429Smarkm /* Look up the host address */ 315557429Smarkm memset(&hints, 0, sizeof(hints)); 315657429Smarkm hints.ai_family = IPv4or6; 315757429Smarkm hints.ai_socktype = SOCK_STREAM; 3158162856Sdes snprintf(strport, sizeof strport, "%u", 6000 + display_number); 315957429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 3160181111Sdes error("%.100s: unknown host. (%s)", buf, 3161181111Sdes ssh_gai_strerror(gaierr)); 316260573Skris return -1; 316357429Smarkm } 316457429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 316557429Smarkm /* Create a socket. */ 3166124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 316757429Smarkm if (sock < 0) { 3168124207Sdes debug2("socket: %.100s", strerror(errno)); 316960573Skris continue; 317060573Skris } 317160573Skris /* Connect it to the display. */ 317260573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3173162856Sdes debug2("connect %.100s port %u: %.100s", buf, 317460573Skris 6000 + display_number, strerror(errno)); 317560573Skris close(sock); 317660573Skris continue; 317760573Skris } 317860573Skris /* Success */ 317960573Skris break; 318057429Smarkm } 318157429Smarkm freeaddrinfo(aitop); 318257429Smarkm if (!ai) { 3183162856Sdes error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 318457429Smarkm strerror(errno)); 318560573Skris return -1; 318657429Smarkm } 318792559Sdes set_nodelay(sock); 318860573Skris return sock; 318960573Skris} 319057429Smarkm 319160573Skris/* 319260573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 319360573Skris * the remote channel number. We should do whatever we want, and respond 319460573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 319560573Skris */ 319657429Smarkm 3197162856Sdes/* ARGSUSED */ 319860573Skrisvoid 319992559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 320060573Skris{ 320192559Sdes Channel *c = NULL; 320292559Sdes int remote_id, sock = 0; 320360573Skris char *remote_host; 320457429Smarkm 320592559Sdes debug("Received X11 open request."); 320657429Smarkm 320792559Sdes remote_id = packet_get_int(); 320892559Sdes 320992559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 321092559Sdes remote_host = packet_get_string(NULL); 321160573Skris } else { 321260573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 321360573Skris } 321492559Sdes packet_check_eom(); 321560573Skris 321660573Skris /* Obtain a connection to the real X display. */ 321760573Skris sock = x11_connect_display(); 321892559Sdes if (sock != -1) { 321992559Sdes /* Allocate a channel for this connection. */ 322092559Sdes c = channel_new("connected x11 socket", 322192559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 322292559Sdes remote_host, 1); 322392559Sdes c->remote_id = remote_id; 322492559Sdes c->force_drain = 1; 322592559Sdes } 3226124207Sdes xfree(remote_host); 322792559Sdes if (c == NULL) { 322860573Skris /* Send refusal to the remote host. */ 322960573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 323092559Sdes packet_put_int(remote_id); 323160573Skris } else { 323260573Skris /* Send a confirmation to the remote host. */ 323360573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 323492559Sdes packet_put_int(remote_id); 323592559Sdes packet_put_int(c->self); 323660573Skris } 323792559Sdes packet_send(); 323857429Smarkm} 323957429Smarkm 324069587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 3241162856Sdes/* ARGSUSED */ 324269587Sgreenvoid 324392559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 324469587Sgreen{ 324569587Sgreen int rchan = packet_get_int(); 3246106130Sdes 324792559Sdes switch (type) { 324869587Sgreen case SSH_SMSG_AGENT_OPEN: 324969587Sgreen error("Warning: ssh server tried agent forwarding."); 325069587Sgreen break; 325169587Sgreen case SSH_SMSG_X11_OPEN: 325269587Sgreen error("Warning: ssh server tried X11 forwarding."); 325369587Sgreen break; 325469587Sgreen default: 325592559Sdes error("deny_input_open: type %d", type); 325669587Sgreen break; 325769587Sgreen } 3258157019Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 325969587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 326069587Sgreen packet_put_int(rchan); 326169587Sgreen packet_send(); 326269587Sgreen} 326369587Sgreen 326457429Smarkm/* 326557429Smarkm * Requests forwarding of X11 connections, generates fake authentication 326657429Smarkm * data, and enables authentication spoofing. 326792559Sdes * This should be called in the client only. 326857429Smarkm */ 326960573Skrisvoid 3270149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 327160573Skris const char *proto, const char *data) 327257429Smarkm{ 327376262Sgreen u_int data_len = (u_int) strlen(data) / 2; 3274149753Sdes u_int i, value; 327557429Smarkm char *new_data; 327657429Smarkm int screen_number; 327757429Smarkm const char *cp; 3278137019Sdes u_int32_t rnd = 0; 327957429Smarkm 3280149753Sdes if (x11_saved_display == NULL) 3281149753Sdes x11_saved_display = xstrdup(disp); 3282149753Sdes else if (strcmp(disp, x11_saved_display) != 0) { 3283149753Sdes error("x11_request_forwarding_with_spoofing: different " 3284149753Sdes "$DISPLAY already forwarded"); 3285149753Sdes return; 3286149753Sdes } 3287149753Sdes 3288162856Sdes cp = strchr(disp, ':'); 328957429Smarkm if (cp) 329057429Smarkm cp = strchr(cp, '.'); 329157429Smarkm if (cp) 3292162856Sdes screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 329357429Smarkm else 329457429Smarkm screen_number = 0; 329557429Smarkm 3296149753Sdes if (x11_saved_proto == NULL) { 3297149753Sdes /* Save protocol name. */ 3298149753Sdes x11_saved_proto = xstrdup(proto); 3299149753Sdes /* 3300149753Sdes * Extract real authentication data and generate fake data 3301149753Sdes * of the same length. 3302149753Sdes */ 3303149753Sdes x11_saved_data = xmalloc(data_len); 3304149753Sdes x11_fake_data = xmalloc(data_len); 3305149753Sdes for (i = 0; i < data_len; i++) { 3306149753Sdes if (sscanf(data + 2 * i, "%2x", &value) != 1) 3307149753Sdes fatal("x11_request_forwarding: bad " 3308149753Sdes "authentication data: %.100s", data); 3309149753Sdes if (i % 4 == 0) 3310149753Sdes rnd = arc4random(); 3311149753Sdes x11_saved_data[i] = value; 3312149753Sdes x11_fake_data[i] = rnd & 0xff; 3313149753Sdes rnd >>= 8; 3314149753Sdes } 3315149753Sdes x11_saved_data_len = data_len; 3316149753Sdes x11_fake_data_len = data_len; 331757429Smarkm } 331857429Smarkm 331957429Smarkm /* Convert the fake data into hex. */ 3320149753Sdes new_data = tohex(x11_fake_data, data_len); 332157429Smarkm 332257429Smarkm /* Send the request packet. */ 332360573Skris if (compat20) { 332460573Skris channel_request_start(client_session_id, "x11-req", 0); 332560573Skris packet_put_char(0); /* XXX bool single connection */ 332660573Skris } else { 332760573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 332860573Skris } 332960573Skris packet_put_cstring(proto); 333060573Skris packet_put_cstring(new_data); 333157429Smarkm packet_put_int(screen_number); 333257429Smarkm packet_send(); 333357429Smarkm packet_write_wait(); 333457429Smarkm xfree(new_data); 333557429Smarkm} 333657429Smarkm 333792559Sdes 333892559Sdes/* -- agent forwarding */ 333992559Sdes 334057429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 334157429Smarkm 334260573Skrisvoid 334392559Sdesauth_request_forwarding(void) 334457429Smarkm{ 334557429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 334657429Smarkm packet_send(); 334757429Smarkm packet_write_wait(); 334857429Smarkm} 3349