channels.c revision 207319
1204917Sdes/* $OpenBSD: channels.c,v 1.303 2010/01/30 21:12:08 djm Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * This file contains functions for generic socket connection forwarding. 757429Smarkm * There is also code for initiating connection forwarding for X11 connections, 857429Smarkm * arbitrary tcp/ip connections, and the authentication agent connection. 960573Skris * 1065668Skris * As far as I am concerned, the code I have written for this software 1165668Skris * can be used freely for any purpose. Any derived versions of this 1265668Skris * software must be clearly marked as such, and if the derived work is 1365668Skris * incompatible with the protocol description in the RFC file, it must be 1465668Skris * called by a name other than "ssh" or "Secure Shell". 1565668Skris * 1660573Skris * SSH2 support added by Markus Friedl. 1792559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 1865668Skris * Copyright (c) 1999 Dug Song. All rights reserved. 1965668Skris * Copyright (c) 1999 Theo de Raadt. All rights reserved. 2065668Skris * 2165668Skris * Redistribution and use in source and binary forms, with or without 2265668Skris * modification, are permitted provided that the following conditions 2365668Skris * are met: 2465668Skris * 1. Redistributions of source code must retain the above copyright 2565668Skris * notice, this list of conditions and the following disclaimer. 2665668Skris * 2. Redistributions in binary form must reproduce the above copyright 2765668Skris * notice, this list of conditions and the following disclaimer in the 2865668Skris * documentation and/or other materials provided with the distribution. 2965668Skris * 3065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 3165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4057429Smarkm */ 4157429Smarkm 4257429Smarkm#include "includes.h" 4357429Smarkm 44162856Sdes#include <sys/types.h> 45162856Sdes#include <sys/ioctl.h> 46162856Sdes#include <sys/un.h> 47162856Sdes#include <sys/socket.h> 48162856Sdes#ifdef HAVE_SYS_TIME_H 49162856Sdes# include <sys/time.h> 50162856Sdes#endif 51162856Sdes 52162856Sdes#include <netinet/in.h> 53162856Sdes#include <arpa/inet.h> 54162856Sdes 55162856Sdes#include <errno.h> 56204917Sdes#include <fcntl.h> 57162856Sdes#include <netdb.h> 58162856Sdes#include <stdio.h> 59162856Sdes#include <stdlib.h> 60162856Sdes#include <string.h> 61162856Sdes#include <termios.h> 62162856Sdes#include <unistd.h> 63162856Sdes#include <stdarg.h> 64162856Sdes 65181111Sdes#include "openbsd-compat/sys-queue.h" 66162856Sdes#include "xmalloc.h" 6757429Smarkm#include "ssh.h" 6876262Sgreen#include "ssh1.h" 6976262Sgreen#include "ssh2.h" 7057429Smarkm#include "packet.h" 7176262Sgreen#include "log.h" 7276262Sgreen#include "misc.h" 73162856Sdes#include "buffer.h" 7457429Smarkm#include "channels.h" 7557429Smarkm#include "compat.h" 7676262Sgreen#include "canohost.h" 7765668Skris#include "key.h" 7865668Skris#include "authfd.h" 7992559Sdes#include "pathnames.h" 8065668Skris 8192559Sdes/* -- channel core */ 8257429Smarkm 8357429Smarkm/* 8457429Smarkm * Pointer to an array containing all allocated channels. The array is 8557429Smarkm * dynamically extended as needed. 8657429Smarkm */ 8792559Sdesstatic Channel **channels = NULL; 8857429Smarkm 8957429Smarkm/* 9057429Smarkm * Size of the channel array. All slots of the array must always be 9192559Sdes * initialized (at least the type field); unused slots set to NULL 9257429Smarkm */ 93137019Sdesstatic u_int channels_alloc = 0; 9457429Smarkm 9557429Smarkm/* 9657429Smarkm * Maximum file descriptor value used in any of the channels. This is 9792559Sdes * updated in channel_new. 9857429Smarkm */ 9976262Sgreenstatic int channel_max_fd = 0; 10057429Smarkm 10157429Smarkm 10292559Sdes/* -- tcp forwarding */ 10357429Smarkm 10457429Smarkm/* 10557429Smarkm * Data structure for storing which hosts are permitted for forward requests. 10657429Smarkm * The local sides of any remote forwards are stored in this array to prevent 10757429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 10857429Smarkm * network (which might be behind a firewall). 10957429Smarkm */ 11057429Smarkmtypedef struct { 11160573Skris char *host_to_connect; /* Connect to 'host'. */ 11260573Skris u_short port_to_connect; /* Connect to 'port'. */ 11360573Skris u_short listen_port; /* Remote side should listen port number. */ 11457429Smarkm} ForwardPermission; 11557429Smarkm 116162856Sdes/* List of all permitted host/port pairs to connect by the user. */ 11757429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 11892559Sdes 119162856Sdes/* List of all permitted host/port pairs to connect by the admin. */ 120162856Sdesstatic ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 121162856Sdes 122162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */ 12357429Smarkmstatic int num_permitted_opens = 0; 124162856Sdes 125162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */ 126162856Sdesstatic int num_adm_permitted_opens = 0; 127162856Sdes 12857429Smarkm/* 12957429Smarkm * If this is true, all opens are permitted. This is the case on the server 13057429Smarkm * on which we have to trust the client anyway, and the user could do 13157429Smarkm * anything after logging in anyway. 13257429Smarkm */ 13357429Smarkmstatic int all_opens_permitted = 0; 13457429Smarkm 13557429Smarkm 13692559Sdes/* -- X11 forwarding */ 13792559Sdes 13892559Sdes/* Maximum number of fake X11 displays to try. */ 13992559Sdes#define MAX_DISPLAYS 1000 14092559Sdes 141149753Sdes/* Saved X11 local (client) display. */ 142149753Sdesstatic char *x11_saved_display = NULL; 143149753Sdes 14492559Sdes/* Saved X11 authentication protocol name. */ 14592559Sdesstatic char *x11_saved_proto = NULL; 14692559Sdes 14792559Sdes/* Saved X11 authentication data. This is the real data. */ 14892559Sdesstatic char *x11_saved_data = NULL; 14992559Sdesstatic u_int x11_saved_data_len = 0; 15092559Sdes 15192559Sdes/* 15292559Sdes * Fake X11 authentication data. This is what the server will be sending us; 15392559Sdes * we should replace any occurrences of this by the real data. 15492559Sdes */ 155162856Sdesstatic u_char *x11_fake_data = NULL; 15692559Sdesstatic u_int x11_fake_data_len; 15792559Sdes 15892559Sdes 15992559Sdes/* -- agent forwarding */ 16092559Sdes 16192559Sdes#define NUM_SOCKS 10 16292559Sdes 16376262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */ 16498941Sdesstatic int IPv4or6 = AF_UNSPEC; 16576262Sgreen 16692559Sdes/* helper */ 16792559Sdesstatic void port_open_helper(Channel *c, char *rtype); 16876262Sgreen 169181111Sdes/* non-blocking connect helpers */ 170181111Sdesstatic int connect_next(struct channel_connect *); 171181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *); 172181111Sdes 17392559Sdes/* -- channel core */ 17457429Smarkm 17560573SkrisChannel * 176157019Sdeschannel_by_id(int id) 17760573Skris{ 17860573Skris Channel *c; 17992559Sdes 180137019Sdes if (id < 0 || (u_int)id >= channels_alloc) { 181157019Sdes logit("channel_by_id: %d: bad id", id); 18260573Skris return NULL; 18360573Skris } 18492559Sdes c = channels[id]; 18592559Sdes if (c == NULL) { 186157019Sdes logit("channel_by_id: %d: bad id: channel free", id); 18760573Skris return NULL; 18860573Skris } 18960573Skris return c; 19060573Skris} 19160573Skris 19257429Smarkm/* 193157019Sdes * Returns the channel if it is allowed to receive protocol messages. 194157019Sdes * Private channels, like listening sockets, may not receive messages. 195157019Sdes */ 196157019SdesChannel * 197157019Sdeschannel_lookup(int id) 198157019Sdes{ 199157019Sdes Channel *c; 200157019Sdes 201157019Sdes if ((c = channel_by_id(id)) == NULL) 202157019Sdes return (NULL); 203157019Sdes 204162856Sdes switch (c->type) { 205157019Sdes case SSH_CHANNEL_X11_OPEN: 206157019Sdes case SSH_CHANNEL_LARVAL: 207157019Sdes case SSH_CHANNEL_CONNECTING: 208157019Sdes case SSH_CHANNEL_DYNAMIC: 209157019Sdes case SSH_CHANNEL_OPENING: 210157019Sdes case SSH_CHANNEL_OPEN: 211157019Sdes case SSH_CHANNEL_INPUT_DRAINING: 212157019Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 213157019Sdes return (c); 214157019Sdes } 215157019Sdes logit("Non-public channel %d, type %d.", id, c->type); 216157019Sdes return (NULL); 217157019Sdes} 218157019Sdes 219157019Sdes/* 22060573Skris * Register filedescriptors for a channel, used when allocating a channel or 22160573Skris * when the channel consumer/producer is ready, e.g. shell exec'd 22260573Skris */ 22392559Sdesstatic void 22469587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd, 225181111Sdes int extusage, int nonblock, int is_tty) 22660573Skris{ 22760573Skris /* Update the maximum file descriptor value. */ 22876262Sgreen channel_max_fd = MAX(channel_max_fd, rfd); 22976262Sgreen channel_max_fd = MAX(channel_max_fd, wfd); 23076262Sgreen channel_max_fd = MAX(channel_max_fd, efd); 23176262Sgreen 232204917Sdes if (rfd != -1) 233204917Sdes fcntl(rfd, F_SETFD, FD_CLOEXEC); 234204917Sdes if (wfd != -1 && wfd != rfd) 235204917Sdes fcntl(wfd, F_SETFD, FD_CLOEXEC); 236204917Sdes if (efd != -1 && efd != rfd && efd != wfd) 237204917Sdes fcntl(efd, F_SETFD, FD_CLOEXEC); 23860573Skris 23960573Skris c->rfd = rfd; 24060573Skris c->wfd = wfd; 24160573Skris c->sock = (rfd == wfd) ? rfd : -1; 24260573Skris c->efd = efd; 24360573Skris c->extended_usage = extusage; 24469587Sgreen 245181111Sdes if ((c->isatty = is_tty) != 0) 246124207Sdes debug2("channel %d: rfd %d isatty", c->self, c->rfd); 247181111Sdes c->wfd_isatty = is_tty || isatty(c->wfd); 24874500Sgreen 24969587Sgreen /* enable nonblocking mode */ 25069587Sgreen if (nonblock) { 25169587Sgreen if (rfd != -1) 25269587Sgreen set_nonblock(rfd); 25369587Sgreen if (wfd != -1) 25469587Sgreen set_nonblock(wfd); 25569587Sgreen if (efd != -1) 25669587Sgreen set_nonblock(efd); 25769587Sgreen } 25860573Skris} 25960573Skris 26060573Skris/* 26157429Smarkm * Allocate a new channel object and set its type and socket. This will cause 26257429Smarkm * remote_name to be freed. 26357429Smarkm */ 26492559SdesChannel * 26560573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd, 26699063Sdes u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 26757429Smarkm{ 268137019Sdes int found; 269137019Sdes u_int i; 27057429Smarkm Channel *c; 27157429Smarkm 27257429Smarkm /* Do initial allocation if this is the first call. */ 27357429Smarkm if (channels_alloc == 0) { 27457429Smarkm channels_alloc = 10; 275162856Sdes channels = xcalloc(channels_alloc, sizeof(Channel *)); 27657429Smarkm for (i = 0; i < channels_alloc; i++) 27792559Sdes channels[i] = NULL; 27857429Smarkm } 27957429Smarkm /* Try to find a free slot where to put the new channel. */ 28057429Smarkm for (found = -1, i = 0; i < channels_alloc; i++) 28192559Sdes if (channels[i] == NULL) { 28257429Smarkm /* Found a free slot. */ 283137019Sdes found = (int)i; 28457429Smarkm break; 28557429Smarkm } 286137019Sdes if (found < 0) { 28757429Smarkm /* There are no free slots. Take last+1 slot and expand the array. */ 28857429Smarkm found = channels_alloc; 28999063Sdes if (channels_alloc > 10000) 29099063Sdes fatal("channel_new: internal error: channels_alloc %d " 29199063Sdes "too big.", channels_alloc); 292162856Sdes channels = xrealloc(channels, channels_alloc + 10, 293162856Sdes sizeof(Channel *)); 294120489Sjoe channels_alloc += 10; 29569587Sgreen debug2("channel: expanding %d", channels_alloc); 29657429Smarkm for (i = found; i < channels_alloc; i++) 29792559Sdes channels[i] = NULL; 29857429Smarkm } 29992559Sdes /* Initialize and return new channel. */ 300162856Sdes c = channels[found] = xcalloc(1, sizeof(Channel)); 30157429Smarkm buffer_init(&c->input); 30257429Smarkm buffer_init(&c->output); 30360573Skris buffer_init(&c->extended); 304192595Sdes c->path = NULL; 30592559Sdes c->ostate = CHAN_OUTPUT_OPEN; 30692559Sdes c->istate = CHAN_INPUT_OPEN; 30792559Sdes c->flags = 0; 308181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 30957429Smarkm c->self = found; 31057429Smarkm c->type = type; 31160573Skris c->ctype = ctype; 31260573Skris c->local_window = window; 31360573Skris c->local_window_max = window; 31460573Skris c->local_consumed = 0; 31560573Skris c->local_maxpacket = maxpack; 31657429Smarkm c->remote_id = -1; 317124207Sdes c->remote_name = xstrdup(remote_name); 31860573Skris c->remote_window = 0; 31960573Skris c->remote_maxpacket = 0; 32092559Sdes c->force_drain = 0; 32192559Sdes c->single_connection = 0; 32292559Sdes c->detach_user = NULL; 323157019Sdes c->detach_close = 0; 324181111Sdes c->open_confirm = NULL; 325181111Sdes c->open_confirm_ctx = NULL; 32665668Skris c->input_filter = NULL; 327157019Sdes c->output_filter = NULL; 328181111Sdes c->filter_ctx = NULL; 329181111Sdes c->filter_cleanup = NULL; 330204917Sdes c->ctl_chan = -1; 331204917Sdes c->mux_rcb = NULL; 332204917Sdes c->mux_ctx = NULL; 333204917Sdes c->delayed = 1; /* prevent call to channel_post handler */ 334181111Sdes TAILQ_INIT(&c->status_confirms); 33557429Smarkm debug("channel %d: new [%s]", found, remote_name); 33692559Sdes return c; 33757429Smarkm} 33892559Sdes 33992559Sdesstatic int 34092559Sdeschannel_find_maxfd(void) 34192559Sdes{ 342137019Sdes u_int i; 343137019Sdes int max = 0; 34492559Sdes Channel *c; 34592559Sdes 34692559Sdes for (i = 0; i < channels_alloc; i++) { 34792559Sdes c = channels[i]; 34892559Sdes if (c != NULL) { 34992559Sdes max = MAX(max, c->rfd); 35092559Sdes max = MAX(max, c->wfd); 35192559Sdes max = MAX(max, c->efd); 35292559Sdes } 35392559Sdes } 35492559Sdes return max; 35592559Sdes} 35692559Sdes 35760573Skrisint 35892559Sdeschannel_close_fd(int *fdp) 35960573Skris{ 36092559Sdes int ret = 0, fd = *fdp; 36192559Sdes 36292559Sdes if (fd != -1) { 36392559Sdes ret = close(fd); 36492559Sdes *fdp = -1; 36592559Sdes if (fd == channel_max_fd) 36692559Sdes channel_max_fd = channel_find_maxfd(); 36792559Sdes } 36892559Sdes return ret; 36960573Skris} 37057429Smarkm 37160573Skris/* Close all channel fd/socket. */ 37292559Sdesstatic void 37360573Skrischannel_close_fds(Channel *c) 37457429Smarkm{ 375204917Sdes debug3("channel %d: close_fds r %d w %d e %d", 376204917Sdes c->self, c->rfd, c->wfd, c->efd); 37792559Sdes 37892559Sdes channel_close_fd(&c->sock); 37992559Sdes channel_close_fd(&c->rfd); 38092559Sdes channel_close_fd(&c->wfd); 38192559Sdes channel_close_fd(&c->efd); 38260573Skris} 38357429Smarkm 38460573Skris/* Free the channel and close its fd/socket. */ 38560573Skrisvoid 38692559Sdeschannel_free(Channel *c) 38760573Skris{ 38892559Sdes char *s; 389137019Sdes u_int i, n; 390181111Sdes struct channel_confirm *cc; 39176262Sgreen 39292559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 39392559Sdes if (channels[i]) 39492559Sdes n++; 395137019Sdes debug("channel %d: free: %s, nchannels %u", c->self, 39692559Sdes c->remote_name ? c->remote_name : "???", n); 39792559Sdes 39892559Sdes s = channel_open_message(); 399124207Sdes debug3("channel %d: status: %s", c->self, s); 40076262Sgreen xfree(s); 40176262Sgreen 40260573Skris if (c->sock != -1) 40360573Skris shutdown(c->sock, SHUT_RDWR); 40460573Skris channel_close_fds(c); 40560573Skris buffer_free(&c->input); 40660573Skris buffer_free(&c->output); 40760573Skris buffer_free(&c->extended); 40860573Skris if (c->remote_name) { 40960573Skris xfree(c->remote_name); 41060573Skris c->remote_name = NULL; 41160573Skris } 412192595Sdes if (c->path) { 413192595Sdes xfree(c->path); 414192595Sdes c->path = NULL; 415192595Sdes } 416181111Sdes while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 417181111Sdes if (cc->abandon_cb != NULL) 418181111Sdes cc->abandon_cb(c, cc->ctx); 419181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 420181111Sdes bzero(cc, sizeof(*cc)); 421181111Sdes xfree(cc); 422181111Sdes } 423181111Sdes if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 424181111Sdes c->filter_cleanup(c->self, c->filter_ctx); 42592559Sdes channels[c->self] = NULL; 42692559Sdes xfree(c); 42757429Smarkm} 42857429Smarkm 42992559Sdesvoid 43092559Sdeschannel_free_all(void) 43192559Sdes{ 432137019Sdes u_int i; 43392559Sdes 43492559Sdes for (i = 0; i < channels_alloc; i++) 43592559Sdes if (channels[i] != NULL) 43692559Sdes channel_free(channels[i]); 43792559Sdes} 43892559Sdes 43957429Smarkm/* 44092559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 44192559Sdes * descriptors after a fork. 44292559Sdes */ 44392559Sdesvoid 44492559Sdeschannel_close_all(void) 44592559Sdes{ 446137019Sdes u_int i; 44792559Sdes 44892559Sdes for (i = 0; i < channels_alloc; i++) 44992559Sdes if (channels[i] != NULL) 45092559Sdes channel_close_fds(channels[i]); 45192559Sdes} 45292559Sdes 45392559Sdes/* 45492559Sdes * Stop listening to channels. 45592559Sdes */ 45692559Sdesvoid 45792559Sdeschannel_stop_listening(void) 45892559Sdes{ 459137019Sdes u_int i; 46092559Sdes Channel *c; 46192559Sdes 46292559Sdes for (i = 0; i < channels_alloc; i++) { 46392559Sdes c = channels[i]; 46492559Sdes if (c != NULL) { 46592559Sdes switch (c->type) { 46692559Sdes case SSH_CHANNEL_AUTH_SOCKET: 46792559Sdes case SSH_CHANNEL_PORT_LISTENER: 46892559Sdes case SSH_CHANNEL_RPORT_LISTENER: 46992559Sdes case SSH_CHANNEL_X11_LISTENER: 47092559Sdes channel_close_fd(&c->sock); 47192559Sdes channel_free(c); 47292559Sdes break; 47392559Sdes } 47492559Sdes } 47592559Sdes } 47692559Sdes} 47792559Sdes 47892559Sdes/* 47992559Sdes * Returns true if no channel has too much buffered data, and false if one or 48092559Sdes * more channel is overfull. 48192559Sdes */ 48292559Sdesint 48392559Sdeschannel_not_very_much_buffered_data(void) 48492559Sdes{ 48592559Sdes u_int i; 48692559Sdes Channel *c; 48792559Sdes 48892559Sdes for (i = 0; i < channels_alloc; i++) { 48992559Sdes c = channels[i]; 49092559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 49192559Sdes#if 0 49292559Sdes if (!compat20 && 49392559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 494113911Sdes debug2("channel %d: big input buffer %d", 49592559Sdes c->self, buffer_len(&c->input)); 49692559Sdes return 0; 49792559Sdes } 49892559Sdes#endif 49992559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 500124207Sdes debug2("channel %d: big output buffer %u > %u", 50192559Sdes c->self, buffer_len(&c->output), 50292559Sdes packet_get_maxsize()); 50392559Sdes return 0; 50492559Sdes } 50592559Sdes } 50692559Sdes } 50792559Sdes return 1; 50892559Sdes} 50992559Sdes 51092559Sdes/* Returns true if any channel is still open. */ 51192559Sdesint 51292559Sdeschannel_still_open(void) 51392559Sdes{ 514137019Sdes u_int i; 51592559Sdes Channel *c; 51692559Sdes 51792559Sdes for (i = 0; i < channels_alloc; i++) { 51892559Sdes c = channels[i]; 51992559Sdes if (c == NULL) 52092559Sdes continue; 52192559Sdes switch (c->type) { 52292559Sdes case SSH_CHANNEL_X11_LISTENER: 52392559Sdes case SSH_CHANNEL_PORT_LISTENER: 52492559Sdes case SSH_CHANNEL_RPORT_LISTENER: 525204917Sdes case SSH_CHANNEL_MUX_LISTENER: 52692559Sdes case SSH_CHANNEL_CLOSED: 52792559Sdes case SSH_CHANNEL_AUTH_SOCKET: 52892559Sdes case SSH_CHANNEL_DYNAMIC: 52992559Sdes case SSH_CHANNEL_CONNECTING: 53092559Sdes case SSH_CHANNEL_ZOMBIE: 53192559Sdes continue; 53292559Sdes case SSH_CHANNEL_LARVAL: 53392559Sdes if (!compat20) 53492559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 53592559Sdes continue; 53692559Sdes case SSH_CHANNEL_OPENING: 53792559Sdes case SSH_CHANNEL_OPEN: 53892559Sdes case SSH_CHANNEL_X11_OPEN: 539204917Sdes case SSH_CHANNEL_MUX_CLIENT: 54092559Sdes return 1; 54192559Sdes case SSH_CHANNEL_INPUT_DRAINING: 54292559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 54392559Sdes if (!compat13) 54492559Sdes fatal("cannot happen: OUT_DRAIN"); 54592559Sdes return 1; 54692559Sdes default: 54792559Sdes fatal("channel_still_open: bad channel type %d", c->type); 54892559Sdes /* NOTREACHED */ 54992559Sdes } 55092559Sdes } 55192559Sdes return 0; 55292559Sdes} 55392559Sdes 55492559Sdes/* Returns the id of an open channel suitable for keepaliving */ 55592559Sdesint 55692559Sdeschannel_find_open(void) 55792559Sdes{ 558137019Sdes u_int i; 55992559Sdes Channel *c; 56092559Sdes 56192559Sdes for (i = 0; i < channels_alloc; i++) { 56292559Sdes c = channels[i]; 563137019Sdes if (c == NULL || c->remote_id < 0) 56492559Sdes continue; 56592559Sdes switch (c->type) { 56692559Sdes case SSH_CHANNEL_CLOSED: 56792559Sdes case SSH_CHANNEL_DYNAMIC: 56892559Sdes case SSH_CHANNEL_X11_LISTENER: 56992559Sdes case SSH_CHANNEL_PORT_LISTENER: 57092559Sdes case SSH_CHANNEL_RPORT_LISTENER: 571204917Sdes case SSH_CHANNEL_MUX_LISTENER: 572204917Sdes case SSH_CHANNEL_MUX_CLIENT: 57392559Sdes case SSH_CHANNEL_OPENING: 57492559Sdes case SSH_CHANNEL_CONNECTING: 57592559Sdes case SSH_CHANNEL_ZOMBIE: 57692559Sdes continue; 57792559Sdes case SSH_CHANNEL_LARVAL: 57892559Sdes case SSH_CHANNEL_AUTH_SOCKET: 57992559Sdes case SSH_CHANNEL_OPEN: 58092559Sdes case SSH_CHANNEL_X11_OPEN: 58192559Sdes return i; 58292559Sdes case SSH_CHANNEL_INPUT_DRAINING: 58392559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 58492559Sdes if (!compat13) 58592559Sdes fatal("cannot happen: OUT_DRAIN"); 58692559Sdes return i; 58792559Sdes default: 58892559Sdes fatal("channel_find_open: bad channel type %d", c->type); 58992559Sdes /* NOTREACHED */ 59092559Sdes } 59192559Sdes } 59292559Sdes return -1; 59392559Sdes} 59492559Sdes 59592559Sdes 59692559Sdes/* 59792559Sdes * Returns a message describing the currently open forwarded connections, 59892559Sdes * suitable for sending to the client. The message contains crlf pairs for 59992559Sdes * newlines. 60092559Sdes */ 60192559Sdeschar * 60292559Sdeschannel_open_message(void) 60392559Sdes{ 60492559Sdes Buffer buffer; 60592559Sdes Channel *c; 60692559Sdes char buf[1024], *cp; 607137019Sdes u_int i; 60892559Sdes 60992559Sdes buffer_init(&buffer); 61092559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 61192559Sdes buffer_append(&buffer, buf, strlen(buf)); 61292559Sdes for (i = 0; i < channels_alloc; i++) { 61392559Sdes c = channels[i]; 61492559Sdes if (c == NULL) 61592559Sdes continue; 61692559Sdes switch (c->type) { 61792559Sdes case SSH_CHANNEL_X11_LISTENER: 61892559Sdes case SSH_CHANNEL_PORT_LISTENER: 61992559Sdes case SSH_CHANNEL_RPORT_LISTENER: 62092559Sdes case SSH_CHANNEL_CLOSED: 62192559Sdes case SSH_CHANNEL_AUTH_SOCKET: 62292559Sdes case SSH_CHANNEL_ZOMBIE: 623204917Sdes case SSH_CHANNEL_MUX_CLIENT: 624204917Sdes case SSH_CHANNEL_MUX_LISTENER: 62592559Sdes continue; 62692559Sdes case SSH_CHANNEL_LARVAL: 62792559Sdes case SSH_CHANNEL_OPENING: 62892559Sdes case SSH_CHANNEL_CONNECTING: 62992559Sdes case SSH_CHANNEL_DYNAMIC: 63092559Sdes case SSH_CHANNEL_OPEN: 63192559Sdes case SSH_CHANNEL_X11_OPEN: 63292559Sdes case SSH_CHANNEL_INPUT_DRAINING: 63392559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 634137019Sdes snprintf(buf, sizeof buf, 635204917Sdes " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n", 63692559Sdes c->self, c->remote_name, 63792559Sdes c->type, c->remote_id, 63892559Sdes c->istate, buffer_len(&c->input), 63992559Sdes c->ostate, buffer_len(&c->output), 640204917Sdes c->rfd, c->wfd, c->ctl_chan); 64192559Sdes buffer_append(&buffer, buf, strlen(buf)); 64292559Sdes continue; 64392559Sdes default: 64492559Sdes fatal("channel_open_message: bad channel type %d", c->type); 64592559Sdes /* NOTREACHED */ 64692559Sdes } 64792559Sdes } 64892559Sdes buffer_append(&buffer, "\0", 1); 64992559Sdes cp = xstrdup(buffer_ptr(&buffer)); 65092559Sdes buffer_free(&buffer); 65192559Sdes return cp; 65292559Sdes} 65392559Sdes 65492559Sdesvoid 65592559Sdeschannel_send_open(int id) 65692559Sdes{ 65792559Sdes Channel *c = channel_lookup(id); 658106130Sdes 65992559Sdes if (c == NULL) { 660124207Sdes logit("channel_send_open: %d: bad id", id); 66192559Sdes return; 66292559Sdes } 663113911Sdes debug2("channel %d: send open", id); 66492559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 66592559Sdes packet_put_cstring(c->ctype); 66692559Sdes packet_put_int(c->self); 66792559Sdes packet_put_int(c->local_window); 66892559Sdes packet_put_int(c->local_maxpacket); 66992559Sdes packet_send(); 67092559Sdes} 67192559Sdes 67292559Sdesvoid 673113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 67492559Sdes{ 675113911Sdes Channel *c = channel_lookup(id); 676106130Sdes 67792559Sdes if (c == NULL) { 678124207Sdes logit("channel_request_start: %d: unknown channel id", id); 67992559Sdes return; 68092559Sdes } 681137019Sdes debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 68292559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 68392559Sdes packet_put_int(c->remote_id); 68492559Sdes packet_put_cstring(service); 68592559Sdes packet_put_char(wantconfirm); 68692559Sdes} 687162856Sdes 68892559Sdesvoid 689181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb, 690181111Sdes channel_confirm_abandon_cb *abandon_cb, void *ctx) 69192559Sdes{ 692181111Sdes struct channel_confirm *cc; 693181111Sdes Channel *c; 694181111Sdes 695181111Sdes if ((c = channel_lookup(id)) == NULL) 696181111Sdes fatal("channel_register_expect: %d: bad id", id); 697181111Sdes 698181111Sdes cc = xmalloc(sizeof(*cc)); 699181111Sdes cc->cb = cb; 700181111Sdes cc->abandon_cb = abandon_cb; 701181111Sdes cc->ctx = ctx; 702181111Sdes TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 703181111Sdes} 704181111Sdes 705181111Sdesvoid 706181111Sdeschannel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) 707181111Sdes{ 70892559Sdes Channel *c = channel_lookup(id); 709106130Sdes 71092559Sdes if (c == NULL) { 711192595Sdes logit("channel_register_open_confirm: %d: bad id", id); 71292559Sdes return; 71392559Sdes } 714181111Sdes c->open_confirm = fn; 715181111Sdes c->open_confirm_ctx = ctx; 71692559Sdes} 717162856Sdes 71892559Sdesvoid 719157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 72092559Sdes{ 721157019Sdes Channel *c = channel_by_id(id); 722106130Sdes 72392559Sdes if (c == NULL) { 724124207Sdes logit("channel_register_cleanup: %d: bad id", id); 72592559Sdes return; 72692559Sdes } 72792559Sdes c->detach_user = fn; 728157019Sdes c->detach_close = do_close; 72992559Sdes} 730162856Sdes 73192559Sdesvoid 73292559Sdeschannel_cancel_cleanup(int id) 73392559Sdes{ 734157019Sdes Channel *c = channel_by_id(id); 735106130Sdes 73692559Sdes if (c == NULL) { 737124207Sdes logit("channel_cancel_cleanup: %d: bad id", id); 73892559Sdes return; 73992559Sdes } 74092559Sdes c->detach_user = NULL; 741157019Sdes c->detach_close = 0; 74292559Sdes} 743162856Sdes 74492559Sdesvoid 745157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn, 746181111Sdes channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 74792559Sdes{ 74892559Sdes Channel *c = channel_lookup(id); 749106130Sdes 75092559Sdes if (c == NULL) { 751124207Sdes logit("channel_register_filter: %d: bad id", id); 75292559Sdes return; 75392559Sdes } 754157019Sdes c->input_filter = ifn; 755157019Sdes c->output_filter = ofn; 756181111Sdes c->filter_ctx = ctx; 757181111Sdes c->filter_cleanup = cfn; 75892559Sdes} 75992559Sdes 76092559Sdesvoid 76192559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 762181111Sdes int extusage, int nonblock, int is_tty, u_int window_max) 76392559Sdes{ 76492559Sdes Channel *c = channel_lookup(id); 765106130Sdes 76692559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 76792559Sdes fatal("channel_activate for non-larval channel %d.", id); 768181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 76992559Sdes c->type = SSH_CHANNEL_OPEN; 77092559Sdes c->local_window = c->local_window_max = window_max; 77192559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 77292559Sdes packet_put_int(c->remote_id); 77392559Sdes packet_put_int(c->local_window); 77492559Sdes packet_send(); 77592559Sdes} 77692559Sdes 77792559Sdes/* 77860573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 77960573Skris * channels in the select bitmasks. 78057429Smarkm */ 78160573Skris/* 78260573Skris * 'channel_post*': perform any appropriate operations for channels which 78360573Skris * have events pending. 78460573Skris */ 785162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 78660573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 78760573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 78857429Smarkm 789162856Sdes/* ARGSUSED */ 79092559Sdesstatic void 791162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 79257429Smarkm{ 79360573Skris FD_SET(c->sock, readset); 79460573Skris} 79560573Skris 796162856Sdes/* ARGSUSED */ 79792559Sdesstatic void 798162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 79976262Sgreen{ 80076262Sgreen debug3("channel %d: waiting for connection", c->self); 80176262Sgreen FD_SET(c->sock, writeset); 80276262Sgreen} 80376262Sgreen 80492559Sdesstatic void 805162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 80660573Skris{ 80760573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 80860573Skris FD_SET(c->sock, readset); 80960573Skris if (buffer_len(&c->output) > 0) 81060573Skris FD_SET(c->sock, writeset); 81160573Skris} 81260573Skris 81392559Sdesstatic void 814162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 81560573Skris{ 81692559Sdes u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 81760573Skris 81860573Skris if (c->istate == CHAN_INPUT_OPEN && 81992559Sdes limit > 0 && 820162856Sdes buffer_len(&c->input) < limit && 821162856Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 82260573Skris FD_SET(c->rfd, readset); 82360573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 82460573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 82560573Skris if (buffer_len(&c->output) > 0) { 82660573Skris FD_SET(c->wfd, writeset); 82760573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 82898684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 829149753Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 830149753Sdes c->self, c->efd, buffer_len(&c->extended)); 83198684Sdes else 83298684Sdes chan_obuf_empty(c); 83360573Skris } 83460573Skris } 83560573Skris /** XXX check close conditions, too */ 836181111Sdes if (compat20 && c->efd != -1 && 837181111Sdes !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 83860573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 83960573Skris buffer_len(&c->extended) > 0) 84060573Skris FD_SET(c->efd, writeset); 84198684Sdes else if (!(c->flags & CHAN_EOF_SENT) && 84298684Sdes c->extended_usage == CHAN_EXTENDED_READ && 84360573Skris buffer_len(&c->extended) < c->remote_window) 84460573Skris FD_SET(c->efd, readset); 84560573Skris } 846137019Sdes /* XXX: What about efd? races? */ 84760573Skris} 84860573Skris 849162856Sdes/* ARGSUSED */ 85092559Sdesstatic void 851162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 85260573Skris{ 85360573Skris if (buffer_len(&c->input) == 0) { 85460573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 85560573Skris packet_put_int(c->remote_id); 85660573Skris packet_send(); 85760573Skris c->type = SSH_CHANNEL_CLOSED; 858124207Sdes debug2("channel %d: closing after input drain.", c->self); 85960573Skris } 86060573Skris} 86160573Skris 862162856Sdes/* ARGSUSED */ 86392559Sdesstatic void 864162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 86560573Skris{ 86660573Skris if (buffer_len(&c->output) == 0) 86792559Sdes chan_mark_dead(c); 86860573Skris else 86960573Skris FD_SET(c->sock, writeset); 87060573Skris} 87160573Skris 87260573Skris/* 87360573Skris * This is a special state for X11 authentication spoofing. An opened X11 87460573Skris * connection (when authentication spoofing is being done) remains in this 87560573Skris * state until the first packet has been completely read. The authentication 87660573Skris * data in that packet is then substituted by the real data if it matches the 87760573Skris * fake data, and the channel is put into normal mode. 87860573Skris * XXX All this happens at the client side. 87992559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 88060573Skris */ 88192559Sdesstatic int 88292559Sdesx11_open_helper(Buffer *b) 88360573Skris{ 88476262Sgreen u_char *ucp; 88576262Sgreen u_int proto_len, data_len; 88657429Smarkm 88760573Skris /* Check if the fixed size part of the packet is in buffer. */ 88892559Sdes if (buffer_len(b) < 12) 88960573Skris return 0; 89057429Smarkm 89160573Skris /* Parse the lengths of variable-length fields. */ 89292559Sdes ucp = buffer_ptr(b); 89360573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 89460573Skris proto_len = 256 * ucp[6] + ucp[7]; 89560573Skris data_len = 256 * ucp[8] + ucp[9]; 89660573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 89760573Skris proto_len = ucp[6] + 256 * ucp[7]; 89860573Skris data_len = ucp[8] + 256 * ucp[9]; 89960573Skris } else { 900124207Sdes debug2("Initial X11 packet contains bad byte order byte: 0x%x", 90192559Sdes ucp[0]); 90260573Skris return -1; 90360573Skris } 90457429Smarkm 90560573Skris /* Check if the whole packet is in buffer. */ 90692559Sdes if (buffer_len(b) < 90760573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 90860573Skris return 0; 90957429Smarkm 91060573Skris /* Check if authentication protocol matches. */ 91160573Skris if (proto_len != strlen(x11_saved_proto) || 91260573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 913124207Sdes debug2("X11 connection uses different authentication protocol."); 91460573Skris return -1; 91560573Skris } 91660573Skris /* Check if authentication data matches our fake data. */ 91760573Skris if (data_len != x11_fake_data_len || 91860573Skris memcmp(ucp + 12 + ((proto_len + 3) & ~3), 91960573Skris x11_fake_data, x11_fake_data_len) != 0) { 920124207Sdes debug2("X11 auth data does not match fake data."); 92160573Skris return -1; 92260573Skris } 92360573Skris /* Check fake data length */ 92460573Skris if (x11_fake_data_len != x11_saved_data_len) { 92560573Skris error("X11 fake_data_len %d != saved_data_len %d", 92660573Skris x11_fake_data_len, x11_saved_data_len); 92760573Skris return -1; 92860573Skris } 92960573Skris /* 93060573Skris * Received authentication protocol and data match 93160573Skris * our fake data. Substitute the fake data with real 93260573Skris * data. 93360573Skris */ 93460573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 93560573Skris x11_saved_data, x11_saved_data_len); 93660573Skris return 1; 93760573Skris} 93857429Smarkm 93992559Sdesstatic void 940162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 94160573Skris{ 94292559Sdes int ret = x11_open_helper(&c->output); 943106130Sdes 94460573Skris if (ret == 1) { 94560573Skris /* Start normal processing for the channel. */ 94660573Skris c->type = SSH_CHANNEL_OPEN; 94760573Skris channel_pre_open_13(c, readset, writeset); 94860573Skris } else if (ret == -1) { 94960573Skris /* 95060573Skris * We have received an X11 connection that has bad 95160573Skris * authentication information. 95260573Skris */ 953124207Sdes logit("X11 connection rejected because of wrong authentication."); 95460573Skris buffer_clear(&c->input); 95560573Skris buffer_clear(&c->output); 95692559Sdes channel_close_fd(&c->sock); 95760573Skris c->sock = -1; 95860573Skris c->type = SSH_CHANNEL_CLOSED; 95960573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 96060573Skris packet_put_int(c->remote_id); 96160573Skris packet_send(); 96260573Skris } 96360573Skris} 96457429Smarkm 96592559Sdesstatic void 966162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 96760573Skris{ 96892559Sdes int ret = x11_open_helper(&c->output); 96992559Sdes 97092559Sdes /* c->force_drain = 1; */ 97192559Sdes 97260573Skris if (ret == 1) { 97360573Skris c->type = SSH_CHANNEL_OPEN; 97492559Sdes channel_pre_open(c, readset, writeset); 97592559Sdes } else if (ret == -1) { 976124207Sdes logit("X11 connection rejected because of wrong authentication."); 977124207Sdes debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 97892559Sdes chan_read_failed(c); 97992559Sdes buffer_clear(&c->input); 98092559Sdes chan_ibuf_empty(c); 98192559Sdes buffer_clear(&c->output); 98292559Sdes /* for proto v1, the peer will send an IEOF */ 98360573Skris if (compat20) 98492559Sdes chan_write_failed(c); 98560573Skris else 98692559Sdes c->type = SSH_CHANNEL_OPEN; 987124207Sdes debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 98860573Skris } 98960573Skris} 99057429Smarkm 991204917Sdesstatic void 992204917Sdeschannel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 993204917Sdes{ 994204917Sdes if (c->istate == CHAN_INPUT_OPEN && 995204917Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 996204917Sdes FD_SET(c->rfd, readset); 997204917Sdes if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 998204917Sdes /* clear buffer immediately (discard any partial packet) */ 999204917Sdes buffer_clear(&c->input); 1000204917Sdes chan_ibuf_empty(c); 1001204917Sdes /* Start output drain. XXX just kill chan? */ 1002204917Sdes chan_rcvd_oclose(c); 1003204917Sdes } 1004204917Sdes if (c->ostate == CHAN_OUTPUT_OPEN || 1005204917Sdes c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1006204917Sdes if (buffer_len(&c->output) > 0) 1007204917Sdes FD_SET(c->wfd, writeset); 1008204917Sdes else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 1009204917Sdes chan_obuf_empty(c); 1010204917Sdes } 1011204917Sdes} 1012204917Sdes 101376262Sgreen/* try to decode a socks4 header */ 1014162856Sdes/* ARGSUSED */ 101592559Sdesstatic int 1016162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 101776262Sgreen{ 1018106130Sdes char *p, *host; 1019192595Sdes u_int len, have, i, found, need; 102092559Sdes char username[256]; 102176262Sgreen struct { 102276262Sgreen u_int8_t version; 102376262Sgreen u_int8_t command; 102476262Sgreen u_int16_t dest_port; 102576262Sgreen struct in_addr dest_addr; 102676262Sgreen } s4_req, s4_rsp; 102776262Sgreen 102876262Sgreen debug2("channel %d: decode socks4", c->self); 102976262Sgreen 103076262Sgreen have = buffer_len(&c->input); 103176262Sgreen len = sizeof(s4_req); 103276262Sgreen if (have < len) 103376262Sgreen return 0; 103476262Sgreen p = buffer_ptr(&c->input); 1035192595Sdes 1036192595Sdes need = 1; 1037192595Sdes /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1038192595Sdes if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1039192595Sdes debug2("channel %d: socks4a request", c->self); 1040192595Sdes /* ... and needs an extra string (the hostname) */ 1041192595Sdes need = 2; 1042192595Sdes } 1043192595Sdes /* Check for terminating NUL on the string(s) */ 104476262Sgreen for (found = 0, i = len; i < have; i++) { 104576262Sgreen if (p[i] == '\0') { 1046192595Sdes found++; 1047192595Sdes if (found == need) 1048192595Sdes break; 104976262Sgreen } 105076262Sgreen if (i > 1024) { 105176262Sgreen /* the peer is probably sending garbage */ 105276262Sgreen debug("channel %d: decode socks4: too long", 105376262Sgreen c->self); 105476262Sgreen return -1; 105576262Sgreen } 105676262Sgreen } 1057192595Sdes if (found < need) 105876262Sgreen return 0; 105976262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 106076262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 106176262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 106276262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 106376262Sgreen have = buffer_len(&c->input); 106476262Sgreen p = buffer_ptr(&c->input); 106576262Sgreen len = strlen(p); 106676262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1067192595Sdes len++; /* trailing '\0' */ 106876262Sgreen if (len > have) 106976262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 107076262Sgreen c->self, len, have); 107176262Sgreen strlcpy(username, p, sizeof(username)); 107276262Sgreen buffer_consume(&c->input, len); 107376262Sgreen 1074192595Sdes if (c->path != NULL) { 1075192595Sdes xfree(c->path); 1076192595Sdes c->path = NULL; 1077192595Sdes } 1078192595Sdes if (need == 1) { /* SOCKS4: one string */ 1079192595Sdes host = inet_ntoa(s4_req.dest_addr); 1080192595Sdes c->path = xstrdup(host); 1081192595Sdes } else { /* SOCKS4A: two strings */ 1082192595Sdes have = buffer_len(&c->input); 1083192595Sdes p = buffer_ptr(&c->input); 1084192595Sdes len = strlen(p); 1085192595Sdes debug2("channel %d: decode socks4a: host %s/%d", 1086192595Sdes c->self, p, len); 1087192595Sdes len++; /* trailing '\0' */ 1088192595Sdes if (len > have) 1089192595Sdes fatal("channel %d: decode socks4a: len %d > have %d", 1090192595Sdes c->self, len, have); 1091192595Sdes if (len > NI_MAXHOST) { 1092192595Sdes error("channel %d: hostname \"%.100s\" too long", 1093192595Sdes c->self, p); 1094192595Sdes return -1; 1095192595Sdes } 1096192595Sdes c->path = xstrdup(p); 1097192595Sdes buffer_consume(&c->input, len); 1098192595Sdes } 109976262Sgreen c->host_port = ntohs(s4_req.dest_port); 110092559Sdes 1101124207Sdes debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1102192595Sdes c->self, c->path, c->host_port, s4_req.command); 110376262Sgreen 110476262Sgreen if (s4_req.command != 1) { 1105192595Sdes debug("channel %d: cannot handle: %s cn %d", 1106192595Sdes c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 110776262Sgreen return -1; 110876262Sgreen } 110976262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 111076262Sgreen s4_rsp.command = 90; /* cd: req granted */ 111176262Sgreen s4_rsp.dest_port = 0; /* ignored */ 111276262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1113162856Sdes buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 111476262Sgreen return 1; 111576262Sgreen} 111676262Sgreen 1117124207Sdes/* try to decode a socks5 header */ 1118124207Sdes#define SSH_SOCKS5_AUTHDONE 0x1000 1119124207Sdes#define SSH_SOCKS5_NOAUTH 0x00 1120124207Sdes#define SSH_SOCKS5_IPV4 0x01 1121124207Sdes#define SSH_SOCKS5_DOMAIN 0x03 1122124207Sdes#define SSH_SOCKS5_IPV6 0x04 1123124207Sdes#define SSH_SOCKS5_CONNECT 0x01 1124124207Sdes#define SSH_SOCKS5_SUCCESS 0x00 1125124207Sdes 1126162856Sdes/* ARGSUSED */ 1127124207Sdesstatic int 1128162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1129124207Sdes{ 1130124207Sdes struct { 1131124207Sdes u_int8_t version; 1132124207Sdes u_int8_t command; 1133124207Sdes u_int8_t reserved; 1134124207Sdes u_int8_t atyp; 1135124207Sdes } s5_req, s5_rsp; 1136124207Sdes u_int16_t dest_port; 1137192595Sdes u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1138162856Sdes u_int have, need, i, found, nmethods, addrlen, af; 1139124207Sdes 1140124207Sdes debug2("channel %d: decode socks5", c->self); 1141124207Sdes p = buffer_ptr(&c->input); 1142124207Sdes if (p[0] != 0x05) 1143124207Sdes return -1; 1144124207Sdes have = buffer_len(&c->input); 1145124207Sdes if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1146124207Sdes /* format: ver | nmethods | methods */ 1147126273Sdes if (have < 2) 1148124207Sdes return 0; 1149124207Sdes nmethods = p[1]; 1150124207Sdes if (have < nmethods + 2) 1151124207Sdes return 0; 1152124207Sdes /* look for method: "NO AUTHENTICATION REQUIRED" */ 1153181111Sdes for (found = 0, i = 2; i < nmethods + 2; i++) { 1154162856Sdes if (p[i] == SSH_SOCKS5_NOAUTH) { 1155124207Sdes found = 1; 1156124207Sdes break; 1157124207Sdes } 1158124207Sdes } 1159124207Sdes if (!found) { 1160124207Sdes debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1161124207Sdes c->self); 1162124207Sdes return -1; 1163124207Sdes } 1164124207Sdes buffer_consume(&c->input, nmethods + 2); 1165124207Sdes buffer_put_char(&c->output, 0x05); /* version */ 1166124207Sdes buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1167124207Sdes FD_SET(c->sock, writeset); 1168124207Sdes c->flags |= SSH_SOCKS5_AUTHDONE; 1169124207Sdes debug2("channel %d: socks5 auth done", c->self); 1170124207Sdes return 0; /* need more */ 1171124207Sdes } 1172124207Sdes debug2("channel %d: socks5 post auth", c->self); 1173124207Sdes if (have < sizeof(s5_req)+1) 1174124207Sdes return 0; /* need more */ 1175162856Sdes memcpy(&s5_req, p, sizeof(s5_req)); 1176124207Sdes if (s5_req.version != 0x05 || 1177124207Sdes s5_req.command != SSH_SOCKS5_CONNECT || 1178124207Sdes s5_req.reserved != 0x00) { 1179124207Sdes debug2("channel %d: only socks5 connect supported", c->self); 1180124207Sdes return -1; 1181124207Sdes } 1182147005Sdes switch (s5_req.atyp){ 1183124207Sdes case SSH_SOCKS5_IPV4: 1184124207Sdes addrlen = 4; 1185124207Sdes af = AF_INET; 1186124207Sdes break; 1187124207Sdes case SSH_SOCKS5_DOMAIN: 1188124207Sdes addrlen = p[sizeof(s5_req)]; 1189124207Sdes af = -1; 1190124207Sdes break; 1191124207Sdes case SSH_SOCKS5_IPV6: 1192124207Sdes addrlen = 16; 1193124207Sdes af = AF_INET6; 1194124207Sdes break; 1195124207Sdes default: 1196124207Sdes debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1197124207Sdes return -1; 1198124207Sdes } 1199162856Sdes need = sizeof(s5_req) + addrlen + 2; 1200162856Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1201162856Sdes need++; 1202162856Sdes if (have < need) 1203124207Sdes return 0; 1204124207Sdes buffer_consume(&c->input, sizeof(s5_req)); 1205124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1206124207Sdes buffer_consume(&c->input, 1); /* host string length */ 1207124207Sdes buffer_get(&c->input, (char *)&dest_addr, addrlen); 1208124207Sdes buffer_get(&c->input, (char *)&dest_port, 2); 1209124207Sdes dest_addr[addrlen] = '\0'; 1210192595Sdes if (c->path != NULL) { 1211192595Sdes xfree(c->path); 1212192595Sdes c->path = NULL; 1213192595Sdes } 1214192595Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1215192595Sdes if (addrlen >= NI_MAXHOST) { 1216192595Sdes error("channel %d: dynamic request: socks5 hostname " 1217192595Sdes "\"%.100s\" too long", c->self, dest_addr); 1218192595Sdes return -1; 1219192595Sdes } 1220192595Sdes c->path = xstrdup(dest_addr); 1221192595Sdes } else { 1222192595Sdes if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1223192595Sdes return -1; 1224192595Sdes c->path = xstrdup(ntop); 1225192595Sdes } 1226124207Sdes c->host_port = ntohs(dest_port); 1227126273Sdes 1228124207Sdes debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1229124207Sdes c->self, c->path, c->host_port, s5_req.command); 1230124207Sdes 1231124207Sdes s5_rsp.version = 0x05; 1232124207Sdes s5_rsp.command = SSH_SOCKS5_SUCCESS; 1233124207Sdes s5_rsp.reserved = 0; /* ignored */ 1234124207Sdes s5_rsp.atyp = SSH_SOCKS5_IPV4; 1235124207Sdes ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; 1236124207Sdes dest_port = 0; /* ignored */ 1237124207Sdes 1238162856Sdes buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1239162856Sdes buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); 1240162856Sdes buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1241124207Sdes return 1; 1242124207Sdes} 1243124207Sdes 1244204917SdesChannel * 1245204917Sdeschannel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, 1246204917Sdes int in, int out) 1247204917Sdes{ 1248204917Sdes Channel *c; 1249204917Sdes 1250204917Sdes debug("channel_connect_stdio_fwd %s:%d", host_to_connect, 1251204917Sdes port_to_connect); 1252204917Sdes 1253204917Sdes c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, 1254204917Sdes -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1255204917Sdes 0, "stdio-forward", /*nonblock*/0); 1256204917Sdes 1257204917Sdes c->path = xstrdup(host_to_connect); 1258204917Sdes c->host_port = port_to_connect; 1259204917Sdes c->listening_port = 0; 1260204917Sdes c->force_drain = 1; 1261204917Sdes 1262204917Sdes channel_register_fds(c, in, out, -1, 0, 1, 0); 1263204917Sdes port_open_helper(c, "direct-tcpip"); 1264204917Sdes 1265204917Sdes return c; 1266204917Sdes} 1267204917Sdes 126876262Sgreen/* dynamic port forwarding */ 126992559Sdesstatic void 1270162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 127176262Sgreen{ 127276262Sgreen u_char *p; 1273149753Sdes u_int have; 1274149753Sdes int ret; 127576262Sgreen 127676262Sgreen have = buffer_len(&c->input); 127776262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 127876262Sgreen /* buffer_dump(&c->input); */ 127976262Sgreen /* check if the fixed size part of the packet is in buffer. */ 1280124207Sdes if (have < 3) { 128176262Sgreen /* need more */ 128276262Sgreen FD_SET(c->sock, readset); 128376262Sgreen return; 128476262Sgreen } 128576262Sgreen /* try to guess the protocol */ 128676262Sgreen p = buffer_ptr(&c->input); 128776262Sgreen switch (p[0]) { 128876262Sgreen case 0x04: 128976262Sgreen ret = channel_decode_socks4(c, readset, writeset); 129076262Sgreen break; 1291124207Sdes case 0x05: 1292124207Sdes ret = channel_decode_socks5(c, readset, writeset); 1293124207Sdes break; 129476262Sgreen default: 129576262Sgreen ret = -1; 129676262Sgreen break; 129776262Sgreen } 129876262Sgreen if (ret < 0) { 129992559Sdes chan_mark_dead(c); 130076262Sgreen } else if (ret == 0) { 130176262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 130276262Sgreen /* need more */ 130376262Sgreen FD_SET(c->sock, readset); 130476262Sgreen } else { 130576262Sgreen /* switch to the next state */ 130676262Sgreen c->type = SSH_CHANNEL_OPENING; 130776262Sgreen port_open_helper(c, "direct-tcpip"); 130876262Sgreen } 130976262Sgreen} 131076262Sgreen 131160573Skris/* This is our fake X11 server socket. */ 1312162856Sdes/* ARGSUSED */ 131392559Sdesstatic void 1314162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 131560573Skris{ 131692559Sdes Channel *nc; 1317181111Sdes struct sockaddr_storage addr; 131892559Sdes int newsock; 131960573Skris socklen_t addrlen; 132076262Sgreen char buf[16384], *remote_ipaddr; 132160573Skris int remote_port; 132257429Smarkm 132360573Skris if (FD_ISSET(c->sock, readset)) { 132460573Skris debug("X11 connection requested."); 132560573Skris addrlen = sizeof(addr); 1326181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 132792559Sdes if (c->single_connection) { 1328124207Sdes debug2("single_connection: closing X11 listener."); 132992559Sdes channel_close_fd(&c->sock); 133092559Sdes chan_mark_dead(c); 133192559Sdes } 133260573Skris if (newsock < 0) { 133360573Skris error("accept: %.100s", strerror(errno)); 133460573Skris return; 133560573Skris } 133692559Sdes set_nodelay(newsock); 133776262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 133860573Skris remote_port = get_peer_port(newsock); 133960573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 134076262Sgreen remote_ipaddr, remote_port); 134157429Smarkm 134292559Sdes nc = channel_new("accepted x11 socket", 134360573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 1344124207Sdes c->local_window_max, c->local_maxpacket, 0, buf, 1); 134560573Skris if (compat20) { 134660573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 134760573Skris packet_put_cstring("x11"); 134892559Sdes packet_put_int(nc->self); 134992559Sdes packet_put_int(nc->local_window_max); 135092559Sdes packet_put_int(nc->local_maxpacket); 135176262Sgreen /* originator ipaddr and port */ 135276262Sgreen packet_put_cstring(remote_ipaddr); 135360573Skris if (datafellows & SSH_BUG_X11FWD) { 1354124207Sdes debug2("ssh2 x11 bug compat mode"); 135557429Smarkm } else { 135660573Skris packet_put_int(remote_port); 135757429Smarkm } 135860573Skris packet_send(); 135960573Skris } else { 136060573Skris packet_start(SSH_SMSG_X11_OPEN); 136192559Sdes packet_put_int(nc->self); 136292559Sdes if (packet_get_protocol_flags() & 136392559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 136492559Sdes packet_put_cstring(buf); 136560573Skris packet_send(); 136657429Smarkm } 136776262Sgreen xfree(remote_ipaddr); 136857429Smarkm } 136957429Smarkm} 137057429Smarkm 137192559Sdesstatic void 137276262Sgreenport_open_helper(Channel *c, char *rtype) 137376262Sgreen{ 137476262Sgreen int direct; 137576262Sgreen char buf[1024]; 137676262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 1377149753Sdes int remote_port = get_peer_port(c->sock); 137876262Sgreen 1379204917Sdes if (remote_port == -1) { 1380204917Sdes /* Fake addr/port to appease peers that validate it (Tectia) */ 1381204917Sdes xfree(remote_ipaddr); 1382204917Sdes remote_ipaddr = xstrdup("127.0.0.1"); 1383204917Sdes remote_port = 65535; 1384204917Sdes } 1385204917Sdes 138676262Sgreen direct = (strcmp(rtype, "direct-tcpip") == 0); 138776262Sgreen 138876262Sgreen snprintf(buf, sizeof buf, 138976262Sgreen "%s: listening port %d for %.100s port %d, " 139076262Sgreen "connect from %.200s port %d", 139176262Sgreen rtype, c->listening_port, c->path, c->host_port, 139276262Sgreen remote_ipaddr, remote_port); 139376262Sgreen 139476262Sgreen xfree(c->remote_name); 139576262Sgreen c->remote_name = xstrdup(buf); 139676262Sgreen 139776262Sgreen if (compat20) { 139876262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 139976262Sgreen packet_put_cstring(rtype); 140076262Sgreen packet_put_int(c->self); 140176262Sgreen packet_put_int(c->local_window_max); 140276262Sgreen packet_put_int(c->local_maxpacket); 140376262Sgreen if (direct) { 140476262Sgreen /* target host, port */ 140576262Sgreen packet_put_cstring(c->path); 140676262Sgreen packet_put_int(c->host_port); 140776262Sgreen } else { 140876262Sgreen /* listen address, port */ 140976262Sgreen packet_put_cstring(c->path); 141076262Sgreen packet_put_int(c->listening_port); 141176262Sgreen } 141276262Sgreen /* originator host and port */ 141376262Sgreen packet_put_cstring(remote_ipaddr); 1414149753Sdes packet_put_int((u_int)remote_port); 141576262Sgreen packet_send(); 141676262Sgreen } else { 141776262Sgreen packet_start(SSH_MSG_PORT_OPEN); 141876262Sgreen packet_put_int(c->self); 141976262Sgreen packet_put_cstring(c->path); 142076262Sgreen packet_put_int(c->host_port); 142192559Sdes if (packet_get_protocol_flags() & 142292559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 142376262Sgreen packet_put_cstring(c->remote_name); 142476262Sgreen packet_send(); 142576262Sgreen } 142676262Sgreen xfree(remote_ipaddr); 142776262Sgreen} 142876262Sgreen 1429157019Sdesstatic void 1430157019Sdeschannel_set_reuseaddr(int fd) 1431157019Sdes{ 1432157019Sdes int on = 1; 1433157019Sdes 1434157019Sdes /* 1435157019Sdes * Set socket options. 1436157019Sdes * Allow local port reuse in TIME_WAIT. 1437157019Sdes */ 1438157019Sdes if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1439157019Sdes error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1440157019Sdes} 1441157019Sdes 144257429Smarkm/* 144360573Skris * This socket is listening for connections to a forwarded TCP/IP port. 144457429Smarkm */ 1445162856Sdes/* ARGSUSED */ 144692559Sdesstatic void 1447162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 144857429Smarkm{ 144976262Sgreen Channel *nc; 1450181111Sdes struct sockaddr_storage addr; 145192559Sdes int newsock, nextstate; 145257429Smarkm socklen_t addrlen; 145376262Sgreen char *rtype; 145457429Smarkm 145560573Skris if (FD_ISSET(c->sock, readset)) { 145660573Skris debug("Connection to port %d forwarding " 145760573Skris "to %.100s port %d requested.", 145860573Skris c->listening_port, c->path, c->host_port); 145976262Sgreen 146092559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 146192559Sdes nextstate = SSH_CHANNEL_OPENING; 146292559Sdes rtype = "forwarded-tcpip"; 146392559Sdes } else { 146492559Sdes if (c->host_port == 0) { 146592559Sdes nextstate = SSH_CHANNEL_DYNAMIC; 146692559Sdes rtype = "dynamic-tcpip"; 146792559Sdes } else { 146892559Sdes nextstate = SSH_CHANNEL_OPENING; 146992559Sdes rtype = "direct-tcpip"; 147092559Sdes } 147192559Sdes } 147276262Sgreen 147360573Skris addrlen = sizeof(addr); 1474181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 147560573Skris if (newsock < 0) { 147660573Skris error("accept: %.100s", strerror(errno)); 147760573Skris return; 147860573Skris } 147992559Sdes set_nodelay(newsock); 1480124207Sdes nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1481124207Sdes c->local_window_max, c->local_maxpacket, 0, rtype, 1); 148276262Sgreen nc->listening_port = c->listening_port; 148376262Sgreen nc->host_port = c->host_port; 1484192595Sdes if (c->path != NULL) 1485192595Sdes nc->path = xstrdup(c->path); 148676262Sgreen 1487204917Sdes if (nextstate != SSH_CHANNEL_DYNAMIC) 148876262Sgreen port_open_helper(nc, rtype); 148960573Skris } 149060573Skris} 149157429Smarkm 149260573Skris/* 149360573Skris * This is the authentication agent socket listening for connections from 149460573Skris * clients. 149560573Skris */ 1496162856Sdes/* ARGSUSED */ 149792559Sdesstatic void 1498162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 149960573Skris{ 150092559Sdes Channel *nc; 150192559Sdes int newsock; 1502181111Sdes struct sockaddr_storage addr; 150360573Skris socklen_t addrlen; 150457429Smarkm 150560573Skris if (FD_ISSET(c->sock, readset)) { 150660573Skris addrlen = sizeof(addr); 1507181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 150860573Skris if (newsock < 0) { 150960573Skris error("accept from auth socket: %.100s", strerror(errno)); 151060573Skris return; 151160573Skris } 151292559Sdes nc = channel_new("accepted auth socket", 151376262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 151476262Sgreen c->local_window_max, c->local_maxpacket, 1515124207Sdes 0, "accepted auth socket", 1); 151676262Sgreen if (compat20) { 151776262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 151876262Sgreen packet_put_cstring("auth-agent@openssh.com"); 151992559Sdes packet_put_int(nc->self); 152076262Sgreen packet_put_int(c->local_window_max); 152176262Sgreen packet_put_int(c->local_maxpacket); 152276262Sgreen } else { 152376262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 152492559Sdes packet_put_int(nc->self); 152576262Sgreen } 152660573Skris packet_send(); 152760573Skris } 152860573Skris} 152957429Smarkm 1530162856Sdes/* ARGSUSED */ 153192559Sdesstatic void 1532162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 153376262Sgreen{ 1534181111Sdes int err = 0, sock; 153592559Sdes socklen_t sz = sizeof(err); 153692559Sdes 153776262Sgreen if (FD_ISSET(c->sock, writeset)) { 153892559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 153992559Sdes err = errno; 154092559Sdes error("getsockopt SO_ERROR failed"); 154192559Sdes } 154292559Sdes if (err == 0) { 1543181111Sdes debug("channel %d: connected to %s port %d", 1544181111Sdes c->self, c->connect_ctx.host, c->connect_ctx.port); 1545181111Sdes channel_connect_ctx_free(&c->connect_ctx); 154692559Sdes c->type = SSH_CHANNEL_OPEN; 154792559Sdes if (compat20) { 154892559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 154992559Sdes packet_put_int(c->remote_id); 155092559Sdes packet_put_int(c->self); 155192559Sdes packet_put_int(c->local_window); 155292559Sdes packet_put_int(c->local_maxpacket); 155392559Sdes } else { 155492559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 155592559Sdes packet_put_int(c->remote_id); 155692559Sdes packet_put_int(c->self); 155792559Sdes } 155876262Sgreen } else { 1559181111Sdes debug("channel %d: connection failed: %s", 156092559Sdes c->self, strerror(err)); 1561181111Sdes /* Try next address, if any */ 1562181111Sdes if ((sock = connect_next(&c->connect_ctx)) > 0) { 1563181111Sdes close(c->sock); 1564181111Sdes c->sock = c->rfd = c->wfd = sock; 1565181111Sdes channel_max_fd = channel_find_maxfd(); 1566181111Sdes return; 1567181111Sdes } 1568181111Sdes /* Exhausted all addresses */ 1569181111Sdes error("connect_to %.100s port %d: failed.", 1570181111Sdes c->connect_ctx.host, c->connect_ctx.port); 1571181111Sdes channel_connect_ctx_free(&c->connect_ctx); 157292559Sdes if (compat20) { 157392559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 157492559Sdes packet_put_int(c->remote_id); 157592559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 157692559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 157792559Sdes packet_put_cstring(strerror(err)); 157892559Sdes packet_put_cstring(""); 157992559Sdes } 158076262Sgreen } else { 158192559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 158292559Sdes packet_put_int(c->remote_id); 158376262Sgreen } 158492559Sdes chan_mark_dead(c); 158576262Sgreen } 158692559Sdes packet_send(); 158776262Sgreen } 158876262Sgreen} 158976262Sgreen 1590162856Sdes/* ARGSUSED */ 159192559Sdesstatic int 1592162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 159360573Skris{ 1594147005Sdes char buf[CHAN_RBUF]; 1595181111Sdes int len, force; 159657429Smarkm 1597181111Sdes force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1598181111Sdes if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1599162856Sdes errno = 0; 160060573Skris len = read(c->rfd, buf, sizeof(buf)); 1601181111Sdes if (len < 0 && (errno == EINTR || 1602181111Sdes ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 160360573Skris return 1; 1604162856Sdes#ifndef PTY_ZEROREAD 160578827Sgreen if (len <= 0) { 1606162856Sdes#else 1607162856Sdes if ((!c->isatty && len <= 0) || 1608162856Sdes (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1609162856Sdes#endif 1610124207Sdes debug2("channel %d: read<=0 rfd %d len %d", 161160573Skris c->self, c->rfd, len); 161276262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1613124207Sdes debug2("channel %d: not open", c->self); 161492559Sdes chan_mark_dead(c); 161576262Sgreen return -1; 161676262Sgreen } else if (compat13) { 161792559Sdes buffer_clear(&c->output); 161860573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 1619124207Sdes debug2("channel %d: input draining.", c->self); 162060573Skris } else { 162160573Skris chan_read_failed(c); 162257429Smarkm } 162360573Skris return -1; 162460573Skris } 162592559Sdes if (c->input_filter != NULL) { 162665668Skris if (c->input_filter(c, buf, len) == -1) { 1627124207Sdes debug2("channel %d: filter stops", c->self); 162865668Skris chan_read_failed(c); 162965668Skris } 1630157019Sdes } else if (c->datagram) { 1631157019Sdes buffer_put_string(&c->input, buf, len); 163265668Skris } else { 163365668Skris buffer_append(&c->input, buf, len); 163465668Skris } 163560573Skris } 163660573Skris return 1; 163760573Skris} 1638162856Sdes 1639162856Sdes/* ARGSUSED */ 164092559Sdesstatic int 1641162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 164260573Skris{ 164376262Sgreen struct termios tio; 1644157019Sdes u_char *data = NULL, *buf; 164592559Sdes u_int dlen; 164660573Skris int len; 164760573Skris 164860573Skris /* Send buffered output data to the socket. */ 164960573Skris if (c->wfd != -1 && 165060573Skris FD_ISSET(c->wfd, writeset) && 165160573Skris buffer_len(&c->output) > 0) { 1652157019Sdes if (c->output_filter != NULL) { 1653157019Sdes if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1654157019Sdes debug2("channel %d: filter stops", c->self); 1655157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1656157019Sdes chan_mark_dead(c); 1657157019Sdes else 1658157019Sdes chan_write_failed(c); 1659157019Sdes return -1; 1660157019Sdes } 1661157019Sdes } else if (c->datagram) { 1662157019Sdes buf = data = buffer_get_string(&c->output, &dlen); 1663157019Sdes } else { 1664157019Sdes buf = data = buffer_ptr(&c->output); 1665157019Sdes dlen = buffer_len(&c->output); 1666157019Sdes } 1667157019Sdes 1668157019Sdes if (c->datagram) { 1669157019Sdes /* ignore truncated writes, datagrams might get lost */ 1670157019Sdes c->local_consumed += dlen + 4; 1671157019Sdes len = write(c->wfd, buf, dlen); 1672157019Sdes xfree(data); 1673181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1674181111Sdes errno == EWOULDBLOCK)) 1675157019Sdes return 1; 1676157019Sdes if (len <= 0) { 1677157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1678157019Sdes chan_mark_dead(c); 1679157019Sdes else 1680157019Sdes chan_write_failed(c); 1681157019Sdes return -1; 1682157019Sdes } 1683157019Sdes return 1; 1684157019Sdes } 1685106130Sdes#ifdef _AIX 1686126273Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1687126273Sdes if (compat20 && c->wfd_isatty) 1688126273Sdes dlen = MIN(dlen, 8*1024); 1689106130Sdes#endif 1690157019Sdes 1691157019Sdes len = write(c->wfd, buf, dlen); 1692181111Sdes if (len < 0 && 1693181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 169460573Skris return 1; 169560573Skris if (len <= 0) { 169676262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1697124207Sdes debug2("channel %d: not open", c->self); 169892559Sdes chan_mark_dead(c); 169976262Sgreen return -1; 170076262Sgreen } else if (compat13) { 170192559Sdes buffer_clear(&c->output); 1702124207Sdes debug2("channel %d: input draining.", c->self); 170360573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 170460573Skris } else { 170560573Skris chan_write_failed(c); 170657429Smarkm } 170760573Skris return -1; 170860573Skris } 1709197679Sdes#ifndef BROKEN_TCGETATTR_ICANON 1710157019Sdes if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 171174500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 171274500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 171374500Sgreen /* 171474500Sgreen * Simulate echo to reduce the impact of 171576262Sgreen * traffic analysis. We need to match the 171676262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 1717157019Sdes * (4 byte channel id + buf) 171874500Sgreen */ 171976262Sgreen packet_send_ignore(4 + len); 172074500Sgreen packet_send(); 172174500Sgreen } 172274500Sgreen } 1723197679Sdes#endif 172460573Skris buffer_consume(&c->output, len); 172560573Skris if (compat20 && len > 0) { 172660573Skris c->local_consumed += len; 172760573Skris } 172860573Skris } 172960573Skris return 1; 173060573Skris} 1731162856Sdes 173292559Sdesstatic int 1733162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 173460573Skris{ 1735147005Sdes char buf[CHAN_RBUF]; 173660573Skris int len; 173757429Smarkm 173860573Skris/** XXX handle drain efd, too */ 173960573Skris if (c->efd != -1) { 174060573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 174160573Skris FD_ISSET(c->efd, writeset) && 174260573Skris buffer_len(&c->extended) > 0) { 174360573Skris len = write(c->efd, buffer_ptr(&c->extended), 174460573Skris buffer_len(&c->extended)); 174569587Sgreen debug2("channel %d: written %d to efd %d", 174660573Skris c->self, len, c->efd); 1747181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1748181111Sdes errno == EWOULDBLOCK)) 174976262Sgreen return 1; 175076262Sgreen if (len <= 0) { 175176262Sgreen debug2("channel %d: closing write-efd %d", 175276262Sgreen c->self, c->efd); 175392559Sdes channel_close_fd(&c->efd); 175476262Sgreen } else { 175560573Skris buffer_consume(&c->extended, len); 175660573Skris c->local_consumed += len; 175757429Smarkm } 175860573Skris } else if (c->extended_usage == CHAN_EXTENDED_READ && 1759181111Sdes (c->detach_close || FD_ISSET(c->efd, readset))) { 176060573Skris len = read(c->efd, buf, sizeof(buf)); 176169587Sgreen debug2("channel %d: read %d from efd %d", 176292559Sdes c->self, len, c->efd); 1763181111Sdes if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1764181111Sdes errno == EWOULDBLOCK) && !c->detach_close))) 176576262Sgreen return 1; 176676262Sgreen if (len <= 0) { 176776262Sgreen debug2("channel %d: closing read-efd %d", 176860573Skris c->self, c->efd); 176992559Sdes channel_close_fd(&c->efd); 177076262Sgreen } else { 177160573Skris buffer_append(&c->extended, buf, len); 177276262Sgreen } 177360573Skris } 177460573Skris } 177560573Skris return 1; 177660573Skris} 1777162856Sdes 177892559Sdesstatic int 177976262Sgreenchannel_check_window(Channel *c) 178060573Skris{ 178176262Sgreen if (c->type == SSH_CHANNEL_OPEN && 178276262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1783181111Sdes ((c->local_window_max - c->local_window > 1784181111Sdes c->local_maxpacket*3) || 1785181111Sdes c->local_window < c->local_window_max/2) && 178660573Skris c->local_consumed > 0) { 178760573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 178860573Skris packet_put_int(c->remote_id); 178960573Skris packet_put_int(c->local_consumed); 179060573Skris packet_send(); 179169587Sgreen debug2("channel %d: window %d sent adjust %d", 179260573Skris c->self, c->local_window, 179360573Skris c->local_consumed); 179460573Skris c->local_window += c->local_consumed; 179560573Skris c->local_consumed = 0; 179660573Skris } 179760573Skris return 1; 179860573Skris} 179957429Smarkm 180092559Sdesstatic void 1801162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 180260573Skris{ 180360573Skris channel_handle_rfd(c, readset, writeset); 180460573Skris channel_handle_wfd(c, readset, writeset); 180592559Sdes if (!compat20) 180692559Sdes return; 180760573Skris channel_handle_efd(c, readset, writeset); 180876262Sgreen channel_check_window(c); 180960573Skris} 181060573Skris 1811204917Sdesstatic u_int 1812204917Sdesread_mux(Channel *c, u_int need) 1813204917Sdes{ 1814204917Sdes char buf[CHAN_RBUF]; 1815204917Sdes int len; 1816204917Sdes u_int rlen; 1817204917Sdes 1818204917Sdes if (buffer_len(&c->input) < need) { 1819204917Sdes rlen = need - buffer_len(&c->input); 1820204917Sdes len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); 1821204917Sdes if (len <= 0) { 1822204917Sdes if (errno != EINTR && errno != EAGAIN) { 1823204917Sdes debug2("channel %d: ctl read<=0 rfd %d len %d", 1824204917Sdes c->self, c->rfd, len); 1825204917Sdes chan_read_failed(c); 1826204917Sdes return 0; 1827204917Sdes } 1828204917Sdes } else 1829204917Sdes buffer_append(&c->input, buf, len); 1830204917Sdes } 1831204917Sdes return buffer_len(&c->input); 1832204917Sdes} 1833204917Sdes 1834204917Sdesstatic void 1835204917Sdeschannel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1836204917Sdes{ 1837204917Sdes u_int need; 1838204917Sdes ssize_t len; 1839204917Sdes 1840204917Sdes if (!compat20) 1841204917Sdes fatal("%s: entered with !compat20", __func__); 1842204917Sdes 1843204917Sdes if (c->rfd != -1 && FD_ISSET(c->rfd, readset) && 1844204917Sdes (c->istate == CHAN_INPUT_OPEN || 1845204917Sdes c->istate == CHAN_INPUT_WAIT_DRAIN)) { 1846204917Sdes /* 1847204917Sdes * Don't not read past the precise end of packets to 1848204917Sdes * avoid disrupting fd passing. 1849204917Sdes */ 1850204917Sdes if (read_mux(c, 4) < 4) /* read header */ 1851204917Sdes return; 1852204917Sdes need = get_u32(buffer_ptr(&c->input)); 1853204917Sdes#define CHANNEL_MUX_MAX_PACKET (256 * 1024) 1854204917Sdes if (need > CHANNEL_MUX_MAX_PACKET) { 1855204917Sdes debug2("channel %d: packet too big %u > %u", 1856204917Sdes c->self, CHANNEL_MUX_MAX_PACKET, need); 1857204917Sdes chan_rcvd_oclose(c); 1858204917Sdes return; 1859204917Sdes } 1860204917Sdes if (read_mux(c, need + 4) < need + 4) /* read body */ 1861204917Sdes return; 1862204917Sdes if (c->mux_rcb(c) != 0) { 1863204917Sdes debug("channel %d: mux_rcb failed", c->self); 1864204917Sdes chan_mark_dead(c); 1865204917Sdes return; 1866204917Sdes } 1867204917Sdes } 1868204917Sdes 1869204917Sdes if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && 1870204917Sdes buffer_len(&c->output) > 0) { 1871204917Sdes len = write(c->wfd, buffer_ptr(&c->output), 1872204917Sdes buffer_len(&c->output)); 1873204917Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1874204917Sdes return; 1875204917Sdes if (len <= 0) { 1876204917Sdes chan_mark_dead(c); 1877204917Sdes return; 1878204917Sdes } 1879204917Sdes buffer_consume(&c->output, len); 1880204917Sdes } 1881204917Sdes} 1882204917Sdes 1883204917Sdesstatic void 1884204917Sdeschannel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) 1885204917Sdes{ 1886204917Sdes Channel *nc; 1887204917Sdes struct sockaddr_storage addr; 1888204917Sdes socklen_t addrlen; 1889204917Sdes int newsock; 1890204917Sdes uid_t euid; 1891204917Sdes gid_t egid; 1892204917Sdes 1893204917Sdes if (!FD_ISSET(c->sock, readset)) 1894204917Sdes return; 1895204917Sdes 1896204917Sdes debug("multiplexing control connection"); 1897204917Sdes 1898204917Sdes /* 1899204917Sdes * Accept connection on control socket 1900204917Sdes */ 1901204917Sdes memset(&addr, 0, sizeof(addr)); 1902204917Sdes addrlen = sizeof(addr); 1903204917Sdes if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 1904204917Sdes &addrlen)) == -1) { 1905204917Sdes error("%s accept: %s", __func__, strerror(errno)); 1906204917Sdes return; 1907204917Sdes } 1908204917Sdes 1909204917Sdes if (getpeereid(newsock, &euid, &egid) < 0) { 1910204917Sdes error("%s getpeereid failed: %s", __func__, 1911204917Sdes strerror(errno)); 1912204917Sdes close(newsock); 1913204917Sdes return; 1914204917Sdes } 1915204917Sdes if ((euid != 0) && (getuid() != euid)) { 1916204917Sdes error("multiplex uid mismatch: peer euid %u != uid %u", 1917204917Sdes (u_int)euid, (u_int)getuid()); 1918204917Sdes close(newsock); 1919204917Sdes return; 1920204917Sdes } 1921204917Sdes nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, 1922204917Sdes newsock, newsock, -1, c->local_window_max, 1923204917Sdes c->local_maxpacket, 0, "mux-control", 1); 1924204917Sdes nc->mux_rcb = c->mux_rcb; 1925204917Sdes debug3("%s: new mux channel %d fd %d", __func__, 1926204917Sdes nc->self, nc->sock); 1927204917Sdes /* establish state */ 1928204917Sdes nc->mux_rcb(nc); 1929204917Sdes /* mux state transitions must not elicit protocol messages */ 1930204917Sdes nc->flags |= CHAN_LOCAL; 1931204917Sdes} 1932204917Sdes 1933162856Sdes/* ARGSUSED */ 193492559Sdesstatic void 1935162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 193660573Skris{ 193760573Skris int len; 1938106130Sdes 193960573Skris /* Send buffered output data to the socket. */ 194060573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 194160573Skris len = write(c->sock, buffer_ptr(&c->output), 194260573Skris buffer_len(&c->output)); 194360573Skris if (len <= 0) 194492559Sdes buffer_clear(&c->output); 194560573Skris else 194660573Skris buffer_consume(&c->output, len); 194760573Skris } 194860573Skris} 194960573Skris 195092559Sdesstatic void 195160573Skrischannel_handler_init_20(void) 195260573Skris{ 195392559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 195460573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 195560573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 195676262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 195760573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 195876262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 195976262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 196076262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1961204917Sdes channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 1962204917Sdes channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 196360573Skris 196492559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 196560573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 196676262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 196760573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 196876262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 196976262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 197092559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1971204917Sdes channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 1972204917Sdes channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 197360573Skris} 197460573Skris 197592559Sdesstatic void 197660573Skrischannel_handler_init_13(void) 197760573Skris{ 197860573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 197960573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 198060573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 198160573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 198260573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 198360573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 198460573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 198576262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 198676262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 198760573Skris 198892559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 198960573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 199060573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 199160573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 199260573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 199376262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 199492559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 199560573Skris} 199660573Skris 199792559Sdesstatic void 199860573Skrischannel_handler_init_15(void) 199960573Skris{ 200092559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 200160573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 200260573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 200360573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 200460573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 200576262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 200676262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 200760573Skris 200860573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 200960573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 201060573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 201192559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 201276262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 201392559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 201460573Skris} 201560573Skris 201692559Sdesstatic void 201760573Skrischannel_handler_init(void) 201860573Skris{ 201960573Skris int i; 2020106130Sdes 202192559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 202260573Skris channel_pre[i] = NULL; 202360573Skris channel_post[i] = NULL; 202460573Skris } 202560573Skris if (compat20) 202660573Skris channel_handler_init_20(); 202760573Skris else if (compat13) 202860573Skris channel_handler_init_13(); 202960573Skris else 203060573Skris channel_handler_init_15(); 203160573Skris} 203260573Skris 203392559Sdes/* gc dead channels */ 203492559Sdesstatic void 203592559Sdeschannel_garbage_collect(Channel *c) 203692559Sdes{ 203792559Sdes if (c == NULL) 203892559Sdes return; 203992559Sdes if (c->detach_user != NULL) { 2040157019Sdes if (!chan_is_dead(c, c->detach_close)) 204192559Sdes return; 2042124207Sdes debug2("channel %d: gc: notify user", c->self); 204392559Sdes c->detach_user(c->self, NULL); 204492559Sdes /* if we still have a callback */ 204592559Sdes if (c->detach_user != NULL) 204692559Sdes return; 2047124207Sdes debug2("channel %d: gc: user detached", c->self); 204892559Sdes } 204992559Sdes if (!chan_is_dead(c, 1)) 205092559Sdes return; 2051124207Sdes debug2("channel %d: garbage collecting", c->self); 205292559Sdes channel_free(c); 205392559Sdes} 205492559Sdes 205592559Sdesstatic void 2056162856Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) 205760573Skris{ 205860573Skris static int did_init = 0; 2059204917Sdes u_int i, oalloc; 206060573Skris Channel *c; 206160573Skris 206260573Skris if (!did_init) { 206360573Skris channel_handler_init(); 206460573Skris did_init = 1; 206560573Skris } 2066204917Sdes for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { 206792559Sdes c = channels[i]; 206892559Sdes if (c == NULL) 206957429Smarkm continue; 2070204917Sdes if (c->delayed) { 2071204917Sdes if (ftab == channel_pre) 2072204917Sdes c->delayed = 0; 2073204917Sdes else 2074204917Sdes continue; 2075204917Sdes } 207692559Sdes if (ftab[c->type] != NULL) 207792559Sdes (*ftab[c->type])(c, readset, writeset); 207892559Sdes channel_garbage_collect(c); 207957429Smarkm } 208057429Smarkm} 208157429Smarkm 208292559Sdes/* 208392559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 208492559Sdes * select bitmasks. 208592559Sdes */ 208660573Skrisvoid 208776262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2088137019Sdes u_int *nallocp, int rekeying) 208960573Skris{ 2090162856Sdes u_int n, sz, nfdset; 209176262Sgreen 209276262Sgreen n = MAX(*maxfdp, channel_max_fd); 209376262Sgreen 2094162856Sdes nfdset = howmany(n+1, NFDBITS); 2095162856Sdes /* Explicitly test here, because xrealloc isn't always called */ 2096162856Sdes if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) 2097162856Sdes fatal("channel_prepare_select: max_fd (%d) is too large", n); 2098162856Sdes sz = nfdset * sizeof(fd_mask); 2099162856Sdes 210092559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 210192559Sdes if (*readsetp == NULL || sz > *nallocp) { 2102162856Sdes *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); 2103162856Sdes *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); 210492559Sdes *nallocp = sz; 210576262Sgreen } 210692559Sdes *maxfdp = n; 210776262Sgreen memset(*readsetp, 0, sz); 210876262Sgreen memset(*writesetp, 0, sz); 210976262Sgreen 211076262Sgreen if (!rekeying) 211176262Sgreen channel_handler(channel_pre, *readsetp, *writesetp); 211260573Skris} 211360573Skris 211492559Sdes/* 211592559Sdes * After select, perform any appropriate operations for channels which have 211692559Sdes * events pending. 211792559Sdes */ 211860573Skrisvoid 2119162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset) 212060573Skris{ 212160573Skris channel_handler(channel_post, readset, writeset); 212260573Skris} 212360573Skris 212492559Sdes 212576262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 212660573Skrisvoid 212792559Sdeschannel_output_poll(void) 212857429Smarkm{ 212960573Skris Channel *c; 2130137019Sdes u_int i, len; 213157429Smarkm 213257429Smarkm for (i = 0; i < channels_alloc; i++) { 213392559Sdes c = channels[i]; 213492559Sdes if (c == NULL) 213592559Sdes continue; 213657429Smarkm 213792559Sdes /* 213892559Sdes * We are only interested in channels that can have buffered 213992559Sdes * incoming data. 214092559Sdes */ 214157429Smarkm if (compat13) { 214260573Skris if (c->type != SSH_CHANNEL_OPEN && 214360573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 214457429Smarkm continue; 214557429Smarkm } else { 214660573Skris if (c->type != SSH_CHANNEL_OPEN) 214757429Smarkm continue; 214857429Smarkm } 214960573Skris if (compat20 && 215060573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 215176262Sgreen /* XXX is this true? */ 215292559Sdes debug3("channel %d: will not send data after close", c->self); 215360573Skris continue; 215460573Skris } 215557429Smarkm 215657429Smarkm /* Get the amount of buffered data for this channel. */ 215776262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 215876262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 215976262Sgreen (len = buffer_len(&c->input)) > 0) { 2160157019Sdes if (c->datagram) { 2161157019Sdes if (len > 0) { 2162157019Sdes u_char *data; 2163157019Sdes u_int dlen; 2164157019Sdes 2165157019Sdes data = buffer_get_string(&c->input, 2166157019Sdes &dlen); 2167157019Sdes packet_start(SSH2_MSG_CHANNEL_DATA); 2168157019Sdes packet_put_int(c->remote_id); 2169157019Sdes packet_put_string(data, dlen); 2170157019Sdes packet_send(); 2171157019Sdes c->remote_window -= dlen + 4; 2172157019Sdes xfree(data); 2173157019Sdes } 2174157019Sdes continue; 2175157019Sdes } 217692559Sdes /* 217792559Sdes * Send some data for the other side over the secure 217892559Sdes * connection. 217992559Sdes */ 218060573Skris if (compat20) { 218160573Skris if (len > c->remote_window) 218260573Skris len = c->remote_window; 218360573Skris if (len > c->remote_maxpacket) 218460573Skris len = c->remote_maxpacket; 218557429Smarkm } else { 218660573Skris if (packet_is_interactive()) { 218760573Skris if (len > 1024) 218860573Skris len = 512; 218960573Skris } else { 219060573Skris /* Keep the packets at reasonable size. */ 219160573Skris if (len > packet_get_maxsize()/2) 219260573Skris len = packet_get_maxsize()/2; 219360573Skris } 219457429Smarkm } 219560573Skris if (len > 0) { 219660573Skris packet_start(compat20 ? 219760573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 219860573Skris packet_put_int(c->remote_id); 219960573Skris packet_put_string(buffer_ptr(&c->input), len); 220060573Skris packet_send(); 220160573Skris buffer_consume(&c->input, len); 220260573Skris c->remote_window -= len; 220360573Skris } 220460573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 220557429Smarkm if (compat13) 220657429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 220757429Smarkm /* 220857429Smarkm * input-buffer is empty and read-socket shutdown: 220998684Sdes * tell peer, that we will not send more data: send IEOF. 221098684Sdes * hack for extended data: delay EOF if EFD still in use. 221157429Smarkm */ 221298684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 2213149753Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 2214149753Sdes c->self, c->efd, buffer_len(&c->extended)); 221598684Sdes else 221698684Sdes chan_ibuf_empty(c); 221757429Smarkm } 221860573Skris /* Send extended data, i.e. stderr */ 221960573Skris if (compat20 && 222098684Sdes !(c->flags & CHAN_EOF_SENT) && 222160573Skris c->remote_window > 0 && 222260573Skris (len = buffer_len(&c->extended)) > 0 && 222360573Skris c->extended_usage == CHAN_EXTENDED_READ) { 222499063Sdes debug2("channel %d: rwin %u elen %u euse %d", 222576262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 222676262Sgreen c->extended_usage); 222760573Skris if (len > c->remote_window) 222860573Skris len = c->remote_window; 222960573Skris if (len > c->remote_maxpacket) 223060573Skris len = c->remote_maxpacket; 223160573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 223260573Skris packet_put_int(c->remote_id); 223360573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 223460573Skris packet_put_string(buffer_ptr(&c->extended), len); 223560573Skris packet_send(); 223660573Skris buffer_consume(&c->extended, len); 223760573Skris c->remote_window -= len; 223876262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 223960573Skris } 224057429Smarkm } 224157429Smarkm} 224257429Smarkm 224357429Smarkm 224492559Sdes/* -- protocol input */ 224592559Sdes 2246162856Sdes/* ARGSUSED */ 224760573Skrisvoid 224892559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 224957429Smarkm{ 225057429Smarkm int id; 225157429Smarkm char *data; 225276262Sgreen u_int data_len; 225360573Skris Channel *c; 225457429Smarkm 225557429Smarkm /* Get the channel number and verify it. */ 225657429Smarkm id = packet_get_int(); 225760573Skris c = channel_lookup(id); 225860573Skris if (c == NULL) 225957429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 226057429Smarkm 226157429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 226260573Skris if (c->type != SSH_CHANNEL_OPEN && 226360573Skris c->type != SSH_CHANNEL_X11_OPEN) 226457429Smarkm return; 226557429Smarkm 226657429Smarkm /* Get the data. */ 2267181111Sdes data = packet_get_string_ptr(&data_len); 226860573Skris 2269126273Sdes /* 2270126273Sdes * Ignore data for protocol > 1.3 if output end is no longer open. 2271126273Sdes * For protocol 2 the sending side is reducing its window as it sends 2272126273Sdes * data, so we must 'fake' consumption of the data in order to ensure 2273126273Sdes * that window updates are sent back. Otherwise the connection might 2274126273Sdes * deadlock. 2275126273Sdes */ 2276126273Sdes if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2277126273Sdes if (compat20) { 2278126273Sdes c->local_window -= data_len; 2279126273Sdes c->local_consumed += data_len; 2280126273Sdes } 2281126273Sdes return; 2282126273Sdes } 2283126273Sdes 228492559Sdes if (compat20) { 228560573Skris if (data_len > c->local_maxpacket) { 2286124207Sdes logit("channel %d: rcvd big packet %d, maxpack %d", 228760573Skris c->self, data_len, c->local_maxpacket); 228860573Skris } 228960573Skris if (data_len > c->local_window) { 2290124207Sdes logit("channel %d: rcvd too much data %d, win %d", 229160573Skris c->self, data_len, c->local_window); 229260573Skris return; 229360573Skris } 229460573Skris c->local_window -= data_len; 229560573Skris } 2296157019Sdes if (c->datagram) 2297157019Sdes buffer_put_string(&c->output, data, data_len); 2298157019Sdes else 2299157019Sdes buffer_append(&c->output, data, data_len); 2300181111Sdes packet_check_eom(); 230157429Smarkm} 230292559Sdes 2303162856Sdes/* ARGSUSED */ 230460573Skrisvoid 230592559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 230660573Skris{ 230760573Skris int id; 230860573Skris char *data; 230999063Sdes u_int data_len, tcode; 231060573Skris Channel *c; 231157429Smarkm 231260573Skris /* Get the channel number and verify it. */ 231360573Skris id = packet_get_int(); 231460573Skris c = channel_lookup(id); 231560573Skris 231660573Skris if (c == NULL) 231760573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 231860573Skris if (c->type != SSH_CHANNEL_OPEN) { 2319124207Sdes logit("channel %d: ext data for non open", id); 232060573Skris return; 232160573Skris } 232298684Sdes if (c->flags & CHAN_EOF_RCVD) { 232398684Sdes if (datafellows & SSH_BUG_EXTEOF) 232498684Sdes debug("channel %d: accepting ext data after eof", id); 232598684Sdes else 232698684Sdes packet_disconnect("Received extended_data after EOF " 232798684Sdes "on channel %d.", id); 232898684Sdes } 232960573Skris tcode = packet_get_int(); 233060573Skris if (c->efd == -1 || 233160573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 233260573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 2333124207Sdes logit("channel %d: bad ext data", c->self); 233460573Skris return; 233560573Skris } 233660573Skris data = packet_get_string(&data_len); 233792559Sdes packet_check_eom(); 233860573Skris if (data_len > c->local_window) { 2339124207Sdes logit("channel %d: rcvd too much extended_data %d, win %d", 234060573Skris c->self, data_len, c->local_window); 234160573Skris xfree(data); 234260573Skris return; 234360573Skris } 234469587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 234560573Skris c->local_window -= data_len; 234660573Skris buffer_append(&c->extended, data, data_len); 234760573Skris xfree(data); 234860573Skris} 234960573Skris 2350162856Sdes/* ARGSUSED */ 235160573Skrisvoid 235292559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 235360573Skris{ 235460573Skris int id; 235560573Skris Channel *c; 235657429Smarkm 235760573Skris id = packet_get_int(); 235892559Sdes packet_check_eom(); 235960573Skris c = channel_lookup(id); 236060573Skris if (c == NULL) 236160573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 236260573Skris chan_rcvd_ieof(c); 236392559Sdes 236492559Sdes /* XXX force input close */ 236592559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 236692559Sdes debug("channel %d: FORCE input drain", c->self); 236792559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 236892559Sdes if (buffer_len(&c->input) == 0) 236992559Sdes chan_ibuf_empty(c); 237092559Sdes } 237192559Sdes 237260573Skris} 237360573Skris 2374162856Sdes/* ARGSUSED */ 237560573Skrisvoid 237692559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 237757429Smarkm{ 237860573Skris int id; 237960573Skris Channel *c; 238057429Smarkm 238160573Skris id = packet_get_int(); 238292559Sdes packet_check_eom(); 238360573Skris c = channel_lookup(id); 238460573Skris if (c == NULL) 238560573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 238657429Smarkm 238757429Smarkm /* 238857429Smarkm * Send a confirmation that we have closed the channel and no more 238957429Smarkm * data is coming for it. 239057429Smarkm */ 239157429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 239260573Skris packet_put_int(c->remote_id); 239357429Smarkm packet_send(); 239457429Smarkm 239557429Smarkm /* 239657429Smarkm * If the channel is in closed state, we have sent a close request, 239757429Smarkm * and the other side will eventually respond with a confirmation. 239857429Smarkm * Thus, we cannot free the channel here, because then there would be 239957429Smarkm * no-one to receive the confirmation. The channel gets freed when 240057429Smarkm * the confirmation arrives. 240157429Smarkm */ 240260573Skris if (c->type != SSH_CHANNEL_CLOSED) { 240357429Smarkm /* 240457429Smarkm * Not a closed channel - mark it as draining, which will 240557429Smarkm * cause it to be freed later. 240657429Smarkm */ 240792559Sdes buffer_clear(&c->input); 240860573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 240957429Smarkm } 241057429Smarkm} 241157429Smarkm 241260573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2413162856Sdes/* ARGSUSED */ 241460573Skrisvoid 241592559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 241660573Skris{ 241760573Skris int id = packet_get_int(); 241860573Skris Channel *c = channel_lookup(id); 241992559Sdes 242092559Sdes packet_check_eom(); 242160573Skris if (c == NULL) 242260573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 242360573Skris chan_rcvd_oclose(c); 242460573Skris} 242557429Smarkm 2426162856Sdes/* ARGSUSED */ 242760573Skrisvoid 242892559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 242957429Smarkm{ 243060573Skris int id = packet_get_int(); 243160573Skris Channel *c = channel_lookup(id); 243257429Smarkm 243392559Sdes packet_check_eom(); 243460573Skris if (c == NULL) 243560573Skris packet_disconnect("Received close confirmation for " 243660573Skris "out-of-range channel %d.", id); 243760573Skris if (c->type != SSH_CHANNEL_CLOSED) 243860573Skris packet_disconnect("Received close confirmation for " 243960573Skris "non-closed channel %d (type %d).", id, c->type); 244092559Sdes channel_free(c); 244160573Skris} 244257429Smarkm 2443162856Sdes/* ARGSUSED */ 244460573Skrisvoid 244592559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 244660573Skris{ 244760573Skris int id, remote_id; 244860573Skris Channel *c; 244960573Skris 245060573Skris id = packet_get_int(); 245160573Skris c = channel_lookup(id); 245260573Skris 245360573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 245460573Skris packet_disconnect("Received open confirmation for " 245560573Skris "non-opening channel %d.", id); 245660573Skris remote_id = packet_get_int(); 245760573Skris /* Record the remote channel number and mark that the channel is now open. */ 245860573Skris c->remote_id = remote_id; 245960573Skris c->type = SSH_CHANNEL_OPEN; 246060573Skris 246160573Skris if (compat20) { 246260573Skris c->remote_window = packet_get_int(); 246360573Skris c->remote_maxpacket = packet_get_int(); 2464181111Sdes if (c->open_confirm) { 246569587Sgreen debug2("callback start"); 2466181111Sdes c->open_confirm(c->self, c->open_confirm_ctx); 246769587Sgreen debug2("callback done"); 246860573Skris } 2469124207Sdes debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 247060573Skris c->remote_window, c->remote_maxpacket); 247157429Smarkm } 247292559Sdes packet_check_eom(); 247357429Smarkm} 247457429Smarkm 247592559Sdesstatic char * 247692559Sdesreason2txt(int reason) 247792559Sdes{ 247892559Sdes switch (reason) { 247992559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 248092559Sdes return "administratively prohibited"; 248192559Sdes case SSH2_OPEN_CONNECT_FAILED: 248292559Sdes return "connect failed"; 248392559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 248492559Sdes return "unknown channel type"; 248592559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 248692559Sdes return "resource shortage"; 248792559Sdes } 248892559Sdes return "unknown reason"; 248992559Sdes} 249092559Sdes 2491162856Sdes/* ARGSUSED */ 249260573Skrisvoid 249392559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 249457429Smarkm{ 249576262Sgreen int id, reason; 249676262Sgreen char *msg = NULL, *lang = NULL; 249760573Skris Channel *c; 249857429Smarkm 249960573Skris id = packet_get_int(); 250060573Skris c = channel_lookup(id); 250157429Smarkm 250260573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 250360573Skris packet_disconnect("Received open failure for " 250460573Skris "non-opening channel %d.", id); 250560573Skris if (compat20) { 250676262Sgreen reason = packet_get_int(); 250792559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 250876262Sgreen msg = packet_get_string(NULL); 250976262Sgreen lang = packet_get_string(NULL); 251076262Sgreen } 2511124207Sdes logit("channel %d: open failed: %s%s%s", id, 251292559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 251376262Sgreen if (msg != NULL) 251476262Sgreen xfree(msg); 251576262Sgreen if (lang != NULL) 251676262Sgreen xfree(lang); 251760573Skris } 251892559Sdes packet_check_eom(); 2519192595Sdes /* Schedule the channel for cleanup/deletion. */ 2520192595Sdes chan_mark_dead(c); 252157429Smarkm} 252257429Smarkm 2523162856Sdes/* ARGSUSED */ 252460573Skrisvoid 252592559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 252660573Skris{ 252760573Skris Channel *c; 252899063Sdes int id; 252999063Sdes u_int adjust; 253057429Smarkm 253160573Skris if (!compat20) 253260573Skris return; 253360573Skris 253457429Smarkm /* Get the channel number and verify it. */ 253560573Skris id = packet_get_int(); 253660573Skris c = channel_lookup(id); 253757429Smarkm 2538157019Sdes if (c == NULL) { 2539157019Sdes logit("Received window adjust for non-open channel %d.", id); 254060573Skris return; 254160573Skris } 254260573Skris adjust = packet_get_int(); 254392559Sdes packet_check_eom(); 254499063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 254560573Skris c->remote_window += adjust; 254657429Smarkm} 254757429Smarkm 2548162856Sdes/* ARGSUSED */ 254960573Skrisvoid 255092559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 255157429Smarkm{ 255292559Sdes Channel *c = NULL; 255392559Sdes u_short host_port; 255492559Sdes char *host, *originator_string; 2555181111Sdes int remote_id; 255657429Smarkm 255792559Sdes remote_id = packet_get_int(); 255892559Sdes host = packet_get_string(NULL); 255992559Sdes host_port = packet_get_int(); 256057429Smarkm 256192559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 256292559Sdes originator_string = packet_get_string(NULL); 256392559Sdes } else { 256492559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 256592559Sdes } 256692559Sdes packet_check_eom(); 2567181111Sdes c = channel_connect_to(host, host_port, 2568181111Sdes "connected socket", originator_string); 2569124207Sdes xfree(originator_string); 2570181111Sdes xfree(host); 257192559Sdes if (c == NULL) { 257292559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 257392559Sdes packet_put_int(remote_id); 257492559Sdes packet_send(); 2575181111Sdes } else 2576181111Sdes c->remote_id = remote_id; 257757429Smarkm} 257857429Smarkm 2579181111Sdes/* ARGSUSED */ 2580181111Sdesvoid 2581181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2582181111Sdes{ 2583181111Sdes Channel *c; 2584181111Sdes struct channel_confirm *cc; 2585192595Sdes int id; 258657429Smarkm 2587181111Sdes /* Reset keepalive timeout */ 2588197679Sdes packet_set_alive_timeouts(0); 2589181111Sdes 2590192595Sdes id = packet_get_int(); 2591181111Sdes packet_check_eom(); 2592181111Sdes 2593192595Sdes debug2("channel_input_status_confirm: type %d id %d", type, id); 2594181111Sdes 2595192595Sdes if ((c = channel_lookup(id)) == NULL) { 2596192595Sdes logit("channel_input_status_confirm: %d: unknown", id); 2597181111Sdes return; 2598181111Sdes } 2599181111Sdes ; 2600181111Sdes if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2601181111Sdes return; 2602181111Sdes cc->cb(type, c, cc->ctx); 2603181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 2604181111Sdes bzero(cc, sizeof(*cc)); 2605181111Sdes xfree(cc); 2606181111Sdes} 2607181111Sdes 260892559Sdes/* -- tcp forwarding */ 260957429Smarkm 261092559Sdesvoid 261192559Sdeschannel_set_af(int af) 261276262Sgreen{ 261392559Sdes IPv4or6 = af; 261476262Sgreen} 261576262Sgreen 261692559Sdesstatic int 2617192595Sdeschannel_setup_fwd_listener(int type, const char *listen_addr, 2618192595Sdes u_short listen_port, int *allocated_listen_port, 261992559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 262057429Smarkm{ 262192559Sdes Channel *c; 2622157019Sdes int sock, r, success = 0, wildcard = 0, is_client; 262357429Smarkm struct addrinfo hints, *ai, *aitop; 2624147005Sdes const char *host, *addr; 262557429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2626192595Sdes in_port_t *lport_p; 262757429Smarkm 262892559Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 262992559Sdes listen_addr : host_to_connect; 2630147005Sdes is_client = (type == SSH_CHANNEL_PORT_LISTENER); 263157429Smarkm 263292559Sdes if (host == NULL) { 263392559Sdes error("No forward host name."); 2634149753Sdes return 0; 263576262Sgreen } 2636192595Sdes if (strlen(host) >= NI_MAXHOST) { 263776262Sgreen error("Forward host name too long."); 2638149753Sdes return 0; 263976262Sgreen } 264076262Sgreen 264157429Smarkm /* 2642147005Sdes * Determine whether or not a port forward listens to loopback, 2643147005Sdes * specified address or wildcard. On the client, a specified bind 2644147005Sdes * address will always override gateway_ports. On the server, a 2645147005Sdes * gateway_ports of 1 (``yes'') will override the client's 2646147005Sdes * specification and force a wildcard bind, whereas a value of 2 2647147005Sdes * (``clientspecified'') will bind to whatever address the client 2648147005Sdes * asked for. 2649147005Sdes * 2650147005Sdes * Special-case listen_addrs are: 2651147005Sdes * 2652147005Sdes * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2653147005Sdes * "" (empty string), "*" -> wildcard v4/v6 2654147005Sdes * "localhost" -> loopback v4/v6 2655147005Sdes */ 2656147005Sdes addr = NULL; 2657147005Sdes if (listen_addr == NULL) { 2658147005Sdes /* No address specified: default to gateway_ports setting */ 2659147005Sdes if (gateway_ports) 2660147005Sdes wildcard = 1; 2661147005Sdes } else if (gateway_ports || is_client) { 2662147005Sdes if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2663181111Sdes strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2664147005Sdes *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2665147005Sdes (!is_client && gateway_ports == 1)) 2666147005Sdes wildcard = 1; 2667147005Sdes else if (strcmp(listen_addr, "localhost") != 0) 2668147005Sdes addr = listen_addr; 2669147005Sdes } 2670147005Sdes 2671147005Sdes debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", 2672147005Sdes type, wildcard, (addr == NULL) ? "NULL" : addr); 2673147005Sdes 2674147005Sdes /* 267557429Smarkm * getaddrinfo returns a loopback address if the hostname is 267657429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 267757429Smarkm */ 267857429Smarkm memset(&hints, 0, sizeof(hints)); 267957429Smarkm hints.ai_family = IPv4or6; 2680147005Sdes hints.ai_flags = wildcard ? AI_PASSIVE : 0; 268157429Smarkm hints.ai_socktype = SOCK_STREAM; 268276262Sgreen snprintf(strport, sizeof strport, "%d", listen_port); 2683147005Sdes if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2684147005Sdes if (addr == NULL) { 2685147005Sdes /* This really shouldn't happen */ 2686147005Sdes packet_disconnect("getaddrinfo: fatal error: %s", 2687181111Sdes ssh_gai_strerror(r)); 2688147005Sdes } else { 2689149753Sdes error("channel_setup_fwd_listener: " 2690181111Sdes "getaddrinfo(%.64s): %s", addr, 2691181111Sdes ssh_gai_strerror(r)); 2692147005Sdes } 2693149753Sdes return 0; 2694147005Sdes } 2695192595Sdes if (allocated_listen_port != NULL) 2696192595Sdes *allocated_listen_port = 0; 269757429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 2698192595Sdes switch (ai->ai_family) { 2699192595Sdes case AF_INET: 2700192595Sdes lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 2701192595Sdes sin_port; 2702192595Sdes break; 2703192595Sdes case AF_INET6: 2704192595Sdes lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 2705192595Sdes sin6_port; 2706192595Sdes break; 2707192595Sdes default: 270857429Smarkm continue; 2709192595Sdes } 2710192595Sdes /* 2711192595Sdes * If allocating a port for -R forwards, then use the 2712192595Sdes * same port for all address families. 2713192595Sdes */ 2714192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2715192595Sdes allocated_listen_port != NULL && *allocated_listen_port > 0) 2716192595Sdes *lport_p = htons(*allocated_listen_port); 2717192595Sdes 271857429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 271957429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 272092559Sdes error("channel_setup_fwd_listener: getnameinfo failed"); 272157429Smarkm continue; 272257429Smarkm } 272357429Smarkm /* Create a port to listen for the host. */ 2724124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 272557429Smarkm if (sock < 0) { 272657429Smarkm /* this is no error since kernel may not support ipv6 */ 272757429Smarkm verbose("socket: %.100s", strerror(errno)); 272857429Smarkm continue; 272957429Smarkm } 2730106130Sdes 2731157019Sdes channel_set_reuseaddr(sock); 2732204917Sdes if (ai->ai_family == AF_INET6) 2733204917Sdes sock_set_v6only(sock); 2734157019Sdes 2735192595Sdes debug("Local forwarding listening on %s port %s.", 2736192595Sdes ntop, strport); 273757429Smarkm 273857429Smarkm /* Bind the socket to the address. */ 273957429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 274057429Smarkm /* address can be in use ipv6 address is already bound */ 274198941Sdes if (!ai->ai_next) 274298941Sdes error("bind: %.100s", strerror(errno)); 274398941Sdes else 274498941Sdes verbose("bind: %.100s", strerror(errno)); 274598941Sdes 274657429Smarkm close(sock); 274757429Smarkm continue; 274857429Smarkm } 274957429Smarkm /* Start listening for connections on the socket. */ 2750126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 275157429Smarkm error("listen: %.100s", strerror(errno)); 275257429Smarkm close(sock); 275357429Smarkm continue; 275457429Smarkm } 2755192595Sdes 2756192595Sdes /* 2757192595Sdes * listen_port == 0 requests a dynamically allocated port - 2758192595Sdes * record what we got. 2759192595Sdes */ 2760192595Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && 2761192595Sdes allocated_listen_port != NULL && 2762192595Sdes *allocated_listen_port == 0) { 2763192595Sdes *allocated_listen_port = get_sock_port(sock, 1); 2764192595Sdes debug("Allocated listen port %d", 2765192595Sdes *allocated_listen_port); 2766192595Sdes } 2767192595Sdes 276857429Smarkm /* Allocate a channel number for the socket. */ 276992559Sdes c = channel_new("port listener", type, sock, sock, -1, 277060573Skris CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2771124207Sdes 0, "port listener", 1); 2772192595Sdes c->path = xstrdup(host); 277392559Sdes c->host_port = port_to_connect; 277492559Sdes c->listening_port = listen_port; 277557429Smarkm success = 1; 277657429Smarkm } 277757429Smarkm if (success == 0) 277892559Sdes error("channel_setup_fwd_listener: cannot listen to port: %d", 277976262Sgreen listen_port); 278057429Smarkm freeaddrinfo(aitop); 278176262Sgreen return success; 278257429Smarkm} 278357429Smarkm 2784137019Sdesint 2785137019Sdeschannel_cancel_rport_listener(const char *host, u_short port) 2786137019Sdes{ 2787137019Sdes u_int i; 2788137019Sdes int found = 0; 2789137019Sdes 2790147005Sdes for (i = 0; i < channels_alloc; i++) { 2791137019Sdes Channel *c = channels[i]; 2792137019Sdes 2793137019Sdes if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && 2794192595Sdes strcmp(c->path, host) == 0 && c->listening_port == port) { 2795147005Sdes debug2("%s: close channel %d", __func__, i); 2796137019Sdes channel_free(c); 2797137019Sdes found = 1; 2798137019Sdes } 2799137019Sdes } 2800137019Sdes 2801137019Sdes return (found); 2802137019Sdes} 2803137019Sdes 280492559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 280592559Sdesint 2806147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, 280792559Sdes const char *host_to_connect, u_short port_to_connect, int gateway_ports) 280892559Sdes{ 280992559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2810192595Sdes listen_host, listen_port, NULL, host_to_connect, port_to_connect, 2811147005Sdes gateway_ports); 281292559Sdes} 281392559Sdes 281492559Sdes/* protocol v2 remote port fwd, used by sshd */ 281592559Sdesint 281692559Sdeschannel_setup_remote_fwd_listener(const char *listen_address, 2817192595Sdes u_short listen_port, int *allocated_listen_port, int gateway_ports) 281892559Sdes{ 281992559Sdes return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2820192595Sdes listen_address, listen_port, allocated_listen_port, 2821192595Sdes NULL, 0, gateway_ports); 282292559Sdes} 282392559Sdes 282457429Smarkm/* 282557429Smarkm * Initiate forwarding of connections to port "port" on remote host through 282657429Smarkm * the secure channel to host:port from local side. 282757429Smarkm */ 282857429Smarkm 2829162856Sdesint 2830147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port, 283176262Sgreen const char *host_to_connect, u_short port_to_connect) 283257429Smarkm{ 283392559Sdes int type, success = 0; 283476262Sgreen 283557429Smarkm /* Record locally that connection to this host/port is permitted. */ 283657429Smarkm if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 283757429Smarkm fatal("channel_request_remote_forwarding: too many forwards"); 283857429Smarkm 283957429Smarkm /* Send the forward request to the remote side. */ 284060573Skris if (compat20) { 2841147005Sdes const char *address_to_bind; 2842181111Sdes if (listen_host == NULL) { 2843181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2844181111Sdes address_to_bind = "127.0.0.1"; 2845181111Sdes else 2846181111Sdes address_to_bind = "localhost"; 2847181111Sdes } else if (*listen_host == '\0' || 2848181111Sdes strcmp(listen_host, "*") == 0) { 2849181111Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 2850181111Sdes address_to_bind = "0.0.0.0"; 2851181111Sdes else 2852181111Sdes address_to_bind = ""; 2853181111Sdes } else 2854147005Sdes address_to_bind = listen_host; 2855147005Sdes 285660573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 285760573Skris packet_put_cstring("tcpip-forward"); 285898684Sdes packet_put_char(1); /* boolean: want reply */ 285960573Skris packet_put_cstring(address_to_bind); 286060573Skris packet_put_int(listen_port); 286176262Sgreen packet_send(); 286276262Sgreen packet_write_wait(); 286376262Sgreen /* Assume that server accepts the request */ 286476262Sgreen success = 1; 286560573Skris } else { 286660573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 286760573Skris packet_put_int(listen_port); 286860573Skris packet_put_cstring(host_to_connect); 286960573Skris packet_put_int(port_to_connect); 287060573Skris packet_send(); 287160573Skris packet_write_wait(); 287276262Sgreen 287376262Sgreen /* Wait for response from the remote side. */ 287492559Sdes type = packet_read(); 287576262Sgreen switch (type) { 287676262Sgreen case SSH_SMSG_SUCCESS: 287776262Sgreen success = 1; 287876262Sgreen break; 287976262Sgreen case SSH_SMSG_FAILURE: 288076262Sgreen break; 288176262Sgreen default: 288276262Sgreen /* Unknown packet */ 288376262Sgreen packet_disconnect("Protocol error for port forward request:" 288476262Sgreen "received packet type %d.", type); 288576262Sgreen } 288660573Skris } 288776262Sgreen if (success) { 288876262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 288976262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 289076262Sgreen permitted_opens[num_permitted_opens].listen_port = listen_port; 289176262Sgreen num_permitted_opens++; 289276262Sgreen } 2893162856Sdes return (success ? 0 : -1); 289457429Smarkm} 289557429Smarkm 289657429Smarkm/* 2897137019Sdes * Request cancellation of remote forwarding of connection host:port from 2898137019Sdes * local side. 2899137019Sdes */ 2900137019Sdesvoid 2901147005Sdeschannel_request_rforward_cancel(const char *host, u_short port) 2902137019Sdes{ 2903137019Sdes int i; 2904137019Sdes 2905137019Sdes if (!compat20) 2906137019Sdes return; 2907137019Sdes 2908137019Sdes for (i = 0; i < num_permitted_opens; i++) { 2909137019Sdes if (permitted_opens[i].host_to_connect != NULL && 2910137019Sdes permitted_opens[i].listen_port == port) 2911137019Sdes break; 2912137019Sdes } 2913137019Sdes if (i >= num_permitted_opens) { 2914137019Sdes debug("%s: requested forward not found", __func__); 2915137019Sdes return; 2916137019Sdes } 2917137019Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 2918137019Sdes packet_put_cstring("cancel-tcpip-forward"); 2919137019Sdes packet_put_char(0); 2920147005Sdes packet_put_cstring(host == NULL ? "" : host); 2921137019Sdes packet_put_int(port); 2922137019Sdes packet_send(); 2923137019Sdes 2924137019Sdes permitted_opens[i].listen_port = 0; 2925137019Sdes permitted_opens[i].port_to_connect = 0; 2926157019Sdes xfree(permitted_opens[i].host_to_connect); 2927137019Sdes permitted_opens[i].host_to_connect = NULL; 2928137019Sdes} 2929137019Sdes 2930137019Sdes/* 293157429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 293257429Smarkm * listening for the port, and sends back a success reply (or disconnect 2933162856Sdes * message if there was an error). 293457429Smarkm */ 2935162856Sdesint 293660573Skrischannel_input_port_forward_request(int is_root, int gateway_ports) 293757429Smarkm{ 293857429Smarkm u_short port, host_port; 2939162856Sdes int success = 0; 294057429Smarkm char *hostname; 294157429Smarkm 294257429Smarkm /* Get arguments from the packet. */ 294357429Smarkm port = packet_get_int(); 294457429Smarkm hostname = packet_get_string(NULL); 294557429Smarkm host_port = packet_get_int(); 294657429Smarkm 294798941Sdes#ifndef HAVE_CYGWIN 294857429Smarkm /* 294957429Smarkm * Check that an unprivileged user is not trying to forward a 295057429Smarkm * privileged port. 295157429Smarkm */ 295257429Smarkm if (port < IPPORT_RESERVED && !is_root) 2953124207Sdes packet_disconnect( 2954124207Sdes "Requested forwarding of port %d but user is not root.", 2955124207Sdes port); 2956124207Sdes if (host_port == 0) 2957124207Sdes packet_disconnect("Dynamic forwarding denied."); 295898941Sdes#endif 2959124207Sdes 296076262Sgreen /* Initiate forwarding */ 2961162856Sdes success = channel_setup_local_fwd_listener(NULL, port, hostname, 2962147005Sdes host_port, gateway_ports); 296357429Smarkm 296457429Smarkm /* Free the argument string. */ 296557429Smarkm xfree(hostname); 2966162856Sdes 2967162856Sdes return (success ? 0 : -1); 296857429Smarkm} 296957429Smarkm 297076262Sgreen/* 297176262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 297276262Sgreen * usually called by the server, because the user could connect to any port 297376262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 297476262Sgreen */ 297576262Sgreenvoid 297692559Sdeschannel_permit_all_opens(void) 297776262Sgreen{ 297876262Sgreen if (num_permitted_opens == 0) 297976262Sgreen all_opens_permitted = 1; 298076262Sgreen} 298176262Sgreen 298276262Sgreenvoid 298376262Sgreenchannel_add_permitted_opens(char *host, int port) 298476262Sgreen{ 298576262Sgreen if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2986162856Sdes fatal("channel_add_permitted_opens: too many forwards"); 298776262Sgreen debug("allow port forwarding to host %s port %d", host, port); 298876262Sgreen 298976262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 299076262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 299176262Sgreen num_permitted_opens++; 299276262Sgreen 299376262Sgreen all_opens_permitted = 0; 299476262Sgreen} 299576262Sgreen 2996162856Sdesint 2997162856Sdeschannel_add_adm_permitted_opens(char *host, int port) 2998162856Sdes{ 2999162856Sdes if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 3000162856Sdes fatal("channel_add_adm_permitted_opens: too many forwards"); 3001162856Sdes debug("config allows port forwarding to host %s port %d", host, port); 3002162856Sdes 3003162856Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect 3004162856Sdes = xstrdup(host); 3005162856Sdes permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 3006162856Sdes return ++num_adm_permitted_opens; 3007162856Sdes} 3008162856Sdes 300976262Sgreenvoid 301076262Sgreenchannel_clear_permitted_opens(void) 301176262Sgreen{ 301276262Sgreen int i; 301376262Sgreen 301476262Sgreen for (i = 0; i < num_permitted_opens; i++) 3015137019Sdes if (permitted_opens[i].host_to_connect != NULL) 3016137019Sdes xfree(permitted_opens[i].host_to_connect); 301776262Sgreen num_permitted_opens = 0; 3018162856Sdes} 301976262Sgreen 3020162856Sdesvoid 3021162856Sdeschannel_clear_adm_permitted_opens(void) 3022162856Sdes{ 3023162856Sdes int i; 3024162856Sdes 3025162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3026162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 3027162856Sdes xfree(permitted_adm_opens[i].host_to_connect); 3028162856Sdes num_adm_permitted_opens = 0; 302976262Sgreen} 303076262Sgreen 3031181111Sdesvoid 3032181111Sdeschannel_print_adm_permitted_opens(void) 3033181111Sdes{ 3034181111Sdes int i; 3035181111Sdes 3036192595Sdes printf("permitopen"); 3037192595Sdes if (num_adm_permitted_opens == 0) { 3038192595Sdes printf(" any\n"); 3039192595Sdes return; 3040192595Sdes } 3041181111Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3042181111Sdes if (permitted_adm_opens[i].host_to_connect != NULL) 3043181111Sdes printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 3044181111Sdes permitted_adm_opens[i].port_to_connect); 3045192595Sdes printf("\n"); 3046181111Sdes} 3047181111Sdes 3048181111Sdes/* Try to start non-blocking connect to next host in cctx list */ 304992559Sdesstatic int 3050181111Sdesconnect_next(struct channel_connect *cctx) 305160573Skris{ 3052181111Sdes int sock, saved_errno; 305360573Skris char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 305460573Skris 3055181111Sdes for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 3056181111Sdes if (cctx->ai->ai_family != AF_INET && 3057181111Sdes cctx->ai->ai_family != AF_INET6) 305860573Skris continue; 3059181111Sdes if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 3060181111Sdes ntop, sizeof(ntop), strport, sizeof(strport), 3061181111Sdes NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3062181111Sdes error("connect_next: getnameinfo failed"); 306360573Skris continue; 306460573Skris } 3065181111Sdes if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 3066181111Sdes cctx->ai->ai_protocol)) == -1) { 3067181111Sdes if (cctx->ai->ai_next == NULL) 3068113911Sdes error("socket: %.100s", strerror(errno)); 3069113911Sdes else 3070113911Sdes verbose("socket: %.100s", strerror(errno)); 307160573Skris continue; 307260573Skris } 3073137019Sdes if (set_nonblock(sock) == -1) 3074137019Sdes fatal("%s: set_nonblock(%d)", __func__, sock); 3075181111Sdes if (connect(sock, cctx->ai->ai_addr, 3076181111Sdes cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 3077181111Sdes debug("connect_next: host %.100s ([%.100s]:%s): " 3078181111Sdes "%.100s", cctx->host, ntop, strport, 307960573Skris strerror(errno)); 3080181111Sdes saved_errno = errno; 308160573Skris close(sock); 3082181111Sdes errno = saved_errno; 308376262Sgreen continue; /* fail -- try next */ 308460573Skris } 3085181111Sdes debug("connect_next: host %.100s ([%.100s]:%s) " 3086181111Sdes "in progress, fd=%d", cctx->host, ntop, strport, sock); 3087181111Sdes cctx->ai = cctx->ai->ai_next; 3088181111Sdes set_nodelay(sock); 3089181111Sdes return sock; 3090181111Sdes } 3091181111Sdes return -1; 3092181111Sdes} 309360573Skris 3094181111Sdesstatic void 3095181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx) 3096181111Sdes{ 3097181111Sdes xfree(cctx->host); 3098181111Sdes if (cctx->aitop) 3099181111Sdes freeaddrinfo(cctx->aitop); 3100181111Sdes bzero(cctx, sizeof(*cctx)); 3101181111Sdes cctx->host = NULL; 3102181111Sdes cctx->ai = cctx->aitop = NULL; 3103181111Sdes} 3104181111Sdes 3105181111Sdes/* Return CONNECTING channel to remote host, port */ 3106181111Sdesstatic Channel * 3107181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname) 3108181111Sdes{ 3109181111Sdes struct addrinfo hints; 3110181111Sdes int gaierr; 3111181111Sdes int sock = -1; 3112181111Sdes char strport[NI_MAXSERV]; 3113181111Sdes struct channel_connect cctx; 3114181111Sdes Channel *c; 3115181111Sdes 3116181111Sdes memset(&cctx, 0, sizeof(cctx)); 3117181111Sdes memset(&hints, 0, sizeof(hints)); 3118181111Sdes hints.ai_family = IPv4or6; 3119181111Sdes hints.ai_socktype = SOCK_STREAM; 3120181111Sdes snprintf(strport, sizeof strport, "%d", port); 3121181111Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { 3122181111Sdes error("connect_to %.100s: unknown host (%s)", host, 3123181111Sdes ssh_gai_strerror(gaierr)); 3124181111Sdes return NULL; 312560573Skris } 3126181111Sdes 3127181111Sdes cctx.host = xstrdup(host); 3128181111Sdes cctx.port = port; 3129181111Sdes cctx.ai = cctx.aitop; 3130181111Sdes 3131181111Sdes if ((sock = connect_next(&cctx)) == -1) { 3132181111Sdes error("connect to %.100s port %d failed: %s", 3133181111Sdes host, port, strerror(errno)); 3134181111Sdes channel_connect_ctx_free(&cctx); 3135181111Sdes return NULL; 313660573Skris } 3137181111Sdes c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 3138181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 3139181111Sdes c->connect_ctx = cctx; 3140181111Sdes return c; 314160573Skris} 314276262Sgreen 3143181111SdesChannel * 3144181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) 314576262Sgreen{ 314676262Sgreen int i; 314776262Sgreen 3148181111Sdes for (i = 0; i < num_permitted_opens; i++) { 3149137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3150181111Sdes permitted_opens[i].listen_port == listen_port) { 315176262Sgreen return connect_to( 315276262Sgreen permitted_opens[i].host_to_connect, 3153181111Sdes permitted_opens[i].port_to_connect, ctype, rname); 3154181111Sdes } 3155181111Sdes } 315676262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 315776262Sgreen listen_port); 3158181111Sdes return NULL; 315976262Sgreen} 316076262Sgreen 316176262Sgreen/* Check if connecting to that port is permitted and connect. */ 3162181111SdesChannel * 3163181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname) 316476262Sgreen{ 3165162856Sdes int i, permit, permit_adm = 1; 316676262Sgreen 316776262Sgreen permit = all_opens_permitted; 316876262Sgreen if (!permit) { 316976262Sgreen for (i = 0; i < num_permitted_opens; i++) 3170137019Sdes if (permitted_opens[i].host_to_connect != NULL && 3171137019Sdes permitted_opens[i].port_to_connect == port && 317276262Sgreen strcmp(permitted_opens[i].host_to_connect, host) == 0) 317376262Sgreen permit = 1; 3174162856Sdes } 317576262Sgreen 3176162856Sdes if (num_adm_permitted_opens > 0) { 3177162856Sdes permit_adm = 0; 3178162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3179162856Sdes if (permitted_adm_opens[i].host_to_connect != NULL && 3180162856Sdes permitted_adm_opens[i].port_to_connect == port && 3181162856Sdes strcmp(permitted_adm_opens[i].host_to_connect, host) 3182162856Sdes == 0) 3183162856Sdes permit_adm = 1; 318476262Sgreen } 3185162856Sdes 3186162856Sdes if (!permit || !permit_adm) { 3187124207Sdes logit("Received request to connect to host %.100s port %d, " 318876262Sgreen "but the request was denied.", host, port); 3189181111Sdes return NULL; 319076262Sgreen } 3191181111Sdes return connect_to(host, port, ctype, rname); 319276262Sgreen} 319376262Sgreen 3194137019Sdesvoid 3195137019Sdeschannel_send_window_changes(void) 3196137019Sdes{ 3197137019Sdes u_int i; 3198137019Sdes struct winsize ws; 3199137019Sdes 3200137019Sdes for (i = 0; i < channels_alloc; i++) { 3201147005Sdes if (channels[i] == NULL || !channels[i]->client_tty || 3202137019Sdes channels[i]->type != SSH_CHANNEL_OPEN) 3203137019Sdes continue; 3204137019Sdes if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 3205137019Sdes continue; 3206137019Sdes channel_request_start(i, "window-change", 0); 3207162856Sdes packet_put_int((u_int)ws.ws_col); 3208162856Sdes packet_put_int((u_int)ws.ws_row); 3209162856Sdes packet_put_int((u_int)ws.ws_xpixel); 3210162856Sdes packet_put_int((u_int)ws.ws_ypixel); 3211137019Sdes packet_send(); 3212137019Sdes } 3213137019Sdes} 3214137019Sdes 321592559Sdes/* -- X11 forwarding */ 321657429Smarkm 321757429Smarkm/* 321857429Smarkm * Creates an internet domain socket for listening for X11 connections. 321999063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 322099063Sdes * stored in display_numberp , or -1 if an error occurs. 322157429Smarkm */ 322292559Sdesint 322392559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 3224149753Sdes int single_connection, u_int *display_numberp, int **chanids) 322557429Smarkm{ 322692559Sdes Channel *nc = NULL; 322757429Smarkm int display_number, sock; 322857429Smarkm u_short port; 322957429Smarkm struct addrinfo hints, *ai, *aitop; 323057429Smarkm char strport[NI_MAXSERV]; 323157429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 323257429Smarkm 3233157019Sdes if (chanids == NULL) 3234157019Sdes return -1; 3235157019Sdes 323657429Smarkm for (display_number = x11_display_offset; 323792559Sdes display_number < MAX_DISPLAYS; 323892559Sdes display_number++) { 323957429Smarkm port = 6000 + display_number; 324057429Smarkm memset(&hints, 0, sizeof(hints)); 324157429Smarkm hints.ai_family = IPv4or6; 324292559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 324357429Smarkm hints.ai_socktype = SOCK_STREAM; 324457429Smarkm snprintf(strport, sizeof strport, "%d", port); 324557429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 3246181111Sdes error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 324792559Sdes return -1; 324857429Smarkm } 324957429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 325057429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 325157429Smarkm continue; 3252124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, 3253124207Sdes ai->ai_protocol); 325457429Smarkm if (sock < 0) { 3255207319Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 3256207319Sdes#ifdef EPFNOSUPPORT 3257207319Sdes && (errno != EPFNOSUPPORT) 3258207319Sdes#endif 3259207319Sdes ) { 326098941Sdes error("socket: %.100s", strerror(errno)); 3261137019Sdes freeaddrinfo(aitop); 326298941Sdes return -1; 326398941Sdes } else { 326498941Sdes debug("x11_create_display_inet: Socket family %d not supported", 326598941Sdes ai->ai_family); 326698941Sdes continue; 326798941Sdes } 326857429Smarkm } 3269204917Sdes if (ai->ai_family == AF_INET6) 3270204917Sdes sock_set_v6only(sock); 3271181111Sdes if (x11_use_localhost) 3272181111Sdes channel_set_reuseaddr(sock); 327357429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3274124207Sdes debug2("bind port %d: %.100s", port, strerror(errno)); 327557429Smarkm close(sock); 327698941Sdes 327757429Smarkm for (n = 0; n < num_socks; n++) { 327857429Smarkm close(socks[n]); 327957429Smarkm } 328057429Smarkm num_socks = 0; 328157429Smarkm break; 328257429Smarkm } 328357429Smarkm socks[num_socks++] = sock; 328457429Smarkm if (num_socks == NUM_SOCKS) 328557429Smarkm break; 328657429Smarkm } 328776262Sgreen freeaddrinfo(aitop); 328857429Smarkm if (num_socks > 0) 328957429Smarkm break; 329057429Smarkm } 329157429Smarkm if (display_number >= MAX_DISPLAYS) { 329257429Smarkm error("Failed to allocate internet-domain X11 display socket."); 329392559Sdes return -1; 329457429Smarkm } 329557429Smarkm /* Start listening for connections on the socket. */ 329657429Smarkm for (n = 0; n < num_socks; n++) { 329757429Smarkm sock = socks[n]; 3298126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 329957429Smarkm error("listen: %.100s", strerror(errno)); 330057429Smarkm close(sock); 330192559Sdes return -1; 330257429Smarkm } 330357429Smarkm } 330457429Smarkm 330557429Smarkm /* Allocate a channel for each socket. */ 3306162856Sdes *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 330757429Smarkm for (n = 0; n < num_socks; n++) { 330857429Smarkm sock = socks[n]; 330992559Sdes nc = channel_new("x11 listener", 331060573Skris SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 331160573Skris CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3312124207Sdes 0, "X11 inet listener", 1); 331392559Sdes nc->single_connection = single_connection; 3314157019Sdes (*chanids)[n] = nc->self; 331557429Smarkm } 3316157019Sdes (*chanids)[n] = -1; 331757429Smarkm 331892559Sdes /* Return the display number for the DISPLAY environment variable. */ 331999063Sdes *display_numberp = display_number; 332099063Sdes return (0); 332157429Smarkm} 332257429Smarkm 332392559Sdesstatic int 3324192595Sdesconnect_local_xsocket_path(const char *pathname) 332557429Smarkm{ 332657429Smarkm int sock; 332757429Smarkm struct sockaddr_un addr; 332857429Smarkm 332992559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 333092559Sdes if (sock < 0) 333192559Sdes error("socket: %.100s", strerror(errno)); 333292559Sdes memset(&addr, 0, sizeof(addr)); 333392559Sdes addr.sun_family = AF_UNIX; 3334192595Sdes strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 3335162856Sdes if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 333692559Sdes return sock; 333792559Sdes close(sock); 333857429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 333957429Smarkm return -1; 334057429Smarkm} 334157429Smarkm 3342192595Sdesstatic int 3343192595Sdesconnect_local_xsocket(u_int dnr) 3344192595Sdes{ 3345192595Sdes char buf[1024]; 3346192595Sdes snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 3347192595Sdes return connect_local_xsocket_path(buf); 3348192595Sdes} 3349192595Sdes 335060573Skrisint 335160573Skrisx11_connect_display(void) 335257429Smarkm{ 3353162856Sdes u_int display_number; 335457429Smarkm const char *display; 335560573Skris char buf[1024], *cp; 335657429Smarkm struct addrinfo hints, *ai, *aitop; 335757429Smarkm char strport[NI_MAXSERV]; 3358162856Sdes int gaierr, sock = 0; 335957429Smarkm 336057429Smarkm /* Try to open a socket for the local X server. */ 336157429Smarkm display = getenv("DISPLAY"); 336257429Smarkm if (!display) { 336357429Smarkm error("DISPLAY not set."); 336460573Skris return -1; 336557429Smarkm } 336657429Smarkm /* 336757429Smarkm * Now we decode the value of the DISPLAY variable and make a 336857429Smarkm * connection to the real X server. 336957429Smarkm */ 337057429Smarkm 3371192595Sdes /* Check if the display is from launchd. */ 3372192595Sdes#ifdef __APPLE__ 3373192595Sdes if (strncmp(display, "/tmp/launch", 11) == 0) { 3374192595Sdes sock = connect_local_xsocket_path(display); 3375192595Sdes if (sock < 0) 3376192595Sdes return -1; 3377192595Sdes 3378192595Sdes /* OK, we now have a connection to the display. */ 3379192595Sdes return sock; 3380192595Sdes } 3381192595Sdes#endif 338257429Smarkm /* 338357429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 338457429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 338557429Smarkm */ 338657429Smarkm if (strncmp(display, "unix:", 5) == 0 || 338757429Smarkm display[0] == ':') { 338857429Smarkm /* Connect to the unix domain socket. */ 3389162856Sdes if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 339057429Smarkm error("Could not parse display number from DISPLAY: %.100s", 339192559Sdes display); 339260573Skris return -1; 339357429Smarkm } 339457429Smarkm /* Create a socket. */ 339557429Smarkm sock = connect_local_xsocket(display_number); 339657429Smarkm if (sock < 0) 339760573Skris return -1; 339857429Smarkm 339957429Smarkm /* OK, we now have a connection to the display. */ 340060573Skris return sock; 340157429Smarkm } 340257429Smarkm /* 340357429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 340457429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 340557429Smarkm */ 340692559Sdes strlcpy(buf, display, sizeof(buf)); 340757429Smarkm cp = strchr(buf, ':'); 340857429Smarkm if (!cp) { 340957429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 341060573Skris return -1; 341157429Smarkm } 341257429Smarkm *cp = 0; 341357429Smarkm /* buf now contains the host name. But first we parse the display number. */ 3414162856Sdes if (sscanf(cp + 1, "%u", &display_number) != 1) { 341557429Smarkm error("Could not parse display number from DISPLAY: %.100s", 341692559Sdes display); 341760573Skris return -1; 341857429Smarkm } 341957429Smarkm 342057429Smarkm /* Look up the host address */ 342157429Smarkm memset(&hints, 0, sizeof(hints)); 342257429Smarkm hints.ai_family = IPv4or6; 342357429Smarkm hints.ai_socktype = SOCK_STREAM; 3424162856Sdes snprintf(strport, sizeof strport, "%u", 6000 + display_number); 342557429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 3426181111Sdes error("%.100s: unknown host. (%s)", buf, 3427181111Sdes ssh_gai_strerror(gaierr)); 342860573Skris return -1; 342957429Smarkm } 343057429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 343157429Smarkm /* Create a socket. */ 3432124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 343357429Smarkm if (sock < 0) { 3434124207Sdes debug2("socket: %.100s", strerror(errno)); 343560573Skris continue; 343660573Skris } 343760573Skris /* Connect it to the display. */ 343860573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3439162856Sdes debug2("connect %.100s port %u: %.100s", buf, 344060573Skris 6000 + display_number, strerror(errno)); 344160573Skris close(sock); 344260573Skris continue; 344360573Skris } 344460573Skris /* Success */ 344560573Skris break; 344657429Smarkm } 344757429Smarkm freeaddrinfo(aitop); 344857429Smarkm if (!ai) { 3449162856Sdes error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 345057429Smarkm strerror(errno)); 345160573Skris return -1; 345257429Smarkm } 345392559Sdes set_nodelay(sock); 345460573Skris return sock; 345560573Skris} 345657429Smarkm 345760573Skris/* 345860573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 345960573Skris * the remote channel number. We should do whatever we want, and respond 346060573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 346160573Skris */ 346257429Smarkm 3463162856Sdes/* ARGSUSED */ 346460573Skrisvoid 346592559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 346660573Skris{ 346792559Sdes Channel *c = NULL; 346892559Sdes int remote_id, sock = 0; 346960573Skris char *remote_host; 347057429Smarkm 347192559Sdes debug("Received X11 open request."); 347257429Smarkm 347392559Sdes remote_id = packet_get_int(); 347492559Sdes 347592559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 347692559Sdes remote_host = packet_get_string(NULL); 347760573Skris } else { 347860573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 347960573Skris } 348092559Sdes packet_check_eom(); 348160573Skris 348260573Skris /* Obtain a connection to the real X display. */ 348360573Skris sock = x11_connect_display(); 348492559Sdes if (sock != -1) { 348592559Sdes /* Allocate a channel for this connection. */ 348692559Sdes c = channel_new("connected x11 socket", 348792559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 348892559Sdes remote_host, 1); 348992559Sdes c->remote_id = remote_id; 349092559Sdes c->force_drain = 1; 349192559Sdes } 3492124207Sdes xfree(remote_host); 349392559Sdes if (c == NULL) { 349460573Skris /* Send refusal to the remote host. */ 349560573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 349692559Sdes packet_put_int(remote_id); 349760573Skris } else { 349860573Skris /* Send a confirmation to the remote host. */ 349960573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 350092559Sdes packet_put_int(remote_id); 350192559Sdes packet_put_int(c->self); 350260573Skris } 350392559Sdes packet_send(); 350457429Smarkm} 350557429Smarkm 350669587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 3507162856Sdes/* ARGSUSED */ 350869587Sgreenvoid 350992559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 351069587Sgreen{ 351169587Sgreen int rchan = packet_get_int(); 3512106130Sdes 351392559Sdes switch (type) { 351469587Sgreen case SSH_SMSG_AGENT_OPEN: 351569587Sgreen error("Warning: ssh server tried agent forwarding."); 351669587Sgreen break; 351769587Sgreen case SSH_SMSG_X11_OPEN: 351869587Sgreen error("Warning: ssh server tried X11 forwarding."); 351969587Sgreen break; 352069587Sgreen default: 352192559Sdes error("deny_input_open: type %d", type); 352269587Sgreen break; 352369587Sgreen } 3524157019Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 352569587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 352669587Sgreen packet_put_int(rchan); 352769587Sgreen packet_send(); 352869587Sgreen} 352969587Sgreen 353057429Smarkm/* 353157429Smarkm * Requests forwarding of X11 connections, generates fake authentication 353257429Smarkm * data, and enables authentication spoofing. 353392559Sdes * This should be called in the client only. 353457429Smarkm */ 353560573Skrisvoid 3536149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 353760573Skris const char *proto, const char *data) 353857429Smarkm{ 353976262Sgreen u_int data_len = (u_int) strlen(data) / 2; 3540149753Sdes u_int i, value; 354157429Smarkm char *new_data; 354257429Smarkm int screen_number; 354357429Smarkm const char *cp; 3544137019Sdes u_int32_t rnd = 0; 354557429Smarkm 3546149753Sdes if (x11_saved_display == NULL) 3547149753Sdes x11_saved_display = xstrdup(disp); 3548149753Sdes else if (strcmp(disp, x11_saved_display) != 0) { 3549149753Sdes error("x11_request_forwarding_with_spoofing: different " 3550149753Sdes "$DISPLAY already forwarded"); 3551149753Sdes return; 3552149753Sdes } 3553149753Sdes 3554162856Sdes cp = strchr(disp, ':'); 355557429Smarkm if (cp) 355657429Smarkm cp = strchr(cp, '.'); 355757429Smarkm if (cp) 3558162856Sdes screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 355957429Smarkm else 356057429Smarkm screen_number = 0; 356157429Smarkm 3562149753Sdes if (x11_saved_proto == NULL) { 3563149753Sdes /* Save protocol name. */ 3564149753Sdes x11_saved_proto = xstrdup(proto); 3565149753Sdes /* 3566149753Sdes * Extract real authentication data and generate fake data 3567149753Sdes * of the same length. 3568149753Sdes */ 3569149753Sdes x11_saved_data = xmalloc(data_len); 3570149753Sdes x11_fake_data = xmalloc(data_len); 3571149753Sdes for (i = 0; i < data_len; i++) { 3572149753Sdes if (sscanf(data + 2 * i, "%2x", &value) != 1) 3573149753Sdes fatal("x11_request_forwarding: bad " 3574149753Sdes "authentication data: %.100s", data); 3575149753Sdes if (i % 4 == 0) 3576149753Sdes rnd = arc4random(); 3577149753Sdes x11_saved_data[i] = value; 3578149753Sdes x11_fake_data[i] = rnd & 0xff; 3579149753Sdes rnd >>= 8; 3580149753Sdes } 3581149753Sdes x11_saved_data_len = data_len; 3582149753Sdes x11_fake_data_len = data_len; 358357429Smarkm } 358457429Smarkm 358557429Smarkm /* Convert the fake data into hex. */ 3586149753Sdes new_data = tohex(x11_fake_data, data_len); 358757429Smarkm 358857429Smarkm /* Send the request packet. */ 358960573Skris if (compat20) { 359060573Skris channel_request_start(client_session_id, "x11-req", 0); 359160573Skris packet_put_char(0); /* XXX bool single connection */ 359260573Skris } else { 359360573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 359460573Skris } 359560573Skris packet_put_cstring(proto); 359660573Skris packet_put_cstring(new_data); 359757429Smarkm packet_put_int(screen_number); 359857429Smarkm packet_send(); 359957429Smarkm packet_write_wait(); 360057429Smarkm xfree(new_data); 360157429Smarkm} 360257429Smarkm 360392559Sdes 360492559Sdes/* -- agent forwarding */ 360592559Sdes 360657429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 360757429Smarkm 360860573Skrisvoid 360992559Sdesauth_request_forwarding(void) 361057429Smarkm{ 361157429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 361257429Smarkm packet_send(); 361357429Smarkm packet_write_wait(); 361457429Smarkm} 3615