channels.c revision 197679
1197679Sdes/* $OpenBSD: channels.c,v 1.296 2009/05/25 06:48:00 andreas 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); 299192595Sdes c->path = NULL; 30092559Sdes c->ostate = CHAN_OUTPUT_OPEN; 30192559Sdes c->istate = CHAN_INPUT_OPEN; 30292559Sdes c->flags = 0; 303181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 30457429Smarkm c->self = found; 30557429Smarkm c->type = type; 30660573Skris c->ctype = ctype; 30760573Skris c->local_window = window; 30860573Skris c->local_window_max = window; 30960573Skris c->local_consumed = 0; 31060573Skris c->local_maxpacket = maxpack; 31157429Smarkm c->remote_id = -1; 312124207Sdes c->remote_name = xstrdup(remote_name); 31360573Skris c->remote_window = 0; 31460573Skris c->remote_maxpacket = 0; 31592559Sdes c->force_drain = 0; 31692559Sdes c->single_connection = 0; 31792559Sdes c->detach_user = NULL; 318157019Sdes c->detach_close = 0; 319181111Sdes c->open_confirm = NULL; 320181111Sdes c->open_confirm_ctx = NULL; 32165668Skris c->input_filter = NULL; 322157019Sdes c->output_filter = NULL; 323181111Sdes c->filter_ctx = NULL; 324181111Sdes c->filter_cleanup = NULL; 325181111Sdes TAILQ_INIT(&c->status_confirms); 32657429Smarkm debug("channel %d: new [%s]", found, remote_name); 32792559Sdes return c; 32857429Smarkm} 32992559Sdes 33092559Sdesstatic int 33192559Sdeschannel_find_maxfd(void) 33292559Sdes{ 333137019Sdes u_int i; 334137019Sdes int max = 0; 33592559Sdes Channel *c; 33692559Sdes 33792559Sdes for (i = 0; i < channels_alloc; i++) { 33892559Sdes c = channels[i]; 33992559Sdes if (c != NULL) { 34092559Sdes max = MAX(max, c->rfd); 34192559Sdes max = MAX(max, c->wfd); 34292559Sdes max = MAX(max, c->efd); 34392559Sdes } 34492559Sdes } 34592559Sdes return max; 34692559Sdes} 34792559Sdes 34860573Skrisint 34992559Sdeschannel_close_fd(int *fdp) 35060573Skris{ 35192559Sdes int ret = 0, fd = *fdp; 35292559Sdes 35392559Sdes if (fd != -1) { 35492559Sdes ret = close(fd); 35592559Sdes *fdp = -1; 35692559Sdes if (fd == channel_max_fd) 35792559Sdes channel_max_fd = channel_find_maxfd(); 35892559Sdes } 35992559Sdes return ret; 36060573Skris} 36157429Smarkm 36260573Skris/* Close all channel fd/socket. */ 36392559Sdesstatic void 36460573Skrischannel_close_fds(Channel *c) 36557429Smarkm{ 366137019Sdes debug3("channel %d: close_fds r %d w %d e %d c %d", 367137019Sdes c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); 36892559Sdes 36992559Sdes channel_close_fd(&c->sock); 370137019Sdes channel_close_fd(&c->ctl_fd); 37192559Sdes channel_close_fd(&c->rfd); 37292559Sdes channel_close_fd(&c->wfd); 37392559Sdes channel_close_fd(&c->efd); 37460573Skris} 37557429Smarkm 37660573Skris/* Free the channel and close its fd/socket. */ 37760573Skrisvoid 37892559Sdeschannel_free(Channel *c) 37960573Skris{ 38092559Sdes char *s; 381137019Sdes u_int i, n; 382181111Sdes struct channel_confirm *cc; 38376262Sgreen 38492559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 38592559Sdes if (channels[i]) 38692559Sdes n++; 387137019Sdes debug("channel %d: free: %s, nchannels %u", c->self, 38892559Sdes c->remote_name ? c->remote_name : "???", n); 38992559Sdes 39092559Sdes s = channel_open_message(); 391124207Sdes debug3("channel %d: status: %s", c->self, s); 39276262Sgreen xfree(s); 39376262Sgreen 39460573Skris if (c->sock != -1) 39560573Skris shutdown(c->sock, SHUT_RDWR); 396137019Sdes if (c->ctl_fd != -1) 397137019Sdes shutdown(c->ctl_fd, SHUT_RDWR); 39860573Skris channel_close_fds(c); 39960573Skris buffer_free(&c->input); 40060573Skris buffer_free(&c->output); 40160573Skris buffer_free(&c->extended); 40260573Skris if (c->remote_name) { 40360573Skris xfree(c->remote_name); 40460573Skris c->remote_name = NULL; 40560573Skris } 406192595Sdes if (c->path) { 407192595Sdes xfree(c->path); 408192595Sdes c->path = NULL; 409192595Sdes } 410181111Sdes while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 411181111Sdes if (cc->abandon_cb != NULL) 412181111Sdes cc->abandon_cb(c, cc->ctx); 413181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 414181111Sdes bzero(cc, sizeof(*cc)); 415181111Sdes xfree(cc); 416181111Sdes } 417181111Sdes if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 418181111Sdes c->filter_cleanup(c->self, c->filter_ctx); 41992559Sdes channels[c->self] = NULL; 42092559Sdes xfree(c); 42157429Smarkm} 42257429Smarkm 42392559Sdesvoid 42492559Sdeschannel_free_all(void) 42592559Sdes{ 426137019Sdes u_int i; 42792559Sdes 42892559Sdes for (i = 0; i < channels_alloc; i++) 42992559Sdes if (channels[i] != NULL) 43092559Sdes channel_free(channels[i]); 43192559Sdes} 43292559Sdes 43357429Smarkm/* 43492559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 43592559Sdes * descriptors after a fork. 43692559Sdes */ 43792559Sdesvoid 43892559Sdeschannel_close_all(void) 43992559Sdes{ 440137019Sdes u_int i; 44192559Sdes 44292559Sdes for (i = 0; i < channels_alloc; i++) 44392559Sdes if (channels[i] != NULL) 44492559Sdes channel_close_fds(channels[i]); 44592559Sdes} 44692559Sdes 44792559Sdes/* 44892559Sdes * Stop listening to channels. 44992559Sdes */ 45092559Sdesvoid 45192559Sdeschannel_stop_listening(void) 45292559Sdes{ 453137019Sdes u_int i; 45492559Sdes Channel *c; 45592559Sdes 45692559Sdes for (i = 0; i < channels_alloc; i++) { 45792559Sdes c = channels[i]; 45892559Sdes if (c != NULL) { 45992559Sdes switch (c->type) { 46092559Sdes case SSH_CHANNEL_AUTH_SOCKET: 46192559Sdes case SSH_CHANNEL_PORT_LISTENER: 46292559Sdes case SSH_CHANNEL_RPORT_LISTENER: 46392559Sdes case SSH_CHANNEL_X11_LISTENER: 46492559Sdes channel_close_fd(&c->sock); 46592559Sdes channel_free(c); 46692559Sdes break; 46792559Sdes } 46892559Sdes } 46992559Sdes } 47092559Sdes} 47192559Sdes 47292559Sdes/* 47392559Sdes * Returns true if no channel has too much buffered data, and false if one or 47492559Sdes * more channel is overfull. 47592559Sdes */ 47692559Sdesint 47792559Sdeschannel_not_very_much_buffered_data(void) 47892559Sdes{ 47992559Sdes u_int i; 48092559Sdes Channel *c; 48192559Sdes 48292559Sdes for (i = 0; i < channels_alloc; i++) { 48392559Sdes c = channels[i]; 48492559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 48592559Sdes#if 0 48692559Sdes if (!compat20 && 48792559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 488113911Sdes debug2("channel %d: big input buffer %d", 48992559Sdes c->self, buffer_len(&c->input)); 49092559Sdes return 0; 49192559Sdes } 49292559Sdes#endif 49392559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 494124207Sdes debug2("channel %d: big output buffer %u > %u", 49592559Sdes c->self, buffer_len(&c->output), 49692559Sdes packet_get_maxsize()); 49792559Sdes return 0; 49892559Sdes } 49992559Sdes } 50092559Sdes } 50192559Sdes return 1; 50292559Sdes} 50392559Sdes 50492559Sdes/* Returns true if any channel is still open. */ 50592559Sdesint 50692559Sdeschannel_still_open(void) 50792559Sdes{ 508137019Sdes u_int i; 50992559Sdes Channel *c; 51092559Sdes 51192559Sdes for (i = 0; i < channels_alloc; i++) { 51292559Sdes c = channels[i]; 51392559Sdes if (c == NULL) 51492559Sdes continue; 51592559Sdes switch (c->type) { 51692559Sdes case SSH_CHANNEL_X11_LISTENER: 51792559Sdes case SSH_CHANNEL_PORT_LISTENER: 51892559Sdes case SSH_CHANNEL_RPORT_LISTENER: 51992559Sdes case SSH_CHANNEL_CLOSED: 52092559Sdes case SSH_CHANNEL_AUTH_SOCKET: 52192559Sdes case SSH_CHANNEL_DYNAMIC: 52292559Sdes case SSH_CHANNEL_CONNECTING: 52392559Sdes case SSH_CHANNEL_ZOMBIE: 52492559Sdes continue; 52592559Sdes case SSH_CHANNEL_LARVAL: 52692559Sdes if (!compat20) 52792559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 52892559Sdes continue; 52992559Sdes case SSH_CHANNEL_OPENING: 53092559Sdes case SSH_CHANNEL_OPEN: 53192559Sdes case SSH_CHANNEL_X11_OPEN: 53292559Sdes return 1; 53392559Sdes case SSH_CHANNEL_INPUT_DRAINING: 53492559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 53592559Sdes if (!compat13) 53692559Sdes fatal("cannot happen: OUT_DRAIN"); 53792559Sdes return 1; 53892559Sdes default: 53992559Sdes fatal("channel_still_open: bad channel type %d", c->type); 54092559Sdes /* NOTREACHED */ 54192559Sdes } 54292559Sdes } 54392559Sdes return 0; 54492559Sdes} 54592559Sdes 54692559Sdes/* Returns the id of an open channel suitable for keepaliving */ 54792559Sdesint 54892559Sdeschannel_find_open(void) 54992559Sdes{ 550137019Sdes u_int i; 55192559Sdes Channel *c; 55292559Sdes 55392559Sdes for (i = 0; i < channels_alloc; i++) { 55492559Sdes c = channels[i]; 555137019Sdes if (c == NULL || c->remote_id < 0) 55692559Sdes continue; 55792559Sdes switch (c->type) { 55892559Sdes case SSH_CHANNEL_CLOSED: 55992559Sdes case SSH_CHANNEL_DYNAMIC: 56092559Sdes case SSH_CHANNEL_X11_LISTENER: 56192559Sdes case SSH_CHANNEL_PORT_LISTENER: 56292559Sdes case SSH_CHANNEL_RPORT_LISTENER: 56392559Sdes case SSH_CHANNEL_OPENING: 56492559Sdes case SSH_CHANNEL_CONNECTING: 56592559Sdes case SSH_CHANNEL_ZOMBIE: 56692559Sdes continue; 56792559Sdes case SSH_CHANNEL_LARVAL: 56892559Sdes case SSH_CHANNEL_AUTH_SOCKET: 56992559Sdes case SSH_CHANNEL_OPEN: 57092559Sdes case SSH_CHANNEL_X11_OPEN: 57192559Sdes return i; 57292559Sdes case SSH_CHANNEL_INPUT_DRAINING: 57392559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 57492559Sdes if (!compat13) 57592559Sdes fatal("cannot happen: OUT_DRAIN"); 57692559Sdes return i; 57792559Sdes default: 57892559Sdes fatal("channel_find_open: bad channel type %d", c->type); 57992559Sdes /* NOTREACHED */ 58092559Sdes } 58192559Sdes } 58292559Sdes return -1; 58392559Sdes} 58492559Sdes 58592559Sdes 58692559Sdes/* 58792559Sdes * Returns a message describing the currently open forwarded connections, 58892559Sdes * suitable for sending to the client. The message contains crlf pairs for 58992559Sdes * newlines. 59092559Sdes */ 59192559Sdeschar * 59292559Sdeschannel_open_message(void) 59392559Sdes{ 59492559Sdes Buffer buffer; 59592559Sdes Channel *c; 59692559Sdes char buf[1024], *cp; 597137019Sdes u_int i; 59892559Sdes 59992559Sdes buffer_init(&buffer); 60092559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 60192559Sdes buffer_append(&buffer, buf, strlen(buf)); 60292559Sdes for (i = 0; i < channels_alloc; i++) { 60392559Sdes c = channels[i]; 60492559Sdes if (c == NULL) 60592559Sdes continue; 60692559Sdes switch (c->type) { 60792559Sdes case SSH_CHANNEL_X11_LISTENER: 60892559Sdes case SSH_CHANNEL_PORT_LISTENER: 60992559Sdes case SSH_CHANNEL_RPORT_LISTENER: 61092559Sdes case SSH_CHANNEL_CLOSED: 61192559Sdes case SSH_CHANNEL_AUTH_SOCKET: 61292559Sdes case SSH_CHANNEL_ZOMBIE: 61392559Sdes continue; 61492559Sdes case SSH_CHANNEL_LARVAL: 61592559Sdes case SSH_CHANNEL_OPENING: 61692559Sdes case SSH_CHANNEL_CONNECTING: 61792559Sdes case SSH_CHANNEL_DYNAMIC: 61892559Sdes case SSH_CHANNEL_OPEN: 61992559Sdes case SSH_CHANNEL_X11_OPEN: 62092559Sdes case SSH_CHANNEL_INPUT_DRAINING: 62192559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 622137019Sdes snprintf(buf, sizeof buf, 623137019Sdes " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", 62492559Sdes c->self, c->remote_name, 62592559Sdes c->type, c->remote_id, 62692559Sdes c->istate, buffer_len(&c->input), 62792559Sdes c->ostate, buffer_len(&c->output), 628137019Sdes c->rfd, c->wfd, c->ctl_fd); 62992559Sdes buffer_append(&buffer, buf, strlen(buf)); 63092559Sdes continue; 63192559Sdes default: 63292559Sdes fatal("channel_open_message: bad channel type %d", c->type); 63392559Sdes /* NOTREACHED */ 63492559Sdes } 63592559Sdes } 63692559Sdes buffer_append(&buffer, "\0", 1); 63792559Sdes cp = xstrdup(buffer_ptr(&buffer)); 63892559Sdes buffer_free(&buffer); 63992559Sdes return cp; 64092559Sdes} 64192559Sdes 64292559Sdesvoid 64392559Sdeschannel_send_open(int id) 64492559Sdes{ 64592559Sdes Channel *c = channel_lookup(id); 646106130Sdes 64792559Sdes if (c == NULL) { 648124207Sdes logit("channel_send_open: %d: bad id", id); 64992559Sdes return; 65092559Sdes } 651113911Sdes debug2("channel %d: send open", id); 65292559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 65392559Sdes packet_put_cstring(c->ctype); 65492559Sdes packet_put_int(c->self); 65592559Sdes packet_put_int(c->local_window); 65692559Sdes packet_put_int(c->local_maxpacket); 65792559Sdes packet_send(); 65892559Sdes} 65992559Sdes 66092559Sdesvoid 661113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 66292559Sdes{ 663113911Sdes Channel *c = channel_lookup(id); 664106130Sdes 66592559Sdes if (c == NULL) { 666124207Sdes logit("channel_request_start: %d: unknown channel id", id); 66792559Sdes return; 66892559Sdes } 669137019Sdes debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 67092559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 67192559Sdes packet_put_int(c->remote_id); 67292559Sdes packet_put_cstring(service); 67392559Sdes packet_put_char(wantconfirm); 67492559Sdes} 675162856Sdes 67692559Sdesvoid 677181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb, 678181111Sdes channel_confirm_abandon_cb *abandon_cb, void *ctx) 67992559Sdes{ 680181111Sdes struct channel_confirm *cc; 681181111Sdes Channel *c; 682181111Sdes 683181111Sdes if ((c = channel_lookup(id)) == NULL) 684181111Sdes fatal("channel_register_expect: %d: bad id", id); 685181111Sdes 686181111Sdes cc = xmalloc(sizeof(*cc)); 687181111Sdes cc->cb = cb; 688181111Sdes cc->abandon_cb = abandon_cb; 689181111Sdes cc->ctx = ctx; 690181111Sdes TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 691181111Sdes} 692181111Sdes 693181111Sdesvoid 694181111Sdeschannel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) 695181111Sdes{ 69692559Sdes Channel *c = channel_lookup(id); 697106130Sdes 69892559Sdes if (c == NULL) { 699192595Sdes logit("channel_register_open_confirm: %d: bad id", id); 70092559Sdes return; 70192559Sdes } 702181111Sdes c->open_confirm = fn; 703181111Sdes c->open_confirm_ctx = ctx; 70492559Sdes} 705162856Sdes 70692559Sdesvoid 707157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 70892559Sdes{ 709157019Sdes Channel *c = channel_by_id(id); 710106130Sdes 71192559Sdes if (c == NULL) { 712124207Sdes logit("channel_register_cleanup: %d: bad id", id); 71392559Sdes return; 71492559Sdes } 71592559Sdes c->detach_user = fn; 716157019Sdes c->detach_close = do_close; 71792559Sdes} 718162856Sdes 71992559Sdesvoid 72092559Sdeschannel_cancel_cleanup(int id) 72192559Sdes{ 722157019Sdes Channel *c = channel_by_id(id); 723106130Sdes 72492559Sdes if (c == NULL) { 725124207Sdes logit("channel_cancel_cleanup: %d: bad id", id); 72692559Sdes return; 72792559Sdes } 72892559Sdes c->detach_user = NULL; 729157019Sdes c->detach_close = 0; 73092559Sdes} 731162856Sdes 73292559Sdesvoid 733157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn, 734181111Sdes channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 73592559Sdes{ 73692559Sdes Channel *c = channel_lookup(id); 737106130Sdes 73892559Sdes if (c == NULL) { 739124207Sdes logit("channel_register_filter: %d: bad id", id); 74092559Sdes return; 74192559Sdes } 742157019Sdes c->input_filter = ifn; 743157019Sdes c->output_filter = ofn; 744181111Sdes c->filter_ctx = ctx; 745181111Sdes c->filter_cleanup = cfn; 74692559Sdes} 74792559Sdes 74892559Sdesvoid 74992559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 750181111Sdes int extusage, int nonblock, int is_tty, u_int window_max) 75192559Sdes{ 75292559Sdes Channel *c = channel_lookup(id); 753106130Sdes 75492559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 75592559Sdes fatal("channel_activate for non-larval channel %d.", id); 756181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 75792559Sdes c->type = SSH_CHANNEL_OPEN; 75892559Sdes c->local_window = c->local_window_max = window_max; 75992559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 76092559Sdes packet_put_int(c->remote_id); 76192559Sdes packet_put_int(c->local_window); 76292559Sdes packet_send(); 76392559Sdes} 76492559Sdes 76592559Sdes/* 76660573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 76760573Skris * channels in the select bitmasks. 76857429Smarkm */ 76960573Skris/* 77060573Skris * 'channel_post*': perform any appropriate operations for channels which 77160573Skris * have events pending. 77260573Skris */ 773162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 77460573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 77560573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 77657429Smarkm 777162856Sdes/* ARGSUSED */ 77892559Sdesstatic void 779162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 78057429Smarkm{ 78160573Skris FD_SET(c->sock, readset); 78260573Skris} 78360573Skris 784162856Sdes/* ARGSUSED */ 78592559Sdesstatic void 786162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 78776262Sgreen{ 78876262Sgreen debug3("channel %d: waiting for connection", c->self); 78976262Sgreen FD_SET(c->sock, writeset); 79076262Sgreen} 79176262Sgreen 79292559Sdesstatic void 793162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 79460573Skris{ 79560573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 79660573Skris FD_SET(c->sock, readset); 79760573Skris if (buffer_len(&c->output) > 0) 79860573Skris FD_SET(c->sock, writeset); 79960573Skris} 80060573Skris 80192559Sdesstatic void 802162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 80360573Skris{ 80492559Sdes u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 80560573Skris 80660573Skris if (c->istate == CHAN_INPUT_OPEN && 80792559Sdes limit > 0 && 808162856Sdes buffer_len(&c->input) < limit && 809162856Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 81060573Skris FD_SET(c->rfd, readset); 81160573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 81260573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 81360573Skris if (buffer_len(&c->output) > 0) { 81460573Skris FD_SET(c->wfd, writeset); 81560573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 81698684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 817149753Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 818149753Sdes c->self, c->efd, buffer_len(&c->extended)); 81998684Sdes else 82098684Sdes chan_obuf_empty(c); 82160573Skris } 82260573Skris } 82360573Skris /** XXX check close conditions, too */ 824181111Sdes if (compat20 && c->efd != -1 && 825181111Sdes !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 82660573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 82760573Skris buffer_len(&c->extended) > 0) 82860573Skris FD_SET(c->efd, writeset); 82998684Sdes else if (!(c->flags & CHAN_EOF_SENT) && 83098684Sdes c->extended_usage == CHAN_EXTENDED_READ && 83160573Skris buffer_len(&c->extended) < c->remote_window) 83260573Skris FD_SET(c->efd, readset); 83360573Skris } 834137019Sdes /* XXX: What about efd? races? */ 835137019Sdes if (compat20 && c->ctl_fd != -1 && 836137019Sdes c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) 837137019Sdes FD_SET(c->ctl_fd, readset); 83860573Skris} 83960573Skris 840162856Sdes/* ARGSUSED */ 84192559Sdesstatic void 842162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 84360573Skris{ 84460573Skris if (buffer_len(&c->input) == 0) { 84560573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 84660573Skris packet_put_int(c->remote_id); 84760573Skris packet_send(); 84860573Skris c->type = SSH_CHANNEL_CLOSED; 849124207Sdes debug2("channel %d: closing after input drain.", c->self); 85060573Skris } 85160573Skris} 85260573Skris 853162856Sdes/* ARGSUSED */ 85492559Sdesstatic void 855162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 85660573Skris{ 85760573Skris if (buffer_len(&c->output) == 0) 85892559Sdes chan_mark_dead(c); 85960573Skris else 86060573Skris FD_SET(c->sock, writeset); 86160573Skris} 86260573Skris 86360573Skris/* 86460573Skris * This is a special state for X11 authentication spoofing. An opened X11 86560573Skris * connection (when authentication spoofing is being done) remains in this 86660573Skris * state until the first packet has been completely read. The authentication 86760573Skris * data in that packet is then substituted by the real data if it matches the 86860573Skris * fake data, and the channel is put into normal mode. 86960573Skris * XXX All this happens at the client side. 87092559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 87160573Skris */ 87292559Sdesstatic int 87392559Sdesx11_open_helper(Buffer *b) 87460573Skris{ 87576262Sgreen u_char *ucp; 87676262Sgreen u_int proto_len, data_len; 87757429Smarkm 87860573Skris /* Check if the fixed size part of the packet is in buffer. */ 87992559Sdes if (buffer_len(b) < 12) 88060573Skris return 0; 88157429Smarkm 88260573Skris /* Parse the lengths of variable-length fields. */ 88392559Sdes ucp = buffer_ptr(b); 88460573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 88560573Skris proto_len = 256 * ucp[6] + ucp[7]; 88660573Skris data_len = 256 * ucp[8] + ucp[9]; 88760573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 88860573Skris proto_len = ucp[6] + 256 * ucp[7]; 88960573Skris data_len = ucp[8] + 256 * ucp[9]; 89060573Skris } else { 891124207Sdes debug2("Initial X11 packet contains bad byte order byte: 0x%x", 89292559Sdes ucp[0]); 89360573Skris return -1; 89460573Skris } 89557429Smarkm 89660573Skris /* Check if the whole packet is in buffer. */ 89792559Sdes if (buffer_len(b) < 89860573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 89960573Skris return 0; 90057429Smarkm 90160573Skris /* Check if authentication protocol matches. */ 90260573Skris if (proto_len != strlen(x11_saved_proto) || 90360573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 904124207Sdes debug2("X11 connection uses different authentication protocol."); 90560573Skris return -1; 90660573Skris } 90760573Skris /* Check if authentication data matches our fake data. */ 90860573Skris if (data_len != x11_fake_data_len || 90960573Skris memcmp(ucp + 12 + ((proto_len + 3) & ~3), 91060573Skris x11_fake_data, x11_fake_data_len) != 0) { 911124207Sdes debug2("X11 auth data does not match fake data."); 91260573Skris return -1; 91360573Skris } 91460573Skris /* Check fake data length */ 91560573Skris if (x11_fake_data_len != x11_saved_data_len) { 91660573Skris error("X11 fake_data_len %d != saved_data_len %d", 91760573Skris x11_fake_data_len, x11_saved_data_len); 91860573Skris return -1; 91960573Skris } 92060573Skris /* 92160573Skris * Received authentication protocol and data match 92260573Skris * our fake data. Substitute the fake data with real 92360573Skris * data. 92460573Skris */ 92560573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 92660573Skris x11_saved_data, x11_saved_data_len); 92760573Skris return 1; 92860573Skris} 92957429Smarkm 93092559Sdesstatic void 931162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 93260573Skris{ 93392559Sdes int ret = x11_open_helper(&c->output); 934106130Sdes 93560573Skris if (ret == 1) { 93660573Skris /* Start normal processing for the channel. */ 93760573Skris c->type = SSH_CHANNEL_OPEN; 93860573Skris channel_pre_open_13(c, readset, writeset); 93960573Skris } else if (ret == -1) { 94060573Skris /* 94160573Skris * We have received an X11 connection that has bad 94260573Skris * authentication information. 94360573Skris */ 944124207Sdes logit("X11 connection rejected because of wrong authentication."); 94560573Skris buffer_clear(&c->input); 94660573Skris buffer_clear(&c->output); 94792559Sdes channel_close_fd(&c->sock); 94860573Skris c->sock = -1; 94960573Skris c->type = SSH_CHANNEL_CLOSED; 95060573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 95160573Skris packet_put_int(c->remote_id); 95260573Skris packet_send(); 95360573Skris } 95460573Skris} 95557429Smarkm 95692559Sdesstatic void 957162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 95860573Skris{ 95992559Sdes int ret = x11_open_helper(&c->output); 96092559Sdes 96192559Sdes /* c->force_drain = 1; */ 96292559Sdes 96360573Skris if (ret == 1) { 96460573Skris c->type = SSH_CHANNEL_OPEN; 96592559Sdes channel_pre_open(c, readset, writeset); 96692559Sdes } else if (ret == -1) { 967124207Sdes logit("X11 connection rejected because of wrong authentication."); 968124207Sdes debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 96992559Sdes chan_read_failed(c); 97092559Sdes buffer_clear(&c->input); 97192559Sdes chan_ibuf_empty(c); 97292559Sdes buffer_clear(&c->output); 97392559Sdes /* for proto v1, the peer will send an IEOF */ 97460573Skris if (compat20) 97592559Sdes chan_write_failed(c); 97660573Skris else 97792559Sdes c->type = SSH_CHANNEL_OPEN; 978124207Sdes debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 97960573Skris } 98060573Skris} 98157429Smarkm 98276262Sgreen/* try to decode a socks4 header */ 983162856Sdes/* ARGSUSED */ 98492559Sdesstatic int 985162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 98676262Sgreen{ 987106130Sdes char *p, *host; 988192595Sdes u_int len, have, i, found, need; 98992559Sdes char username[256]; 99076262Sgreen struct { 99176262Sgreen u_int8_t version; 99276262Sgreen u_int8_t command; 99376262Sgreen u_int16_t dest_port; 99476262Sgreen struct in_addr dest_addr; 99576262Sgreen } s4_req, s4_rsp; 99676262Sgreen 99776262Sgreen debug2("channel %d: decode socks4", c->self); 99876262Sgreen 99976262Sgreen have = buffer_len(&c->input); 100076262Sgreen len = sizeof(s4_req); 100176262Sgreen if (have < len) 100276262Sgreen return 0; 100376262Sgreen p = buffer_ptr(&c->input); 1004192595Sdes 1005192595Sdes need = 1; 1006192595Sdes /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1007192595Sdes if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1008192595Sdes debug2("channel %d: socks4a request", c->self); 1009192595Sdes /* ... and needs an extra string (the hostname) */ 1010192595Sdes need = 2; 1011192595Sdes } 1012192595Sdes /* Check for terminating NUL on the string(s) */ 101376262Sgreen for (found = 0, i = len; i < have; i++) { 101476262Sgreen if (p[i] == '\0') { 1015192595Sdes found++; 1016192595Sdes if (found == need) 1017192595Sdes break; 101876262Sgreen } 101976262Sgreen if (i > 1024) { 102076262Sgreen /* the peer is probably sending garbage */ 102176262Sgreen debug("channel %d: decode socks4: too long", 102276262Sgreen c->self); 102376262Sgreen return -1; 102476262Sgreen } 102576262Sgreen } 1026192595Sdes if (found < need) 102776262Sgreen return 0; 102876262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 102976262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 103076262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 103176262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 103276262Sgreen have = buffer_len(&c->input); 103376262Sgreen p = buffer_ptr(&c->input); 103476262Sgreen len = strlen(p); 103576262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1036192595Sdes len++; /* trailing '\0' */ 103776262Sgreen if (len > have) 103876262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 103976262Sgreen c->self, len, have); 104076262Sgreen strlcpy(username, p, sizeof(username)); 104176262Sgreen buffer_consume(&c->input, len); 104276262Sgreen 1043192595Sdes if (c->path != NULL) { 1044192595Sdes xfree(c->path); 1045192595Sdes c->path = NULL; 1046192595Sdes } 1047192595Sdes if (need == 1) { /* SOCKS4: one string */ 1048192595Sdes host = inet_ntoa(s4_req.dest_addr); 1049192595Sdes c->path = xstrdup(host); 1050192595Sdes } else { /* SOCKS4A: two strings */ 1051192595Sdes have = buffer_len(&c->input); 1052192595Sdes p = buffer_ptr(&c->input); 1053192595Sdes len = strlen(p); 1054192595Sdes debug2("channel %d: decode socks4a: host %s/%d", 1055192595Sdes c->self, p, len); 1056192595Sdes len++; /* trailing '\0' */ 1057192595Sdes if (len > have) 1058192595Sdes fatal("channel %d: decode socks4a: len %d > have %d", 1059192595Sdes c->self, len, have); 1060192595Sdes if (len > NI_MAXHOST) { 1061192595Sdes error("channel %d: hostname \"%.100s\" too long", 1062192595Sdes c->self, p); 1063192595Sdes return -1; 1064192595Sdes } 1065192595Sdes c->path = xstrdup(p); 1066192595Sdes buffer_consume(&c->input, len); 1067192595Sdes } 106876262Sgreen c->host_port = ntohs(s4_req.dest_port); 106992559Sdes 1070124207Sdes debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1071192595Sdes c->self, c->path, c->host_port, s4_req.command); 107276262Sgreen 107376262Sgreen if (s4_req.command != 1) { 1074192595Sdes debug("channel %d: cannot handle: %s cn %d", 1075192595Sdes c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 107676262Sgreen return -1; 107776262Sgreen } 107876262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 107976262Sgreen s4_rsp.command = 90; /* cd: req granted */ 108076262Sgreen s4_rsp.dest_port = 0; /* ignored */ 108176262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1082162856Sdes buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 108376262Sgreen return 1; 108476262Sgreen} 108576262Sgreen 1086124207Sdes/* try to decode a socks5 header */ 1087124207Sdes#define SSH_SOCKS5_AUTHDONE 0x1000 1088124207Sdes#define SSH_SOCKS5_NOAUTH 0x00 1089124207Sdes#define SSH_SOCKS5_IPV4 0x01 1090124207Sdes#define SSH_SOCKS5_DOMAIN 0x03 1091124207Sdes#define SSH_SOCKS5_IPV6 0x04 1092124207Sdes#define SSH_SOCKS5_CONNECT 0x01 1093124207Sdes#define SSH_SOCKS5_SUCCESS 0x00 1094124207Sdes 1095162856Sdes/* ARGSUSED */ 1096124207Sdesstatic int 1097162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1098124207Sdes{ 1099124207Sdes struct { 1100124207Sdes u_int8_t version; 1101124207Sdes u_int8_t command; 1102124207Sdes u_int8_t reserved; 1103124207Sdes u_int8_t atyp; 1104124207Sdes } s5_req, s5_rsp; 1105124207Sdes u_int16_t dest_port; 1106192595Sdes u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1107162856Sdes u_int have, need, i, found, nmethods, addrlen, af; 1108124207Sdes 1109124207Sdes debug2("channel %d: decode socks5", c->self); 1110124207Sdes p = buffer_ptr(&c->input); 1111124207Sdes if (p[0] != 0x05) 1112124207Sdes return -1; 1113124207Sdes have = buffer_len(&c->input); 1114124207Sdes if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1115124207Sdes /* format: ver | nmethods | methods */ 1116126273Sdes if (have < 2) 1117124207Sdes return 0; 1118124207Sdes nmethods = p[1]; 1119124207Sdes if (have < nmethods + 2) 1120124207Sdes return 0; 1121124207Sdes /* look for method: "NO AUTHENTICATION REQUIRED" */ 1122181111Sdes for (found = 0, i = 2; i < nmethods + 2; i++) { 1123162856Sdes if (p[i] == SSH_SOCKS5_NOAUTH) { 1124124207Sdes found = 1; 1125124207Sdes break; 1126124207Sdes } 1127124207Sdes } 1128124207Sdes if (!found) { 1129124207Sdes debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1130124207Sdes c->self); 1131124207Sdes return -1; 1132124207Sdes } 1133124207Sdes buffer_consume(&c->input, nmethods + 2); 1134124207Sdes buffer_put_char(&c->output, 0x05); /* version */ 1135124207Sdes buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1136124207Sdes FD_SET(c->sock, writeset); 1137124207Sdes c->flags |= SSH_SOCKS5_AUTHDONE; 1138124207Sdes debug2("channel %d: socks5 auth done", c->self); 1139124207Sdes return 0; /* need more */ 1140124207Sdes } 1141124207Sdes debug2("channel %d: socks5 post auth", c->self); 1142124207Sdes if (have < sizeof(s5_req)+1) 1143124207Sdes return 0; /* need more */ 1144162856Sdes memcpy(&s5_req, p, sizeof(s5_req)); 1145124207Sdes if (s5_req.version != 0x05 || 1146124207Sdes s5_req.command != SSH_SOCKS5_CONNECT || 1147124207Sdes s5_req.reserved != 0x00) { 1148124207Sdes debug2("channel %d: only socks5 connect supported", c->self); 1149124207Sdes return -1; 1150124207Sdes } 1151147005Sdes switch (s5_req.atyp){ 1152124207Sdes case SSH_SOCKS5_IPV4: 1153124207Sdes addrlen = 4; 1154124207Sdes af = AF_INET; 1155124207Sdes break; 1156124207Sdes case SSH_SOCKS5_DOMAIN: 1157124207Sdes addrlen = p[sizeof(s5_req)]; 1158124207Sdes af = -1; 1159124207Sdes break; 1160124207Sdes case SSH_SOCKS5_IPV6: 1161124207Sdes addrlen = 16; 1162124207Sdes af = AF_INET6; 1163124207Sdes break; 1164124207Sdes default: 1165124207Sdes debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1166124207Sdes return -1; 1167124207Sdes } 1168162856Sdes need = sizeof(s5_req) + addrlen + 2; 1169162856Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1170162856Sdes need++; 1171162856Sdes if (have < need) 1172124207Sdes return 0; 1173124207Sdes buffer_consume(&c->input, sizeof(s5_req)); 1174124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1175124207Sdes buffer_consume(&c->input, 1); /* host string length */ 1176124207Sdes buffer_get(&c->input, (char *)&dest_addr, addrlen); 1177124207Sdes buffer_get(&c->input, (char *)&dest_port, 2); 1178124207Sdes dest_addr[addrlen] = '\0'; 1179192595Sdes if (c->path != NULL) { 1180192595Sdes xfree(c->path); 1181192595Sdes c->path = NULL; 1182192595Sdes } 1183192595Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1184192595Sdes if (addrlen >= NI_MAXHOST) { 1185192595Sdes error("channel %d: dynamic request: socks5 hostname " 1186192595Sdes "\"%.100s\" too long", c->self, dest_addr); 1187192595Sdes return -1; 1188192595Sdes } 1189192595Sdes c->path = xstrdup(dest_addr); 1190192595Sdes } else { 1191192595Sdes if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1192192595Sdes return -1; 1193192595Sdes c->path = xstrdup(ntop); 1194192595Sdes } 1195124207Sdes c->host_port = ntohs(dest_port); 1196126273Sdes 1197124207Sdes debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1198124207Sdes c->self, c->path, c->host_port, s5_req.command); 1199124207Sdes 1200124207Sdes s5_rsp.version = 0x05; 1201124207Sdes s5_rsp.command = SSH_SOCKS5_SUCCESS; 1202124207Sdes s5_rsp.reserved = 0; /* ignored */ 1203124207Sdes s5_rsp.atyp = SSH_SOCKS5_IPV4; 1204124207Sdes ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; 1205124207Sdes dest_port = 0; /* ignored */ 1206124207Sdes 1207162856Sdes buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1208162856Sdes buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); 1209162856Sdes buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1210124207Sdes return 1; 1211124207Sdes} 1212124207Sdes 121376262Sgreen/* dynamic port forwarding */ 121492559Sdesstatic void 1215162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 121676262Sgreen{ 121776262Sgreen u_char *p; 1218149753Sdes u_int have; 1219149753Sdes int ret; 122076262Sgreen 122176262Sgreen have = buffer_len(&c->input); 122292559Sdes c->delayed = 0; 122376262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 122476262Sgreen /* buffer_dump(&c->input); */ 122576262Sgreen /* check if the fixed size part of the packet is in buffer. */ 1226124207Sdes if (have < 3) { 122776262Sgreen /* need more */ 122876262Sgreen FD_SET(c->sock, readset); 122976262Sgreen return; 123076262Sgreen } 123176262Sgreen /* try to guess the protocol */ 123276262Sgreen p = buffer_ptr(&c->input); 123376262Sgreen switch (p[0]) { 123476262Sgreen case 0x04: 123576262Sgreen ret = channel_decode_socks4(c, readset, writeset); 123676262Sgreen break; 1237124207Sdes case 0x05: 1238124207Sdes ret = channel_decode_socks5(c, readset, writeset); 1239124207Sdes break; 124076262Sgreen default: 124176262Sgreen ret = -1; 124276262Sgreen break; 124376262Sgreen } 124476262Sgreen if (ret < 0) { 124592559Sdes chan_mark_dead(c); 124676262Sgreen } else if (ret == 0) { 124776262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 124876262Sgreen /* need more */ 124976262Sgreen FD_SET(c->sock, readset); 125076262Sgreen } else { 125176262Sgreen /* switch to the next state */ 125276262Sgreen c->type = SSH_CHANNEL_OPENING; 125376262Sgreen port_open_helper(c, "direct-tcpip"); 125476262Sgreen } 125576262Sgreen} 125676262Sgreen 125760573Skris/* This is our fake X11 server socket. */ 1258162856Sdes/* ARGSUSED */ 125992559Sdesstatic void 1260162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 126160573Skris{ 126292559Sdes Channel *nc; 1263181111Sdes struct sockaddr_storage addr; 126492559Sdes int newsock; 126560573Skris socklen_t addrlen; 126676262Sgreen char buf[16384], *remote_ipaddr; 126760573Skris int remote_port; 126857429Smarkm 126960573Skris if (FD_ISSET(c->sock, readset)) { 127060573Skris debug("X11 connection requested."); 127160573Skris addrlen = sizeof(addr); 1272181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 127392559Sdes if (c->single_connection) { 1274124207Sdes debug2("single_connection: closing X11 listener."); 127592559Sdes channel_close_fd(&c->sock); 127692559Sdes chan_mark_dead(c); 127792559Sdes } 127860573Skris if (newsock < 0) { 127960573Skris error("accept: %.100s", strerror(errno)); 128060573Skris return; 128160573Skris } 128292559Sdes set_nodelay(newsock); 128376262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 128460573Skris remote_port = get_peer_port(newsock); 128560573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 128676262Sgreen remote_ipaddr, remote_port); 128757429Smarkm 128892559Sdes nc = channel_new("accepted x11 socket", 128960573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 1290124207Sdes c->local_window_max, c->local_maxpacket, 0, buf, 1); 129160573Skris if (compat20) { 129260573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 129360573Skris packet_put_cstring("x11"); 129492559Sdes packet_put_int(nc->self); 129592559Sdes packet_put_int(nc->local_window_max); 129692559Sdes packet_put_int(nc->local_maxpacket); 129776262Sgreen /* originator ipaddr and port */ 129876262Sgreen packet_put_cstring(remote_ipaddr); 129960573Skris if (datafellows & SSH_BUG_X11FWD) { 1300124207Sdes debug2("ssh2 x11 bug compat mode"); 130157429Smarkm } else { 130260573Skris packet_put_int(remote_port); 130357429Smarkm } 130460573Skris packet_send(); 130560573Skris } else { 130660573Skris packet_start(SSH_SMSG_X11_OPEN); 130792559Sdes packet_put_int(nc->self); 130892559Sdes if (packet_get_protocol_flags() & 130992559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 131092559Sdes packet_put_cstring(buf); 131160573Skris packet_send(); 131257429Smarkm } 131376262Sgreen xfree(remote_ipaddr); 131457429Smarkm } 131557429Smarkm} 131657429Smarkm 131792559Sdesstatic void 131876262Sgreenport_open_helper(Channel *c, char *rtype) 131976262Sgreen{ 132076262Sgreen int direct; 132176262Sgreen char buf[1024]; 132276262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 1323149753Sdes int remote_port = get_peer_port(c->sock); 132476262Sgreen 132576262Sgreen direct = (strcmp(rtype, "direct-tcpip") == 0); 132676262Sgreen 132776262Sgreen snprintf(buf, sizeof buf, 132876262Sgreen "%s: listening port %d for %.100s port %d, " 132976262Sgreen "connect from %.200s port %d", 133076262Sgreen rtype, c->listening_port, c->path, c->host_port, 133176262Sgreen remote_ipaddr, remote_port); 133276262Sgreen 133376262Sgreen xfree(c->remote_name); 133476262Sgreen c->remote_name = xstrdup(buf); 133576262Sgreen 133676262Sgreen if (compat20) { 133776262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 133876262Sgreen packet_put_cstring(rtype); 133976262Sgreen packet_put_int(c->self); 134076262Sgreen packet_put_int(c->local_window_max); 134176262Sgreen packet_put_int(c->local_maxpacket); 134276262Sgreen if (direct) { 134376262Sgreen /* target host, port */ 134476262Sgreen packet_put_cstring(c->path); 134576262Sgreen packet_put_int(c->host_port); 134676262Sgreen } else { 134776262Sgreen /* listen address, port */ 134876262Sgreen packet_put_cstring(c->path); 134976262Sgreen packet_put_int(c->listening_port); 135076262Sgreen } 135176262Sgreen /* originator host and port */ 135276262Sgreen packet_put_cstring(remote_ipaddr); 1353149753Sdes packet_put_int((u_int)remote_port); 135476262Sgreen packet_send(); 135576262Sgreen } else { 135676262Sgreen packet_start(SSH_MSG_PORT_OPEN); 135776262Sgreen packet_put_int(c->self); 135876262Sgreen packet_put_cstring(c->path); 135976262Sgreen packet_put_int(c->host_port); 136092559Sdes if (packet_get_protocol_flags() & 136192559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 136276262Sgreen packet_put_cstring(c->remote_name); 136376262Sgreen packet_send(); 136476262Sgreen } 136576262Sgreen xfree(remote_ipaddr); 136676262Sgreen} 136776262Sgreen 1368157019Sdesstatic void 1369157019Sdeschannel_set_reuseaddr(int fd) 1370157019Sdes{ 1371157019Sdes int on = 1; 1372157019Sdes 1373157019Sdes /* 1374157019Sdes * Set socket options. 1375157019Sdes * Allow local port reuse in TIME_WAIT. 1376157019Sdes */ 1377157019Sdes if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1378157019Sdes error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1379157019Sdes} 1380157019Sdes 138157429Smarkm/* 138260573Skris * This socket is listening for connections to a forwarded TCP/IP port. 138357429Smarkm */ 1384162856Sdes/* ARGSUSED */ 138592559Sdesstatic void 1386162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 138757429Smarkm{ 138876262Sgreen Channel *nc; 1389181111Sdes struct sockaddr_storage addr; 139092559Sdes int newsock, nextstate; 139157429Smarkm socklen_t addrlen; 139276262Sgreen char *rtype; 139357429Smarkm 139460573Skris if (FD_ISSET(c->sock, readset)) { 139560573Skris debug("Connection to port %d forwarding " 139660573Skris "to %.100s port %d requested.", 139760573Skris c->listening_port, c->path, c->host_port); 139876262Sgreen 139992559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 140092559Sdes nextstate = SSH_CHANNEL_OPENING; 140192559Sdes rtype = "forwarded-tcpip"; 140292559Sdes } else { 140392559Sdes if (c->host_port == 0) { 140492559Sdes nextstate = SSH_CHANNEL_DYNAMIC; 140592559Sdes rtype = "dynamic-tcpip"; 140692559Sdes } else { 140792559Sdes nextstate = SSH_CHANNEL_OPENING; 140892559Sdes rtype = "direct-tcpip"; 140992559Sdes } 141092559Sdes } 141176262Sgreen 141260573Skris addrlen = sizeof(addr); 1413181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 141460573Skris if (newsock < 0) { 141560573Skris error("accept: %.100s", strerror(errno)); 141660573Skris return; 141760573Skris } 141892559Sdes set_nodelay(newsock); 1419124207Sdes nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1420124207Sdes c->local_window_max, c->local_maxpacket, 0, rtype, 1); 142176262Sgreen nc->listening_port = c->listening_port; 142276262Sgreen nc->host_port = c->host_port; 1423192595Sdes if (c->path != NULL) 1424192595Sdes nc->path = xstrdup(c->path); 142576262Sgreen 142692559Sdes if (nextstate == SSH_CHANNEL_DYNAMIC) { 142792559Sdes /* 142892559Sdes * do not call the channel_post handler until 142992559Sdes * this flag has been reset by a pre-handler. 143092559Sdes * otherwise the FD_ISSET calls might overflow 143192559Sdes */ 143292559Sdes nc->delayed = 1; 143392559Sdes } else { 143476262Sgreen port_open_helper(nc, rtype); 143592559Sdes } 143660573Skris } 143760573Skris} 143857429Smarkm 143960573Skris/* 144060573Skris * This is the authentication agent socket listening for connections from 144160573Skris * clients. 144260573Skris */ 1443162856Sdes/* ARGSUSED */ 144492559Sdesstatic void 1445162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 144660573Skris{ 144792559Sdes Channel *nc; 144892559Sdes int newsock; 1449181111Sdes struct sockaddr_storage addr; 145060573Skris socklen_t addrlen; 145157429Smarkm 145260573Skris if (FD_ISSET(c->sock, readset)) { 145360573Skris addrlen = sizeof(addr); 1454181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 145560573Skris if (newsock < 0) { 145660573Skris error("accept from auth socket: %.100s", strerror(errno)); 145760573Skris return; 145860573Skris } 145992559Sdes nc = channel_new("accepted auth socket", 146076262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 146176262Sgreen c->local_window_max, c->local_maxpacket, 1462124207Sdes 0, "accepted auth socket", 1); 146376262Sgreen if (compat20) { 146476262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 146576262Sgreen packet_put_cstring("auth-agent@openssh.com"); 146692559Sdes packet_put_int(nc->self); 146776262Sgreen packet_put_int(c->local_window_max); 146876262Sgreen packet_put_int(c->local_maxpacket); 146976262Sgreen } else { 147076262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 147192559Sdes packet_put_int(nc->self); 147276262Sgreen } 147360573Skris packet_send(); 147460573Skris } 147560573Skris} 147657429Smarkm 1477162856Sdes/* ARGSUSED */ 147892559Sdesstatic void 1479162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 148076262Sgreen{ 1481181111Sdes int err = 0, sock; 148292559Sdes socklen_t sz = sizeof(err); 148392559Sdes 148476262Sgreen if (FD_ISSET(c->sock, writeset)) { 148592559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 148692559Sdes err = errno; 148792559Sdes error("getsockopt SO_ERROR failed"); 148892559Sdes } 148992559Sdes if (err == 0) { 1490181111Sdes debug("channel %d: connected to %s port %d", 1491181111Sdes c->self, c->connect_ctx.host, c->connect_ctx.port); 1492181111Sdes channel_connect_ctx_free(&c->connect_ctx); 149392559Sdes c->type = SSH_CHANNEL_OPEN; 149492559Sdes if (compat20) { 149592559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 149692559Sdes packet_put_int(c->remote_id); 149792559Sdes packet_put_int(c->self); 149892559Sdes packet_put_int(c->local_window); 149992559Sdes packet_put_int(c->local_maxpacket); 150092559Sdes } else { 150192559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 150292559Sdes packet_put_int(c->remote_id); 150392559Sdes packet_put_int(c->self); 150492559Sdes } 150576262Sgreen } else { 1506181111Sdes debug("channel %d: connection failed: %s", 150792559Sdes c->self, strerror(err)); 1508181111Sdes /* Try next address, if any */ 1509181111Sdes if ((sock = connect_next(&c->connect_ctx)) > 0) { 1510181111Sdes close(c->sock); 1511181111Sdes c->sock = c->rfd = c->wfd = sock; 1512181111Sdes channel_max_fd = channel_find_maxfd(); 1513181111Sdes return; 1514181111Sdes } 1515181111Sdes /* Exhausted all addresses */ 1516181111Sdes error("connect_to %.100s port %d: failed.", 1517181111Sdes c->connect_ctx.host, c->connect_ctx.port); 1518181111Sdes channel_connect_ctx_free(&c->connect_ctx); 151992559Sdes if (compat20) { 152092559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 152192559Sdes packet_put_int(c->remote_id); 152292559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 152392559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 152492559Sdes packet_put_cstring(strerror(err)); 152592559Sdes packet_put_cstring(""); 152692559Sdes } 152776262Sgreen } else { 152892559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 152992559Sdes packet_put_int(c->remote_id); 153076262Sgreen } 153192559Sdes chan_mark_dead(c); 153276262Sgreen } 153392559Sdes packet_send(); 153476262Sgreen } 153576262Sgreen} 153676262Sgreen 1537162856Sdes/* ARGSUSED */ 153892559Sdesstatic int 1539162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 154060573Skris{ 1541147005Sdes char buf[CHAN_RBUF]; 1542181111Sdes int len, force; 154357429Smarkm 1544181111Sdes force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1545181111Sdes if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1546162856Sdes errno = 0; 154760573Skris len = read(c->rfd, buf, sizeof(buf)); 1548181111Sdes if (len < 0 && (errno == EINTR || 1549181111Sdes ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 155060573Skris return 1; 1551162856Sdes#ifndef PTY_ZEROREAD 155278827Sgreen if (len <= 0) { 1553162856Sdes#else 1554162856Sdes if ((!c->isatty && len <= 0) || 1555162856Sdes (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1556162856Sdes#endif 1557124207Sdes debug2("channel %d: read<=0 rfd %d len %d", 155860573Skris c->self, c->rfd, len); 155976262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1560124207Sdes debug2("channel %d: not open", c->self); 156192559Sdes chan_mark_dead(c); 156276262Sgreen return -1; 156376262Sgreen } else if (compat13) { 156492559Sdes buffer_clear(&c->output); 156560573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 1566124207Sdes debug2("channel %d: input draining.", c->self); 156760573Skris } else { 156860573Skris chan_read_failed(c); 156957429Smarkm } 157060573Skris return -1; 157160573Skris } 157292559Sdes if (c->input_filter != NULL) { 157365668Skris if (c->input_filter(c, buf, len) == -1) { 1574124207Sdes debug2("channel %d: filter stops", c->self); 157565668Skris chan_read_failed(c); 157665668Skris } 1577157019Sdes } else if (c->datagram) { 1578157019Sdes buffer_put_string(&c->input, buf, len); 157965668Skris } else { 158065668Skris buffer_append(&c->input, buf, len); 158165668Skris } 158260573Skris } 158360573Skris return 1; 158460573Skris} 1585162856Sdes 1586162856Sdes/* ARGSUSED */ 158792559Sdesstatic int 1588162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 158960573Skris{ 159076262Sgreen struct termios tio; 1591157019Sdes u_char *data = NULL, *buf; 159292559Sdes u_int dlen; 159360573Skris int len; 159460573Skris 159560573Skris /* Send buffered output data to the socket. */ 159660573Skris if (c->wfd != -1 && 159760573Skris FD_ISSET(c->wfd, writeset) && 159860573Skris buffer_len(&c->output) > 0) { 1599157019Sdes if (c->output_filter != NULL) { 1600157019Sdes if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1601157019Sdes debug2("channel %d: filter stops", c->self); 1602157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1603157019Sdes chan_mark_dead(c); 1604157019Sdes else 1605157019Sdes chan_write_failed(c); 1606157019Sdes return -1; 1607157019Sdes } 1608157019Sdes } else if (c->datagram) { 1609157019Sdes buf = data = buffer_get_string(&c->output, &dlen); 1610157019Sdes } else { 1611157019Sdes buf = data = buffer_ptr(&c->output); 1612157019Sdes dlen = buffer_len(&c->output); 1613157019Sdes } 1614157019Sdes 1615157019Sdes if (c->datagram) { 1616157019Sdes /* ignore truncated writes, datagrams might get lost */ 1617157019Sdes c->local_consumed += dlen + 4; 1618157019Sdes len = write(c->wfd, buf, dlen); 1619157019Sdes xfree(data); 1620181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1621181111Sdes errno == EWOULDBLOCK)) 1622157019Sdes return 1; 1623157019Sdes if (len <= 0) { 1624157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1625157019Sdes chan_mark_dead(c); 1626157019Sdes else 1627157019Sdes chan_write_failed(c); 1628157019Sdes return -1; 1629157019Sdes } 1630157019Sdes return 1; 1631157019Sdes } 1632106130Sdes#ifdef _AIX 1633126273Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1634126273Sdes if (compat20 && c->wfd_isatty) 1635126273Sdes dlen = MIN(dlen, 8*1024); 1636106130Sdes#endif 1637157019Sdes 1638157019Sdes len = write(c->wfd, buf, dlen); 1639181111Sdes if (len < 0 && 1640181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 164160573Skris return 1; 164260573Skris if (len <= 0) { 164376262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1644124207Sdes debug2("channel %d: not open", c->self); 164592559Sdes chan_mark_dead(c); 164676262Sgreen return -1; 164776262Sgreen } else if (compat13) { 164892559Sdes buffer_clear(&c->output); 1649124207Sdes debug2("channel %d: input draining.", c->self); 165060573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 165160573Skris } else { 165260573Skris chan_write_failed(c); 165357429Smarkm } 165460573Skris return -1; 165560573Skris } 1656197679Sdes#ifndef BROKEN_TCGETATTR_ICANON 1657157019Sdes if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 165874500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 165974500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 166074500Sgreen /* 166174500Sgreen * Simulate echo to reduce the impact of 166276262Sgreen * traffic analysis. We need to match the 166376262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 1664157019Sdes * (4 byte channel id + buf) 166574500Sgreen */ 166676262Sgreen packet_send_ignore(4 + len); 166774500Sgreen packet_send(); 166874500Sgreen } 166974500Sgreen } 1670197679Sdes#endif 167160573Skris buffer_consume(&c->output, len); 167260573Skris if (compat20 && len > 0) { 167360573Skris c->local_consumed += len; 167460573Skris } 167560573Skris } 167660573Skris return 1; 167760573Skris} 1678162856Sdes 167992559Sdesstatic int 1680162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 168160573Skris{ 1682147005Sdes char buf[CHAN_RBUF]; 168360573Skris int len; 168457429Smarkm 168560573Skris/** XXX handle drain efd, too */ 168660573Skris if (c->efd != -1) { 168760573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 168860573Skris FD_ISSET(c->efd, writeset) && 168960573Skris buffer_len(&c->extended) > 0) { 169060573Skris len = write(c->efd, buffer_ptr(&c->extended), 169160573Skris buffer_len(&c->extended)); 169269587Sgreen debug2("channel %d: written %d to efd %d", 169360573Skris c->self, len, c->efd); 1694181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1695181111Sdes errno == EWOULDBLOCK)) 169676262Sgreen return 1; 169776262Sgreen if (len <= 0) { 169876262Sgreen debug2("channel %d: closing write-efd %d", 169976262Sgreen c->self, c->efd); 170092559Sdes channel_close_fd(&c->efd); 170176262Sgreen } else { 170260573Skris buffer_consume(&c->extended, len); 170360573Skris c->local_consumed += len; 170457429Smarkm } 170560573Skris } else if (c->extended_usage == CHAN_EXTENDED_READ && 1706181111Sdes (c->detach_close || FD_ISSET(c->efd, readset))) { 170760573Skris len = read(c->efd, buf, sizeof(buf)); 170869587Sgreen debug2("channel %d: read %d from efd %d", 170992559Sdes c->self, len, c->efd); 1710181111Sdes if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1711181111Sdes errno == EWOULDBLOCK) && !c->detach_close))) 171276262Sgreen return 1; 171376262Sgreen if (len <= 0) { 171476262Sgreen debug2("channel %d: closing read-efd %d", 171560573Skris c->self, c->efd); 171692559Sdes channel_close_fd(&c->efd); 171776262Sgreen } else { 171860573Skris buffer_append(&c->extended, buf, len); 171976262Sgreen } 172060573Skris } 172160573Skris } 172260573Skris return 1; 172360573Skris} 1724162856Sdes 1725162856Sdes/* ARGSUSED */ 172692559Sdesstatic int 1727162856Sdeschannel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) 1728137019Sdes{ 1729137019Sdes char buf[16]; 1730137019Sdes int len; 1731137019Sdes 1732137019Sdes /* Monitor control fd to detect if the slave client exits */ 1733137019Sdes if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { 1734137019Sdes len = read(c->ctl_fd, buf, sizeof(buf)); 1735181111Sdes if (len < 0 && 1736181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 1737137019Sdes return 1; 1738137019Sdes if (len <= 0) { 1739137019Sdes debug2("channel %d: ctl read<=0", c->self); 1740137019Sdes if (c->type != SSH_CHANNEL_OPEN) { 1741137019Sdes debug2("channel %d: not open", c->self); 1742137019Sdes chan_mark_dead(c); 1743137019Sdes return -1; 1744137019Sdes } else { 1745137019Sdes chan_read_failed(c); 1746137019Sdes chan_write_failed(c); 1747137019Sdes } 1748137019Sdes return -1; 1749137019Sdes } else 1750137019Sdes fatal("%s: unexpected data on ctl fd", __func__); 1751137019Sdes } 1752137019Sdes return 1; 1753137019Sdes} 1754162856Sdes 1755137019Sdesstatic int 175676262Sgreenchannel_check_window(Channel *c) 175760573Skris{ 175876262Sgreen if (c->type == SSH_CHANNEL_OPEN && 175976262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1760181111Sdes ((c->local_window_max - c->local_window > 1761181111Sdes c->local_maxpacket*3) || 1762181111Sdes c->local_window < c->local_window_max/2) && 176360573Skris c->local_consumed > 0) { 176460573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 176560573Skris packet_put_int(c->remote_id); 176660573Skris packet_put_int(c->local_consumed); 176760573Skris packet_send(); 176869587Sgreen debug2("channel %d: window %d sent adjust %d", 176960573Skris c->self, c->local_window, 177060573Skris c->local_consumed); 177160573Skris c->local_window += c->local_consumed; 177260573Skris c->local_consumed = 0; 177360573Skris } 177460573Skris return 1; 177560573Skris} 177657429Smarkm 177792559Sdesstatic void 1778162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 177960573Skris{ 178092559Sdes if (c->delayed) 178192559Sdes return; 178260573Skris channel_handle_rfd(c, readset, writeset); 178360573Skris channel_handle_wfd(c, readset, writeset); 178492559Sdes if (!compat20) 178592559Sdes return; 178660573Skris channel_handle_efd(c, readset, writeset); 1787137019Sdes channel_handle_ctl(c, readset, writeset); 178876262Sgreen channel_check_window(c); 178960573Skris} 179060573Skris 1791162856Sdes/* ARGSUSED */ 179292559Sdesstatic void 1793162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 179460573Skris{ 179560573Skris int len; 1796106130Sdes 179760573Skris /* Send buffered output data to the socket. */ 179860573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 179960573Skris len = write(c->sock, buffer_ptr(&c->output), 180060573Skris buffer_len(&c->output)); 180160573Skris if (len <= 0) 180292559Sdes buffer_clear(&c->output); 180360573Skris else 180460573Skris buffer_consume(&c->output, len); 180560573Skris } 180660573Skris} 180760573Skris 180892559Sdesstatic void 180960573Skrischannel_handler_init_20(void) 181060573Skris{ 181192559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 181260573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 181360573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 181476262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 181560573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 181676262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 181776262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 181876262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 181960573Skris 182092559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 182160573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 182276262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 182360573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 182476262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 182576262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 182692559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 182760573Skris} 182860573Skris 182992559Sdesstatic void 183060573Skrischannel_handler_init_13(void) 183160573Skris{ 183260573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 183360573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 183460573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 183560573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 183660573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 183760573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 183860573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 183976262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 184076262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 184160573Skris 184292559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 184360573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 184460573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 184560573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 184660573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 184776262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 184892559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 184960573Skris} 185060573Skris 185192559Sdesstatic void 185260573Skrischannel_handler_init_15(void) 185360573Skris{ 185492559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 185560573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 185660573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 185760573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 185860573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 185976262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 186076262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 186160573Skris 186260573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 186360573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 186460573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 186592559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 186676262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 186792559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 186860573Skris} 186960573Skris 187092559Sdesstatic void 187160573Skrischannel_handler_init(void) 187260573Skris{ 187360573Skris int i; 1874106130Sdes 187592559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 187660573Skris channel_pre[i] = NULL; 187760573Skris channel_post[i] = NULL; 187860573Skris } 187960573Skris if (compat20) 188060573Skris channel_handler_init_20(); 188160573Skris else if (compat13) 188260573Skris channel_handler_init_13(); 188360573Skris else 188460573Skris channel_handler_init_15(); 188560573Skris} 188660573Skris 188792559Sdes/* gc dead channels */ 188892559Sdesstatic void 188992559Sdeschannel_garbage_collect(Channel *c) 189092559Sdes{ 189192559Sdes if (c == NULL) 189292559Sdes return; 189392559Sdes if (c->detach_user != NULL) { 1894157019Sdes if (!chan_is_dead(c, c->detach_close)) 189592559Sdes return; 1896124207Sdes debug2("channel %d: gc: notify user", c->self); 189792559Sdes c->detach_user(c->self, NULL); 189892559Sdes /* if we still have a callback */ 189992559Sdes if (c->detach_user != NULL) 190092559Sdes return; 1901124207Sdes debug2("channel %d: gc: user detached", c->self); 190292559Sdes } 190392559Sdes if (!chan_is_dead(c, 1)) 190492559Sdes return; 1905124207Sdes debug2("channel %d: garbage collecting", c->self); 190692559Sdes channel_free(c); 190792559Sdes} 190892559Sdes 190992559Sdesstatic void 1910162856Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) 191160573Skris{ 191260573Skris static int did_init = 0; 1913137019Sdes u_int i; 191460573Skris Channel *c; 191560573Skris 191660573Skris if (!did_init) { 191760573Skris channel_handler_init(); 191860573Skris did_init = 1; 191960573Skris } 192060573Skris for (i = 0; i < channels_alloc; i++) { 192192559Sdes c = channels[i]; 192292559Sdes if (c == NULL) 192357429Smarkm continue; 192492559Sdes if (ftab[c->type] != NULL) 192592559Sdes (*ftab[c->type])(c, readset, writeset); 192692559Sdes channel_garbage_collect(c); 192757429Smarkm } 192857429Smarkm} 192957429Smarkm 193092559Sdes/* 193192559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 193292559Sdes * select bitmasks. 193392559Sdes */ 193460573Skrisvoid 193576262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1936137019Sdes u_int *nallocp, int rekeying) 193760573Skris{ 1938162856Sdes u_int n, sz, nfdset; 193976262Sgreen 194076262Sgreen n = MAX(*maxfdp, channel_max_fd); 194176262Sgreen 1942162856Sdes nfdset = howmany(n+1, NFDBITS); 1943162856Sdes /* Explicitly test here, because xrealloc isn't always called */ 1944162856Sdes if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) 1945162856Sdes fatal("channel_prepare_select: max_fd (%d) is too large", n); 1946162856Sdes sz = nfdset * sizeof(fd_mask); 1947162856Sdes 194892559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 194992559Sdes if (*readsetp == NULL || sz > *nallocp) { 1950162856Sdes *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); 1951162856Sdes *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); 195292559Sdes *nallocp = sz; 195376262Sgreen } 195492559Sdes *maxfdp = n; 195576262Sgreen memset(*readsetp, 0, sz); 195676262Sgreen memset(*writesetp, 0, sz); 195776262Sgreen 195876262Sgreen if (!rekeying) 195976262Sgreen channel_handler(channel_pre, *readsetp, *writesetp); 196060573Skris} 196160573Skris 196292559Sdes/* 196392559Sdes * After select, perform any appropriate operations for channels which have 196492559Sdes * events pending. 196592559Sdes */ 196660573Skrisvoid 1967162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset) 196860573Skris{ 196960573Skris channel_handler(channel_post, readset, writeset); 197060573Skris} 197160573Skris 197292559Sdes 197376262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 197460573Skrisvoid 197592559Sdeschannel_output_poll(void) 197657429Smarkm{ 197760573Skris Channel *c; 1978137019Sdes u_int i, len; 197957429Smarkm 198057429Smarkm for (i = 0; i < channels_alloc; i++) { 198192559Sdes c = channels[i]; 198292559Sdes if (c == NULL) 198392559Sdes continue; 198457429Smarkm 198592559Sdes /* 198692559Sdes * We are only interested in channels that can have buffered 198792559Sdes * incoming data. 198892559Sdes */ 198957429Smarkm if (compat13) { 199060573Skris if (c->type != SSH_CHANNEL_OPEN && 199160573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 199257429Smarkm continue; 199357429Smarkm } else { 199460573Skris if (c->type != SSH_CHANNEL_OPEN) 199557429Smarkm continue; 199657429Smarkm } 199760573Skris if (compat20 && 199860573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 199976262Sgreen /* XXX is this true? */ 200092559Sdes debug3("channel %d: will not send data after close", c->self); 200160573Skris continue; 200260573Skris } 200357429Smarkm 200457429Smarkm /* Get the amount of buffered data for this channel. */ 200576262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 200676262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 200776262Sgreen (len = buffer_len(&c->input)) > 0) { 2008157019Sdes if (c->datagram) { 2009157019Sdes if (len > 0) { 2010157019Sdes u_char *data; 2011157019Sdes u_int dlen; 2012157019Sdes 2013157019Sdes data = buffer_get_string(&c->input, 2014157019Sdes &dlen); 2015157019Sdes packet_start(SSH2_MSG_CHANNEL_DATA); 2016157019Sdes packet_put_int(c->remote_id); 2017157019Sdes packet_put_string(data, dlen); 2018157019Sdes packet_send(); 2019157019Sdes c->remote_window -= dlen + 4; 2020157019Sdes xfree(data); 2021157019Sdes } 2022157019Sdes continue; 2023157019Sdes } 202492559Sdes /* 202592559Sdes * Send some data for the other side over the secure 202692559Sdes * connection. 202792559Sdes */ 202860573Skris if (compat20) { 202960573Skris if (len > c->remote_window) 203060573Skris len = c->remote_window; 203160573Skris if (len > c->remote_maxpacket) 203260573Skris len = c->remote_maxpacket; 203357429Smarkm } else { 203460573Skris if (packet_is_interactive()) { 203560573Skris if (len > 1024) 203660573Skris len = 512; 203760573Skris } else { 203860573Skris /* Keep the packets at reasonable size. */ 203960573Skris if (len > packet_get_maxsize()/2) 204060573Skris len = packet_get_maxsize()/2; 204160573Skris } 204257429Smarkm } 204360573Skris if (len > 0) { 204460573Skris packet_start(compat20 ? 204560573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 204660573Skris packet_put_int(c->remote_id); 204760573Skris packet_put_string(buffer_ptr(&c->input), len); 204860573Skris packet_send(); 204960573Skris buffer_consume(&c->input, len); 205060573Skris c->remote_window -= len; 205160573Skris } 205260573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 205357429Smarkm if (compat13) 205457429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 205557429Smarkm /* 205657429Smarkm * input-buffer is empty and read-socket shutdown: 205798684Sdes * tell peer, that we will not send more data: send IEOF. 205898684Sdes * hack for extended data: delay EOF if EFD still in use. 205957429Smarkm */ 206098684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 2061149753Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 2062149753Sdes c->self, c->efd, buffer_len(&c->extended)); 206398684Sdes else 206498684Sdes chan_ibuf_empty(c); 206557429Smarkm } 206660573Skris /* Send extended data, i.e. stderr */ 206760573Skris if (compat20 && 206898684Sdes !(c->flags & CHAN_EOF_SENT) && 206960573Skris c->remote_window > 0 && 207060573Skris (len = buffer_len(&c->extended)) > 0 && 207160573Skris c->extended_usage == CHAN_EXTENDED_READ) { 207299063Sdes debug2("channel %d: rwin %u elen %u euse %d", 207376262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 207476262Sgreen c->extended_usage); 207560573Skris if (len > c->remote_window) 207660573Skris len = c->remote_window; 207760573Skris if (len > c->remote_maxpacket) 207860573Skris len = c->remote_maxpacket; 207960573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 208060573Skris packet_put_int(c->remote_id); 208160573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 208260573Skris packet_put_string(buffer_ptr(&c->extended), len); 208360573Skris packet_send(); 208460573Skris buffer_consume(&c->extended, len); 208560573Skris c->remote_window -= len; 208676262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 208760573Skris } 208857429Smarkm } 208957429Smarkm} 209057429Smarkm 209157429Smarkm 209292559Sdes/* -- protocol input */ 209392559Sdes 2094162856Sdes/* ARGSUSED */ 209560573Skrisvoid 209692559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 209757429Smarkm{ 209857429Smarkm int id; 209957429Smarkm char *data; 210076262Sgreen u_int data_len; 210160573Skris Channel *c; 210257429Smarkm 210357429Smarkm /* Get the channel number and verify it. */ 210457429Smarkm id = packet_get_int(); 210560573Skris c = channel_lookup(id); 210660573Skris if (c == NULL) 210757429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 210857429Smarkm 210957429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 211060573Skris if (c->type != SSH_CHANNEL_OPEN && 211160573Skris c->type != SSH_CHANNEL_X11_OPEN) 211257429Smarkm return; 211357429Smarkm 211457429Smarkm /* Get the data. */ 2115181111Sdes data = packet_get_string_ptr(&data_len); 211660573Skris 2117126273Sdes /* 2118126273Sdes * Ignore data for protocol > 1.3 if output end is no longer open. 2119126273Sdes * For protocol 2 the sending side is reducing its window as it sends 2120126273Sdes * data, so we must 'fake' consumption of the data in order to ensure 2121126273Sdes * that window updates are sent back. Otherwise the connection might 2122126273Sdes * deadlock. 2123126273Sdes */ 2124126273Sdes if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2125126273Sdes if (compat20) { 2126126273Sdes c->local_window -= data_len; 2127126273Sdes c->local_consumed += data_len; 2128126273Sdes } 2129126273Sdes return; 2130126273Sdes } 2131126273Sdes 213292559Sdes if (compat20) { 213360573Skris if (data_len > c->local_maxpacket) { 2134124207Sdes logit("channel %d: rcvd big packet %d, maxpack %d", 213560573Skris c->self, data_len, c->local_maxpacket); 213660573Skris } 213760573Skris if (data_len > c->local_window) { 2138124207Sdes logit("channel %d: rcvd too much data %d, win %d", 213960573Skris c->self, data_len, c->local_window); 214060573Skris return; 214160573Skris } 214260573Skris c->local_window -= data_len; 214360573Skris } 2144157019Sdes if (c->datagram) 2145157019Sdes buffer_put_string(&c->output, data, data_len); 2146157019Sdes else 2147157019Sdes buffer_append(&c->output, data, data_len); 2148181111Sdes packet_check_eom(); 214957429Smarkm} 215092559Sdes 2151162856Sdes/* ARGSUSED */ 215260573Skrisvoid 215392559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 215460573Skris{ 215560573Skris int id; 215660573Skris char *data; 215799063Sdes u_int data_len, tcode; 215860573Skris Channel *c; 215957429Smarkm 216060573Skris /* Get the channel number and verify it. */ 216160573Skris id = packet_get_int(); 216260573Skris c = channel_lookup(id); 216360573Skris 216460573Skris if (c == NULL) 216560573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 216660573Skris if (c->type != SSH_CHANNEL_OPEN) { 2167124207Sdes logit("channel %d: ext data for non open", id); 216860573Skris return; 216960573Skris } 217098684Sdes if (c->flags & CHAN_EOF_RCVD) { 217198684Sdes if (datafellows & SSH_BUG_EXTEOF) 217298684Sdes debug("channel %d: accepting ext data after eof", id); 217398684Sdes else 217498684Sdes packet_disconnect("Received extended_data after EOF " 217598684Sdes "on channel %d.", id); 217698684Sdes } 217760573Skris tcode = packet_get_int(); 217860573Skris if (c->efd == -1 || 217960573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 218060573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 2181124207Sdes logit("channel %d: bad ext data", c->self); 218260573Skris return; 218360573Skris } 218460573Skris data = packet_get_string(&data_len); 218592559Sdes packet_check_eom(); 218660573Skris if (data_len > c->local_window) { 2187124207Sdes logit("channel %d: rcvd too much extended_data %d, win %d", 218860573Skris c->self, data_len, c->local_window); 218960573Skris xfree(data); 219060573Skris return; 219160573Skris } 219269587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 219360573Skris c->local_window -= data_len; 219460573Skris buffer_append(&c->extended, data, data_len); 219560573Skris xfree(data); 219660573Skris} 219760573Skris 2198162856Sdes/* ARGSUSED */ 219960573Skrisvoid 220092559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 220160573Skris{ 220260573Skris int id; 220360573Skris Channel *c; 220457429Smarkm 220560573Skris id = packet_get_int(); 220692559Sdes packet_check_eom(); 220760573Skris c = channel_lookup(id); 220860573Skris if (c == NULL) 220960573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 221060573Skris chan_rcvd_ieof(c); 221192559Sdes 221292559Sdes /* XXX force input close */ 221392559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 221492559Sdes debug("channel %d: FORCE input drain", c->self); 221592559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 221692559Sdes if (buffer_len(&c->input) == 0) 221792559Sdes chan_ibuf_empty(c); 221892559Sdes } 221992559Sdes 222060573Skris} 222160573Skris 2222162856Sdes/* ARGSUSED */ 222360573Skrisvoid 222492559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 222557429Smarkm{ 222660573Skris int id; 222760573Skris Channel *c; 222857429Smarkm 222960573Skris id = packet_get_int(); 223092559Sdes packet_check_eom(); 223160573Skris c = channel_lookup(id); 223260573Skris if (c == NULL) 223360573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 223457429Smarkm 223557429Smarkm /* 223657429Smarkm * Send a confirmation that we have closed the channel and no more 223757429Smarkm * data is coming for it. 223857429Smarkm */ 223957429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 224060573Skris packet_put_int(c->remote_id); 224157429Smarkm packet_send(); 224257429Smarkm 224357429Smarkm /* 224457429Smarkm * If the channel is in closed state, we have sent a close request, 224557429Smarkm * and the other side will eventually respond with a confirmation. 224657429Smarkm * Thus, we cannot free the channel here, because then there would be 224757429Smarkm * no-one to receive the confirmation. The channel gets freed when 224857429Smarkm * the confirmation arrives. 224957429Smarkm */ 225060573Skris if (c->type != SSH_CHANNEL_CLOSED) { 225157429Smarkm /* 225257429Smarkm * Not a closed channel - mark it as draining, which will 225357429Smarkm * cause it to be freed later. 225457429Smarkm */ 225592559Sdes buffer_clear(&c->input); 225660573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 225757429Smarkm } 225857429Smarkm} 225957429Smarkm 226060573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2261162856Sdes/* ARGSUSED */ 226260573Skrisvoid 226392559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 226460573Skris{ 226560573Skris int id = packet_get_int(); 226660573Skris Channel *c = channel_lookup(id); 226792559Sdes 226892559Sdes packet_check_eom(); 226960573Skris if (c == NULL) 227060573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 227160573Skris chan_rcvd_oclose(c); 227260573Skris} 227357429Smarkm 2274162856Sdes/* ARGSUSED */ 227560573Skrisvoid 227692559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 227757429Smarkm{ 227860573Skris int id = packet_get_int(); 227960573Skris Channel *c = channel_lookup(id); 228057429Smarkm 228192559Sdes packet_check_eom(); 228260573Skris if (c == NULL) 228360573Skris packet_disconnect("Received close confirmation for " 228460573Skris "out-of-range channel %d.", id); 228560573Skris if (c->type != SSH_CHANNEL_CLOSED) 228660573Skris packet_disconnect("Received close confirmation for " 228760573Skris "non-closed channel %d (type %d).", id, c->type); 228892559Sdes channel_free(c); 228960573Skris} 229057429Smarkm 2291162856Sdes/* ARGSUSED */ 229260573Skrisvoid 229392559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 229460573Skris{ 229560573Skris int id, remote_id; 229660573Skris Channel *c; 229760573Skris 229860573Skris id = packet_get_int(); 229960573Skris c = channel_lookup(id); 230060573Skris 230160573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 230260573Skris packet_disconnect("Received open confirmation for " 230360573Skris "non-opening channel %d.", id); 230460573Skris remote_id = packet_get_int(); 230560573Skris /* Record the remote channel number and mark that the channel is now open. */ 230660573Skris c->remote_id = remote_id; 230760573Skris c->type = SSH_CHANNEL_OPEN; 230860573Skris 230960573Skris if (compat20) { 231060573Skris c->remote_window = packet_get_int(); 231160573Skris c->remote_maxpacket = packet_get_int(); 2312181111Sdes if (c->open_confirm) { 231369587Sgreen debug2("callback start"); 2314181111Sdes c->open_confirm(c->self, c->open_confirm_ctx); 231569587Sgreen debug2("callback done"); 231660573Skris } 2317124207Sdes debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 231860573Skris c->remote_window, c->remote_maxpacket); 231957429Smarkm } 232092559Sdes packet_check_eom(); 232157429Smarkm} 232257429Smarkm 232392559Sdesstatic char * 232492559Sdesreason2txt(int reason) 232592559Sdes{ 232692559Sdes switch (reason) { 232792559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 232892559Sdes return "administratively prohibited"; 232992559Sdes case SSH2_OPEN_CONNECT_FAILED: 233092559Sdes return "connect failed"; 233192559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 233292559Sdes return "unknown channel type"; 233392559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 233492559Sdes return "resource shortage"; 233592559Sdes } 233692559Sdes return "unknown reason"; 233792559Sdes} 233892559Sdes 2339162856Sdes/* ARGSUSED */ 234060573Skrisvoid 234192559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 234257429Smarkm{ 234376262Sgreen int id, reason; 234476262Sgreen char *msg = NULL, *lang = NULL; 234560573Skris Channel *c; 234657429Smarkm 234760573Skris id = packet_get_int(); 234860573Skris c = channel_lookup(id); 234957429Smarkm 235060573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 235160573Skris packet_disconnect("Received open failure for " 235260573Skris "non-opening channel %d.", id); 235360573Skris if (compat20) { 235476262Sgreen reason = packet_get_int(); 235592559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 235676262Sgreen msg = packet_get_string(NULL); 235776262Sgreen lang = packet_get_string(NULL); 235876262Sgreen } 2359124207Sdes logit("channel %d: open failed: %s%s%s", id, 236092559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 236176262Sgreen if (msg != NULL) 236276262Sgreen xfree(msg); 236376262Sgreen if (lang != NULL) 236476262Sgreen xfree(lang); 236560573Skris } 236692559Sdes packet_check_eom(); 2367192595Sdes /* Schedule the channel for cleanup/deletion. */ 2368192595Sdes chan_mark_dead(c); 236957429Smarkm} 237057429Smarkm 2371162856Sdes/* ARGSUSED */ 237260573Skrisvoid 237392559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 237460573Skris{ 237560573Skris Channel *c; 237699063Sdes int id; 237799063Sdes u_int adjust; 237857429Smarkm 237960573Skris if (!compat20) 238060573Skris return; 238160573Skris 238257429Smarkm /* Get the channel number and verify it. */ 238360573Skris id = packet_get_int(); 238460573Skris c = channel_lookup(id); 238557429Smarkm 2386157019Sdes if (c == NULL) { 2387157019Sdes logit("Received window adjust for non-open channel %d.", id); 238860573Skris return; 238960573Skris } 239060573Skris adjust = packet_get_int(); 239192559Sdes packet_check_eom(); 239299063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 239360573Skris c->remote_window += adjust; 239457429Smarkm} 239557429Smarkm 2396162856Sdes/* ARGSUSED */ 239760573Skrisvoid 239892559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 239957429Smarkm{ 240092559Sdes Channel *c = NULL; 240192559Sdes u_short host_port; 240292559Sdes char *host, *originator_string; 2403181111Sdes int remote_id; 240457429Smarkm 240592559Sdes remote_id = packet_get_int(); 240692559Sdes host = packet_get_string(NULL); 240792559Sdes host_port = packet_get_int(); 240857429Smarkm 240992559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 241092559Sdes originator_string = packet_get_string(NULL); 241192559Sdes } else { 241292559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 241392559Sdes } 241492559Sdes packet_check_eom(); 2415181111Sdes c = channel_connect_to(host, host_port, 2416181111Sdes "connected socket", originator_string); 2417124207Sdes xfree(originator_string); 2418181111Sdes xfree(host); 241992559Sdes if (c == NULL) { 242092559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 242192559Sdes packet_put_int(remote_id); 242292559Sdes packet_send(); 2423181111Sdes } else 2424181111Sdes c->remote_id = remote_id; 242557429Smarkm} 242657429Smarkm 2427181111Sdes/* ARGSUSED */ 2428181111Sdesvoid 2429181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2430181111Sdes{ 2431181111Sdes Channel *c; 2432181111Sdes struct channel_confirm *cc; 2433192595Sdes int id; 243457429Smarkm 2435181111Sdes /* Reset keepalive timeout */ 2436197679Sdes packet_set_alive_timeouts(0); 2437181111Sdes 2438192595Sdes id = packet_get_int(); 2439181111Sdes packet_check_eom(); 2440181111Sdes 2441192595Sdes debug2("channel_input_status_confirm: type %d id %d", type, id); 2442181111Sdes 2443192595Sdes if ((c = channel_lookup(id)) == NULL) { 2444192595Sdes logit("channel_input_status_confirm: %d: unknown", id); 2445181111Sdes return; 2446181111Sdes } 2447181111Sdes ; 2448181111Sdes if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2449181111Sdes return; 2450181111Sdes cc->cb(type, c, cc->ctx); 2451181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 2452181111Sdes bzero(cc, sizeof(*cc)); 2453181111Sdes xfree(cc); 2454181111Sdes} 2455181111Sdes 245692559Sdes/* -- tcp forwarding */ 245757429Smarkm 245892559Sdesvoid 245992559Sdeschannel_set_af(int af) 246076262Sgreen{ 246192559Sdes IPv4or6 = af; 246276262Sgreen} 246376262Sgreen 246492559Sdesstatic int 2465192595Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, 2466192595Sdes u_short listen_port, int *allocated_listen_port, 246792559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 246857429Smarkm{ 246992559Sdes Channel *c; 2470157019Sdes int sock, r, success = 0, wildcard = 0, is_client; 247157429Smarkm struct addrinfo hints, *ai, *aitop; 2472147005Sdes const char *host, *addr; 247357429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2474192595Sdes in_port_t *lport_p; 247557429Smarkm 247692559Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 247792559Sdes listen_addr : host_to_connect; 2478147005Sdes is_client = (type == SSH_CHANNEL_PORT_LISTENER); 247957429Smarkm 248092559Sdes if (host == NULL) { 248192559Sdes error("No forward host name."); 2482149753Sdes return 0; 248376262Sgreen } 2484192595Sdes if (strlen(host) >= NI_MAXHOST) { 248576262Sgreen error("Forward host name too long."); 2486149753Sdes return 0; 248776262Sgreen } 248876262Sgreen 248957429Smarkm /* 2490147005Sdes * Determine whether or not a port forward listens to loopback, 2491147005Sdes * specified address or wildcard. On the client, a specified bind 2492147005Sdes * address will always override gateway_ports. On the server, a 2493147005Sdes * gateway_ports of 1 (``yes'') will override the client's 2494147005Sdes * specification and force a wildcard bind, whereas a value of 2 2495147005Sdes * (``clientspecified'') will bind to whatever address the client 2496147005Sdes * asked for. 2497147005Sdes * 2498147005Sdes * Special-case listen_addrs are: 2499147005Sdes * 2500147005Sdes * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2501147005Sdes * "" (empty string), "*" -> wildcard v4/v6 2502147005Sdes * "localhost" -> loopback v4/v6 2503147005Sdes */ 2504147005Sdes addr = NULL; 2505147005Sdes if (listen_addr == NULL) { 2506147005Sdes /* No address specified: default to gateway_ports setting */ 2507147005Sdes if (gateway_ports) 2508147005Sdes wildcard = 1; 2509147005Sdes } else if (gateway_ports || is_client) { 2510147005Sdes if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2511181111Sdes strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2512147005Sdes *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2513147005Sdes (!is_client && gateway_ports == 1)) 2514147005Sdes wildcard = 1; 2515147005Sdes else if (strcmp(listen_addr, "localhost") != 0) 2516147005Sdes addr = listen_addr; 2517147005Sdes } 2518147005Sdes 2519147005Sdes debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", 2520147005Sdes type, wildcard, (addr == NULL) ? "NULL" : addr); 2521147005Sdes 2522147005Sdes /* 252357429Smarkm * getaddrinfo returns a loopback address if the hostname is 252457429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 252557429Smarkm */ 252657429Smarkm memset(&hints, 0, sizeof(hints)); 252757429Smarkm hints.ai_family = IPv4or6; 2528147005Sdes hints.ai_flags = wildcard ? AI_PASSIVE : 0; 252957429Smarkm hints.ai_socktype = SOCK_STREAM; 253076262Sgreen snprintf(strport, sizeof strport, "%d", listen_port); 2531147005Sdes if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2532147005Sdes if (addr == NULL) { 2533147005Sdes /* This really shouldn't happen */ 2534147005Sdes packet_disconnect("getaddrinfo: fatal error: %s", 2535181111Sdes ssh_gai_strerror(r)); 2536147005Sdes } else { 2537149753Sdes error("channel_setup_fwd_listener: " 2538181111Sdes "getaddrinfo(%.64s): %s", addr, 2539181111Sdes ssh_gai_strerror(r)); 2540147005Sdes } 2541149753Sdes return 0; 2542147005Sdes } 2543192595Sdes if (allocated_listen_port != NULL) 2544192595Sdes *allocated_listen_port = 0; 254557429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 2546192595Sdes switch (ai->ai_family) { 2547192595Sdes case AF_INET: 2548192595Sdes lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 2549192595Sdes sin_port; 2550192595Sdes break; 2551192595Sdes case AF_INET6: 2552192595Sdes lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 2553192595Sdes sin6_port; 2554192595Sdes break; 2555192595Sdes default: 255657429Smarkm continue; 2557192595Sdes } 2558192595Sdes /* 2559192595Sdes * If allocating a port for -R forwards, then use the 2560192595Sdes * same port for all address families. 2561192595Sdes */ 2562192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2563192595Sdes allocated_listen_port != NULL && *allocated_listen_port > 0) 2564192595Sdes *lport_p = htons(*allocated_listen_port); 2565192595Sdes 256657429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 256757429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 256892559Sdes error("channel_setup_fwd_listener: getnameinfo failed"); 256957429Smarkm continue; 257057429Smarkm } 257157429Smarkm /* Create a port to listen for the host. */ 2572124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 257357429Smarkm if (sock < 0) { 257457429Smarkm /* this is no error since kernel may not support ipv6 */ 257557429Smarkm verbose("socket: %.100s", strerror(errno)); 257657429Smarkm continue; 257757429Smarkm } 2578106130Sdes 2579157019Sdes channel_set_reuseaddr(sock); 2580157019Sdes 2581192595Sdes debug("Local forwarding listening on %s port %s.", 2582192595Sdes ntop, strport); 258357429Smarkm 258457429Smarkm /* Bind the socket to the address. */ 258557429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 258657429Smarkm /* address can be in use ipv6 address is already bound */ 258798941Sdes if (!ai->ai_next) 258898941Sdes error("bind: %.100s", strerror(errno)); 258998941Sdes else 259098941Sdes verbose("bind: %.100s", strerror(errno)); 259198941Sdes 259257429Smarkm close(sock); 259357429Smarkm continue; 259457429Smarkm } 259557429Smarkm /* Start listening for connections on the socket. */ 2596126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 259757429Smarkm error("listen: %.100s", strerror(errno)); 259857429Smarkm close(sock); 259957429Smarkm continue; 260057429Smarkm } 2601192595Sdes 2602192595Sdes /* 2603192595Sdes * listen_port == 0 requests a dynamically allocated port - 2604192595Sdes * record what we got. 2605192595Sdes */ 2606192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2607192595Sdes allocated_listen_port != NULL && 2608192595Sdes *allocated_listen_port == 0) { 2609192595Sdes *allocated_listen_port = get_sock_port(sock, 1); 2610192595Sdes debug("Allocated listen port %d", 2611192595Sdes *allocated_listen_port); 2612192595Sdes } 2613192595Sdes 261457429Smarkm /* Allocate a channel number for the socket. */ 261592559Sdes c = channel_new("port listener", type, sock, sock, -1, 261660573Skris CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2617124207Sdes 0, "port listener", 1); 2618192595Sdes c->path = xstrdup(host); 261992559Sdes c->host_port = port_to_connect; 262092559Sdes c->listening_port = listen_port; 262157429Smarkm success = 1; 262257429Smarkm } 262357429Smarkm if (success == 0) 262492559Sdes error("channel_setup_fwd_listener: cannot listen to port: %d", 262576262Sgreen listen_port); 262657429Smarkm freeaddrinfo(aitop); 262776262Sgreen return success; 262857429Smarkm} 262957429Smarkm 2630137019Sdesint 2631137019Sdeschannel_cancel_rport_listener(const char *host, u_short port) 2632137019Sdes{ 2633137019Sdes u_int i; 2634137019Sdes int found = 0; 2635137019Sdes 2636147005Sdes for (i = 0; i < channels_alloc; i++) { 2637137019Sdes Channel *c = channels[i]; 2638137019Sdes 2639137019Sdes if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && 2640192595Sdes strcmp(c->path, host) == 0 && c->listening_port == port) { 2641147005Sdes debug2("%s: close channel %d", __func__, i); 2642137019Sdes channel_free(c); 2643137019Sdes found = 1; 2644137019Sdes } 2645137019Sdes } 2646137019Sdes 2647137019Sdes return (found); 2648137019Sdes} 2649137019Sdes 265092559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 265192559Sdesint 2652147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, 265392559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 265492559Sdes{ 265592559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2656192595Sdes listen_host, listen_port, NULL, host_to_connect, port_to_connect, 2657147005Sdes gateway_ports); 265892559Sdes} 265992559Sdes 266092559Sdes/* protocol v2 remote port fwd, used by sshd */ 266192559Sdesint 266292559Sdeschannel_setup_remote_fwd_listener(const char *listen_address, 2663192595Sdes u_short listen_port, int *allocated_listen_port, int gateway_ports) 266492559Sdes{ 266592559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2666192595Sdes listen_address, listen_port, allocated_listen_port, 2667192595Sdes NULL, 0, gateway_ports); 266892559Sdes} 266992559Sdes 267057429Smarkm/* 267157429Smarkm * Initiate forwarding of connections to port "port" on remote host through 267257429Smarkm * the secure channel to host:port from local side. 267357429Smarkm */ 267457429Smarkm 2675162856Sdesint 2676147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port, 267776262Sgreen const char *host_to_connect, u_short port_to_connect) 267857429Smarkm{ 267992559Sdes int type, success = 0; 268076262Sgreen 268157429Smarkm /* Record locally that connection to this host/port is permitted. */ 268257429Smarkm if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 268357429Smarkm fatal("channel_request_remote_forwarding: too many forwards"); 268457429Smarkm 268557429Smarkm /* Send the forward request to the remote side. */ 268660573Skris if (compat20) { 2687147005Sdes const char *address_to_bind; 2688181111Sdes if (listen_host == NULL) { 2689181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2690181111Sdes address_to_bind = "127.0.0.1"; 2691181111Sdes else 2692181111Sdes address_to_bind = "localhost"; 2693181111Sdes } else if (*listen_host == '\0' || 2694181111Sdes strcmp(listen_host, "*") == 0) { 2695181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2696181111Sdes address_to_bind = "0.0.0.0"; 2697181111Sdes else 2698181111Sdes address_to_bind = ""; 2699181111Sdes } else 2700147005Sdes address_to_bind = listen_host; 2701147005Sdes 270260573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 270360573Skris packet_put_cstring("tcpip-forward"); 270498684Sdes packet_put_char(1); /* boolean: want reply */ 270560573Skris packet_put_cstring(address_to_bind); 270660573Skris packet_put_int(listen_port); 270776262Sgreen packet_send(); 270876262Sgreen packet_write_wait(); 270976262Sgreen /* Assume that server accepts the request */ 271076262Sgreen success = 1; 271160573Skris } else { 271260573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 271360573Skris packet_put_int(listen_port); 271460573Skris packet_put_cstring(host_to_connect); 271560573Skris packet_put_int(port_to_connect); 271660573Skris packet_send(); 271760573Skris packet_write_wait(); 271876262Sgreen 271976262Sgreen /* Wait for response from the remote side. */ 272092559Sdes type = packet_read(); 272176262Sgreen switch (type) { 272276262Sgreen case SSH_SMSG_SUCCESS: 272376262Sgreen success = 1; 272476262Sgreen break; 272576262Sgreen case SSH_SMSG_FAILURE: 272676262Sgreen break; 272776262Sgreen default: 272876262Sgreen /* Unknown packet */ 272976262Sgreen packet_disconnect("Protocol error for port forward request:" 273076262Sgreen "received packet type %d.", type); 273176262Sgreen } 273260573Skris } 273376262Sgreen if (success) { 273476262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 273576262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 273676262Sgreen permitted_opens[num_permitted_opens].listen_port = listen_port; 273776262Sgreen num_permitted_opens++; 273876262Sgreen } 2739162856Sdes return (success ? 0 : -1); 274057429Smarkm} 274157429Smarkm 274257429Smarkm/* 2743137019Sdes * Request cancellation of remote forwarding of connection host:port from 2744137019Sdes * local side. 2745137019Sdes */ 2746137019Sdesvoid 2747147005Sdeschannel_request_rforward_cancel(const char *host, u_short port) 2748137019Sdes{ 2749137019Sdes int i; 2750137019Sdes 2751137019Sdes if (!compat20) 2752137019Sdes return; 2753137019Sdes 2754137019Sdes for (i = 0; i < num_permitted_opens; i++) { 2755137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2756137019Sdes permitted_opens[i].listen_port == port) 2757137019Sdes break; 2758137019Sdes } 2759137019Sdes if (i >= num_permitted_opens) { 2760137019Sdes debug("%s: requested forward not found", __func__); 2761137019Sdes return; 2762137019Sdes } 2763137019Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 2764137019Sdes packet_put_cstring("cancel-tcpip-forward"); 2765137019Sdes packet_put_char(0); 2766147005Sdes packet_put_cstring(host == NULL ? "" : host); 2767137019Sdes packet_put_int(port); 2768137019Sdes packet_send(); 2769137019Sdes 2770137019Sdes permitted_opens[i].listen_port = 0; 2771137019Sdes permitted_opens[i].port_to_connect = 0; 2772157019Sdes xfree(permitted_opens[i].host_to_connect); 2773137019Sdes permitted_opens[i].host_to_connect = NULL; 2774137019Sdes} 2775137019Sdes 2776137019Sdes/* 277757429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 277857429Smarkm * listening for the port, and sends back a success reply (or disconnect 2779162856Sdes * message if there was an error). 278057429Smarkm */ 2781162856Sdesint 278260573Skrischannel_input_port_forward_request(int is_root, int gateway_ports) 278357429Smarkm{ 278457429Smarkm u_short port, host_port; 2785162856Sdes int success = 0; 278657429Smarkm char *hostname; 278757429Smarkm 278857429Smarkm /* Get arguments from the packet. */ 278957429Smarkm port = packet_get_int(); 279057429Smarkm hostname = packet_get_string(NULL); 279157429Smarkm host_port = packet_get_int(); 279257429Smarkm 279398941Sdes#ifndef HAVE_CYGWIN 279457429Smarkm /* 279557429Smarkm * Check that an unprivileged user is not trying to forward a 279657429Smarkm * privileged port. 279757429Smarkm */ 279857429Smarkm if (port < IPPORT_RESERVED && !is_root) 2799124207Sdes packet_disconnect( 2800124207Sdes "Requested forwarding of port %d but user is not root.", 2801124207Sdes port); 2802124207Sdes if (host_port == 0) 2803124207Sdes packet_disconnect("Dynamic forwarding denied."); 280498941Sdes#endif 2805124207Sdes 280676262Sgreen /* Initiate forwarding */ 2807162856Sdes success = channel_setup_local_fwd_listener(NULL, port, hostname, 2808147005Sdes host_port, gateway_ports); 280957429Smarkm 281057429Smarkm /* Free the argument string. */ 281157429Smarkm xfree(hostname); 2812162856Sdes 2813162856Sdes return (success ? 0 : -1); 281457429Smarkm} 281557429Smarkm 281676262Sgreen/* 281776262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 281876262Sgreen * usually called by the server, because the user could connect to any port 281976262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 282076262Sgreen */ 282176262Sgreenvoid 282292559Sdeschannel_permit_all_opens(void) 282376262Sgreen{ 282476262Sgreen if (num_permitted_opens == 0) 282576262Sgreen all_opens_permitted = 1; 282676262Sgreen} 282776262Sgreen 282876262Sgreenvoid 282976262Sgreenchannel_add_permitted_opens(char *host, int port) 283076262Sgreen{ 283176262Sgreen if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2832162856Sdes fatal("channel_add_permitted_opens: too many forwards"); 283376262Sgreen debug("allow port forwarding to host %s port %d", host, port); 283476262Sgreen 283576262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 283676262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 283776262Sgreen num_permitted_opens++; 283876262Sgreen 283976262Sgreen all_opens_permitted = 0; 284076262Sgreen} 284176262Sgreen 2842162856Sdesint 2843162856Sdeschannel_add_adm_permitted_opens(char *host, int port) 2844162856Sdes{ 2845162856Sdes if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2846162856Sdes fatal("channel_add_adm_permitted_opens: too many forwards"); 2847162856Sdes debug("config allows port forwarding to host %s port %d", host, port); 2848162856Sdes 2849162856Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect 2850162856Sdes = xstrdup(host); 2851162856Sdes permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 2852162856Sdes return ++num_adm_permitted_opens; 2853162856Sdes} 2854162856Sdes 285576262Sgreenvoid 285676262Sgreenchannel_clear_permitted_opens(void) 285776262Sgreen{ 285876262Sgreen int i; 285976262Sgreen 286076262Sgreen for (i = 0; i < num_permitted_opens; i++) 2861137019Sdes if (permitted_opens[i].host_to_connect != NULL) 2862137019Sdes xfree(permitted_opens[i].host_to_connect); 286376262Sgreen num_permitted_opens = 0; 2864162856Sdes} 286576262Sgreen 2866162856Sdesvoid 2867162856Sdeschannel_clear_adm_permitted_opens(void) 2868162856Sdes{ 2869162856Sdes int i; 2870162856Sdes 2871162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 2872162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 2873162856Sdes xfree(permitted_adm_opens[i].host_to_connect); 2874162856Sdes num_adm_permitted_opens = 0; 287576262Sgreen} 287676262Sgreen 2877181111Sdesvoid 2878181111Sdeschannel_print_adm_permitted_opens(void) 2879181111Sdes{ 2880181111Sdes int i; 2881181111Sdes 2882192595Sdes printf("permitopen"); 2883192595Sdes if (num_adm_permitted_opens == 0) { 2884192595Sdes printf(" any\n"); 2885192595Sdes return; 2886192595Sdes } 2887181111Sdes for (i = 0; i < num_adm_permitted_opens; i++) 2888181111Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 2889181111Sdes printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 2890181111Sdes permitted_adm_opens[i].port_to_connect); 2891192595Sdes printf("\n"); 2892181111Sdes} 2893181111Sdes 2894181111Sdes/* Try to start non-blocking connect to next host in cctx list */ 289592559Sdesstatic int 2896181111Sdesconnect_next(struct channel_connect *cctx) 289760573Skris{ 2898181111Sdes int sock, saved_errno; 289960573Skris char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 290060573Skris 2901181111Sdes for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 2902181111Sdes if (cctx->ai->ai_family != AF_INET && 2903181111Sdes cctx->ai->ai_family != AF_INET6) 290460573Skris continue; 2905181111Sdes if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 2906181111Sdes ntop, sizeof(ntop), strport, sizeof(strport), 2907181111Sdes NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2908181111Sdes error("connect_next: getnameinfo failed"); 290960573Skris continue; 291060573Skris } 2911181111Sdes if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 2912181111Sdes cctx->ai->ai_protocol)) == -1) { 2913181111Sdes if (cctx->ai->ai_next == NULL) 2914113911Sdes error("socket: %.100s", strerror(errno)); 2915113911Sdes else 2916113911Sdes verbose("socket: %.100s", strerror(errno)); 291760573Skris continue; 291860573Skris } 2919137019Sdes if (set_nonblock(sock) == -1) 2920137019Sdes fatal("%s: set_nonblock(%d)", __func__, sock); 2921181111Sdes if (connect(sock, cctx->ai->ai_addr, 2922181111Sdes cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 2923181111Sdes debug("connect_next: host %.100s ([%.100s]:%s): " 2924181111Sdes "%.100s", cctx->host, ntop, strport, 292560573Skris strerror(errno)); 2926181111Sdes saved_errno = errno; 292760573Skris close(sock); 2928181111Sdes errno = saved_errno; 292976262Sgreen continue; /* fail -- try next */ 293060573Skris } 2931181111Sdes debug("connect_next: host %.100s ([%.100s]:%s) " 2932181111Sdes "in progress, fd=%d", cctx->host, ntop, strport, sock); 2933181111Sdes cctx->ai = cctx->ai->ai_next; 2934181111Sdes set_nodelay(sock); 2935181111Sdes return sock; 2936181111Sdes } 2937181111Sdes return -1; 2938181111Sdes} 293960573Skris 2940181111Sdesstatic void 2941181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx) 2942181111Sdes{ 2943181111Sdes xfree(cctx->host); 2944181111Sdes if (cctx->aitop) 2945181111Sdes freeaddrinfo(cctx->aitop); 2946181111Sdes bzero(cctx, sizeof(*cctx)); 2947181111Sdes cctx->host = NULL; 2948181111Sdes cctx->ai = cctx->aitop = NULL; 2949181111Sdes} 2950181111Sdes 2951181111Sdes/* Return CONNECTING channel to remote host, port */ 2952181111Sdesstatic Channel * 2953181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname) 2954181111Sdes{ 2955181111Sdes struct addrinfo hints; 2956181111Sdes int gaierr; 2957181111Sdes int sock = -1; 2958181111Sdes char strport[NI_MAXSERV]; 2959181111Sdes struct channel_connect cctx; 2960181111Sdes Channel *c; 2961181111Sdes 2962181111Sdes memset(&cctx, 0, sizeof(cctx)); 2963181111Sdes memset(&hints, 0, sizeof(hints)); 2964181111Sdes hints.ai_family = IPv4or6; 2965181111Sdes hints.ai_socktype = SOCK_STREAM; 2966181111Sdes snprintf(strport, sizeof strport, "%d", port); 2967181111Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { 2968181111Sdes error("connect_to %.100s: unknown host (%s)", host, 2969181111Sdes ssh_gai_strerror(gaierr)); 2970181111Sdes return NULL; 297160573Skris } 2972181111Sdes 2973181111Sdes cctx.host = xstrdup(host); 2974181111Sdes cctx.port = port; 2975181111Sdes cctx.ai = cctx.aitop; 2976181111Sdes 2977181111Sdes if ((sock = connect_next(&cctx)) == -1) { 2978181111Sdes error("connect to %.100s port %d failed: %s", 2979181111Sdes host, port, strerror(errno)); 2980181111Sdes channel_connect_ctx_free(&cctx); 2981181111Sdes return NULL; 298260573Skris } 2983181111Sdes c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 2984181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 2985181111Sdes c->connect_ctx = cctx; 2986181111Sdes return c; 298760573Skris} 298876262Sgreen 2989181111SdesChannel * 2990181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) 299176262Sgreen{ 299276262Sgreen int i; 299376262Sgreen 2994181111Sdes for (i = 0; i < num_permitted_opens; i++) { 2995137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2996181111Sdes permitted_opens[i].listen_port == listen_port) { 299776262Sgreen return connect_to( 299876262Sgreen permitted_opens[i].host_to_connect, 2999181111Sdes permitted_opens[i].port_to_connect, ctype, rname); 3000181111Sdes } 3001181111Sdes } 300276262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 300376262Sgreen listen_port); 3004181111Sdes return NULL; 300576262Sgreen} 300676262Sgreen 300776262Sgreen/* Check if connecting to that port is permitted and connect. */ 3008181111SdesChannel * 3009181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname) 301076262Sgreen{ 3011162856Sdes int i, permit, permit_adm = 1; 301276262Sgreen 301376262Sgreen permit = all_opens_permitted; 301476262Sgreen if (!permit) { 301576262Sgreen for (i = 0; i < num_permitted_opens; i++) 3016137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3017137019Sdes permitted_opens[i].port_to_connect == port && 301876262Sgreen strcmp(permitted_opens[i].host_to_connect, host) == 0) 301976262Sgreen permit = 1; 3020162856Sdes } 302176262Sgreen 3022162856Sdes if (num_adm_permitted_opens > 0) { 3023162856Sdes permit_adm = 0; 3024162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3025162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL && 3026162856Sdes permitted_adm_opens[i].port_to_connect == port && 3027162856Sdes strcmp(permitted_adm_opens[i].host_to_connect, host) 3028162856Sdes == 0) 3029162856Sdes permit_adm = 1; 303076262Sgreen } 3031162856Sdes 3032162856Sdes if (!permit || !permit_adm) { 3033124207Sdes logit("Received request to connect to host %.100s port %d, " 303476262Sgreen "but the request was denied.", host, port); 3035181111Sdes return NULL; 303676262Sgreen } 3037181111Sdes return connect_to(host, port, ctype, rname); 303876262Sgreen} 303976262Sgreen 3040137019Sdesvoid 3041137019Sdeschannel_send_window_changes(void) 3042137019Sdes{ 3043137019Sdes u_int i; 3044137019Sdes struct winsize ws; 3045137019Sdes 3046137019Sdes for (i = 0; i < channels_alloc; i++) { 3047147005Sdes if (channels[i] == NULL || !channels[i]->client_tty || 3048137019Sdes channels[i]->type != SSH_CHANNEL_OPEN) 3049137019Sdes continue; 3050137019Sdes if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 3051137019Sdes continue; 3052137019Sdes channel_request_start(i, "window-change", 0); 3053162856Sdes packet_put_int((u_int)ws.ws_col); 3054162856Sdes packet_put_int((u_int)ws.ws_row); 3055162856Sdes packet_put_int((u_int)ws.ws_xpixel); 3056162856Sdes packet_put_int((u_int)ws.ws_ypixel); 3057137019Sdes packet_send(); 3058137019Sdes } 3059137019Sdes} 3060137019Sdes 306192559Sdes/* -- X11 forwarding */ 306257429Smarkm 306357429Smarkm/* 306457429Smarkm * Creates an internet domain socket for listening for X11 connections. 306599063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 306699063Sdes * stored in display_numberp , or -1 if an error occurs. 306757429Smarkm */ 306892559Sdesint 306992559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 3070149753Sdes int single_connection, u_int *display_numberp, int **chanids) 307157429Smarkm{ 307292559Sdes Channel *nc = NULL; 307357429Smarkm int display_number, sock; 307457429Smarkm u_short port; 307557429Smarkm struct addrinfo hints, *ai, *aitop; 307657429Smarkm char strport[NI_MAXSERV]; 307757429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 307857429Smarkm 3079157019Sdes if (chanids == NULL) 3080157019Sdes return -1; 3081157019Sdes 308257429Smarkm for (display_number = x11_display_offset; 308392559Sdes display_number < MAX_DISPLAYS; 308492559Sdes display_number++) { 308557429Smarkm port = 6000 + display_number; 308657429Smarkm memset(&hints, 0, sizeof(hints)); 308757429Smarkm hints.ai_family = IPv4or6; 308892559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 308957429Smarkm hints.ai_socktype = SOCK_STREAM; 309057429Smarkm snprintf(strport, sizeof strport, "%d", port); 309157429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 3092181111Sdes error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 309392559Sdes return -1; 309457429Smarkm } 309557429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 309657429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 309757429Smarkm continue; 3098124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, 3099124207Sdes ai->ai_protocol); 310057429Smarkm if (sock < 0) { 310198941Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 310298941Sdes error("socket: %.100s", strerror(errno)); 3103137019Sdes freeaddrinfo(aitop); 310498941Sdes return -1; 310598941Sdes } else { 310698941Sdes debug("x11_create_display_inet: Socket family %d not supported", 310798941Sdes ai->ai_family); 310898941Sdes continue; 310998941Sdes } 311057429Smarkm } 311198941Sdes#ifdef IPV6_V6ONLY 311298941Sdes if (ai->ai_family == AF_INET6) { 311398941Sdes int on = 1; 311498941Sdes if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 311598941Sdes error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 311698941Sdes } 311798941Sdes#endif 3118181111Sdes if (x11_use_localhost) 3119181111Sdes channel_set_reuseaddr(sock); 312057429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3121124207Sdes debug2("bind port %d: %.100s", port, strerror(errno)); 312257429Smarkm close(sock); 312398941Sdes 312457429Smarkm for (n = 0; n < num_socks; n++) { 312557429Smarkm close(socks[n]); 312657429Smarkm } 312757429Smarkm num_socks = 0; 312857429Smarkm break; 312957429Smarkm } 313057429Smarkm socks[num_socks++] = sock; 313157429Smarkm if (num_socks == NUM_SOCKS) 313257429Smarkm break; 313357429Smarkm } 313476262Sgreen freeaddrinfo(aitop); 313557429Smarkm if (num_socks > 0) 313657429Smarkm break; 313757429Smarkm } 313857429Smarkm if (display_number >= MAX_DISPLAYS) { 313957429Smarkm error("Failed to allocate internet-domain X11 display socket."); 314092559Sdes return -1; 314157429Smarkm } 314257429Smarkm /* Start listening for connections on the socket. */ 314357429Smarkm for (n = 0; n < num_socks; n++) { 314457429Smarkm sock = socks[n]; 3145126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 314657429Smarkm error("listen: %.100s", strerror(errno)); 314757429Smarkm close(sock); 314892559Sdes return -1; 314957429Smarkm } 315057429Smarkm } 315157429Smarkm 315257429Smarkm /* Allocate a channel for each socket. */ 3153162856Sdes *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 315457429Smarkm for (n = 0; n < num_socks; n++) { 315557429Smarkm sock = socks[n]; 315692559Sdes nc = channel_new("x11 listener", 315760573Skris SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 315860573Skris CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3159124207Sdes 0, "X11 inet listener", 1); 316092559Sdes nc->single_connection = single_connection; 3161157019Sdes (*chanids)[n] = nc->self; 316257429Smarkm } 3163157019Sdes (*chanids)[n] = -1; 316457429Smarkm 316592559Sdes /* Return the display number for the DISPLAY environment variable. */ 316699063Sdes *display_numberp = display_number; 316799063Sdes return (0); 316857429Smarkm} 316957429Smarkm 317092559Sdesstatic int 3171192595Sdesconnect_local_xsocket_path(const char *pathname) 317257429Smarkm{ 317357429Smarkm int sock; 317457429Smarkm struct sockaddr_un addr; 317557429Smarkm 317692559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 317792559Sdes if (sock < 0) 317892559Sdes error("socket: %.100s", strerror(errno)); 317992559Sdes memset(&addr, 0, sizeof(addr)); 318092559Sdes addr.sun_family = AF_UNIX; 3181192595Sdes strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 3182162856Sdes if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 318392559Sdes return sock; 318492559Sdes close(sock); 318557429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 318657429Smarkm return -1; 318757429Smarkm} 318857429Smarkm 3189192595Sdesstatic int 3190192595Sdesconnect_local_xsocket(u_int dnr) 3191192595Sdes{ 3192192595Sdes char buf[1024]; 3193192595Sdes snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 3194192595Sdes return connect_local_xsocket_path(buf); 3195192595Sdes} 3196192595Sdes 319760573Skrisint 319860573Skrisx11_connect_display(void) 319957429Smarkm{ 3200162856Sdes u_int display_number; 320157429Smarkm const char *display; 320260573Skris char buf[1024], *cp; 320357429Smarkm struct addrinfo hints, *ai, *aitop; 320457429Smarkm char strport[NI_MAXSERV]; 3205162856Sdes int gaierr, sock = 0; 320657429Smarkm 320757429Smarkm /* Try to open a socket for the local X server. */ 320857429Smarkm display = getenv("DISPLAY"); 320957429Smarkm if (!display) { 321057429Smarkm error("DISPLAY not set."); 321160573Skris return -1; 321257429Smarkm } 321357429Smarkm /* 321457429Smarkm * Now we decode the value of the DISPLAY variable and make a 321557429Smarkm * connection to the real X server. 321657429Smarkm */ 321757429Smarkm 3218192595Sdes /* Check if the display is from launchd. */ 3219192595Sdes#ifdef __APPLE__ 3220192595Sdes if (strncmp(display, "/tmp/launch", 11) == 0) { 3221192595Sdes sock = connect_local_xsocket_path(display); 3222192595Sdes if (sock < 0) 3223192595Sdes return -1; 3224192595Sdes 3225192595Sdes /* OK, we now have a connection to the display. */ 3226192595Sdes return sock; 3227192595Sdes } 3228192595Sdes#endif 322957429Smarkm /* 323057429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 323157429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 323257429Smarkm */ 323357429Smarkm if (strncmp(display, "unix:", 5) == 0 || 323457429Smarkm display[0] == ':') { 323557429Smarkm /* Connect to the unix domain socket. */ 3236162856Sdes if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 323757429Smarkm error("Could not parse display number from DISPLAY: %.100s", 323892559Sdes display); 323960573Skris return -1; 324057429Smarkm } 324157429Smarkm /* Create a socket. */ 324257429Smarkm sock = connect_local_xsocket(display_number); 324357429Smarkm if (sock < 0) 324460573Skris return -1; 324557429Smarkm 324657429Smarkm /* OK, we now have a connection to the display. */ 324760573Skris return sock; 324857429Smarkm } 324957429Smarkm /* 325057429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 325157429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 325257429Smarkm */ 325392559Sdes strlcpy(buf, display, sizeof(buf)); 325457429Smarkm cp = strchr(buf, ':'); 325557429Smarkm if (!cp) { 325657429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 325760573Skris return -1; 325857429Smarkm } 325957429Smarkm *cp = 0; 326057429Smarkm /* buf now contains the host name. But first we parse the display number. */ 3261162856Sdes if (sscanf(cp + 1, "%u", &display_number) != 1) { 326257429Smarkm error("Could not parse display number from DISPLAY: %.100s", 326392559Sdes display); 326460573Skris return -1; 326557429Smarkm } 326657429Smarkm 326757429Smarkm /* Look up the host address */ 326857429Smarkm memset(&hints, 0, sizeof(hints)); 326957429Smarkm hints.ai_family = IPv4or6; 327057429Smarkm hints.ai_socktype = SOCK_STREAM; 3271162856Sdes snprintf(strport, sizeof strport, "%u", 6000 + display_number); 327257429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 3273181111Sdes error("%.100s: unknown host. (%s)", buf, 3274181111Sdes ssh_gai_strerror(gaierr)); 327560573Skris return -1; 327657429Smarkm } 327757429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 327857429Smarkm /* Create a socket. */ 3279124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 328057429Smarkm if (sock < 0) { 3281124207Sdes debug2("socket: %.100s", strerror(errno)); 328260573Skris continue; 328360573Skris } 328460573Skris /* Connect it to the display. */ 328560573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3286162856Sdes debug2("connect %.100s port %u: %.100s", buf, 328760573Skris 6000 + display_number, strerror(errno)); 328860573Skris close(sock); 328960573Skris continue; 329060573Skris } 329160573Skris /* Success */ 329260573Skris break; 329357429Smarkm } 329457429Smarkm freeaddrinfo(aitop); 329557429Smarkm if (!ai) { 3296162856Sdes error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 329757429Smarkm strerror(errno)); 329860573Skris return -1; 329957429Smarkm } 330092559Sdes set_nodelay(sock); 330160573Skris return sock; 330260573Skris} 330357429Smarkm 330460573Skris/* 330560573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 330660573Skris * the remote channel number. We should do whatever we want, and respond 330760573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 330860573Skris */ 330957429Smarkm 3310162856Sdes/* ARGSUSED */ 331160573Skrisvoid 331292559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 331360573Skris{ 331492559Sdes Channel *c = NULL; 331592559Sdes int remote_id, sock = 0; 331660573Skris char *remote_host; 331757429Smarkm 331892559Sdes debug("Received X11 open request."); 331957429Smarkm 332092559Sdes remote_id = packet_get_int(); 332192559Sdes 332292559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 332392559Sdes remote_host = packet_get_string(NULL); 332460573Skris } else { 332560573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 332660573Skris } 332792559Sdes packet_check_eom(); 332860573Skris 332960573Skris /* Obtain a connection to the real X display. */ 333060573Skris sock = x11_connect_display(); 333192559Sdes if (sock != -1) { 333292559Sdes /* Allocate a channel for this connection. */ 333392559Sdes c = channel_new("connected x11 socket", 333492559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 333592559Sdes remote_host, 1); 333692559Sdes c->remote_id = remote_id; 333792559Sdes c->force_drain = 1; 333892559Sdes } 3339124207Sdes xfree(remote_host); 334092559Sdes if (c == NULL) { 334160573Skris /* Send refusal to the remote host. */ 334260573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 334392559Sdes packet_put_int(remote_id); 334460573Skris } else { 334560573Skris /* Send a confirmation to the remote host. */ 334660573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 334792559Sdes packet_put_int(remote_id); 334892559Sdes packet_put_int(c->self); 334960573Skris } 335092559Sdes packet_send(); 335157429Smarkm} 335257429Smarkm 335369587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 3354162856Sdes/* ARGSUSED */ 335569587Sgreenvoid 335692559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 335769587Sgreen{ 335869587Sgreen int rchan = packet_get_int(); 3359106130Sdes 336092559Sdes switch (type) { 336169587Sgreen case SSH_SMSG_AGENT_OPEN: 336269587Sgreen error("Warning: ssh server tried agent forwarding."); 336369587Sgreen break; 336469587Sgreen case SSH_SMSG_X11_OPEN: 336569587Sgreen error("Warning: ssh server tried X11 forwarding."); 336669587Sgreen break; 336769587Sgreen default: 336892559Sdes error("deny_input_open: type %d", type); 336969587Sgreen break; 337069587Sgreen } 3371157019Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 337269587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 337369587Sgreen packet_put_int(rchan); 337469587Sgreen packet_send(); 337569587Sgreen} 337669587Sgreen 337757429Smarkm/* 337857429Smarkm * Requests forwarding of X11 connections, generates fake authentication 337957429Smarkm * data, and enables authentication spoofing. 338092559Sdes * This should be called in the client only. 338157429Smarkm */ 338260573Skrisvoid 3383149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 338460573Skris const char *proto, const char *data) 338557429Smarkm{ 338676262Sgreen u_int data_len = (u_int) strlen(data) / 2; 3387149753Sdes u_int i, value; 338857429Smarkm char *new_data; 338957429Smarkm int screen_number; 339057429Smarkm const char *cp; 3391137019Sdes u_int32_t rnd = 0; 339257429Smarkm 3393149753Sdes if (x11_saved_display == NULL) 3394149753Sdes x11_saved_display = xstrdup(disp); 3395149753Sdes else if (strcmp(disp, x11_saved_display) != 0) { 3396149753Sdes error("x11_request_forwarding_with_spoofing: different " 3397149753Sdes "$DISPLAY already forwarded"); 3398149753Sdes return; 3399149753Sdes } 3400149753Sdes 3401162856Sdes cp = strchr(disp, ':'); 340257429Smarkm if (cp) 340357429Smarkm cp = strchr(cp, '.'); 340457429Smarkm if (cp) 3405162856Sdes screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 340657429Smarkm else 340757429Smarkm screen_number = 0; 340857429Smarkm 3409149753Sdes if (x11_saved_proto == NULL) { 3410149753Sdes /* Save protocol name. */ 3411149753Sdes x11_saved_proto = xstrdup(proto); 3412149753Sdes /* 3413149753Sdes * Extract real authentication data and generate fake data 3414149753Sdes * of the same length. 3415149753Sdes */ 3416149753Sdes x11_saved_data = xmalloc(data_len); 3417149753Sdes x11_fake_data = xmalloc(data_len); 3418149753Sdes for (i = 0; i < data_len; i++) { 3419149753Sdes if (sscanf(data + 2 * i, "%2x", &value) != 1) 3420149753Sdes fatal("x11_request_forwarding: bad " 3421149753Sdes "authentication data: %.100s", data); 3422149753Sdes if (i % 4 == 0) 3423149753Sdes rnd = arc4random(); 3424149753Sdes x11_saved_data[i] = value; 3425149753Sdes x11_fake_data[i] = rnd & 0xff; 3426149753Sdes rnd >>= 8; 3427149753Sdes } 3428149753Sdes x11_saved_data_len = data_len; 3429149753Sdes x11_fake_data_len = data_len; 343057429Smarkm } 343157429Smarkm 343257429Smarkm /* Convert the fake data into hex. */ 3433149753Sdes new_data = tohex(x11_fake_data, data_len); 343457429Smarkm 343557429Smarkm /* Send the request packet. */ 343660573Skris if (compat20) { 343760573Skris channel_request_start(client_session_id, "x11-req", 0); 343860573Skris packet_put_char(0); /* XXX bool single connection */ 343960573Skris } else { 344060573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 344160573Skris } 344260573Skris packet_put_cstring(proto); 344360573Skris packet_put_cstring(new_data); 344457429Smarkm packet_put_int(screen_number); 344557429Smarkm packet_send(); 344657429Smarkm packet_write_wait(); 344757429Smarkm xfree(new_data); 344857429Smarkm} 344957429Smarkm 345092559Sdes 345192559Sdes/* -- agent forwarding */ 345292559Sdes 345357429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 345457429Smarkm 345560573Skrisvoid 345692559Sdesauth_request_forwarding(void) 345757429Smarkm{ 345857429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 345957429Smarkm packet_send(); 346057429Smarkm packet_write_wait(); 346157429Smarkm} 3462