1323124Sdes/* $OpenBSD: channels.c,v 1.351 2016/07/19 11:38:53 dtucker 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> 45295367Sdes#include <sys/param.h> /* MIN MAX */ 46295367Sdes#include <sys/stat.h> 47162856Sdes#include <sys/ioctl.h> 48162856Sdes#include <sys/un.h> 49162856Sdes#include <sys/socket.h> 50162856Sdes#ifdef HAVE_SYS_TIME_H 51162856Sdes# include <sys/time.h> 52162856Sdes#endif 53162856Sdes 54162856Sdes#include <netinet/in.h> 55162856Sdes#include <arpa/inet.h> 56162856Sdes 57162856Sdes#include <errno.h> 58204917Sdes#include <fcntl.h> 59162856Sdes#include <netdb.h> 60295367Sdes#ifdef HAVE_STDINT_H 61295367Sdes#include <stdint.h> 62295367Sdes#endif 63162856Sdes#include <stdio.h> 64162856Sdes#include <stdlib.h> 65162856Sdes#include <string.h> 66162856Sdes#include <termios.h> 67162856Sdes#include <unistd.h> 68162856Sdes#include <stdarg.h> 69162856Sdes 70181111Sdes#include "openbsd-compat/sys-queue.h" 71162856Sdes#include "xmalloc.h" 7257429Smarkm#include "ssh.h" 7376262Sgreen#include "ssh1.h" 7476262Sgreen#include "ssh2.h" 7557429Smarkm#include "packet.h" 7676262Sgreen#include "log.h" 7776262Sgreen#include "misc.h" 78162856Sdes#include "buffer.h" 7957429Smarkm#include "channels.h" 8057429Smarkm#include "compat.h" 8176262Sgreen#include "canohost.h" 8265668Skris#include "key.h" 8365668Skris#include "authfd.h" 8492559Sdes#include "pathnames.h" 8565668Skris 8692559Sdes/* -- channel core */ 8757429Smarkm 8857429Smarkm/* 8957429Smarkm * Pointer to an array containing all allocated channels. The array is 9057429Smarkm * dynamically extended as needed. 9157429Smarkm */ 9292559Sdesstatic Channel **channels = NULL; 9357429Smarkm 9457429Smarkm/* 9557429Smarkm * Size of the channel array. All slots of the array must always be 9692559Sdes * initialized (at least the type field); unused slots set to NULL 9757429Smarkm */ 98137019Sdesstatic u_int channels_alloc = 0; 9957429Smarkm 10057429Smarkm/* 10157429Smarkm * Maximum file descriptor value used in any of the channels. This is 10292559Sdes * updated in channel_new. 10357429Smarkm */ 10476262Sgreenstatic int channel_max_fd = 0; 10557429Smarkm 10657429Smarkm 10792559Sdes/* -- tcp forwarding */ 10857429Smarkm 10957429Smarkm/* 11057429Smarkm * Data structure for storing which hosts are permitted for forward requests. 11157429Smarkm * The local sides of any remote forwards are stored in this array to prevent 11257429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 11357429Smarkm * network (which might be behind a firewall). 11457429Smarkm */ 115295367Sdes/* XXX: streamlocal wants a path instead of host:port */ 116295367Sdes/* Overload host_to_connect; we could just make this match Forward */ 117295367Sdes/* XXX - can we use listen_host instead of listen_path? */ 11857429Smarkmtypedef struct { 11960573Skris char *host_to_connect; /* Connect to 'host'. */ 120295367Sdes int port_to_connect; /* Connect to 'port'. */ 121295367Sdes char *listen_host; /* Remote side should listen address. */ 122295367Sdes char *listen_path; /* Remote side should listen path. */ 123295367Sdes int listen_port; /* Remote side should listen port. */ 12457429Smarkm} ForwardPermission; 12557429Smarkm 126162856Sdes/* List of all permitted host/port pairs to connect by the user. */ 127215116Sdesstatic ForwardPermission *permitted_opens = NULL; 12892559Sdes 129162856Sdes/* List of all permitted host/port pairs to connect by the admin. */ 130215116Sdesstatic ForwardPermission *permitted_adm_opens = NULL; 131162856Sdes 132162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */ 13357429Smarkmstatic int num_permitted_opens = 0; 134162856Sdes 135162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */ 136162856Sdesstatic int num_adm_permitted_opens = 0; 137162856Sdes 138240075Sdes/* special-case port number meaning allow any port */ 139240075Sdes#define FWD_PERMIT_ANY_PORT 0 140240075Sdes 141323124Sdes/* special-case wildcard meaning allow any host */ 142323124Sdes#define FWD_PERMIT_ANY_HOST "*" 143323124Sdes 14457429Smarkm/* 14557429Smarkm * If this is true, all opens are permitted. This is the case on the server 14657429Smarkm * on which we have to trust the client anyway, and the user could do 14757429Smarkm * anything after logging in anyway. 14857429Smarkm */ 14957429Smarkmstatic int all_opens_permitted = 0; 15057429Smarkm 15157429Smarkm 15292559Sdes/* -- X11 forwarding */ 15392559Sdes 15492559Sdes/* Maximum number of fake X11 displays to try. */ 15592559Sdes#define MAX_DISPLAYS 1000 15692559Sdes 157149753Sdes/* Saved X11 local (client) display. */ 158149753Sdesstatic char *x11_saved_display = NULL; 159149753Sdes 16092559Sdes/* Saved X11 authentication protocol name. */ 16192559Sdesstatic char *x11_saved_proto = NULL; 16292559Sdes 16392559Sdes/* Saved X11 authentication data. This is the real data. */ 16492559Sdesstatic char *x11_saved_data = NULL; 16592559Sdesstatic u_int x11_saved_data_len = 0; 16692559Sdes 167295367Sdes/* Deadline after which all X11 connections are refused */ 168295367Sdesstatic u_int x11_refuse_time; 169295367Sdes 17092559Sdes/* 17192559Sdes * Fake X11 authentication data. This is what the server will be sending us; 17292559Sdes * we should replace any occurrences of this by the real data. 17392559Sdes */ 174162856Sdesstatic u_char *x11_fake_data = NULL; 17592559Sdesstatic u_int x11_fake_data_len; 17692559Sdes 17792559Sdes 17892559Sdes/* -- agent forwarding */ 17992559Sdes 18092559Sdes#define NUM_SOCKS 10 18192559Sdes 18276262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */ 18398941Sdesstatic int IPv4or6 = AF_UNSPEC; 18476262Sgreen 18592559Sdes/* helper */ 18692559Sdesstatic void port_open_helper(Channel *c, char *rtype); 18776262Sgreen 188181111Sdes/* non-blocking connect helpers */ 189181111Sdesstatic int connect_next(struct channel_connect *); 190181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *); 191181111Sdes 19292559Sdes/* -- channel core */ 19357429Smarkm 19460573SkrisChannel * 195157019Sdeschannel_by_id(int id) 19660573Skris{ 19760573Skris Channel *c; 19892559Sdes 199137019Sdes if (id < 0 || (u_int)id >= channels_alloc) { 200157019Sdes logit("channel_by_id: %d: bad id", id); 20160573Skris return NULL; 20260573Skris } 20392559Sdes c = channels[id]; 20492559Sdes if (c == NULL) { 205157019Sdes logit("channel_by_id: %d: bad id: channel free", id); 20660573Skris return NULL; 20760573Skris } 20860573Skris return c; 20960573Skris} 21060573Skris 21157429Smarkm/* 212157019Sdes * Returns the channel if it is allowed to receive protocol messages. 213157019Sdes * Private channels, like listening sockets, may not receive messages. 214157019Sdes */ 215157019SdesChannel * 216157019Sdeschannel_lookup(int id) 217157019Sdes{ 218157019Sdes Channel *c; 219157019Sdes 220157019Sdes if ((c = channel_by_id(id)) == NULL) 221157019Sdes return (NULL); 222157019Sdes 223162856Sdes switch (c->type) { 224157019Sdes case SSH_CHANNEL_X11_OPEN: 225157019Sdes case SSH_CHANNEL_LARVAL: 226157019Sdes case SSH_CHANNEL_CONNECTING: 227157019Sdes case SSH_CHANNEL_DYNAMIC: 228157019Sdes case SSH_CHANNEL_OPENING: 229157019Sdes case SSH_CHANNEL_OPEN: 230157019Sdes case SSH_CHANNEL_INPUT_DRAINING: 231157019Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 232255767Sdes case SSH_CHANNEL_ABANDONED: 233157019Sdes return (c); 234157019Sdes } 235157019Sdes logit("Non-public channel %d, type %d.", id, c->type); 236157019Sdes return (NULL); 237157019Sdes} 238157019Sdes 239157019Sdes/* 24060573Skris * Register filedescriptors for a channel, used when allocating a channel or 24160573Skris * when the channel consumer/producer is ready, e.g. shell exec'd 24260573Skris */ 24392559Sdesstatic void 24469587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd, 245181111Sdes int extusage, int nonblock, int is_tty) 24660573Skris{ 24760573Skris /* Update the maximum file descriptor value. */ 24876262Sgreen channel_max_fd = MAX(channel_max_fd, rfd); 24976262Sgreen channel_max_fd = MAX(channel_max_fd, wfd); 25076262Sgreen channel_max_fd = MAX(channel_max_fd, efd); 25176262Sgreen 252204917Sdes if (rfd != -1) 253204917Sdes fcntl(rfd, F_SETFD, FD_CLOEXEC); 254204917Sdes if (wfd != -1 && wfd != rfd) 255204917Sdes fcntl(wfd, F_SETFD, FD_CLOEXEC); 256204917Sdes if (efd != -1 && efd != rfd && efd != wfd) 257204917Sdes fcntl(efd, F_SETFD, FD_CLOEXEC); 25860573Skris 25960573Skris c->rfd = rfd; 26060573Skris c->wfd = wfd; 26160573Skris c->sock = (rfd == wfd) ? rfd : -1; 26260573Skris c->efd = efd; 26360573Skris c->extended_usage = extusage; 26469587Sgreen 265181111Sdes if ((c->isatty = is_tty) != 0) 266124207Sdes debug2("channel %d: rfd %d isatty", c->self, c->rfd); 267255767Sdes#ifdef _AIX 268255767Sdes /* XXX: Later AIX versions can't push as much data to tty */ 269181111Sdes c->wfd_isatty = is_tty || isatty(c->wfd); 270255767Sdes#endif 27174500Sgreen 27269587Sgreen /* enable nonblocking mode */ 27369587Sgreen if (nonblock) { 27469587Sgreen if (rfd != -1) 27569587Sgreen set_nonblock(rfd); 27669587Sgreen if (wfd != -1) 27769587Sgreen set_nonblock(wfd); 27869587Sgreen if (efd != -1) 27969587Sgreen set_nonblock(efd); 28069587Sgreen } 28160573Skris} 28260573Skris 28360573Skris/* 28457429Smarkm * Allocate a new channel object and set its type and socket. This will cause 28557429Smarkm * remote_name to be freed. 28657429Smarkm */ 28792559SdesChannel * 28860573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd, 28999063Sdes u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 29057429Smarkm{ 291137019Sdes int found; 292137019Sdes u_int i; 29357429Smarkm Channel *c; 29457429Smarkm 29557429Smarkm /* Do initial allocation if this is the first call. */ 29657429Smarkm if (channels_alloc == 0) { 29757429Smarkm channels_alloc = 10; 298162856Sdes channels = xcalloc(channels_alloc, sizeof(Channel *)); 29957429Smarkm for (i = 0; i < channels_alloc; i++) 30092559Sdes channels[i] = NULL; 30157429Smarkm } 30257429Smarkm /* Try to find a free slot where to put the new channel. */ 30357429Smarkm for (found = -1, i = 0; i < channels_alloc; i++) 30492559Sdes if (channels[i] == NULL) { 30557429Smarkm /* Found a free slot. */ 306137019Sdes found = (int)i; 30757429Smarkm break; 30857429Smarkm } 309137019Sdes if (found < 0) { 31057429Smarkm /* There are no free slots. Take last+1 slot and expand the array. */ 31157429Smarkm found = channels_alloc; 31299063Sdes if (channels_alloc > 10000) 31399063Sdes fatal("channel_new: internal error: channels_alloc %d " 31499063Sdes "too big.", channels_alloc); 315295367Sdes channels = xreallocarray(channels, channels_alloc + 10, 316162856Sdes sizeof(Channel *)); 317120489Sjoe channels_alloc += 10; 31869587Sgreen debug2("channel: expanding %d", channels_alloc); 31957429Smarkm for (i = found; i < channels_alloc; i++) 32092559Sdes channels[i] = NULL; 32157429Smarkm } 32292559Sdes /* Initialize and return new channel. */ 323162856Sdes c = channels[found] = xcalloc(1, sizeof(Channel)); 32457429Smarkm buffer_init(&c->input); 32557429Smarkm buffer_init(&c->output); 32660573Skris buffer_init(&c->extended); 327192595Sdes c->path = NULL; 328240075Sdes c->listening_addr = NULL; 329240075Sdes c->listening_port = 0; 33092559Sdes c->ostate = CHAN_OUTPUT_OPEN; 33192559Sdes c->istate = CHAN_INPUT_OPEN; 33292559Sdes c->flags = 0; 333181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 334240075Sdes c->notbefore = 0; 33557429Smarkm c->self = found; 33657429Smarkm c->type = type; 33760573Skris c->ctype = ctype; 33860573Skris c->local_window = window; 33960573Skris c->local_window_max = window; 34060573Skris c->local_consumed = 0; 34160573Skris c->local_maxpacket = maxpack; 34257429Smarkm c->remote_id = -1; 343124207Sdes c->remote_name = xstrdup(remote_name); 34460573Skris c->remote_window = 0; 34560573Skris c->remote_maxpacket = 0; 34692559Sdes c->force_drain = 0; 34792559Sdes c->single_connection = 0; 34892559Sdes c->detach_user = NULL; 349157019Sdes c->detach_close = 0; 350181111Sdes c->open_confirm = NULL; 351181111Sdes c->open_confirm_ctx = NULL; 35265668Skris c->input_filter = NULL; 353157019Sdes c->output_filter = NULL; 354181111Sdes c->filter_ctx = NULL; 355181111Sdes c->filter_cleanup = NULL; 356204917Sdes c->ctl_chan = -1; 357204917Sdes c->mux_rcb = NULL; 358204917Sdes c->mux_ctx = NULL; 359215116Sdes c->mux_pause = 0; 360204917Sdes c->delayed = 1; /* prevent call to channel_post handler */ 361181111Sdes TAILQ_INIT(&c->status_confirms); 36257429Smarkm debug("channel %d: new [%s]", found, remote_name); 36392559Sdes return c; 36457429Smarkm} 36592559Sdes 36692559Sdesstatic int 36792559Sdeschannel_find_maxfd(void) 36892559Sdes{ 369137019Sdes u_int i; 370137019Sdes int max = 0; 37192559Sdes Channel *c; 37292559Sdes 37392559Sdes for (i = 0; i < channels_alloc; i++) { 37492559Sdes c = channels[i]; 37592559Sdes if (c != NULL) { 37692559Sdes max = MAX(max, c->rfd); 37792559Sdes max = MAX(max, c->wfd); 37892559Sdes max = MAX(max, c->efd); 37992559Sdes } 38092559Sdes } 38192559Sdes return max; 38292559Sdes} 38392559Sdes 38460573Skrisint 38592559Sdeschannel_close_fd(int *fdp) 38660573Skris{ 38792559Sdes int ret = 0, fd = *fdp; 38892559Sdes 38992559Sdes if (fd != -1) { 39092559Sdes ret = close(fd); 39192559Sdes *fdp = -1; 39292559Sdes if (fd == channel_max_fd) 39392559Sdes channel_max_fd = channel_find_maxfd(); 39492559Sdes } 39592559Sdes return ret; 39660573Skris} 39757429Smarkm 39860573Skris/* Close all channel fd/socket. */ 39992559Sdesstatic void 40060573Skrischannel_close_fds(Channel *c) 40157429Smarkm{ 40292559Sdes channel_close_fd(&c->sock); 40392559Sdes channel_close_fd(&c->rfd); 40492559Sdes channel_close_fd(&c->wfd); 40592559Sdes channel_close_fd(&c->efd); 40660573Skris} 40757429Smarkm 40860573Skris/* Free the channel and close its fd/socket. */ 40960573Skrisvoid 41092559Sdeschannel_free(Channel *c) 41160573Skris{ 41292559Sdes char *s; 413137019Sdes u_int i, n; 414181111Sdes struct channel_confirm *cc; 41576262Sgreen 41692559Sdes for (n = 0, i = 0; i < channels_alloc; i++) 41792559Sdes if (channels[i]) 41892559Sdes n++; 419137019Sdes debug("channel %d: free: %s, nchannels %u", c->self, 42092559Sdes c->remote_name ? c->remote_name : "???", n); 42192559Sdes 42292559Sdes s = channel_open_message(); 423124207Sdes debug3("channel %d: status: %s", c->self, s); 424255767Sdes free(s); 42576262Sgreen 42660573Skris if (c->sock != -1) 42760573Skris shutdown(c->sock, SHUT_RDWR); 42860573Skris channel_close_fds(c); 42960573Skris buffer_free(&c->input); 43060573Skris buffer_free(&c->output); 43160573Skris buffer_free(&c->extended); 432255767Sdes free(c->remote_name); 433255767Sdes c->remote_name = NULL; 434255767Sdes free(c->path); 435255767Sdes c->path = NULL; 436255767Sdes free(c->listening_addr); 437255767Sdes c->listening_addr = NULL; 438181111Sdes while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 439181111Sdes if (cc->abandon_cb != NULL) 440181111Sdes cc->abandon_cb(c, cc->ctx); 441181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 442264377Sdes explicit_bzero(cc, sizeof(*cc)); 443255767Sdes free(cc); 444181111Sdes } 445181111Sdes if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 446181111Sdes c->filter_cleanup(c->self, c->filter_ctx); 44792559Sdes channels[c->self] = NULL; 448255767Sdes free(c); 44957429Smarkm} 45057429Smarkm 45192559Sdesvoid 45292559Sdeschannel_free_all(void) 45392559Sdes{ 454137019Sdes u_int i; 45592559Sdes 45692559Sdes for (i = 0; i < channels_alloc; i++) 45792559Sdes if (channels[i] != NULL) 45892559Sdes channel_free(channels[i]); 45992559Sdes} 46092559Sdes 46157429Smarkm/* 46292559Sdes * Closes the sockets/fds of all channels. This is used to close extra file 46392559Sdes * descriptors after a fork. 46492559Sdes */ 46592559Sdesvoid 46692559Sdeschannel_close_all(void) 46792559Sdes{ 468137019Sdes u_int i; 46992559Sdes 47092559Sdes for (i = 0; i < channels_alloc; i++) 47192559Sdes if (channels[i] != NULL) 47292559Sdes channel_close_fds(channels[i]); 47392559Sdes} 47492559Sdes 47592559Sdes/* 47692559Sdes * Stop listening to channels. 47792559Sdes */ 47892559Sdesvoid 47992559Sdeschannel_stop_listening(void) 48092559Sdes{ 481137019Sdes u_int i; 48292559Sdes Channel *c; 48392559Sdes 48492559Sdes for (i = 0; i < channels_alloc; i++) { 48592559Sdes c = channels[i]; 48692559Sdes if (c != NULL) { 48792559Sdes switch (c->type) { 48892559Sdes case SSH_CHANNEL_AUTH_SOCKET: 48992559Sdes case SSH_CHANNEL_PORT_LISTENER: 49092559Sdes case SSH_CHANNEL_RPORT_LISTENER: 49192559Sdes case SSH_CHANNEL_X11_LISTENER: 492295367Sdes case SSH_CHANNEL_UNIX_LISTENER: 493295367Sdes case SSH_CHANNEL_RUNIX_LISTENER: 49492559Sdes channel_close_fd(&c->sock); 49592559Sdes channel_free(c); 49692559Sdes break; 49792559Sdes } 49892559Sdes } 49992559Sdes } 50092559Sdes} 50192559Sdes 50292559Sdes/* 50392559Sdes * Returns true if no channel has too much buffered data, and false if one or 50492559Sdes * more channel is overfull. 50592559Sdes */ 50692559Sdesint 50792559Sdeschannel_not_very_much_buffered_data(void) 50892559Sdes{ 50992559Sdes u_int i; 51092559Sdes Channel *c; 51192559Sdes 51292559Sdes for (i = 0; i < channels_alloc; i++) { 51392559Sdes c = channels[i]; 51492559Sdes if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 51592559Sdes#if 0 51692559Sdes if (!compat20 && 51792559Sdes buffer_len(&c->input) > packet_get_maxsize()) { 518113911Sdes debug2("channel %d: big input buffer %d", 51992559Sdes c->self, buffer_len(&c->input)); 52092559Sdes return 0; 52192559Sdes } 52292559Sdes#endif 52392559Sdes if (buffer_len(&c->output) > packet_get_maxsize()) { 524124207Sdes debug2("channel %d: big output buffer %u > %u", 52592559Sdes c->self, buffer_len(&c->output), 52692559Sdes packet_get_maxsize()); 52792559Sdes return 0; 52892559Sdes } 52992559Sdes } 53092559Sdes } 53192559Sdes return 1; 53292559Sdes} 53392559Sdes 53492559Sdes/* Returns true if any channel is still open. */ 53592559Sdesint 53692559Sdeschannel_still_open(void) 53792559Sdes{ 538137019Sdes u_int i; 53992559Sdes Channel *c; 54092559Sdes 54192559Sdes for (i = 0; i < channels_alloc; i++) { 54292559Sdes c = channels[i]; 54392559Sdes if (c == NULL) 54492559Sdes continue; 54592559Sdes switch (c->type) { 54692559Sdes case SSH_CHANNEL_X11_LISTENER: 54792559Sdes case SSH_CHANNEL_PORT_LISTENER: 54892559Sdes case SSH_CHANNEL_RPORT_LISTENER: 549204917Sdes case SSH_CHANNEL_MUX_LISTENER: 55092559Sdes case SSH_CHANNEL_CLOSED: 55192559Sdes case SSH_CHANNEL_AUTH_SOCKET: 55292559Sdes case SSH_CHANNEL_DYNAMIC: 55392559Sdes case SSH_CHANNEL_CONNECTING: 55492559Sdes case SSH_CHANNEL_ZOMBIE: 555255767Sdes case SSH_CHANNEL_ABANDONED: 556295367Sdes case SSH_CHANNEL_UNIX_LISTENER: 557295367Sdes case SSH_CHANNEL_RUNIX_LISTENER: 55892559Sdes continue; 55992559Sdes case SSH_CHANNEL_LARVAL: 56092559Sdes if (!compat20) 56192559Sdes fatal("cannot happen: SSH_CHANNEL_LARVAL"); 56292559Sdes continue; 56392559Sdes case SSH_CHANNEL_OPENING: 56492559Sdes case SSH_CHANNEL_OPEN: 56592559Sdes case SSH_CHANNEL_X11_OPEN: 566204917Sdes case SSH_CHANNEL_MUX_CLIENT: 56792559Sdes return 1; 56892559Sdes case SSH_CHANNEL_INPUT_DRAINING: 56992559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 57092559Sdes if (!compat13) 57192559Sdes fatal("cannot happen: OUT_DRAIN"); 57292559Sdes return 1; 57392559Sdes default: 57492559Sdes fatal("channel_still_open: bad channel type %d", c->type); 57592559Sdes /* NOTREACHED */ 57692559Sdes } 57792559Sdes } 57892559Sdes return 0; 57992559Sdes} 58092559Sdes 58192559Sdes/* Returns the id of an open channel suitable for keepaliving */ 58292559Sdesint 58392559Sdeschannel_find_open(void) 58492559Sdes{ 585137019Sdes u_int i; 58692559Sdes Channel *c; 58792559Sdes 58892559Sdes for (i = 0; i < channels_alloc; i++) { 58992559Sdes c = channels[i]; 590137019Sdes if (c == NULL || c->remote_id < 0) 59192559Sdes continue; 59292559Sdes switch (c->type) { 59392559Sdes case SSH_CHANNEL_CLOSED: 59492559Sdes case SSH_CHANNEL_DYNAMIC: 59592559Sdes case SSH_CHANNEL_X11_LISTENER: 59692559Sdes case SSH_CHANNEL_PORT_LISTENER: 59792559Sdes case SSH_CHANNEL_RPORT_LISTENER: 598204917Sdes case SSH_CHANNEL_MUX_LISTENER: 599204917Sdes case SSH_CHANNEL_MUX_CLIENT: 60092559Sdes case SSH_CHANNEL_OPENING: 60192559Sdes case SSH_CHANNEL_CONNECTING: 60292559Sdes case SSH_CHANNEL_ZOMBIE: 603255767Sdes case SSH_CHANNEL_ABANDONED: 604295367Sdes case SSH_CHANNEL_UNIX_LISTENER: 605295367Sdes case SSH_CHANNEL_RUNIX_LISTENER: 60692559Sdes continue; 60792559Sdes case SSH_CHANNEL_LARVAL: 60892559Sdes case SSH_CHANNEL_AUTH_SOCKET: 60992559Sdes case SSH_CHANNEL_OPEN: 61092559Sdes case SSH_CHANNEL_X11_OPEN: 61192559Sdes return i; 61292559Sdes case SSH_CHANNEL_INPUT_DRAINING: 61392559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 61492559Sdes if (!compat13) 61592559Sdes fatal("cannot happen: OUT_DRAIN"); 61692559Sdes return i; 61792559Sdes default: 61892559Sdes fatal("channel_find_open: bad channel type %d", c->type); 61992559Sdes /* NOTREACHED */ 62092559Sdes } 62192559Sdes } 62292559Sdes return -1; 62392559Sdes} 62492559Sdes 62592559Sdes 62692559Sdes/* 62792559Sdes * Returns a message describing the currently open forwarded connections, 62892559Sdes * suitable for sending to the client. The message contains crlf pairs for 62992559Sdes * newlines. 63092559Sdes */ 63192559Sdeschar * 63292559Sdeschannel_open_message(void) 63392559Sdes{ 63492559Sdes Buffer buffer; 63592559Sdes Channel *c; 63692559Sdes char buf[1024], *cp; 637137019Sdes u_int i; 63892559Sdes 63992559Sdes buffer_init(&buffer); 64092559Sdes snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 64192559Sdes buffer_append(&buffer, buf, strlen(buf)); 64292559Sdes for (i = 0; i < channels_alloc; i++) { 64392559Sdes c = channels[i]; 64492559Sdes if (c == NULL) 64592559Sdes continue; 64692559Sdes switch (c->type) { 64792559Sdes case SSH_CHANNEL_X11_LISTENER: 64892559Sdes case SSH_CHANNEL_PORT_LISTENER: 64992559Sdes case SSH_CHANNEL_RPORT_LISTENER: 65092559Sdes case SSH_CHANNEL_CLOSED: 65192559Sdes case SSH_CHANNEL_AUTH_SOCKET: 65292559Sdes case SSH_CHANNEL_ZOMBIE: 653255767Sdes case SSH_CHANNEL_ABANDONED: 654204917Sdes case SSH_CHANNEL_MUX_CLIENT: 655204917Sdes case SSH_CHANNEL_MUX_LISTENER: 656295367Sdes case SSH_CHANNEL_UNIX_LISTENER: 657295367Sdes case SSH_CHANNEL_RUNIX_LISTENER: 65892559Sdes continue; 65992559Sdes case SSH_CHANNEL_LARVAL: 66092559Sdes case SSH_CHANNEL_OPENING: 66192559Sdes case SSH_CHANNEL_CONNECTING: 66292559Sdes case SSH_CHANNEL_DYNAMIC: 66392559Sdes case SSH_CHANNEL_OPEN: 66492559Sdes case SSH_CHANNEL_X11_OPEN: 66592559Sdes case SSH_CHANNEL_INPUT_DRAINING: 66692559Sdes case SSH_CHANNEL_OUTPUT_DRAINING: 667137019Sdes snprintf(buf, sizeof buf, 668296781Sdes " #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n", 66992559Sdes c->self, c->remote_name, 67092559Sdes c->type, c->remote_id, 67192559Sdes c->istate, buffer_len(&c->input), 67292559Sdes c->ostate, buffer_len(&c->output), 673204917Sdes c->rfd, c->wfd, c->ctl_chan); 67492559Sdes buffer_append(&buffer, buf, strlen(buf)); 67592559Sdes continue; 67692559Sdes default: 67792559Sdes fatal("channel_open_message: bad channel type %d", c->type); 67892559Sdes /* NOTREACHED */ 67992559Sdes } 68092559Sdes } 68192559Sdes buffer_append(&buffer, "\0", 1); 682295367Sdes cp = xstrdup((char *)buffer_ptr(&buffer)); 68392559Sdes buffer_free(&buffer); 68492559Sdes return cp; 68592559Sdes} 68692559Sdes 68792559Sdesvoid 68892559Sdeschannel_send_open(int id) 68992559Sdes{ 69092559Sdes Channel *c = channel_lookup(id); 691106130Sdes 69292559Sdes if (c == NULL) { 693124207Sdes logit("channel_send_open: %d: bad id", id); 69492559Sdes return; 69592559Sdes } 696113911Sdes debug2("channel %d: send open", id); 69792559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 69892559Sdes packet_put_cstring(c->ctype); 69992559Sdes packet_put_int(c->self); 70092559Sdes packet_put_int(c->local_window); 70192559Sdes packet_put_int(c->local_maxpacket); 70292559Sdes packet_send(); 70392559Sdes} 70492559Sdes 70592559Sdesvoid 706113911Sdeschannel_request_start(int id, char *service, int wantconfirm) 70792559Sdes{ 708113911Sdes Channel *c = channel_lookup(id); 709106130Sdes 71092559Sdes if (c == NULL) { 711124207Sdes logit("channel_request_start: %d: unknown channel id", id); 71292559Sdes return; 71392559Sdes } 714137019Sdes debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 71592559Sdes packet_start(SSH2_MSG_CHANNEL_REQUEST); 71692559Sdes packet_put_int(c->remote_id); 71792559Sdes packet_put_cstring(service); 71892559Sdes packet_put_char(wantconfirm); 71992559Sdes} 720162856Sdes 72192559Sdesvoid 722181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb, 723181111Sdes channel_confirm_abandon_cb *abandon_cb, void *ctx) 72492559Sdes{ 725181111Sdes struct channel_confirm *cc; 726181111Sdes Channel *c; 727181111Sdes 728181111Sdes if ((c = channel_lookup(id)) == NULL) 729181111Sdes fatal("channel_register_expect: %d: bad id", id); 730181111Sdes 731258343Sdes cc = xcalloc(1, sizeof(*cc)); 732181111Sdes cc->cb = cb; 733181111Sdes cc->abandon_cb = abandon_cb; 734181111Sdes cc->ctx = ctx; 735181111Sdes TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 736181111Sdes} 737181111Sdes 738181111Sdesvoid 739215116Sdeschannel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) 740181111Sdes{ 74192559Sdes Channel *c = channel_lookup(id); 742106130Sdes 74392559Sdes if (c == NULL) { 744192595Sdes logit("channel_register_open_confirm: %d: bad id", id); 74592559Sdes return; 74692559Sdes } 747181111Sdes c->open_confirm = fn; 748181111Sdes c->open_confirm_ctx = ctx; 74992559Sdes} 750162856Sdes 75192559Sdesvoid 752157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 75392559Sdes{ 754157019Sdes Channel *c = channel_by_id(id); 755106130Sdes 75692559Sdes if (c == NULL) { 757124207Sdes logit("channel_register_cleanup: %d: bad id", id); 75892559Sdes return; 75992559Sdes } 76092559Sdes c->detach_user = fn; 761157019Sdes c->detach_close = do_close; 76292559Sdes} 763162856Sdes 76492559Sdesvoid 76592559Sdeschannel_cancel_cleanup(int id) 76692559Sdes{ 767157019Sdes Channel *c = channel_by_id(id); 768106130Sdes 76992559Sdes if (c == NULL) { 770124207Sdes logit("channel_cancel_cleanup: %d: bad id", id); 77192559Sdes return; 77292559Sdes } 77392559Sdes c->detach_user = NULL; 774157019Sdes c->detach_close = 0; 77592559Sdes} 776162856Sdes 77792559Sdesvoid 778157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn, 779181111Sdes channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 78092559Sdes{ 78192559Sdes Channel *c = channel_lookup(id); 782106130Sdes 78392559Sdes if (c == NULL) { 784124207Sdes logit("channel_register_filter: %d: bad id", id); 78592559Sdes return; 78692559Sdes } 787157019Sdes c->input_filter = ifn; 788157019Sdes c->output_filter = ofn; 789181111Sdes c->filter_ctx = ctx; 790181111Sdes c->filter_cleanup = cfn; 79192559Sdes} 79292559Sdes 79392559Sdesvoid 79492559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd, 795181111Sdes int extusage, int nonblock, int is_tty, u_int window_max) 79692559Sdes{ 79792559Sdes Channel *c = channel_lookup(id); 798106130Sdes 79992559Sdes if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 80092559Sdes fatal("channel_activate for non-larval channel %d.", id); 801181111Sdes channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 80292559Sdes c->type = SSH_CHANNEL_OPEN; 80392559Sdes c->local_window = c->local_window_max = window_max; 80492559Sdes packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 80592559Sdes packet_put_int(c->remote_id); 80692559Sdes packet_put_int(c->local_window); 80792559Sdes packet_send(); 80892559Sdes} 80992559Sdes 81092559Sdes/* 81160573Skris * 'channel_pre*' are called just before select() to add any bits relevant to 81260573Skris * channels in the select bitmasks. 81357429Smarkm */ 81460573Skris/* 81560573Skris * 'channel_post*': perform any appropriate operations for channels which 81660573Skris * have events pending. 81760573Skris */ 818162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 81960573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 82060573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 82157429Smarkm 822162856Sdes/* ARGSUSED */ 82392559Sdesstatic void 824162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 82557429Smarkm{ 82660573Skris FD_SET(c->sock, readset); 82760573Skris} 82860573Skris 829162856Sdes/* ARGSUSED */ 83092559Sdesstatic void 831162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 83276262Sgreen{ 83376262Sgreen debug3("channel %d: waiting for connection", c->self); 83476262Sgreen FD_SET(c->sock, writeset); 83576262Sgreen} 83676262Sgreen 83792559Sdesstatic void 838162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 83960573Skris{ 84060573Skris if (buffer_len(&c->input) < packet_get_maxsize()) 84160573Skris FD_SET(c->sock, readset); 84260573Skris if (buffer_len(&c->output) > 0) 84360573Skris FD_SET(c->sock, writeset); 84460573Skris} 84560573Skris 84692559Sdesstatic void 847162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 84860573Skris{ 849294693Sdes u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 85060573Skris 85160573Skris if (c->istate == CHAN_INPUT_OPEN && 85292559Sdes limit > 0 && 853162856Sdes buffer_len(&c->input) < limit && 854162856Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 85560573Skris FD_SET(c->rfd, readset); 85660573Skris if (c->ostate == CHAN_OUTPUT_OPEN || 85760573Skris c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 85860573Skris if (buffer_len(&c->output) > 0) { 85960573Skris FD_SET(c->wfd, writeset); 86060573Skris } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 86198684Sdes if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 862149753Sdes debug2("channel %d: obuf_empty delayed efd %d/(%d)", 863149753Sdes c->self, c->efd, buffer_len(&c->extended)); 86498684Sdes else 86598684Sdes chan_obuf_empty(c); 86660573Skris } 86760573Skris } 86860573Skris /** XXX check close conditions, too */ 869181111Sdes if (compat20 && c->efd != -1 && 870181111Sdes !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 87160573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 87260573Skris buffer_len(&c->extended) > 0) 87360573Skris FD_SET(c->efd, writeset); 874215116Sdes else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 875215116Sdes (c->extended_usage == CHAN_EXTENDED_READ || 876215116Sdes c->extended_usage == CHAN_EXTENDED_IGNORE) && 87760573Skris buffer_len(&c->extended) < c->remote_window) 87860573Skris FD_SET(c->efd, readset); 87960573Skris } 880137019Sdes /* XXX: What about efd? races? */ 88160573Skris} 88260573Skris 883162856Sdes/* ARGSUSED */ 88492559Sdesstatic void 885162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 88660573Skris{ 88760573Skris if (buffer_len(&c->input) == 0) { 88860573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 88960573Skris packet_put_int(c->remote_id); 89060573Skris packet_send(); 89160573Skris c->type = SSH_CHANNEL_CLOSED; 892124207Sdes debug2("channel %d: closing after input drain.", c->self); 89360573Skris } 89460573Skris} 89560573Skris 896162856Sdes/* ARGSUSED */ 89792559Sdesstatic void 898162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 89960573Skris{ 90060573Skris if (buffer_len(&c->output) == 0) 90192559Sdes chan_mark_dead(c); 90260573Skris else 90360573Skris FD_SET(c->sock, writeset); 90460573Skris} 90560573Skris 90660573Skris/* 90760573Skris * This is a special state for X11 authentication spoofing. An opened X11 90860573Skris * connection (when authentication spoofing is being done) remains in this 90960573Skris * state until the first packet has been completely read. The authentication 91060573Skris * data in that packet is then substituted by the real data if it matches the 91160573Skris * fake data, and the channel is put into normal mode. 91260573Skris * XXX All this happens at the client side. 91392559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 91460573Skris */ 91592559Sdesstatic int 91692559Sdesx11_open_helper(Buffer *b) 91760573Skris{ 91876262Sgreen u_char *ucp; 91976262Sgreen u_int proto_len, data_len; 92057429Smarkm 921295367Sdes /* Is this being called after the refusal deadline? */ 922295367Sdes if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 923295367Sdes verbose("Rejected X11 connection after ForwardX11Timeout " 924295367Sdes "expired"); 925295367Sdes return -1; 926295367Sdes } 927295367Sdes 92860573Skris /* Check if the fixed size part of the packet is in buffer. */ 92992559Sdes if (buffer_len(b) < 12) 93060573Skris return 0; 93157429Smarkm 93260573Skris /* Parse the lengths of variable-length fields. */ 93392559Sdes ucp = buffer_ptr(b); 93460573Skris if (ucp[0] == 0x42) { /* Byte order MSB first. */ 93560573Skris proto_len = 256 * ucp[6] + ucp[7]; 93660573Skris data_len = 256 * ucp[8] + ucp[9]; 93760573Skris } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 93860573Skris proto_len = ucp[6] + 256 * ucp[7]; 93960573Skris data_len = ucp[8] + 256 * ucp[9]; 94060573Skris } else { 941124207Sdes debug2("Initial X11 packet contains bad byte order byte: 0x%x", 94292559Sdes ucp[0]); 94360573Skris return -1; 94460573Skris } 94557429Smarkm 94660573Skris /* Check if the whole packet is in buffer. */ 94792559Sdes if (buffer_len(b) < 94860573Skris 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 94960573Skris return 0; 95057429Smarkm 95160573Skris /* Check if authentication protocol matches. */ 95260573Skris if (proto_len != strlen(x11_saved_proto) || 95360573Skris memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 954124207Sdes debug2("X11 connection uses different authentication protocol."); 95560573Skris return -1; 95660573Skris } 95760573Skris /* Check if authentication data matches our fake data. */ 95860573Skris if (data_len != x11_fake_data_len || 959215116Sdes timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 96060573Skris x11_fake_data, x11_fake_data_len) != 0) { 961124207Sdes debug2("X11 auth data does not match fake data."); 96260573Skris return -1; 96360573Skris } 96460573Skris /* Check fake data length */ 96560573Skris if (x11_fake_data_len != x11_saved_data_len) { 96660573Skris error("X11 fake_data_len %d != saved_data_len %d", 96760573Skris x11_fake_data_len, x11_saved_data_len); 96860573Skris return -1; 96960573Skris } 97060573Skris /* 97160573Skris * Received authentication protocol and data match 97260573Skris * our fake data. Substitute the fake data with real 97360573Skris * data. 97460573Skris */ 97560573Skris memcpy(ucp + 12 + ((proto_len + 3) & ~3), 97660573Skris x11_saved_data, x11_saved_data_len); 97760573Skris return 1; 97860573Skris} 97957429Smarkm 98092559Sdesstatic void 981162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 98260573Skris{ 98392559Sdes int ret = x11_open_helper(&c->output); 984106130Sdes 98560573Skris if (ret == 1) { 98660573Skris /* Start normal processing for the channel. */ 98760573Skris c->type = SSH_CHANNEL_OPEN; 98860573Skris channel_pre_open_13(c, readset, writeset); 98960573Skris } else if (ret == -1) { 99060573Skris /* 99160573Skris * We have received an X11 connection that has bad 99260573Skris * authentication information. 99360573Skris */ 994124207Sdes logit("X11 connection rejected because of wrong authentication."); 99560573Skris buffer_clear(&c->input); 99660573Skris buffer_clear(&c->output); 99792559Sdes channel_close_fd(&c->sock); 99860573Skris c->sock = -1; 99960573Skris c->type = SSH_CHANNEL_CLOSED; 100060573Skris packet_start(SSH_MSG_CHANNEL_CLOSE); 100160573Skris packet_put_int(c->remote_id); 100260573Skris packet_send(); 100360573Skris } 100460573Skris} 100557429Smarkm 100692559Sdesstatic void 1007162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 100860573Skris{ 100992559Sdes int ret = x11_open_helper(&c->output); 101092559Sdes 101192559Sdes /* c->force_drain = 1; */ 101292559Sdes 101360573Skris if (ret == 1) { 101460573Skris c->type = SSH_CHANNEL_OPEN; 101592559Sdes channel_pre_open(c, readset, writeset); 101692559Sdes } else if (ret == -1) { 1017124207Sdes logit("X11 connection rejected because of wrong authentication."); 1018124207Sdes debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 101992559Sdes chan_read_failed(c); 102092559Sdes buffer_clear(&c->input); 102192559Sdes chan_ibuf_empty(c); 102292559Sdes buffer_clear(&c->output); 102392559Sdes /* for proto v1, the peer will send an IEOF */ 102460573Skris if (compat20) 102592559Sdes chan_write_failed(c); 102660573Skris else 102792559Sdes c->type = SSH_CHANNEL_OPEN; 1028124207Sdes debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 102960573Skris } 103060573Skris} 103157429Smarkm 1032204917Sdesstatic void 1033204917Sdeschannel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1034204917Sdes{ 1035215116Sdes if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 1036204917Sdes buffer_check_alloc(&c->input, CHAN_RBUF)) 1037204917Sdes FD_SET(c->rfd, readset); 1038204917Sdes if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1039204917Sdes /* clear buffer immediately (discard any partial packet) */ 1040204917Sdes buffer_clear(&c->input); 1041204917Sdes chan_ibuf_empty(c); 1042204917Sdes /* Start output drain. XXX just kill chan? */ 1043204917Sdes chan_rcvd_oclose(c); 1044204917Sdes } 1045204917Sdes if (c->ostate == CHAN_OUTPUT_OPEN || 1046204917Sdes c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1047204917Sdes if (buffer_len(&c->output) > 0) 1048204917Sdes FD_SET(c->wfd, writeset); 1049204917Sdes else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 1050204917Sdes chan_obuf_empty(c); 1051204917Sdes } 1052204917Sdes} 1053204917Sdes 105476262Sgreen/* try to decode a socks4 header */ 1055162856Sdes/* ARGSUSED */ 105692559Sdesstatic int 1057162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 105876262Sgreen{ 1059106130Sdes char *p, *host; 1060192595Sdes u_int len, have, i, found, need; 106192559Sdes char username[256]; 106276262Sgreen struct { 106376262Sgreen u_int8_t version; 106476262Sgreen u_int8_t command; 106576262Sgreen u_int16_t dest_port; 106676262Sgreen struct in_addr dest_addr; 106776262Sgreen } s4_req, s4_rsp; 106876262Sgreen 106976262Sgreen debug2("channel %d: decode socks4", c->self); 107076262Sgreen 107176262Sgreen have = buffer_len(&c->input); 107276262Sgreen len = sizeof(s4_req); 107376262Sgreen if (have < len) 107476262Sgreen return 0; 1075295367Sdes p = (char *)buffer_ptr(&c->input); 1076192595Sdes 1077192595Sdes need = 1; 1078192595Sdes /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1079192595Sdes if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1080192595Sdes debug2("channel %d: socks4a request", c->self); 1081192595Sdes /* ... and needs an extra string (the hostname) */ 1082192595Sdes need = 2; 1083192595Sdes } 1084192595Sdes /* Check for terminating NUL on the string(s) */ 108576262Sgreen for (found = 0, i = len; i < have; i++) { 108676262Sgreen if (p[i] == '\0') { 1087192595Sdes found++; 1088192595Sdes if (found == need) 1089192595Sdes break; 109076262Sgreen } 109176262Sgreen if (i > 1024) { 109276262Sgreen /* the peer is probably sending garbage */ 109376262Sgreen debug("channel %d: decode socks4: too long", 109476262Sgreen c->self); 109576262Sgreen return -1; 109676262Sgreen } 109776262Sgreen } 1098192595Sdes if (found < need) 109976262Sgreen return 0; 110076262Sgreen buffer_get(&c->input, (char *)&s4_req.version, 1); 110176262Sgreen buffer_get(&c->input, (char *)&s4_req.command, 1); 110276262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 110376262Sgreen buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 110476262Sgreen have = buffer_len(&c->input); 1105295367Sdes p = (char *)buffer_ptr(&c->input); 1106264377Sdes if (memchr(p, '\0', have) == NULL) 1107264377Sdes fatal("channel %d: decode socks4: user not nul terminated", 1108264377Sdes c->self); 110976262Sgreen len = strlen(p); 111076262Sgreen debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1111192595Sdes len++; /* trailing '\0' */ 111276262Sgreen if (len > have) 111376262Sgreen fatal("channel %d: decode socks4: len %d > have %d", 111476262Sgreen c->self, len, have); 111576262Sgreen strlcpy(username, p, sizeof(username)); 111676262Sgreen buffer_consume(&c->input, len); 111776262Sgreen 1118255767Sdes free(c->path); 1119255767Sdes c->path = NULL; 1120192595Sdes if (need == 1) { /* SOCKS4: one string */ 1121192595Sdes host = inet_ntoa(s4_req.dest_addr); 1122192595Sdes c->path = xstrdup(host); 1123192595Sdes } else { /* SOCKS4A: two strings */ 1124192595Sdes have = buffer_len(&c->input); 1125295367Sdes p = (char *)buffer_ptr(&c->input); 1126192595Sdes len = strlen(p); 1127192595Sdes debug2("channel %d: decode socks4a: host %s/%d", 1128192595Sdes c->self, p, len); 1129192595Sdes len++; /* trailing '\0' */ 1130192595Sdes if (len > have) 1131192595Sdes fatal("channel %d: decode socks4a: len %d > have %d", 1132192595Sdes c->self, len, have); 1133192595Sdes if (len > NI_MAXHOST) { 1134192595Sdes error("channel %d: hostname \"%.100s\" too long", 1135192595Sdes c->self, p); 1136192595Sdes return -1; 1137192595Sdes } 1138192595Sdes c->path = xstrdup(p); 1139192595Sdes buffer_consume(&c->input, len); 1140192595Sdes } 114176262Sgreen c->host_port = ntohs(s4_req.dest_port); 114292559Sdes 1143124207Sdes debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1144192595Sdes c->self, c->path, c->host_port, s4_req.command); 114576262Sgreen 114676262Sgreen if (s4_req.command != 1) { 1147192595Sdes debug("channel %d: cannot handle: %s cn %d", 1148192595Sdes c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 114976262Sgreen return -1; 115076262Sgreen } 115176262Sgreen s4_rsp.version = 0; /* vn: 0 for reply */ 115276262Sgreen s4_rsp.command = 90; /* cd: req granted */ 115376262Sgreen s4_rsp.dest_port = 0; /* ignored */ 115476262Sgreen s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1155162856Sdes buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 115676262Sgreen return 1; 115776262Sgreen} 115876262Sgreen 1159124207Sdes/* try to decode a socks5 header */ 1160124207Sdes#define SSH_SOCKS5_AUTHDONE 0x1000 1161124207Sdes#define SSH_SOCKS5_NOAUTH 0x00 1162124207Sdes#define SSH_SOCKS5_IPV4 0x01 1163124207Sdes#define SSH_SOCKS5_DOMAIN 0x03 1164124207Sdes#define SSH_SOCKS5_IPV6 0x04 1165124207Sdes#define SSH_SOCKS5_CONNECT 0x01 1166124207Sdes#define SSH_SOCKS5_SUCCESS 0x00 1167124207Sdes 1168162856Sdes/* ARGSUSED */ 1169124207Sdesstatic int 1170162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1171124207Sdes{ 1172124207Sdes struct { 1173124207Sdes u_int8_t version; 1174124207Sdes u_int8_t command; 1175124207Sdes u_int8_t reserved; 1176124207Sdes u_int8_t atyp; 1177124207Sdes } s5_req, s5_rsp; 1178124207Sdes u_int16_t dest_port; 1179255767Sdes char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1180255767Sdes u_char *p; 1181162856Sdes u_int have, need, i, found, nmethods, addrlen, af; 1182124207Sdes 1183124207Sdes debug2("channel %d: decode socks5", c->self); 1184124207Sdes p = buffer_ptr(&c->input); 1185124207Sdes if (p[0] != 0x05) 1186124207Sdes return -1; 1187124207Sdes have = buffer_len(&c->input); 1188124207Sdes if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1189124207Sdes /* format: ver | nmethods | methods */ 1190126273Sdes if (have < 2) 1191124207Sdes return 0; 1192124207Sdes nmethods = p[1]; 1193124207Sdes if (have < nmethods + 2) 1194124207Sdes return 0; 1195124207Sdes /* look for method: "NO AUTHENTICATION REQUIRED" */ 1196181111Sdes for (found = 0, i = 2; i < nmethods + 2; i++) { 1197162856Sdes if (p[i] == SSH_SOCKS5_NOAUTH) { 1198124207Sdes found = 1; 1199124207Sdes break; 1200124207Sdes } 1201124207Sdes } 1202124207Sdes if (!found) { 1203124207Sdes debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1204124207Sdes c->self); 1205124207Sdes return -1; 1206124207Sdes } 1207124207Sdes buffer_consume(&c->input, nmethods + 2); 1208124207Sdes buffer_put_char(&c->output, 0x05); /* version */ 1209124207Sdes buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1210124207Sdes FD_SET(c->sock, writeset); 1211124207Sdes c->flags |= SSH_SOCKS5_AUTHDONE; 1212124207Sdes debug2("channel %d: socks5 auth done", c->self); 1213124207Sdes return 0; /* need more */ 1214124207Sdes } 1215124207Sdes debug2("channel %d: socks5 post auth", c->self); 1216124207Sdes if (have < sizeof(s5_req)+1) 1217124207Sdes return 0; /* need more */ 1218162856Sdes memcpy(&s5_req, p, sizeof(s5_req)); 1219124207Sdes if (s5_req.version != 0x05 || 1220124207Sdes s5_req.command != SSH_SOCKS5_CONNECT || 1221124207Sdes s5_req.reserved != 0x00) { 1222124207Sdes debug2("channel %d: only socks5 connect supported", c->self); 1223124207Sdes return -1; 1224124207Sdes } 1225147005Sdes switch (s5_req.atyp){ 1226124207Sdes case SSH_SOCKS5_IPV4: 1227124207Sdes addrlen = 4; 1228124207Sdes af = AF_INET; 1229124207Sdes break; 1230124207Sdes case SSH_SOCKS5_DOMAIN: 1231124207Sdes addrlen = p[sizeof(s5_req)]; 1232124207Sdes af = -1; 1233124207Sdes break; 1234124207Sdes case SSH_SOCKS5_IPV6: 1235124207Sdes addrlen = 16; 1236124207Sdes af = AF_INET6; 1237124207Sdes break; 1238124207Sdes default: 1239124207Sdes debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1240124207Sdes return -1; 1241124207Sdes } 1242162856Sdes need = sizeof(s5_req) + addrlen + 2; 1243162856Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1244162856Sdes need++; 1245162856Sdes if (have < need) 1246124207Sdes return 0; 1247124207Sdes buffer_consume(&c->input, sizeof(s5_req)); 1248124207Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1249124207Sdes buffer_consume(&c->input, 1); /* host string length */ 1250255767Sdes buffer_get(&c->input, &dest_addr, addrlen); 1251124207Sdes buffer_get(&c->input, (char *)&dest_port, 2); 1252124207Sdes dest_addr[addrlen] = '\0'; 1253255767Sdes free(c->path); 1254255767Sdes c->path = NULL; 1255192595Sdes if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1256192595Sdes if (addrlen >= NI_MAXHOST) { 1257192595Sdes error("channel %d: dynamic request: socks5 hostname " 1258192595Sdes "\"%.100s\" too long", c->self, dest_addr); 1259192595Sdes return -1; 1260192595Sdes } 1261192595Sdes c->path = xstrdup(dest_addr); 1262192595Sdes } else { 1263192595Sdes if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1264192595Sdes return -1; 1265192595Sdes c->path = xstrdup(ntop); 1266192595Sdes } 1267124207Sdes c->host_port = ntohs(dest_port); 1268126273Sdes 1269124207Sdes debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1270124207Sdes c->self, c->path, c->host_port, s5_req.command); 1271124207Sdes 1272124207Sdes s5_rsp.version = 0x05; 1273124207Sdes s5_rsp.command = SSH_SOCKS5_SUCCESS; 1274124207Sdes s5_rsp.reserved = 0; /* ignored */ 1275124207Sdes s5_rsp.atyp = SSH_SOCKS5_IPV4; 1276124207Sdes dest_port = 0; /* ignored */ 1277124207Sdes 1278162856Sdes buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1279255767Sdes buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ 1280162856Sdes buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1281124207Sdes return 1; 1282124207Sdes} 1283124207Sdes 1284204917SdesChannel * 1285204917Sdeschannel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, 1286204917Sdes int in, int out) 1287204917Sdes{ 1288204917Sdes Channel *c; 1289204917Sdes 1290204917Sdes debug("channel_connect_stdio_fwd %s:%d", host_to_connect, 1291204917Sdes port_to_connect); 1292204917Sdes 1293204917Sdes c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, 1294204917Sdes -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1295204917Sdes 0, "stdio-forward", /*nonblock*/0); 1296204917Sdes 1297204917Sdes c->path = xstrdup(host_to_connect); 1298204917Sdes c->host_port = port_to_connect; 1299204917Sdes c->listening_port = 0; 1300204917Sdes c->force_drain = 1; 1301204917Sdes 1302204917Sdes channel_register_fds(c, in, out, -1, 0, 1, 0); 1303204917Sdes port_open_helper(c, "direct-tcpip"); 1304204917Sdes 1305204917Sdes return c; 1306204917Sdes} 1307204917Sdes 130876262Sgreen/* dynamic port forwarding */ 130992559Sdesstatic void 1310162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 131176262Sgreen{ 131276262Sgreen u_char *p; 1313149753Sdes u_int have; 1314149753Sdes int ret; 131576262Sgreen 131676262Sgreen have = buffer_len(&c->input); 131776262Sgreen debug2("channel %d: pre_dynamic: have %d", c->self, have); 131876262Sgreen /* buffer_dump(&c->input); */ 131976262Sgreen /* check if the fixed size part of the packet is in buffer. */ 1320124207Sdes if (have < 3) { 132176262Sgreen /* need more */ 132276262Sgreen FD_SET(c->sock, readset); 132376262Sgreen return; 132476262Sgreen } 132576262Sgreen /* try to guess the protocol */ 132676262Sgreen p = buffer_ptr(&c->input); 132776262Sgreen switch (p[0]) { 132876262Sgreen case 0x04: 132976262Sgreen ret = channel_decode_socks4(c, readset, writeset); 133076262Sgreen break; 1331124207Sdes case 0x05: 1332124207Sdes ret = channel_decode_socks5(c, readset, writeset); 1333124207Sdes break; 133476262Sgreen default: 133576262Sgreen ret = -1; 133676262Sgreen break; 133776262Sgreen } 133876262Sgreen if (ret < 0) { 133992559Sdes chan_mark_dead(c); 134076262Sgreen } else if (ret == 0) { 134176262Sgreen debug2("channel %d: pre_dynamic: need more", c->self); 134276262Sgreen /* need more */ 134376262Sgreen FD_SET(c->sock, readset); 134476262Sgreen } else { 134576262Sgreen /* switch to the next state */ 134676262Sgreen c->type = SSH_CHANNEL_OPENING; 134776262Sgreen port_open_helper(c, "direct-tcpip"); 134876262Sgreen } 134976262Sgreen} 135076262Sgreen 135160573Skris/* This is our fake X11 server socket. */ 1352162856Sdes/* ARGSUSED */ 135392559Sdesstatic void 1354162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 135560573Skris{ 135692559Sdes Channel *nc; 1357181111Sdes struct sockaddr_storage addr; 1358255767Sdes int newsock, oerrno; 135960573Skris socklen_t addrlen; 136076262Sgreen char buf[16384], *remote_ipaddr; 136160573Skris int remote_port; 136257429Smarkm 136360573Skris if (FD_ISSET(c->sock, readset)) { 136460573Skris debug("X11 connection requested."); 136560573Skris addrlen = sizeof(addr); 1366181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 136792559Sdes if (c->single_connection) { 1368255767Sdes oerrno = errno; 1369124207Sdes debug2("single_connection: closing X11 listener."); 137092559Sdes channel_close_fd(&c->sock); 137192559Sdes chan_mark_dead(c); 1372255767Sdes errno = oerrno; 137392559Sdes } 137460573Skris if (newsock < 0) { 1375255767Sdes if (errno != EINTR && errno != EWOULDBLOCK && 1376255767Sdes errno != ECONNABORTED) 1377255767Sdes error("accept: %.100s", strerror(errno)); 1378240075Sdes if (errno == EMFILE || errno == ENFILE) 1379255767Sdes c->notbefore = monotime() + 1; 138060573Skris return; 138160573Skris } 138292559Sdes set_nodelay(newsock); 138376262Sgreen remote_ipaddr = get_peer_ipaddr(newsock); 138460573Skris remote_port = get_peer_port(newsock); 138560573Skris snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 138676262Sgreen remote_ipaddr, remote_port); 138757429Smarkm 138892559Sdes nc = channel_new("accepted x11 socket", 138960573Skris SSH_CHANNEL_OPENING, newsock, newsock, -1, 1390124207Sdes c->local_window_max, c->local_maxpacket, 0, buf, 1); 139160573Skris if (compat20) { 139260573Skris packet_start(SSH2_MSG_CHANNEL_OPEN); 139360573Skris packet_put_cstring("x11"); 139492559Sdes packet_put_int(nc->self); 139592559Sdes packet_put_int(nc->local_window_max); 139692559Sdes packet_put_int(nc->local_maxpacket); 139776262Sgreen /* originator ipaddr and port */ 139876262Sgreen packet_put_cstring(remote_ipaddr); 139960573Skris if (datafellows & SSH_BUG_X11FWD) { 1400124207Sdes debug2("ssh2 x11 bug compat mode"); 140157429Smarkm } else { 140260573Skris packet_put_int(remote_port); 140357429Smarkm } 140460573Skris packet_send(); 140560573Skris } else { 140660573Skris packet_start(SSH_SMSG_X11_OPEN); 140792559Sdes packet_put_int(nc->self); 140892559Sdes if (packet_get_protocol_flags() & 140992559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 141092559Sdes packet_put_cstring(buf); 141160573Skris packet_send(); 141257429Smarkm } 1413255767Sdes free(remote_ipaddr); 141457429Smarkm } 141557429Smarkm} 141657429Smarkm 141792559Sdesstatic void 141876262Sgreenport_open_helper(Channel *c, char *rtype) 141976262Sgreen{ 142076262Sgreen char buf[1024]; 1421262566Sdes char *local_ipaddr = get_local_ipaddr(c->sock); 1422323124Sdes int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 142376262Sgreen char *remote_ipaddr = get_peer_ipaddr(c->sock); 1424149753Sdes int remote_port = get_peer_port(c->sock); 142576262Sgreen 1426204917Sdes if (remote_port == -1) { 1427204917Sdes /* Fake addr/port to appease peers that validate it (Tectia) */ 1428255767Sdes free(remote_ipaddr); 1429204917Sdes remote_ipaddr = xstrdup("127.0.0.1"); 1430204917Sdes remote_port = 65535; 1431204917Sdes } 1432204917Sdes 143376262Sgreen snprintf(buf, sizeof buf, 143476262Sgreen "%s: listening port %d for %.100s port %d, " 1435262566Sdes "connect from %.200s port %d to %.100s port %d", 143676262Sgreen rtype, c->listening_port, c->path, c->host_port, 1437262566Sdes remote_ipaddr, remote_port, local_ipaddr, local_port); 143876262Sgreen 1439255767Sdes free(c->remote_name); 144076262Sgreen c->remote_name = xstrdup(buf); 144176262Sgreen 144276262Sgreen if (compat20) { 144376262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 144476262Sgreen packet_put_cstring(rtype); 144576262Sgreen packet_put_int(c->self); 144676262Sgreen packet_put_int(c->local_window_max); 144776262Sgreen packet_put_int(c->local_maxpacket); 1448295367Sdes if (strcmp(rtype, "direct-tcpip") == 0) { 144976262Sgreen /* target host, port */ 145076262Sgreen packet_put_cstring(c->path); 145176262Sgreen packet_put_int(c->host_port); 1452295367Sdes } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1453295367Sdes /* target path */ 1454295367Sdes packet_put_cstring(c->path); 1455295367Sdes } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1456295367Sdes /* listen path */ 1457295367Sdes packet_put_cstring(c->path); 145876262Sgreen } else { 145976262Sgreen /* listen address, port */ 146076262Sgreen packet_put_cstring(c->path); 1461262566Sdes packet_put_int(local_port); 146276262Sgreen } 1463295367Sdes if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1464295367Sdes /* reserved for future owner/mode info */ 1465295367Sdes packet_put_cstring(""); 1466295367Sdes } else { 1467295367Sdes /* originator host and port */ 1468295367Sdes packet_put_cstring(remote_ipaddr); 1469295367Sdes packet_put_int((u_int)remote_port); 1470295367Sdes } 147176262Sgreen packet_send(); 147276262Sgreen } else { 147376262Sgreen packet_start(SSH_MSG_PORT_OPEN); 147476262Sgreen packet_put_int(c->self); 147576262Sgreen packet_put_cstring(c->path); 147676262Sgreen packet_put_int(c->host_port); 147792559Sdes if (packet_get_protocol_flags() & 147892559Sdes SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 147976262Sgreen packet_put_cstring(c->remote_name); 148076262Sgreen packet_send(); 148176262Sgreen } 1482255767Sdes free(remote_ipaddr); 1483262566Sdes free(local_ipaddr); 148476262Sgreen} 148576262Sgreen 1486157019Sdesstatic void 1487157019Sdeschannel_set_reuseaddr(int fd) 1488157019Sdes{ 1489157019Sdes int on = 1; 1490157019Sdes 1491157019Sdes /* 1492157019Sdes * Set socket options. 1493157019Sdes * Allow local port reuse in TIME_WAIT. 1494157019Sdes */ 1495157019Sdes if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1496157019Sdes error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1497157019Sdes} 1498157019Sdes 1499295367Sdesvoid 1500295367Sdeschannel_set_x11_refuse_time(u_int refuse_time) 1501295367Sdes{ 1502295367Sdes x11_refuse_time = refuse_time; 1503295367Sdes} 1504295367Sdes 150557429Smarkm/* 150660573Skris * This socket is listening for connections to a forwarded TCP/IP port. 150757429Smarkm */ 1508162856Sdes/* ARGSUSED */ 150992559Sdesstatic void 1510162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 151157429Smarkm{ 151276262Sgreen Channel *nc; 1513181111Sdes struct sockaddr_storage addr; 151492559Sdes int newsock, nextstate; 151557429Smarkm socklen_t addrlen; 151676262Sgreen char *rtype; 151757429Smarkm 151860573Skris if (FD_ISSET(c->sock, readset)) { 151960573Skris debug("Connection to port %d forwarding " 152060573Skris "to %.100s port %d requested.", 152160573Skris c->listening_port, c->path, c->host_port); 152276262Sgreen 152392559Sdes if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 152492559Sdes nextstate = SSH_CHANNEL_OPENING; 152592559Sdes rtype = "forwarded-tcpip"; 1526295367Sdes } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1527295367Sdes nextstate = SSH_CHANNEL_OPENING; 1528295367Sdes rtype = "forwarded-streamlocal@openssh.com"; 1529295367Sdes } else if (c->host_port == PORT_STREAMLOCAL) { 1530295367Sdes nextstate = SSH_CHANNEL_OPENING; 1531295367Sdes rtype = "direct-streamlocal@openssh.com"; 1532295367Sdes } else if (c->host_port == 0) { 1533295367Sdes nextstate = SSH_CHANNEL_DYNAMIC; 1534295367Sdes rtype = "dynamic-tcpip"; 153592559Sdes } else { 1536295367Sdes nextstate = SSH_CHANNEL_OPENING; 1537295367Sdes rtype = "direct-tcpip"; 153892559Sdes } 153976262Sgreen 154060573Skris addrlen = sizeof(addr); 1541181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 154260573Skris if (newsock < 0) { 1543255767Sdes if (errno != EINTR && errno != EWOULDBLOCK && 1544255767Sdes errno != ECONNABORTED) 1545255767Sdes error("accept: %.100s", strerror(errno)); 1546240075Sdes if (errno == EMFILE || errno == ENFILE) 1547255767Sdes c->notbefore = monotime() + 1; 154860573Skris return; 154960573Skris } 1550295367Sdes if (c->host_port != PORT_STREAMLOCAL) 1551295367Sdes set_nodelay(newsock); 1552124207Sdes nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1553124207Sdes c->local_window_max, c->local_maxpacket, 0, rtype, 1); 155476262Sgreen nc->listening_port = c->listening_port; 155576262Sgreen nc->host_port = c->host_port; 1556192595Sdes if (c->path != NULL) 1557192595Sdes nc->path = xstrdup(c->path); 155876262Sgreen 1559204917Sdes if (nextstate != SSH_CHANNEL_DYNAMIC) 156076262Sgreen port_open_helper(nc, rtype); 156160573Skris } 156260573Skris} 156357429Smarkm 156460573Skris/* 156560573Skris * This is the authentication agent socket listening for connections from 156660573Skris * clients. 156760573Skris */ 1568162856Sdes/* ARGSUSED */ 156992559Sdesstatic void 1570162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 157160573Skris{ 157292559Sdes Channel *nc; 157392559Sdes int newsock; 1574181111Sdes struct sockaddr_storage addr; 157560573Skris socklen_t addrlen; 157657429Smarkm 157760573Skris if (FD_ISSET(c->sock, readset)) { 157860573Skris addrlen = sizeof(addr); 1579181111Sdes newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 158060573Skris if (newsock < 0) { 1581240075Sdes error("accept from auth socket: %.100s", 1582240075Sdes strerror(errno)); 1583240075Sdes if (errno == EMFILE || errno == ENFILE) 1584255767Sdes c->notbefore = monotime() + 1; 158560573Skris return; 158660573Skris } 158792559Sdes nc = channel_new("accepted auth socket", 158876262Sgreen SSH_CHANNEL_OPENING, newsock, newsock, -1, 158976262Sgreen c->local_window_max, c->local_maxpacket, 1590124207Sdes 0, "accepted auth socket", 1); 159176262Sgreen if (compat20) { 159276262Sgreen packet_start(SSH2_MSG_CHANNEL_OPEN); 159376262Sgreen packet_put_cstring("auth-agent@openssh.com"); 159492559Sdes packet_put_int(nc->self); 159576262Sgreen packet_put_int(c->local_window_max); 159676262Sgreen packet_put_int(c->local_maxpacket); 159776262Sgreen } else { 159876262Sgreen packet_start(SSH_SMSG_AGENT_OPEN); 159992559Sdes packet_put_int(nc->self); 160076262Sgreen } 160160573Skris packet_send(); 160260573Skris } 160360573Skris} 160457429Smarkm 1605162856Sdes/* ARGSUSED */ 160692559Sdesstatic void 1607162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 160876262Sgreen{ 1609181111Sdes int err = 0, sock; 161092559Sdes socklen_t sz = sizeof(err); 161192559Sdes 161276262Sgreen if (FD_ISSET(c->sock, writeset)) { 161392559Sdes if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 161492559Sdes err = errno; 161592559Sdes error("getsockopt SO_ERROR failed"); 161692559Sdes } 161792559Sdes if (err == 0) { 1618181111Sdes debug("channel %d: connected to %s port %d", 1619181111Sdes c->self, c->connect_ctx.host, c->connect_ctx.port); 1620181111Sdes channel_connect_ctx_free(&c->connect_ctx); 162192559Sdes c->type = SSH_CHANNEL_OPEN; 162292559Sdes if (compat20) { 162392559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 162492559Sdes packet_put_int(c->remote_id); 162592559Sdes packet_put_int(c->self); 162692559Sdes packet_put_int(c->local_window); 162792559Sdes packet_put_int(c->local_maxpacket); 162892559Sdes } else { 162992559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 163092559Sdes packet_put_int(c->remote_id); 163192559Sdes packet_put_int(c->self); 163292559Sdes } 163376262Sgreen } else { 1634181111Sdes debug("channel %d: connection failed: %s", 163592559Sdes c->self, strerror(err)); 1636181111Sdes /* Try next address, if any */ 1637181111Sdes if ((sock = connect_next(&c->connect_ctx)) > 0) { 1638181111Sdes close(c->sock); 1639181111Sdes c->sock = c->rfd = c->wfd = sock; 1640181111Sdes channel_max_fd = channel_find_maxfd(); 1641181111Sdes return; 1642181111Sdes } 1643181111Sdes /* Exhausted all addresses */ 1644181111Sdes error("connect_to %.100s port %d: failed.", 1645181111Sdes c->connect_ctx.host, c->connect_ctx.port); 1646181111Sdes channel_connect_ctx_free(&c->connect_ctx); 164792559Sdes if (compat20) { 164892559Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 164992559Sdes packet_put_int(c->remote_id); 165092559Sdes packet_put_int(SSH2_OPEN_CONNECT_FAILED); 165192559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 165292559Sdes packet_put_cstring(strerror(err)); 165392559Sdes packet_put_cstring(""); 165492559Sdes } 165576262Sgreen } else { 165692559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 165792559Sdes packet_put_int(c->remote_id); 165876262Sgreen } 165992559Sdes chan_mark_dead(c); 166076262Sgreen } 166192559Sdes packet_send(); 166276262Sgreen } 166376262Sgreen} 166476262Sgreen 1665162856Sdes/* ARGSUSED */ 166692559Sdesstatic int 1667162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 166860573Skris{ 1669147005Sdes char buf[CHAN_RBUF]; 1670181111Sdes int len, force; 167157429Smarkm 1672181111Sdes force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1673181111Sdes if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1674162856Sdes errno = 0; 167560573Skris len = read(c->rfd, buf, sizeof(buf)); 1676181111Sdes if (len < 0 && (errno == EINTR || 1677181111Sdes ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 167860573Skris return 1; 1679162856Sdes#ifndef PTY_ZEROREAD 168078827Sgreen if (len <= 0) { 1681162856Sdes#else 1682162856Sdes if ((!c->isatty && len <= 0) || 1683162856Sdes (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1684162856Sdes#endif 1685124207Sdes debug2("channel %d: read<=0 rfd %d len %d", 168660573Skris c->self, c->rfd, len); 168776262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1688124207Sdes debug2("channel %d: not open", c->self); 168992559Sdes chan_mark_dead(c); 169076262Sgreen return -1; 169176262Sgreen } else if (compat13) { 169292559Sdes buffer_clear(&c->output); 169360573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 1694124207Sdes debug2("channel %d: input draining.", c->self); 169560573Skris } else { 169660573Skris chan_read_failed(c); 169757429Smarkm } 169860573Skris return -1; 169960573Skris } 170092559Sdes if (c->input_filter != NULL) { 170165668Skris if (c->input_filter(c, buf, len) == -1) { 1702124207Sdes debug2("channel %d: filter stops", c->self); 170365668Skris chan_read_failed(c); 170465668Skris } 1705157019Sdes } else if (c->datagram) { 1706157019Sdes buffer_put_string(&c->input, buf, len); 170765668Skris } else { 170865668Skris buffer_append(&c->input, buf, len); 170965668Skris } 171060573Skris } 171160573Skris return 1; 171260573Skris} 1713162856Sdes 1714162856Sdes/* ARGSUSED */ 171592559Sdesstatic int 1716162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 171760573Skris{ 171876262Sgreen struct termios tio; 1719157019Sdes u_char *data = NULL, *buf; 1720215116Sdes u_int dlen, olen = 0; 172160573Skris int len; 172260573Skris 172360573Skris /* Send buffered output data to the socket. */ 172460573Skris if (c->wfd != -1 && 172560573Skris FD_ISSET(c->wfd, writeset) && 172660573Skris buffer_len(&c->output) > 0) { 1727215116Sdes olen = buffer_len(&c->output); 1728157019Sdes if (c->output_filter != NULL) { 1729157019Sdes if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1730157019Sdes debug2("channel %d: filter stops", c->self); 1731157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1732157019Sdes chan_mark_dead(c); 1733157019Sdes else 1734157019Sdes chan_write_failed(c); 1735157019Sdes return -1; 1736157019Sdes } 1737157019Sdes } else if (c->datagram) { 1738157019Sdes buf = data = buffer_get_string(&c->output, &dlen); 1739157019Sdes } else { 1740157019Sdes buf = data = buffer_ptr(&c->output); 1741157019Sdes dlen = buffer_len(&c->output); 1742157019Sdes } 1743157019Sdes 1744157019Sdes if (c->datagram) { 1745157019Sdes /* ignore truncated writes, datagrams might get lost */ 1746157019Sdes len = write(c->wfd, buf, dlen); 1747255767Sdes free(data); 1748181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1749181111Sdes errno == EWOULDBLOCK)) 1750157019Sdes return 1; 1751157019Sdes if (len <= 0) { 1752157019Sdes if (c->type != SSH_CHANNEL_OPEN) 1753157019Sdes chan_mark_dead(c); 1754157019Sdes else 1755157019Sdes chan_write_failed(c); 1756157019Sdes return -1; 1757157019Sdes } 1758215116Sdes goto out; 1759157019Sdes } 1760106130Sdes#ifdef _AIX 1761126273Sdes /* XXX: Later AIX versions can't push as much data to tty */ 1762126273Sdes if (compat20 && c->wfd_isatty) 1763126273Sdes dlen = MIN(dlen, 8*1024); 1764106130Sdes#endif 1765157019Sdes 1766157019Sdes len = write(c->wfd, buf, dlen); 1767181111Sdes if (len < 0 && 1768181111Sdes (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 176960573Skris return 1; 177060573Skris if (len <= 0) { 177176262Sgreen if (c->type != SSH_CHANNEL_OPEN) { 1772124207Sdes debug2("channel %d: not open", c->self); 177392559Sdes chan_mark_dead(c); 177476262Sgreen return -1; 177576262Sgreen } else if (compat13) { 177692559Sdes buffer_clear(&c->output); 1777124207Sdes debug2("channel %d: input draining.", c->self); 177860573Skris c->type = SSH_CHANNEL_INPUT_DRAINING; 177960573Skris } else { 178060573Skris chan_write_failed(c); 178157429Smarkm } 178260573Skris return -1; 178360573Skris } 1784197679Sdes#ifndef BROKEN_TCGETATTR_ICANON 1785157019Sdes if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 178674500Sgreen if (tcgetattr(c->wfd, &tio) == 0 && 178774500Sgreen !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 178874500Sgreen /* 178974500Sgreen * Simulate echo to reduce the impact of 179076262Sgreen * traffic analysis. We need to match the 179176262Sgreen * size of a SSH2_MSG_CHANNEL_DATA message 1792157019Sdes * (4 byte channel id + buf) 179374500Sgreen */ 179476262Sgreen packet_send_ignore(4 + len); 179574500Sgreen packet_send(); 179674500Sgreen } 179774500Sgreen } 1798197679Sdes#endif 179960573Skris buffer_consume(&c->output, len); 180060573Skris } 1801215116Sdes out: 1802215116Sdes if (compat20 && olen > 0) 1803215116Sdes c->local_consumed += olen - buffer_len(&c->output); 180460573Skris return 1; 180560573Skris} 1806162856Sdes 180792559Sdesstatic int 1808162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 180960573Skris{ 1810147005Sdes char buf[CHAN_RBUF]; 181160573Skris int len; 181257429Smarkm 181360573Skris/** XXX handle drain efd, too */ 181460573Skris if (c->efd != -1) { 181560573Skris if (c->extended_usage == CHAN_EXTENDED_WRITE && 181660573Skris FD_ISSET(c->efd, writeset) && 181760573Skris buffer_len(&c->extended) > 0) { 181860573Skris len = write(c->efd, buffer_ptr(&c->extended), 181960573Skris buffer_len(&c->extended)); 182069587Sgreen debug2("channel %d: written %d to efd %d", 182160573Skris c->self, len, c->efd); 1822181111Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN || 1823181111Sdes errno == EWOULDBLOCK)) 182476262Sgreen return 1; 182576262Sgreen if (len <= 0) { 182676262Sgreen debug2("channel %d: closing write-efd %d", 182776262Sgreen c->self, c->efd); 182892559Sdes channel_close_fd(&c->efd); 182976262Sgreen } else { 183060573Skris buffer_consume(&c->extended, len); 183160573Skris c->local_consumed += len; 183257429Smarkm } 1833215116Sdes } else if (c->efd != -1 && 1834215116Sdes (c->extended_usage == CHAN_EXTENDED_READ || 1835215116Sdes c->extended_usage == CHAN_EXTENDED_IGNORE) && 1836181111Sdes (c->detach_close || FD_ISSET(c->efd, readset))) { 183760573Skris len = read(c->efd, buf, sizeof(buf)); 183869587Sgreen debug2("channel %d: read %d from efd %d", 183992559Sdes c->self, len, c->efd); 1840181111Sdes if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1841181111Sdes errno == EWOULDBLOCK) && !c->detach_close))) 184276262Sgreen return 1; 184376262Sgreen if (len <= 0) { 184476262Sgreen debug2("channel %d: closing read-efd %d", 184560573Skris c->self, c->efd); 184692559Sdes channel_close_fd(&c->efd); 184776262Sgreen } else { 1848215116Sdes if (c->extended_usage == CHAN_EXTENDED_IGNORE) { 1849215116Sdes debug3("channel %d: discard efd", 1850215116Sdes c->self); 1851215116Sdes } else 1852215116Sdes buffer_append(&c->extended, buf, len); 185376262Sgreen } 185460573Skris } 185560573Skris } 185660573Skris return 1; 185760573Skris} 1858162856Sdes 185992559Sdesstatic int 186076262Sgreenchannel_check_window(Channel *c) 186160573Skris{ 186276262Sgreen if (c->type == SSH_CHANNEL_OPEN && 186376262Sgreen !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1864181111Sdes ((c->local_window_max - c->local_window > 1865181111Sdes c->local_maxpacket*3) || 1866181111Sdes c->local_window < c->local_window_max/2) && 186760573Skris c->local_consumed > 0) { 186860573Skris packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 186960573Skris packet_put_int(c->remote_id); 1870294693Sdes packet_put_int(c->local_consumed); 187160573Skris packet_send(); 187269587Sgreen debug2("channel %d: window %d sent adjust %d", 187360573Skris c->self, c->local_window, 187460573Skris c->local_consumed); 1875294693Sdes c->local_window += c->local_consumed; 187660573Skris c->local_consumed = 0; 187760573Skris } 187860573Skris return 1; 187960573Skris} 188057429Smarkm 188192559Sdesstatic void 1882162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 188360573Skris{ 188460573Skris channel_handle_rfd(c, readset, writeset); 188560573Skris channel_handle_wfd(c, readset, writeset); 188692559Sdes if (!compat20) 188792559Sdes return; 188860573Skris channel_handle_efd(c, readset, writeset); 188976262Sgreen channel_check_window(c); 189060573Skris} 189160573Skris 1892204917Sdesstatic u_int 1893204917Sdesread_mux(Channel *c, u_int need) 1894204917Sdes{ 1895204917Sdes char buf[CHAN_RBUF]; 1896204917Sdes int len; 1897204917Sdes u_int rlen; 1898204917Sdes 1899204917Sdes if (buffer_len(&c->input) < need) { 1900204917Sdes rlen = need - buffer_len(&c->input); 1901204917Sdes len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); 1902296781Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1903296781Sdes return buffer_len(&c->input); 1904204917Sdes if (len <= 0) { 1905296781Sdes debug2("channel %d: ctl read<=0 rfd %d len %d", 1906296781Sdes c->self, c->rfd, len); 1907296781Sdes chan_read_failed(c); 1908296781Sdes return 0; 1909204917Sdes } else 1910204917Sdes buffer_append(&c->input, buf, len); 1911204917Sdes } 1912204917Sdes return buffer_len(&c->input); 1913204917Sdes} 1914204917Sdes 1915204917Sdesstatic void 1916204917Sdeschannel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1917204917Sdes{ 1918204917Sdes u_int need; 1919204917Sdes ssize_t len; 1920204917Sdes 1921204917Sdes if (!compat20) 1922204917Sdes fatal("%s: entered with !compat20", __func__); 1923204917Sdes 1924215116Sdes if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && 1925204917Sdes (c->istate == CHAN_INPUT_OPEN || 1926204917Sdes c->istate == CHAN_INPUT_WAIT_DRAIN)) { 1927204917Sdes /* 1928204917Sdes * Don't not read past the precise end of packets to 1929204917Sdes * avoid disrupting fd passing. 1930204917Sdes */ 1931204917Sdes if (read_mux(c, 4) < 4) /* read header */ 1932204917Sdes return; 1933204917Sdes need = get_u32(buffer_ptr(&c->input)); 1934204917Sdes#define CHANNEL_MUX_MAX_PACKET (256 * 1024) 1935204917Sdes if (need > CHANNEL_MUX_MAX_PACKET) { 1936204917Sdes debug2("channel %d: packet too big %u > %u", 1937204917Sdes c->self, CHANNEL_MUX_MAX_PACKET, need); 1938204917Sdes chan_rcvd_oclose(c); 1939204917Sdes return; 1940204917Sdes } 1941204917Sdes if (read_mux(c, need + 4) < need + 4) /* read body */ 1942204917Sdes return; 1943204917Sdes if (c->mux_rcb(c) != 0) { 1944204917Sdes debug("channel %d: mux_rcb failed", c->self); 1945204917Sdes chan_mark_dead(c); 1946204917Sdes return; 1947204917Sdes } 1948204917Sdes } 1949204917Sdes 1950204917Sdes if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && 1951204917Sdes buffer_len(&c->output) > 0) { 1952204917Sdes len = write(c->wfd, buffer_ptr(&c->output), 1953204917Sdes buffer_len(&c->output)); 1954204917Sdes if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1955204917Sdes return; 1956204917Sdes if (len <= 0) { 1957204917Sdes chan_mark_dead(c); 1958204917Sdes return; 1959204917Sdes } 1960204917Sdes buffer_consume(&c->output, len); 1961204917Sdes } 1962204917Sdes} 1963204917Sdes 1964204917Sdesstatic void 1965204917Sdeschannel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) 1966204917Sdes{ 1967204917Sdes Channel *nc; 1968204917Sdes struct sockaddr_storage addr; 1969204917Sdes socklen_t addrlen; 1970204917Sdes int newsock; 1971204917Sdes uid_t euid; 1972204917Sdes gid_t egid; 1973204917Sdes 1974204917Sdes if (!FD_ISSET(c->sock, readset)) 1975204917Sdes return; 1976204917Sdes 1977204917Sdes debug("multiplexing control connection"); 1978204917Sdes 1979204917Sdes /* 1980204917Sdes * Accept connection on control socket 1981204917Sdes */ 1982204917Sdes memset(&addr, 0, sizeof(addr)); 1983204917Sdes addrlen = sizeof(addr); 1984204917Sdes if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 1985204917Sdes &addrlen)) == -1) { 1986204917Sdes error("%s accept: %s", __func__, strerror(errno)); 1987240075Sdes if (errno == EMFILE || errno == ENFILE) 1988255767Sdes c->notbefore = monotime() + 1; 1989204917Sdes return; 1990204917Sdes } 1991204917Sdes 1992204917Sdes if (getpeereid(newsock, &euid, &egid) < 0) { 1993204917Sdes error("%s getpeereid failed: %s", __func__, 1994204917Sdes strerror(errno)); 1995204917Sdes close(newsock); 1996204917Sdes return; 1997204917Sdes } 1998204917Sdes if ((euid != 0) && (getuid() != euid)) { 1999204917Sdes error("multiplex uid mismatch: peer euid %u != uid %u", 2000204917Sdes (u_int)euid, (u_int)getuid()); 2001204917Sdes close(newsock); 2002204917Sdes return; 2003204917Sdes } 2004204917Sdes nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, 2005204917Sdes newsock, newsock, -1, c->local_window_max, 2006204917Sdes c->local_maxpacket, 0, "mux-control", 1); 2007204917Sdes nc->mux_rcb = c->mux_rcb; 2008204917Sdes debug3("%s: new mux channel %d fd %d", __func__, 2009204917Sdes nc->self, nc->sock); 2010204917Sdes /* establish state */ 2011204917Sdes nc->mux_rcb(nc); 2012204917Sdes /* mux state transitions must not elicit protocol messages */ 2013204917Sdes nc->flags |= CHAN_LOCAL; 2014204917Sdes} 2015204917Sdes 2016162856Sdes/* ARGSUSED */ 201792559Sdesstatic void 2018162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 201960573Skris{ 202060573Skris int len; 2021106130Sdes 202260573Skris /* Send buffered output data to the socket. */ 202360573Skris if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 202460573Skris len = write(c->sock, buffer_ptr(&c->output), 202560573Skris buffer_len(&c->output)); 202660573Skris if (len <= 0) 202792559Sdes buffer_clear(&c->output); 202860573Skris else 202960573Skris buffer_consume(&c->output, len); 203060573Skris } 203160573Skris} 203260573Skris 203392559Sdesstatic void 203460573Skrischannel_handler_init_20(void) 203560573Skris{ 203692559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 203760573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 203860573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 203976262Sgreen channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 2040295367Sdes channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 2041295367Sdes channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 204260573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 204376262Sgreen channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 204476262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 204576262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2046204917Sdes channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 2047204917Sdes channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 204860573Skris 204992559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 205060573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 205176262Sgreen channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 2052295367Sdes channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 2053295367Sdes channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 205460573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 205576262Sgreen channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 205676262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 205792559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2058204917Sdes channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 2059204917Sdes channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 206060573Skris} 206160573Skris 206292559Sdesstatic void 206360573Skrischannel_handler_init_13(void) 206460573Skris{ 206560573Skris channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 206660573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 206760573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 206860573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 206960573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 207060573Skris channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 207160573Skris channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 207276262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 207376262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 207460573Skris 207592559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 207660573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 207760573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 207860573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 207960573Skris channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 208076262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 208192559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 208260573Skris} 208360573Skris 208492559Sdesstatic void 208560573Skrischannel_handler_init_15(void) 208660573Skris{ 208792559Sdes channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 208860573Skris channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 208960573Skris channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 209060573Skris channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 209160573Skris channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 209276262Sgreen channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 209376262Sgreen channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 209460573Skris 209560573Skris channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 209660573Skris channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 209760573Skris channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 209892559Sdes channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 209976262Sgreen channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 210092559Sdes channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 210160573Skris} 210260573Skris 210392559Sdesstatic void 210460573Skrischannel_handler_init(void) 210560573Skris{ 210660573Skris int i; 2107106130Sdes 210892559Sdes for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 210960573Skris channel_pre[i] = NULL; 211060573Skris channel_post[i] = NULL; 211160573Skris } 211260573Skris if (compat20) 211360573Skris channel_handler_init_20(); 211460573Skris else if (compat13) 211560573Skris channel_handler_init_13(); 211660573Skris else 211760573Skris channel_handler_init_15(); 211860573Skris} 211960573Skris 212092559Sdes/* gc dead channels */ 212192559Sdesstatic void 212292559Sdeschannel_garbage_collect(Channel *c) 212392559Sdes{ 212492559Sdes if (c == NULL) 212592559Sdes return; 212692559Sdes if (c->detach_user != NULL) { 2127157019Sdes if (!chan_is_dead(c, c->detach_close)) 212892559Sdes return; 2129124207Sdes debug2("channel %d: gc: notify user", c->self); 213092559Sdes c->detach_user(c->self, NULL); 213192559Sdes /* if we still have a callback */ 213292559Sdes if (c->detach_user != NULL) 213392559Sdes return; 2134124207Sdes debug2("channel %d: gc: user detached", c->self); 213592559Sdes } 213692559Sdes if (!chan_is_dead(c, 1)) 213792559Sdes return; 2138124207Sdes debug2("channel %d: garbage collecting", c->self); 213992559Sdes channel_free(c); 214092559Sdes} 214192559Sdes 214292559Sdesstatic void 2143240075Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, 2144240075Sdes time_t *unpause_secs) 214560573Skris{ 214660573Skris static int did_init = 0; 2147204917Sdes u_int i, oalloc; 214860573Skris Channel *c; 2149240075Sdes time_t now; 215060573Skris 215160573Skris if (!did_init) { 215260573Skris channel_handler_init(); 215360573Skris did_init = 1; 215460573Skris } 2155255767Sdes now = monotime(); 2156240075Sdes if (unpause_secs != NULL) 2157240075Sdes *unpause_secs = 0; 2158204917Sdes for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { 215992559Sdes c = channels[i]; 216092559Sdes if (c == NULL) 216157429Smarkm continue; 2162204917Sdes if (c->delayed) { 2163204917Sdes if (ftab == channel_pre) 2164204917Sdes c->delayed = 0; 2165204917Sdes else 2166204917Sdes continue; 2167204917Sdes } 2168240075Sdes if (ftab[c->type] != NULL) { 2169240075Sdes /* 2170240075Sdes * Run handlers that are not paused. 2171240075Sdes */ 2172240075Sdes if (c->notbefore <= now) 2173240075Sdes (*ftab[c->type])(c, readset, writeset); 2174240075Sdes else if (unpause_secs != NULL) { 2175240075Sdes /* 2176240075Sdes * Collect the time that the earliest 2177240075Sdes * channel comes off pause. 2178240075Sdes */ 2179240075Sdes debug3("%s: chan %d: skip for %d more seconds", 2180240075Sdes __func__, c->self, 2181240075Sdes (int)(c->notbefore - now)); 2182240075Sdes if (*unpause_secs == 0 || 2183240075Sdes (c->notbefore - now) < *unpause_secs) 2184240075Sdes *unpause_secs = c->notbefore - now; 2185240075Sdes } 2186240075Sdes } 218792559Sdes channel_garbage_collect(c); 218857429Smarkm } 2189240075Sdes if (unpause_secs != NULL && *unpause_secs != 0) 2190240075Sdes debug3("%s: first channel unpauses in %d seconds", 2191240075Sdes __func__, (int)*unpause_secs); 219257429Smarkm} 219357429Smarkm 219492559Sdes/* 219592559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in 219692559Sdes * select bitmasks. 219792559Sdes */ 219860573Skrisvoid 219976262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2200240075Sdes u_int *nallocp, time_t *minwait_secs, int rekeying) 220160573Skris{ 2202162856Sdes u_int n, sz, nfdset; 220376262Sgreen 220476262Sgreen n = MAX(*maxfdp, channel_max_fd); 220576262Sgreen 2206162856Sdes nfdset = howmany(n+1, NFDBITS); 2207162856Sdes /* Explicitly test here, because xrealloc isn't always called */ 2208295367Sdes if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) 2209162856Sdes fatal("channel_prepare_select: max_fd (%d) is too large", n); 2210162856Sdes sz = nfdset * sizeof(fd_mask); 2211162856Sdes 221292559Sdes /* perhaps check sz < nalloc/2 and shrink? */ 221392559Sdes if (*readsetp == NULL || sz > *nallocp) { 2214295367Sdes *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); 2215295367Sdes *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); 221692559Sdes *nallocp = sz; 221776262Sgreen } 221892559Sdes *maxfdp = n; 221976262Sgreen memset(*readsetp, 0, sz); 222076262Sgreen memset(*writesetp, 0, sz); 222176262Sgreen 222276262Sgreen if (!rekeying) 2223240075Sdes channel_handler(channel_pre, *readsetp, *writesetp, 2224240075Sdes minwait_secs); 222560573Skris} 222660573Skris 222792559Sdes/* 222892559Sdes * After select, perform any appropriate operations for channels which have 222992559Sdes * events pending. 223092559Sdes */ 223160573Skrisvoid 2232162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset) 223360573Skris{ 2234240075Sdes channel_handler(channel_post, readset, writeset, NULL); 223560573Skris} 223660573Skris 223792559Sdes 223876262Sgreen/* If there is data to send to the connection, enqueue some of it now. */ 223960573Skrisvoid 224092559Sdeschannel_output_poll(void) 224157429Smarkm{ 224260573Skris Channel *c; 2243137019Sdes u_int i, len; 224457429Smarkm 224557429Smarkm for (i = 0; i < channels_alloc; i++) { 224692559Sdes c = channels[i]; 224792559Sdes if (c == NULL) 224892559Sdes continue; 224957429Smarkm 225092559Sdes /* 225192559Sdes * We are only interested in channels that can have buffered 225292559Sdes * incoming data. 225392559Sdes */ 225457429Smarkm if (compat13) { 225560573Skris if (c->type != SSH_CHANNEL_OPEN && 225660573Skris c->type != SSH_CHANNEL_INPUT_DRAINING) 225757429Smarkm continue; 225857429Smarkm } else { 225960573Skris if (c->type != SSH_CHANNEL_OPEN) 226057429Smarkm continue; 226157429Smarkm } 226260573Skris if (compat20 && 226360573Skris (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 226476262Sgreen /* XXX is this true? */ 226592559Sdes debug3("channel %d: will not send data after close", c->self); 226660573Skris continue; 226760573Skris } 226857429Smarkm 226957429Smarkm /* Get the amount of buffered data for this channel. */ 227076262Sgreen if ((c->istate == CHAN_INPUT_OPEN || 227176262Sgreen c->istate == CHAN_INPUT_WAIT_DRAIN) && 227276262Sgreen (len = buffer_len(&c->input)) > 0) { 2273157019Sdes if (c->datagram) { 2274157019Sdes if (len > 0) { 2275157019Sdes u_char *data; 2276157019Sdes u_int dlen; 2277157019Sdes 2278157019Sdes data = buffer_get_string(&c->input, 2279157019Sdes &dlen); 2280215116Sdes if (dlen > c->remote_window || 2281215116Sdes dlen > c->remote_maxpacket) { 2282215116Sdes debug("channel %d: datagram " 2283215116Sdes "too big for channel", 2284215116Sdes c->self); 2285255767Sdes free(data); 2286215116Sdes continue; 2287215116Sdes } 2288157019Sdes packet_start(SSH2_MSG_CHANNEL_DATA); 2289157019Sdes packet_put_int(c->remote_id); 2290157019Sdes packet_put_string(data, dlen); 2291157019Sdes packet_send(); 2292295367Sdes c->remote_window -= dlen; 2293255767Sdes free(data); 2294157019Sdes } 2295157019Sdes continue; 2296157019Sdes } 229792559Sdes /* 229892559Sdes * Send some data for the other side over the secure 229992559Sdes * connection. 230092559Sdes */ 230160573Skris if (compat20) { 230260573Skris if (len > c->remote_window) 230360573Skris len = c->remote_window; 230460573Skris if (len > c->remote_maxpacket) 230560573Skris len = c->remote_maxpacket; 230657429Smarkm } else { 230760573Skris if (packet_is_interactive()) { 230860573Skris if (len > 1024) 230960573Skris len = 512; 231060573Skris } else { 231160573Skris /* Keep the packets at reasonable size. */ 231260573Skris if (len > packet_get_maxsize()/2) 231360573Skris len = packet_get_maxsize()/2; 231460573Skris } 231557429Smarkm } 231660573Skris if (len > 0) { 231760573Skris packet_start(compat20 ? 231860573Skris SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 231960573Skris packet_put_int(c->remote_id); 232060573Skris packet_put_string(buffer_ptr(&c->input), len); 232160573Skris packet_send(); 232260573Skris buffer_consume(&c->input, len); 232360573Skris c->remote_window -= len; 232460573Skris } 232560573Skris } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 232657429Smarkm if (compat13) 232757429Smarkm fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 232857429Smarkm /* 232957429Smarkm * input-buffer is empty and read-socket shutdown: 233098684Sdes * tell peer, that we will not send more data: send IEOF. 233198684Sdes * hack for extended data: delay EOF if EFD still in use. 233257429Smarkm */ 233398684Sdes if (CHANNEL_EFD_INPUT_ACTIVE(c)) 2334149753Sdes debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 2335149753Sdes c->self, c->efd, buffer_len(&c->extended)); 233698684Sdes else 233798684Sdes chan_ibuf_empty(c); 233857429Smarkm } 233960573Skris /* Send extended data, i.e. stderr */ 234060573Skris if (compat20 && 234198684Sdes !(c->flags & CHAN_EOF_SENT) && 234260573Skris c->remote_window > 0 && 234360573Skris (len = buffer_len(&c->extended)) > 0 && 234460573Skris c->extended_usage == CHAN_EXTENDED_READ) { 234599063Sdes debug2("channel %d: rwin %u elen %u euse %d", 234676262Sgreen c->self, c->remote_window, buffer_len(&c->extended), 234776262Sgreen c->extended_usage); 234860573Skris if (len > c->remote_window) 234960573Skris len = c->remote_window; 235060573Skris if (len > c->remote_maxpacket) 235160573Skris len = c->remote_maxpacket; 235260573Skris packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 235360573Skris packet_put_int(c->remote_id); 235460573Skris packet_put_int(SSH2_EXTENDED_DATA_STDERR); 235560573Skris packet_put_string(buffer_ptr(&c->extended), len); 235660573Skris packet_send(); 235760573Skris buffer_consume(&c->extended, len); 235860573Skris c->remote_window -= len; 235976262Sgreen debug2("channel %d: sent ext data %d", c->self, len); 236060573Skris } 236157429Smarkm } 236257429Smarkm} 236357429Smarkm 236457429Smarkm 236592559Sdes/* -- protocol input */ 236692559Sdes 2367162856Sdes/* ARGSUSED */ 2368295367Sdesint 236992559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt) 237057429Smarkm{ 237157429Smarkm int id; 2372295367Sdes const u_char *data; 2373215116Sdes u_int data_len, win_len; 237460573Skris Channel *c; 237557429Smarkm 237657429Smarkm /* Get the channel number and verify it. */ 237757429Smarkm id = packet_get_int(); 237860573Skris c = channel_lookup(id); 237960573Skris if (c == NULL) 238057429Smarkm packet_disconnect("Received data for nonexistent channel %d.", id); 238157429Smarkm 238257429Smarkm /* Ignore any data for non-open channels (might happen on close) */ 238360573Skris if (c->type != SSH_CHANNEL_OPEN && 238460573Skris c->type != SSH_CHANNEL_X11_OPEN) 2385295367Sdes return 0; 238657429Smarkm 238757429Smarkm /* Get the data. */ 2388181111Sdes data = packet_get_string_ptr(&data_len); 2389215116Sdes win_len = data_len; 2390215116Sdes if (c->datagram) 2391215116Sdes win_len += 4; /* string length header */ 239260573Skris 2393126273Sdes /* 2394126273Sdes * Ignore data for protocol > 1.3 if output end is no longer open. 2395126273Sdes * For protocol 2 the sending side is reducing its window as it sends 2396126273Sdes * data, so we must 'fake' consumption of the data in order to ensure 2397126273Sdes * that window updates are sent back. Otherwise the connection might 2398126273Sdes * deadlock. 2399126273Sdes */ 2400126273Sdes if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2401126273Sdes if (compat20) { 2402215116Sdes c->local_window -= win_len; 2403215116Sdes c->local_consumed += win_len; 2404126273Sdes } 2405295367Sdes return 0; 2406126273Sdes } 2407126273Sdes 240892559Sdes if (compat20) { 2409215116Sdes if (win_len > c->local_maxpacket) { 2410124207Sdes logit("channel %d: rcvd big packet %d, maxpack %d", 2411215116Sdes c->self, win_len, c->local_maxpacket); 241260573Skris } 2413215116Sdes if (win_len > c->local_window) { 2414124207Sdes logit("channel %d: rcvd too much data %d, win %d", 2415215116Sdes c->self, win_len, c->local_window); 2416295367Sdes return 0; 241760573Skris } 2418215116Sdes c->local_window -= win_len; 241960573Skris } 2420157019Sdes if (c->datagram) 2421157019Sdes buffer_put_string(&c->output, data, data_len); 2422157019Sdes else 2423157019Sdes buffer_append(&c->output, data, data_len); 2424181111Sdes packet_check_eom(); 2425295367Sdes return 0; 242657429Smarkm} 242792559Sdes 2428162856Sdes/* ARGSUSED */ 2429295367Sdesint 243092559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt) 243160573Skris{ 243260573Skris int id; 243360573Skris char *data; 243499063Sdes u_int data_len, tcode; 243560573Skris Channel *c; 243657429Smarkm 243760573Skris /* Get the channel number and verify it. */ 243860573Skris id = packet_get_int(); 243960573Skris c = channel_lookup(id); 244060573Skris 244160573Skris if (c == NULL) 244260573Skris packet_disconnect("Received extended_data for bad channel %d.", id); 244360573Skris if (c->type != SSH_CHANNEL_OPEN) { 2444124207Sdes logit("channel %d: ext data for non open", id); 2445295367Sdes return 0; 244660573Skris } 244798684Sdes if (c->flags & CHAN_EOF_RCVD) { 244898684Sdes if (datafellows & SSH_BUG_EXTEOF) 244998684Sdes debug("channel %d: accepting ext data after eof", id); 245098684Sdes else 245198684Sdes packet_disconnect("Received extended_data after EOF " 245298684Sdes "on channel %d.", id); 245398684Sdes } 245460573Skris tcode = packet_get_int(); 245560573Skris if (c->efd == -1 || 245660573Skris c->extended_usage != CHAN_EXTENDED_WRITE || 245760573Skris tcode != SSH2_EXTENDED_DATA_STDERR) { 2458124207Sdes logit("channel %d: bad ext data", c->self); 2459295367Sdes return 0; 246060573Skris } 246160573Skris data = packet_get_string(&data_len); 246292559Sdes packet_check_eom(); 246360573Skris if (data_len > c->local_window) { 2464124207Sdes logit("channel %d: rcvd too much extended_data %d, win %d", 246560573Skris c->self, data_len, c->local_window); 2466255767Sdes free(data); 2467295367Sdes return 0; 246860573Skris } 246969587Sgreen debug2("channel %d: rcvd ext data %d", c->self, data_len); 247060573Skris c->local_window -= data_len; 247160573Skris buffer_append(&c->extended, data, data_len); 2472255767Sdes free(data); 2473295367Sdes return 0; 247460573Skris} 247560573Skris 2476162856Sdes/* ARGSUSED */ 2477295367Sdesint 247892559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt) 247960573Skris{ 248060573Skris int id; 248160573Skris Channel *c; 248257429Smarkm 248360573Skris id = packet_get_int(); 248492559Sdes packet_check_eom(); 248560573Skris c = channel_lookup(id); 248660573Skris if (c == NULL) 248760573Skris packet_disconnect("Received ieof for nonexistent channel %d.", id); 248860573Skris chan_rcvd_ieof(c); 248992559Sdes 249092559Sdes /* XXX force input close */ 249192559Sdes if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 249292559Sdes debug("channel %d: FORCE input drain", c->self); 249392559Sdes c->istate = CHAN_INPUT_WAIT_DRAIN; 249492559Sdes if (buffer_len(&c->input) == 0) 249592559Sdes chan_ibuf_empty(c); 249692559Sdes } 2497295367Sdes return 0; 249860573Skris} 249960573Skris 2500162856Sdes/* ARGSUSED */ 2501295367Sdesint 250292559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt) 250357429Smarkm{ 250460573Skris int id; 250560573Skris Channel *c; 250657429Smarkm 250760573Skris id = packet_get_int(); 250892559Sdes packet_check_eom(); 250960573Skris c = channel_lookup(id); 251060573Skris if (c == NULL) 251160573Skris packet_disconnect("Received close for nonexistent channel %d.", id); 251257429Smarkm 251357429Smarkm /* 251457429Smarkm * Send a confirmation that we have closed the channel and no more 251557429Smarkm * data is coming for it. 251657429Smarkm */ 251757429Smarkm packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 251860573Skris packet_put_int(c->remote_id); 251957429Smarkm packet_send(); 252057429Smarkm 252157429Smarkm /* 252257429Smarkm * If the channel is in closed state, we have sent a close request, 252357429Smarkm * and the other side will eventually respond with a confirmation. 252457429Smarkm * Thus, we cannot free the channel here, because then there would be 252557429Smarkm * no-one to receive the confirmation. The channel gets freed when 252657429Smarkm * the confirmation arrives. 252757429Smarkm */ 252860573Skris if (c->type != SSH_CHANNEL_CLOSED) { 252957429Smarkm /* 253057429Smarkm * Not a closed channel - mark it as draining, which will 253157429Smarkm * cause it to be freed later. 253257429Smarkm */ 253392559Sdes buffer_clear(&c->input); 253460573Skris c->type = SSH_CHANNEL_OUTPUT_DRAINING; 253557429Smarkm } 2536295367Sdes return 0; 253757429Smarkm} 253857429Smarkm 253960573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2540162856Sdes/* ARGSUSED */ 2541295367Sdesint 254292559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt) 254360573Skris{ 254460573Skris int id = packet_get_int(); 254560573Skris Channel *c = channel_lookup(id); 254692559Sdes 254792559Sdes packet_check_eom(); 254860573Skris if (c == NULL) 254960573Skris packet_disconnect("Received oclose for nonexistent channel %d.", id); 255060573Skris chan_rcvd_oclose(c); 2551295367Sdes return 0; 255260573Skris} 255357429Smarkm 2554162856Sdes/* ARGSUSED */ 2555295367Sdesint 255692559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 255757429Smarkm{ 255860573Skris int id = packet_get_int(); 255960573Skris Channel *c = channel_lookup(id); 256057429Smarkm 256192559Sdes packet_check_eom(); 256260573Skris if (c == NULL) 256360573Skris packet_disconnect("Received close confirmation for " 256460573Skris "out-of-range channel %d.", id); 2565255767Sdes if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) 256660573Skris packet_disconnect("Received close confirmation for " 256760573Skris "non-closed channel %d (type %d).", id, c->type); 256892559Sdes channel_free(c); 2569295367Sdes return 0; 257060573Skris} 257157429Smarkm 2572162856Sdes/* ARGSUSED */ 2573295367Sdesint 257492559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 257560573Skris{ 257660573Skris int id, remote_id; 257760573Skris Channel *c; 257860573Skris 257960573Skris id = packet_get_int(); 258060573Skris c = channel_lookup(id); 258160573Skris 258260573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 258360573Skris packet_disconnect("Received open confirmation for " 258460573Skris "non-opening channel %d.", id); 258560573Skris remote_id = packet_get_int(); 258660573Skris /* Record the remote channel number and mark that the channel is now open. */ 258760573Skris c->remote_id = remote_id; 258860573Skris c->type = SSH_CHANNEL_OPEN; 258960573Skris 259060573Skris if (compat20) { 259160573Skris c->remote_window = packet_get_int(); 259260573Skris c->remote_maxpacket = packet_get_int(); 2593181111Sdes if (c->open_confirm) { 259469587Sgreen debug2("callback start"); 2595215116Sdes c->open_confirm(c->self, 1, c->open_confirm_ctx); 259669587Sgreen debug2("callback done"); 259760573Skris } 2598124207Sdes debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 259960573Skris c->remote_window, c->remote_maxpacket); 260057429Smarkm } 260192559Sdes packet_check_eom(); 2602295367Sdes return 0; 260357429Smarkm} 260457429Smarkm 260592559Sdesstatic char * 260692559Sdesreason2txt(int reason) 260792559Sdes{ 260892559Sdes switch (reason) { 260992559Sdes case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 261092559Sdes return "administratively prohibited"; 261192559Sdes case SSH2_OPEN_CONNECT_FAILED: 261292559Sdes return "connect failed"; 261392559Sdes case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 261492559Sdes return "unknown channel type"; 261592559Sdes case SSH2_OPEN_RESOURCE_SHORTAGE: 261692559Sdes return "resource shortage"; 261792559Sdes } 261892559Sdes return "unknown reason"; 261992559Sdes} 262092559Sdes 2621162856Sdes/* ARGSUSED */ 2622295367Sdesint 262392559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt) 262457429Smarkm{ 262576262Sgreen int id, reason; 262676262Sgreen char *msg = NULL, *lang = NULL; 262760573Skris Channel *c; 262857429Smarkm 262960573Skris id = packet_get_int(); 263060573Skris c = channel_lookup(id); 263157429Smarkm 263260573Skris if (c==NULL || c->type != SSH_CHANNEL_OPENING) 263360573Skris packet_disconnect("Received open failure for " 263460573Skris "non-opening channel %d.", id); 263560573Skris if (compat20) { 263676262Sgreen reason = packet_get_int(); 263792559Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 263876262Sgreen msg = packet_get_string(NULL); 263976262Sgreen lang = packet_get_string(NULL); 264076262Sgreen } 2641124207Sdes logit("channel %d: open failed: %s%s%s", id, 264292559Sdes reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 2643255767Sdes free(msg); 2644255767Sdes free(lang); 2645215116Sdes if (c->open_confirm) { 2646215116Sdes debug2("callback start"); 2647215116Sdes c->open_confirm(c->self, 0, c->open_confirm_ctx); 2648215116Sdes debug2("callback done"); 2649215116Sdes } 265060573Skris } 265192559Sdes packet_check_eom(); 2652192595Sdes /* Schedule the channel for cleanup/deletion. */ 2653192595Sdes chan_mark_dead(c); 2654295367Sdes return 0; 265557429Smarkm} 265657429Smarkm 2657162856Sdes/* ARGSUSED */ 2658295367Sdesint 265992559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 266060573Skris{ 266160573Skris Channel *c; 266299063Sdes int id; 2663295367Sdes u_int adjust, tmp; 266457429Smarkm 266560573Skris if (!compat20) 2666295367Sdes return 0; 266760573Skris 266857429Smarkm /* Get the channel number and verify it. */ 266960573Skris id = packet_get_int(); 267060573Skris c = channel_lookup(id); 267157429Smarkm 2672157019Sdes if (c == NULL) { 2673157019Sdes logit("Received window adjust for non-open channel %d.", id); 2674295367Sdes return 0; 267560573Skris } 267660573Skris adjust = packet_get_int(); 267792559Sdes packet_check_eom(); 267899063Sdes debug2("channel %d: rcvd adjust %u", id, adjust); 2679295367Sdes if ((tmp = c->remote_window + adjust) < c->remote_window) 2680295367Sdes fatal("channel %d: adjust %u overflows remote window %u", 2681295367Sdes id, adjust, c->remote_window); 2682295367Sdes c->remote_window = tmp; 2683295367Sdes return 0; 268457429Smarkm} 268557429Smarkm 2686162856Sdes/* ARGSUSED */ 2687295367Sdesint 268892559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt) 268957429Smarkm{ 269092559Sdes Channel *c = NULL; 269192559Sdes u_short host_port; 269292559Sdes char *host, *originator_string; 2693181111Sdes int remote_id; 269457429Smarkm 269592559Sdes remote_id = packet_get_int(); 269692559Sdes host = packet_get_string(NULL); 269792559Sdes host_port = packet_get_int(); 269857429Smarkm 269992559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 270092559Sdes originator_string = packet_get_string(NULL); 270192559Sdes } else { 270292559Sdes originator_string = xstrdup("unknown (remote did not supply name)"); 270392559Sdes } 270492559Sdes packet_check_eom(); 2705295367Sdes c = channel_connect_to_port(host, host_port, 2706181111Sdes "connected socket", originator_string); 2707255767Sdes free(originator_string); 2708255767Sdes free(host); 270992559Sdes if (c == NULL) { 271092559Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 271192559Sdes packet_put_int(remote_id); 271292559Sdes packet_send(); 2713181111Sdes } else 2714181111Sdes c->remote_id = remote_id; 2715295367Sdes return 0; 271657429Smarkm} 271757429Smarkm 2718181111Sdes/* ARGSUSED */ 2719295367Sdesint 2720181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2721181111Sdes{ 2722181111Sdes Channel *c; 2723181111Sdes struct channel_confirm *cc; 2724192595Sdes int id; 272557429Smarkm 2726181111Sdes /* Reset keepalive timeout */ 2727197679Sdes packet_set_alive_timeouts(0); 2728181111Sdes 2729192595Sdes id = packet_get_int(); 2730181111Sdes packet_check_eom(); 2731181111Sdes 2732192595Sdes debug2("channel_input_status_confirm: type %d id %d", type, id); 2733181111Sdes 2734192595Sdes if ((c = channel_lookup(id)) == NULL) { 2735192595Sdes logit("channel_input_status_confirm: %d: unknown", id); 2736295367Sdes return 0; 2737181111Sdes } 2738181111Sdes if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2739295367Sdes return 0; 2740181111Sdes cc->cb(type, c, cc->ctx); 2741181111Sdes TAILQ_REMOVE(&c->status_confirms, cc, entry); 2742264377Sdes explicit_bzero(cc, sizeof(*cc)); 2743255767Sdes free(cc); 2744295367Sdes return 0; 2745181111Sdes} 2746181111Sdes 274792559Sdes/* -- tcp forwarding */ 274857429Smarkm 274992559Sdesvoid 275092559Sdeschannel_set_af(int af) 275176262Sgreen{ 275292559Sdes IPv4or6 = af; 275376262Sgreen} 275476262Sgreen 2755224638Sbrooks 2756240075Sdes/* 2757240075Sdes * Determine whether or not a port forward listens to loopback, the 2758240075Sdes * specified address or wildcard. On the client, a specified bind 2759240075Sdes * address will always override gateway_ports. On the server, a 2760240075Sdes * gateway_ports of 1 (``yes'') will override the client's specification 2761240075Sdes * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 2762240075Sdes * will bind to whatever address the client asked for. 2763240075Sdes * 2764240075Sdes * Special-case listen_addrs are: 2765240075Sdes * 2766240075Sdes * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2767240075Sdes * "" (empty string), "*" -> wildcard v4/v6 2768240075Sdes * "localhost" -> loopback v4/v6 2769295367Sdes * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 2770240075Sdes */ 2771240075Sdesstatic const char * 2772240075Sdeschannel_fwd_bind_addr(const char *listen_addr, int *wildcardp, 2773295367Sdes int is_client, struct ForwardOptions *fwd_opts) 2774240075Sdes{ 2775240075Sdes const char *addr = NULL; 2776240075Sdes int wildcard = 0; 2777240075Sdes 2778240075Sdes if (listen_addr == NULL) { 2779240075Sdes /* No address specified: default to gateway_ports setting */ 2780295367Sdes if (fwd_opts->gateway_ports) 2781240075Sdes wildcard = 1; 2782295367Sdes } else if (fwd_opts->gateway_ports || is_client) { 2783240075Sdes if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2784240075Sdes strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2785240075Sdes *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2786295367Sdes (!is_client && fwd_opts->gateway_ports == 1)) { 2787240075Sdes wildcard = 1; 2788262566Sdes /* 2789262566Sdes * Notify client if they requested a specific listen 2790262566Sdes * address and it was overridden. 2791262566Sdes */ 2792262566Sdes if (*listen_addr != '\0' && 2793262566Sdes strcmp(listen_addr, "0.0.0.0") != 0 && 2794262566Sdes strcmp(listen_addr, "*") != 0) { 2795262566Sdes packet_send_debug("Forwarding listen address " 2796262566Sdes "\"%s\" overridden by server " 2797262566Sdes "GatewayPorts", listen_addr); 2798262566Sdes } 2799295367Sdes } else if (strcmp(listen_addr, "localhost") != 0 || 2800295367Sdes strcmp(listen_addr, "127.0.0.1") == 0 || 2801295367Sdes strcmp(listen_addr, "::1") == 0) { 2802295367Sdes /* Accept localhost address when GatewayPorts=yes */ 2803295367Sdes addr = listen_addr; 2804262566Sdes } 2805295367Sdes } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 2806295367Sdes strcmp(listen_addr, "::1") == 0) { 2807295367Sdes /* 2808295367Sdes * If a specific IPv4/IPv6 localhost address has been 2809295367Sdes * requested then accept it even if gateway_ports is in 2810295367Sdes * effect. This allows the client to prefer IPv4 or IPv6. 2811295367Sdes */ 2812295367Sdes addr = listen_addr; 2813240075Sdes } 2814240075Sdes if (wildcardp != NULL) 2815240075Sdes *wildcardp = wildcard; 2816240075Sdes return addr; 2817240075Sdes} 2818240075Sdes 281992559Sdesstatic int 2820295367Sdeschannel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, 2821295367Sdes int *allocated_listen_port, struct ForwardOptions *fwd_opts) 282257429Smarkm{ 282392559Sdes Channel *c; 2824157019Sdes int sock, r, success = 0, wildcard = 0, is_client; 282557429Smarkm struct addrinfo hints, *ai, *aitop; 2826147005Sdes const char *host, *addr; 282757429Smarkm char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2828192595Sdes in_port_t *lport_p; 282957429Smarkm 2830147005Sdes is_client = (type == SSH_CHANNEL_PORT_LISTENER); 283157429Smarkm 2832295367Sdes if (is_client && fwd->connect_path != NULL) { 2833295367Sdes host = fwd->connect_path; 2834295367Sdes } else { 2835295367Sdes host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2836295367Sdes fwd->listen_host : fwd->connect_host; 2837295367Sdes if (host == NULL) { 2838295367Sdes error("No forward host name."); 2839295367Sdes return 0; 2840295367Sdes } 2841295367Sdes if (strlen(host) >= NI_MAXHOST) { 2842295367Sdes error("Forward host name too long."); 2843295367Sdes return 0; 2844295367Sdes } 284576262Sgreen } 284676262Sgreen 2847240075Sdes /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 2848295367Sdes addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, 2849295367Sdes is_client, fwd_opts); 2850295367Sdes debug3("%s: type %d wildcard %d addr %s", __func__, 2851147005Sdes type, wildcard, (addr == NULL) ? "NULL" : addr); 2852147005Sdes 2853147005Sdes /* 285457429Smarkm * getaddrinfo returns a loopback address if the hostname is 285557429Smarkm * set to NULL and hints.ai_flags is not AI_PASSIVE 285657429Smarkm */ 285757429Smarkm memset(&hints, 0, sizeof(hints)); 285857429Smarkm hints.ai_family = IPv4or6; 2859147005Sdes hints.ai_flags = wildcard ? AI_PASSIVE : 0; 286057429Smarkm hints.ai_socktype = SOCK_STREAM; 2861295367Sdes snprintf(strport, sizeof strport, "%d", fwd->listen_port); 2862147005Sdes if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2863147005Sdes if (addr == NULL) { 2864147005Sdes /* This really shouldn't happen */ 2865147005Sdes packet_disconnect("getaddrinfo: fatal error: %s", 2866181111Sdes ssh_gai_strerror(r)); 2867147005Sdes } else { 2868295367Sdes error("%s: getaddrinfo(%.64s): %s", __func__, addr, 2869181111Sdes ssh_gai_strerror(r)); 2870147005Sdes } 2871149753Sdes return 0; 2872147005Sdes } 2873192595Sdes if (allocated_listen_port != NULL) 2874192595Sdes *allocated_listen_port = 0; 287557429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 2876192595Sdes switch (ai->ai_family) { 2877192595Sdes case AF_INET: 2878192595Sdes lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 2879192595Sdes sin_port; 2880192595Sdes break; 2881192595Sdes case AF_INET6: 2882192595Sdes lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 2883192595Sdes sin6_port; 2884192595Sdes break; 2885192595Sdes default: 288657429Smarkm continue; 2887192595Sdes } 2888192595Sdes /* 2889192595Sdes * If allocating a port for -R forwards, then use the 2890192595Sdes * same port for all address families. 2891192595Sdes */ 2892295367Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 2893192595Sdes allocated_listen_port != NULL && *allocated_listen_port > 0) 2894192595Sdes *lport_p = htons(*allocated_listen_port); 2895192595Sdes 289657429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 289757429Smarkm strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2898295367Sdes error("%s: getnameinfo failed", __func__); 289957429Smarkm continue; 290057429Smarkm } 290157429Smarkm /* Create a port to listen for the host. */ 2902124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 290357429Smarkm if (sock < 0) { 290457429Smarkm /* this is no error since kernel may not support ipv6 */ 290557429Smarkm verbose("socket: %.100s", strerror(errno)); 290657429Smarkm continue; 290757429Smarkm } 2908106130Sdes 2909157019Sdes channel_set_reuseaddr(sock); 2910204917Sdes if (ai->ai_family == AF_INET6) 2911204917Sdes sock_set_v6only(sock); 2912157019Sdes 2913192595Sdes debug("Local forwarding listening on %s port %s.", 2914192595Sdes ntop, strport); 291557429Smarkm 291657429Smarkm /* Bind the socket to the address. */ 291757429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 291857429Smarkm /* address can be in use ipv6 address is already bound */ 291998941Sdes if (!ai->ai_next) 292098941Sdes error("bind: %.100s", strerror(errno)); 292198941Sdes else 292298941Sdes verbose("bind: %.100s", strerror(errno)); 292398941Sdes 292457429Smarkm close(sock); 292557429Smarkm continue; 292657429Smarkm } 292757429Smarkm /* Start listening for connections on the socket. */ 2928126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 292957429Smarkm error("listen: %.100s", strerror(errno)); 293057429Smarkm close(sock); 293157429Smarkm continue; 293257429Smarkm } 2933192595Sdes 2934192595Sdes /* 2935295367Sdes * fwd->listen_port == 0 requests a dynamically allocated port - 2936192595Sdes * record what we got. 2937192595Sdes */ 2938295367Sdes if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 2939192595Sdes allocated_listen_port != NULL && 2940192595Sdes *allocated_listen_port == 0) { 2941323124Sdes *allocated_listen_port = get_local_port(sock); 2942192595Sdes debug("Allocated listen port %d", 2943192595Sdes *allocated_listen_port); 2944192595Sdes } 2945192595Sdes 2946294693Sdes /* Allocate a channel number for the socket. */ 2947294693Sdes c = channel_new("port listener", type, sock, sock, -1, 2948294693Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2949294693Sdes 0, "port listener", 1); 2950192595Sdes c->path = xstrdup(host); 2951295367Sdes c->host_port = fwd->connect_port; 2952240075Sdes c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 2953295367Sdes if (fwd->listen_port == 0 && allocated_listen_port != NULL && 2954240075Sdes !(datafellows & SSH_BUG_DYNAMIC_RPORT)) 2955240075Sdes c->listening_port = *allocated_listen_port; 2956240075Sdes else 2957295367Sdes c->listening_port = fwd->listen_port; 295857429Smarkm success = 1; 295957429Smarkm } 296057429Smarkm if (success == 0) 2961295367Sdes error("%s: cannot listen to port: %d", __func__, 2962295367Sdes fwd->listen_port); 296357429Smarkm freeaddrinfo(aitop); 296476262Sgreen return success; 296557429Smarkm} 296657429Smarkm 2967295367Sdesstatic int 2968295367Sdeschannel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, 2969295367Sdes struct ForwardOptions *fwd_opts) 2970137019Sdes{ 2971295367Sdes struct sockaddr_un sunaddr; 2972295367Sdes const char *path; 2973295367Sdes Channel *c; 2974295367Sdes int port, sock; 2975295367Sdes mode_t omask; 2976295367Sdes 2977295367Sdes switch (type) { 2978295367Sdes case SSH_CHANNEL_UNIX_LISTENER: 2979295367Sdes if (fwd->connect_path != NULL) { 2980295367Sdes if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 2981295367Sdes error("Local connecting path too long: %s", 2982295367Sdes fwd->connect_path); 2983295367Sdes return 0; 2984295367Sdes } 2985295367Sdes path = fwd->connect_path; 2986295367Sdes port = PORT_STREAMLOCAL; 2987295367Sdes } else { 2988295367Sdes if (fwd->connect_host == NULL) { 2989295367Sdes error("No forward host name."); 2990295367Sdes return 0; 2991295367Sdes } 2992295367Sdes if (strlen(fwd->connect_host) >= NI_MAXHOST) { 2993295367Sdes error("Forward host name too long."); 2994295367Sdes return 0; 2995295367Sdes } 2996295367Sdes path = fwd->connect_host; 2997295367Sdes port = fwd->connect_port; 2998295367Sdes } 2999295367Sdes break; 3000295367Sdes case SSH_CHANNEL_RUNIX_LISTENER: 3001295367Sdes path = fwd->listen_path; 3002295367Sdes port = PORT_STREAMLOCAL; 3003295367Sdes break; 3004295367Sdes default: 3005295367Sdes error("%s: unexpected channel type %d", __func__, type); 3006295367Sdes return 0; 3007295367Sdes } 3008295367Sdes 3009295367Sdes if (fwd->listen_path == NULL) { 3010295367Sdes error("No forward path name."); 3011295367Sdes return 0; 3012295367Sdes } 3013295367Sdes if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 3014295367Sdes error("Local listening path too long: %s", fwd->listen_path); 3015295367Sdes return 0; 3016295367Sdes } 3017295367Sdes 3018295367Sdes debug3("%s: type %d path %s", __func__, type, fwd->listen_path); 3019295367Sdes 3020295367Sdes /* Start a Unix domain listener. */ 3021295367Sdes omask = umask(fwd_opts->streamlocal_bind_mask); 3022295367Sdes sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 3023295367Sdes fwd_opts->streamlocal_bind_unlink); 3024295367Sdes umask(omask); 3025295367Sdes if (sock < 0) 3026295367Sdes return 0; 3027295367Sdes 3028295367Sdes debug("Local forwarding listening on path %s.", fwd->listen_path); 3029295367Sdes 3030295367Sdes /* Allocate a channel number for the socket. */ 3031295367Sdes c = channel_new("unix listener", type, sock, sock, -1, 3032295367Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3033295367Sdes 0, "unix listener", 1); 3034295367Sdes c->path = xstrdup(path); 3035295367Sdes c->host_port = port; 3036295367Sdes c->listening_port = PORT_STREAMLOCAL; 3037295367Sdes c->listening_addr = xstrdup(fwd->listen_path); 3038295367Sdes return 1; 3039295367Sdes} 3040295367Sdes 3041295367Sdesstatic int 3042295367Sdeschannel_cancel_rport_listener_tcpip(const char *host, u_short port) 3043295367Sdes{ 3044137019Sdes u_int i; 3045137019Sdes int found = 0; 3046137019Sdes 3047147005Sdes for (i = 0; i < channels_alloc; i++) { 3048137019Sdes Channel *c = channels[i]; 3049240075Sdes if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3050240075Sdes continue; 3051240075Sdes if (strcmp(c->path, host) == 0 && c->listening_port == port) { 3052240075Sdes debug2("%s: close channel %d", __func__, i); 3053240075Sdes channel_free(c); 3054240075Sdes found = 1; 3055240075Sdes } 3056240075Sdes } 3057137019Sdes 3058240075Sdes return (found); 3059240075Sdes} 3060240075Sdes 3061295367Sdesstatic int 3062295367Sdeschannel_cancel_rport_listener_streamlocal(const char *path) 3063295367Sdes{ 3064295367Sdes u_int i; 3065295367Sdes int found = 0; 3066295367Sdes 3067295367Sdes for (i = 0; i < channels_alloc; i++) { 3068295367Sdes Channel *c = channels[i]; 3069295367Sdes if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3070295367Sdes continue; 3071295367Sdes if (c->path == NULL) 3072295367Sdes continue; 3073295367Sdes if (strcmp(c->path, path) == 0) { 3074295367Sdes debug2("%s: close channel %d", __func__, i); 3075295367Sdes channel_free(c); 3076295367Sdes found = 1; 3077295367Sdes } 3078295367Sdes } 3079295367Sdes 3080295367Sdes return (found); 3081295367Sdes} 3082295367Sdes 3083240075Sdesint 3084295367Sdeschannel_cancel_rport_listener(struct Forward *fwd) 3085240075Sdes{ 3086295367Sdes if (fwd->listen_path != NULL) 3087295367Sdes return channel_cancel_rport_listener_streamlocal(fwd->listen_path); 3088295367Sdes else 3089295367Sdes return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); 3090295367Sdes} 3091295367Sdes 3092295367Sdesstatic int 3093295367Sdeschannel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, 3094295367Sdes int cport, struct ForwardOptions *fwd_opts) 3095295367Sdes{ 3096240075Sdes u_int i; 3097240075Sdes int found = 0; 3098295367Sdes const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); 3099240075Sdes 3100240075Sdes for (i = 0; i < channels_alloc; i++) { 3101240075Sdes Channel *c = channels[i]; 3102240075Sdes if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 3103240075Sdes continue; 3104240075Sdes if (c->listening_port != lport) 3105240075Sdes continue; 3106240075Sdes if (cport == CHANNEL_CANCEL_PORT_STATIC) { 3107240075Sdes /* skip dynamic forwardings */ 3108240075Sdes if (c->host_port == 0) 3109240075Sdes continue; 3110240075Sdes } else { 3111240075Sdes if (c->host_port != cport) 3112240075Sdes continue; 3113240075Sdes } 3114240075Sdes if ((c->listening_addr == NULL && addr != NULL) || 3115240075Sdes (c->listening_addr != NULL && addr == NULL)) 3116240075Sdes continue; 3117240075Sdes if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 3118147005Sdes debug2("%s: close channel %d", __func__, i); 3119137019Sdes channel_free(c); 3120137019Sdes found = 1; 3121137019Sdes } 3122137019Sdes } 3123137019Sdes 3124137019Sdes return (found); 3125137019Sdes} 3126137019Sdes 3127295367Sdesstatic int 3128295367Sdeschannel_cancel_lport_listener_streamlocal(const char *path) 3129295367Sdes{ 3130295367Sdes u_int i; 3131295367Sdes int found = 0; 3132295367Sdes 3133295367Sdes if (path == NULL) { 3134295367Sdes error("%s: no path specified.", __func__); 3135295367Sdes return 0; 3136295367Sdes } 3137295367Sdes 3138295367Sdes for (i = 0; i < channels_alloc; i++) { 3139295367Sdes Channel *c = channels[i]; 3140295367Sdes if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 3141295367Sdes continue; 3142295367Sdes if (c->listening_addr == NULL) 3143295367Sdes continue; 3144295367Sdes if (strcmp(c->listening_addr, path) == 0) { 3145295367Sdes debug2("%s: close channel %d", __func__, i); 3146295367Sdes channel_free(c); 3147295367Sdes found = 1; 3148295367Sdes } 3149295367Sdes } 3150295367Sdes 3151295367Sdes return (found); 3152295367Sdes} 3153295367Sdes 3154295367Sdesint 3155295367Sdeschannel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 3156295367Sdes{ 3157295367Sdes if (fwd->listen_path != NULL) 3158295367Sdes return channel_cancel_lport_listener_streamlocal(fwd->listen_path); 3159295367Sdes else 3160295367Sdes return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); 3161295367Sdes} 3162295367Sdes 316392559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */ 316492559Sdesint 3165295367Sdeschannel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) 316692559Sdes{ 3167295367Sdes if (fwd->listen_path != NULL) { 3168295367Sdes return channel_setup_fwd_listener_streamlocal( 3169295367Sdes SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 3170295367Sdes } else { 3171295367Sdes return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, 3172295367Sdes fwd, NULL, fwd_opts); 3173295367Sdes } 317492559Sdes} 317592559Sdes 317692559Sdes/* protocol v2 remote port fwd, used by sshd */ 317792559Sdesint 3178295367Sdeschannel_setup_remote_fwd_listener(struct Forward *fwd, 3179295367Sdes int *allocated_listen_port, struct ForwardOptions *fwd_opts) 318092559Sdes{ 3181295367Sdes if (fwd->listen_path != NULL) { 3182295367Sdes return channel_setup_fwd_listener_streamlocal( 3183295367Sdes SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 3184295367Sdes } else { 3185295367Sdes return channel_setup_fwd_listener_tcpip( 3186295367Sdes SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 3187295367Sdes fwd_opts); 3188295367Sdes } 318992559Sdes} 319092559Sdes 319157429Smarkm/* 3192240075Sdes * Translate the requested rfwd listen host to something usable for 3193240075Sdes * this server. 3194240075Sdes */ 3195240075Sdesstatic const char * 3196240075Sdeschannel_rfwd_bind_host(const char *listen_host) 3197240075Sdes{ 3198240075Sdes if (listen_host == NULL) { 3199240075Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 3200240075Sdes return "127.0.0.1"; 3201240075Sdes else 3202240075Sdes return "localhost"; 3203240075Sdes } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 3204240075Sdes if (datafellows & SSH_BUG_RFWD_ADDR) 3205240075Sdes return "0.0.0.0"; 3206240075Sdes else 3207240075Sdes return ""; 3208240075Sdes } else 3209240075Sdes return listen_host; 3210240075Sdes} 3211240075Sdes 3212240075Sdes/* 321357429Smarkm * Initiate forwarding of connections to port "port" on remote host through 321457429Smarkm * the secure channel to host:port from local side. 3215240075Sdes * Returns handle (index) for updating the dynamic listen port with 3216240075Sdes * channel_update_permitted_opens(). 321757429Smarkm */ 3218162856Sdesint 3219295367Sdeschannel_request_remote_forwarding(struct Forward *fwd) 322057429Smarkm{ 3221240075Sdes int type, success = 0, idx = -1; 322276262Sgreen 322357429Smarkm /* Send the forward request to the remote side. */ 322460573Skris if (compat20) { 322560573Skris packet_start(SSH2_MSG_GLOBAL_REQUEST); 3226295367Sdes if (fwd->listen_path != NULL) { 3227295367Sdes packet_put_cstring("streamlocal-forward@openssh.com"); 3228295367Sdes packet_put_char(1); /* boolean: want reply */ 3229295367Sdes packet_put_cstring(fwd->listen_path); 3230295367Sdes } else { 3231295367Sdes packet_put_cstring("tcpip-forward"); 3232295367Sdes packet_put_char(1); /* boolean: want reply */ 3233295367Sdes packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); 3234295367Sdes packet_put_int(fwd->listen_port); 3235295367Sdes } 323676262Sgreen packet_send(); 323776262Sgreen packet_write_wait(); 323876262Sgreen /* Assume that server accepts the request */ 323976262Sgreen success = 1; 3240295367Sdes } else if (fwd->listen_path == NULL) { 324160573Skris packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 3242295367Sdes packet_put_int(fwd->listen_port); 3243295367Sdes packet_put_cstring(fwd->connect_host); 3244295367Sdes packet_put_int(fwd->connect_port); 324560573Skris packet_send(); 324660573Skris packet_write_wait(); 324776262Sgreen 324876262Sgreen /* Wait for response from the remote side. */ 324992559Sdes type = packet_read(); 325076262Sgreen switch (type) { 325176262Sgreen case SSH_SMSG_SUCCESS: 325276262Sgreen success = 1; 325376262Sgreen break; 325476262Sgreen case SSH_SMSG_FAILURE: 325576262Sgreen break; 325676262Sgreen default: 325776262Sgreen /* Unknown packet */ 325876262Sgreen packet_disconnect("Protocol error for port forward request:" 325976262Sgreen "received packet type %d.", type); 326076262Sgreen } 3261295367Sdes } else { 3262295367Sdes logit("Warning: Server does not support remote stream local forwarding."); 326360573Skris } 326476262Sgreen if (success) { 3265215116Sdes /* Record that connection to this host/port is permitted. */ 3266295367Sdes permitted_opens = xreallocarray(permitted_opens, 3267215116Sdes num_permitted_opens + 1, sizeof(*permitted_opens)); 3268240075Sdes idx = num_permitted_opens++; 3269295367Sdes if (fwd->connect_path != NULL) { 3270295367Sdes permitted_opens[idx].host_to_connect = 3271295367Sdes xstrdup(fwd->connect_path); 3272295367Sdes permitted_opens[idx].port_to_connect = 3273295367Sdes PORT_STREAMLOCAL; 3274295367Sdes } else { 3275295367Sdes permitted_opens[idx].host_to_connect = 3276295367Sdes xstrdup(fwd->connect_host); 3277295367Sdes permitted_opens[idx].port_to_connect = 3278295367Sdes fwd->connect_port; 3279295367Sdes } 3280295367Sdes if (fwd->listen_path != NULL) { 3281295367Sdes permitted_opens[idx].listen_host = NULL; 3282295367Sdes permitted_opens[idx].listen_path = 3283295367Sdes xstrdup(fwd->listen_path); 3284295367Sdes permitted_opens[idx].listen_port = PORT_STREAMLOCAL; 3285295367Sdes } else { 3286295367Sdes permitted_opens[idx].listen_host = 3287295367Sdes fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; 3288295367Sdes permitted_opens[idx].listen_path = NULL; 3289295367Sdes permitted_opens[idx].listen_port = fwd->listen_port; 3290295367Sdes } 329176262Sgreen } 3292240075Sdes return (idx); 329357429Smarkm} 329457429Smarkm 3295295367Sdesstatic int 3296295367Sdesopen_match(ForwardPermission *allowed_open, const char *requestedhost, 3297295367Sdes int requestedport) 3298295367Sdes{ 3299295367Sdes if (allowed_open->host_to_connect == NULL) 3300295367Sdes return 0; 3301295367Sdes if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 3302295367Sdes allowed_open->port_to_connect != requestedport) 3303295367Sdes return 0; 3304323124Sdes if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 3305323124Sdes strcmp(allowed_open->host_to_connect, requestedhost) != 0) 3306295367Sdes return 0; 3307295367Sdes return 1; 3308295367Sdes} 3309295367Sdes 331057429Smarkm/* 3311295367Sdes * Note that in the listen host/port case 3312295367Sdes * we don't support FWD_PERMIT_ANY_PORT and 3313295367Sdes * need to translate between the configured-host (listen_host) 3314295367Sdes * and what we've sent to the remote server (channel_rfwd_bind_host) 3315295367Sdes */ 3316295367Sdesstatic int 3317295367Sdesopen_listen_match_tcpip(ForwardPermission *allowed_open, 3318295367Sdes const char *requestedhost, u_short requestedport, int translate) 3319295367Sdes{ 3320295367Sdes const char *allowed_host; 3321295367Sdes 3322295367Sdes if (allowed_open->host_to_connect == NULL) 3323295367Sdes return 0; 3324295367Sdes if (allowed_open->listen_port != requestedport) 3325295367Sdes return 0; 3326295367Sdes if (!translate && allowed_open->listen_host == NULL && 3327295367Sdes requestedhost == NULL) 3328295367Sdes return 1; 3329295367Sdes allowed_host = translate ? 3330295367Sdes channel_rfwd_bind_host(allowed_open->listen_host) : 3331295367Sdes allowed_open->listen_host; 3332295367Sdes if (allowed_host == NULL || 3333295367Sdes strcmp(allowed_host, requestedhost) != 0) 3334295367Sdes return 0; 3335295367Sdes return 1; 3336295367Sdes} 3337295367Sdes 3338295367Sdesstatic int 3339295367Sdesopen_listen_match_streamlocal(ForwardPermission *allowed_open, 3340295367Sdes const char *requestedpath) 3341295367Sdes{ 3342295367Sdes if (allowed_open->host_to_connect == NULL) 3343295367Sdes return 0; 3344295367Sdes if (allowed_open->listen_port != PORT_STREAMLOCAL) 3345295367Sdes return 0; 3346295367Sdes if (allowed_open->listen_path == NULL || 3347295367Sdes strcmp(allowed_open->listen_path, requestedpath) != 0) 3348295367Sdes return 0; 3349295367Sdes return 1; 3350295367Sdes} 3351295367Sdes 3352295367Sdes/* 3353137019Sdes * Request cancellation of remote forwarding of connection host:port from 3354137019Sdes * local side. 3355137019Sdes */ 3356295367Sdesstatic int 3357295367Sdeschannel_request_rforward_cancel_tcpip(const char *host, u_short port) 3358137019Sdes{ 3359137019Sdes int i; 3360137019Sdes 3361137019Sdes if (!compat20) 3362240075Sdes return -1; 3363137019Sdes 3364137019Sdes for (i = 0; i < num_permitted_opens; i++) { 3365295367Sdes if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) 3366137019Sdes break; 3367137019Sdes } 3368137019Sdes if (i >= num_permitted_opens) { 3369137019Sdes debug("%s: requested forward not found", __func__); 3370240075Sdes return -1; 3371137019Sdes } 3372137019Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 3373137019Sdes packet_put_cstring("cancel-tcpip-forward"); 3374137019Sdes packet_put_char(0); 3375240075Sdes packet_put_cstring(channel_rfwd_bind_host(host)); 3376137019Sdes packet_put_int(port); 3377137019Sdes packet_send(); 3378137019Sdes 3379137019Sdes permitted_opens[i].listen_port = 0; 3380137019Sdes permitted_opens[i].port_to_connect = 0; 3381255767Sdes free(permitted_opens[i].host_to_connect); 3382137019Sdes permitted_opens[i].host_to_connect = NULL; 3383295367Sdes free(permitted_opens[i].listen_host); 3384295367Sdes permitted_opens[i].listen_host = NULL; 3385295367Sdes permitted_opens[i].listen_path = NULL; 3386240075Sdes 3387240075Sdes return 0; 3388137019Sdes} 3389137019Sdes 3390137019Sdes/* 3391295367Sdes * Request cancellation of remote forwarding of Unix domain socket 3392295367Sdes * path from local side. 3393295367Sdes */ 3394295367Sdesstatic int 3395295367Sdeschannel_request_rforward_cancel_streamlocal(const char *path) 3396295367Sdes{ 3397295367Sdes int i; 3398295367Sdes 3399295367Sdes if (!compat20) 3400295367Sdes return -1; 3401295367Sdes 3402295367Sdes for (i = 0; i < num_permitted_opens; i++) { 3403295367Sdes if (open_listen_match_streamlocal(&permitted_opens[i], path)) 3404295367Sdes break; 3405295367Sdes } 3406295367Sdes if (i >= num_permitted_opens) { 3407295367Sdes debug("%s: requested forward not found", __func__); 3408295367Sdes return -1; 3409295367Sdes } 3410295367Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 3411295367Sdes packet_put_cstring("cancel-streamlocal-forward@openssh.com"); 3412295367Sdes packet_put_char(0); 3413295367Sdes packet_put_cstring(path); 3414295367Sdes packet_send(); 3415295367Sdes 3416295367Sdes permitted_opens[i].listen_port = 0; 3417295367Sdes permitted_opens[i].port_to_connect = 0; 3418295367Sdes free(permitted_opens[i].host_to_connect); 3419295367Sdes permitted_opens[i].host_to_connect = NULL; 3420295367Sdes permitted_opens[i].listen_host = NULL; 3421295367Sdes free(permitted_opens[i].listen_path); 3422295367Sdes permitted_opens[i].listen_path = NULL; 3423295367Sdes 3424295367Sdes return 0; 3425295367Sdes} 3426295367Sdes 3427295367Sdes/* 3428295367Sdes * Request cancellation of remote forwarding of a connection from local side. 3429295367Sdes */ 3430295367Sdesint 3431295367Sdeschannel_request_rforward_cancel(struct Forward *fwd) 3432295367Sdes{ 3433295367Sdes if (fwd->listen_path != NULL) { 3434295367Sdes return (channel_request_rforward_cancel_streamlocal( 3435295367Sdes fwd->listen_path)); 3436295367Sdes } else { 3437295367Sdes return (channel_request_rforward_cancel_tcpip(fwd->listen_host, 3438295367Sdes fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); 3439295367Sdes } 3440295367Sdes} 3441295367Sdes 3442295367Sdes/* 344357429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 344457429Smarkm * listening for the port, and sends back a success reply (or disconnect 3445162856Sdes * message if there was an error). 344657429Smarkm */ 3447162856Sdesint 3448295367Sdeschannel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) 344957429Smarkm{ 3450162856Sdes int success = 0; 3451295367Sdes struct Forward fwd; 345257429Smarkm 345357429Smarkm /* Get arguments from the packet. */ 3454295367Sdes memset(&fwd, 0, sizeof(fwd)); 3455295367Sdes fwd.listen_port = packet_get_int(); 3456295367Sdes fwd.connect_host = packet_get_string(NULL); 3457295367Sdes fwd.connect_port = packet_get_int(); 345857429Smarkm 345998941Sdes#ifndef HAVE_CYGWIN 346057429Smarkm /* 346157429Smarkm * Check that an unprivileged user is not trying to forward a 346257429Smarkm * privileged port. 346357429Smarkm */ 3464295367Sdes if (fwd.listen_port < IPPORT_RESERVED && !is_root) 3465124207Sdes packet_disconnect( 3466124207Sdes "Requested forwarding of port %d but user is not root.", 3467295367Sdes fwd.listen_port); 3468295367Sdes if (fwd.connect_port == 0) 3469124207Sdes packet_disconnect("Dynamic forwarding denied."); 347098941Sdes#endif 3471124207Sdes 347276262Sgreen /* Initiate forwarding */ 3473295367Sdes success = channel_setup_local_fwd_listener(&fwd, fwd_opts); 347457429Smarkm 347557429Smarkm /* Free the argument string. */ 3476295367Sdes free(fwd.connect_host); 3477162856Sdes 3478162856Sdes return (success ? 0 : -1); 347957429Smarkm} 348057429Smarkm 348176262Sgreen/* 348276262Sgreen * Permits opening to any host/port if permitted_opens[] is empty. This is 348376262Sgreen * usually called by the server, because the user could connect to any port 348476262Sgreen * anyway, and the server has no way to know but to trust the client anyway. 348576262Sgreen */ 348676262Sgreenvoid 348792559Sdeschannel_permit_all_opens(void) 348876262Sgreen{ 348976262Sgreen if (num_permitted_opens == 0) 349076262Sgreen all_opens_permitted = 1; 349176262Sgreen} 349276262Sgreen 349376262Sgreenvoid 349476262Sgreenchannel_add_permitted_opens(char *host, int port) 349576262Sgreen{ 349676262Sgreen debug("allow port forwarding to host %s port %d", host, port); 349776262Sgreen 3498295367Sdes permitted_opens = xreallocarray(permitted_opens, 3499215116Sdes num_permitted_opens + 1, sizeof(*permitted_opens)); 350076262Sgreen permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 350176262Sgreen permitted_opens[num_permitted_opens].port_to_connect = port; 3502295367Sdes permitted_opens[num_permitted_opens].listen_host = NULL; 3503295367Sdes permitted_opens[num_permitted_opens].listen_path = NULL; 3504295367Sdes permitted_opens[num_permitted_opens].listen_port = 0; 350576262Sgreen num_permitted_opens++; 350676262Sgreen 350776262Sgreen all_opens_permitted = 0; 350876262Sgreen} 350976262Sgreen 3510240075Sdes/* 3511240075Sdes * Update the listen port for a dynamic remote forward, after 3512240075Sdes * the actual 'newport' has been allocated. If 'newport' < 0 is 3513240075Sdes * passed then they entry will be invalidated. 3514240075Sdes */ 3515240075Sdesvoid 3516240075Sdeschannel_update_permitted_opens(int idx, int newport) 3517240075Sdes{ 3518240075Sdes if (idx < 0 || idx >= num_permitted_opens) { 3519240075Sdes debug("channel_update_permitted_opens: index out of range:" 3520240075Sdes " %d num_permitted_opens %d", idx, num_permitted_opens); 3521240075Sdes return; 3522240075Sdes } 3523240075Sdes debug("%s allowed port %d for forwarding to host %s port %d", 3524240075Sdes newport > 0 ? "Updating" : "Removing", 3525240075Sdes newport, 3526240075Sdes permitted_opens[idx].host_to_connect, 3527240075Sdes permitted_opens[idx].port_to_connect); 3528240075Sdes if (newport >= 0) { 3529240075Sdes permitted_opens[idx].listen_port = 3530240075Sdes (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 3531240075Sdes } else { 3532240075Sdes permitted_opens[idx].listen_port = 0; 3533240075Sdes permitted_opens[idx].port_to_connect = 0; 3534255767Sdes free(permitted_opens[idx].host_to_connect); 3535240075Sdes permitted_opens[idx].host_to_connect = NULL; 3536295367Sdes free(permitted_opens[idx].listen_host); 3537295367Sdes permitted_opens[idx].listen_host = NULL; 3538295367Sdes free(permitted_opens[idx].listen_path); 3539295367Sdes permitted_opens[idx].listen_path = NULL; 3540240075Sdes } 3541240075Sdes} 3542240075Sdes 3543162856Sdesint 3544162856Sdeschannel_add_adm_permitted_opens(char *host, int port) 3545162856Sdes{ 3546162856Sdes debug("config allows port forwarding to host %s port %d", host, port); 3547162856Sdes 3548295367Sdes permitted_adm_opens = xreallocarray(permitted_adm_opens, 3549215116Sdes num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); 3550162856Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect 3551162856Sdes = xstrdup(host); 3552162856Sdes permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 3553295367Sdes permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; 3554295367Sdes permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; 3555295367Sdes permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; 3556162856Sdes return ++num_adm_permitted_opens; 3557162856Sdes} 3558162856Sdes 355976262Sgreenvoid 3560240075Sdeschannel_disable_adm_local_opens(void) 3561240075Sdes{ 3562248619Sdes channel_clear_adm_permitted_opens(); 3563295367Sdes permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1); 3564248619Sdes permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; 3565248619Sdes num_adm_permitted_opens = 1; 3566240075Sdes} 3567240075Sdes 3568240075Sdesvoid 356976262Sgreenchannel_clear_permitted_opens(void) 357076262Sgreen{ 357176262Sgreen int i; 357276262Sgreen 3573295367Sdes for (i = 0; i < num_permitted_opens; i++) { 3574255767Sdes free(permitted_opens[i].host_to_connect); 3575295367Sdes free(permitted_opens[i].listen_host); 3576295367Sdes free(permitted_opens[i].listen_path); 3577295367Sdes } 3578255767Sdes free(permitted_opens); 3579255767Sdes permitted_opens = NULL; 358076262Sgreen num_permitted_opens = 0; 3581162856Sdes} 358276262Sgreen 3583162856Sdesvoid 3584162856Sdeschannel_clear_adm_permitted_opens(void) 3585162856Sdes{ 3586162856Sdes int i; 3587162856Sdes 3588295367Sdes for (i = 0; i < num_adm_permitted_opens; i++) { 3589255767Sdes free(permitted_adm_opens[i].host_to_connect); 3590295367Sdes free(permitted_adm_opens[i].listen_host); 3591295367Sdes free(permitted_adm_opens[i].listen_path); 3592295367Sdes } 3593255767Sdes free(permitted_adm_opens); 3594255767Sdes permitted_adm_opens = NULL; 3595162856Sdes num_adm_permitted_opens = 0; 359676262Sgreen} 359776262Sgreen 3598181111Sdesvoid 3599181111Sdeschannel_print_adm_permitted_opens(void) 3600181111Sdes{ 3601181111Sdes int i; 3602181111Sdes 3603192595Sdes printf("permitopen"); 3604192595Sdes if (num_adm_permitted_opens == 0) { 3605192595Sdes printf(" any\n"); 3606192595Sdes return; 3607192595Sdes } 3608181111Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3609240075Sdes if (permitted_adm_opens[i].host_to_connect == NULL) 3610240075Sdes printf(" none"); 3611240075Sdes else 3612181111Sdes printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 3613181111Sdes permitted_adm_opens[i].port_to_connect); 3614192595Sdes printf("\n"); 3615181111Sdes} 3616181111Sdes 3617240075Sdes/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 3618240075Sdesint 3619240075Sdespermitopen_port(const char *p) 3620240075Sdes{ 3621240075Sdes int port; 3622240075Sdes 3623240075Sdes if (strcmp(p, "*") == 0) 3624240075Sdes return FWD_PERMIT_ANY_PORT; 3625240075Sdes if ((port = a2port(p)) > 0) 3626240075Sdes return port; 3627240075Sdes return -1; 3628240075Sdes} 3629240075Sdes 3630181111Sdes/* Try to start non-blocking connect to next host in cctx list */ 363192559Sdesstatic int 3632181111Sdesconnect_next(struct channel_connect *cctx) 363360573Skris{ 3634181111Sdes int sock, saved_errno; 3635295367Sdes struct sockaddr_un *sunaddr; 3636295367Sdes char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; 363760573Skris 3638181111Sdes for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 3639295367Sdes switch (cctx->ai->ai_family) { 3640295367Sdes case AF_UNIX: 3641295367Sdes /* unix:pathname instead of host:port */ 3642295367Sdes sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 3643295367Sdes strlcpy(ntop, "unix", sizeof(ntop)); 3644295367Sdes strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 3645295367Sdes break; 3646295367Sdes case AF_INET: 3647295367Sdes case AF_INET6: 3648295367Sdes if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 3649295367Sdes ntop, sizeof(ntop), strport, sizeof(strport), 3650295367Sdes NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3651295367Sdes error("connect_next: getnameinfo failed"); 3652295367Sdes continue; 3653295367Sdes } 3654295367Sdes break; 3655295367Sdes default: 365660573Skris continue; 365760573Skris } 3658181111Sdes if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 3659181111Sdes cctx->ai->ai_protocol)) == -1) { 3660181111Sdes if (cctx->ai->ai_next == NULL) 3661113911Sdes error("socket: %.100s", strerror(errno)); 3662113911Sdes else 3663113911Sdes verbose("socket: %.100s", strerror(errno)); 366460573Skris continue; 366560573Skris } 3666137019Sdes if (set_nonblock(sock) == -1) 3667137019Sdes fatal("%s: set_nonblock(%d)", __func__, sock); 3668181111Sdes if (connect(sock, cctx->ai->ai_addr, 3669181111Sdes cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 3670181111Sdes debug("connect_next: host %.100s ([%.100s]:%s): " 3671181111Sdes "%.100s", cctx->host, ntop, strport, 367260573Skris strerror(errno)); 3673181111Sdes saved_errno = errno; 367460573Skris close(sock); 3675181111Sdes errno = saved_errno; 367676262Sgreen continue; /* fail -- try next */ 367760573Skris } 3678295367Sdes if (cctx->ai->ai_family != AF_UNIX) 3679295367Sdes set_nodelay(sock); 3680181111Sdes debug("connect_next: host %.100s ([%.100s]:%s) " 3681181111Sdes "in progress, fd=%d", cctx->host, ntop, strport, sock); 3682181111Sdes cctx->ai = cctx->ai->ai_next; 3683181111Sdes return sock; 3684181111Sdes } 3685181111Sdes return -1; 3686181111Sdes} 368760573Skris 3688181111Sdesstatic void 3689181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx) 3690181111Sdes{ 3691255767Sdes free(cctx->host); 3692295367Sdes if (cctx->aitop) { 3693295367Sdes if (cctx->aitop->ai_family == AF_UNIX) 3694295367Sdes free(cctx->aitop); 3695295367Sdes else 3696295367Sdes freeaddrinfo(cctx->aitop); 3697295367Sdes } 3698264377Sdes memset(cctx, 0, sizeof(*cctx)); 3699181111Sdes} 3700181111Sdes 3701295367Sdes/* Return CONNECTING channel to remote host:port or local socket path */ 3702181111Sdesstatic Channel * 3703295367Sdesconnect_to(const char *name, int port, char *ctype, char *rname) 3704181111Sdes{ 3705181111Sdes struct addrinfo hints; 3706181111Sdes int gaierr; 3707181111Sdes int sock = -1; 3708181111Sdes char strport[NI_MAXSERV]; 3709181111Sdes struct channel_connect cctx; 3710181111Sdes Channel *c; 3711181111Sdes 3712181111Sdes memset(&cctx, 0, sizeof(cctx)); 3713295367Sdes 3714295367Sdes if (port == PORT_STREAMLOCAL) { 3715295367Sdes struct sockaddr_un *sunaddr; 3716295367Sdes struct addrinfo *ai; 3717295367Sdes 3718295367Sdes if (strlen(name) > sizeof(sunaddr->sun_path)) { 3719295367Sdes error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 3720295367Sdes return (NULL); 3721295367Sdes } 3722295367Sdes 3723295367Sdes /* 3724295367Sdes * Fake up a struct addrinfo for AF_UNIX connections. 3725295367Sdes * channel_connect_ctx_free() must check ai_family 3726295367Sdes * and use free() not freeaddirinfo() for AF_UNIX. 3727295367Sdes */ 3728295367Sdes ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 3729295367Sdes memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 3730295367Sdes ai->ai_addr = (struct sockaddr *)(ai + 1); 3731295367Sdes ai->ai_addrlen = sizeof(*sunaddr); 3732295367Sdes ai->ai_family = AF_UNIX; 3733295367Sdes ai->ai_socktype = SOCK_STREAM; 3734295367Sdes ai->ai_protocol = PF_UNSPEC; 3735295367Sdes sunaddr = (struct sockaddr_un *)ai->ai_addr; 3736295367Sdes sunaddr->sun_family = AF_UNIX; 3737295367Sdes strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 3738295367Sdes cctx.aitop = ai; 3739295367Sdes } else { 3740295367Sdes memset(&hints, 0, sizeof(hints)); 3741295367Sdes hints.ai_family = IPv4or6; 3742295367Sdes hints.ai_socktype = SOCK_STREAM; 3743295367Sdes snprintf(strport, sizeof strport, "%d", port); 3744295367Sdes if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { 3745295367Sdes error("connect_to %.100s: unknown host (%s)", name, 3746295367Sdes ssh_gai_strerror(gaierr)); 3747295367Sdes return NULL; 3748295367Sdes } 374960573Skris } 3750181111Sdes 3751295367Sdes cctx.host = xstrdup(name); 3752181111Sdes cctx.port = port; 3753181111Sdes cctx.ai = cctx.aitop; 3754181111Sdes 3755181111Sdes if ((sock = connect_next(&cctx)) == -1) { 3756181111Sdes error("connect to %.100s port %d failed: %s", 3757295367Sdes name, port, strerror(errno)); 3758181111Sdes channel_connect_ctx_free(&cctx); 3759181111Sdes return NULL; 376060573Skris } 3761181111Sdes c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 3762181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 3763181111Sdes c->connect_ctx = cctx; 3764181111Sdes return c; 376560573Skris} 376676262Sgreen 3767181111SdesChannel * 3768295367Sdeschannel_connect_by_listen_address(const char *listen_host, 3769295367Sdes u_short listen_port, char *ctype, char *rname) 377076262Sgreen{ 377176262Sgreen int i; 377276262Sgreen 3773181111Sdes for (i = 0; i < num_permitted_opens; i++) { 3774295367Sdes if (open_listen_match_tcpip(&permitted_opens[i], listen_host, 3775295367Sdes listen_port, 1)) { 377676262Sgreen return connect_to( 377776262Sgreen permitted_opens[i].host_to_connect, 3778181111Sdes permitted_opens[i].port_to_connect, ctype, rname); 3779181111Sdes } 3780181111Sdes } 378176262Sgreen error("WARNING: Server requests forwarding for unknown listen_port %d", 378276262Sgreen listen_port); 3783181111Sdes return NULL; 378476262Sgreen} 378576262Sgreen 3786295367SdesChannel * 3787295367Sdeschannel_connect_by_listen_path(const char *path, char *ctype, char *rname) 3788295367Sdes{ 3789295367Sdes int i; 3790295367Sdes 3791295367Sdes for (i = 0; i < num_permitted_opens; i++) { 3792295367Sdes if (open_listen_match_streamlocal(&permitted_opens[i], path)) { 3793295367Sdes return connect_to( 3794295367Sdes permitted_opens[i].host_to_connect, 3795295367Sdes permitted_opens[i].port_to_connect, ctype, rname); 3796295367Sdes } 3797295367Sdes } 3798295367Sdes error("WARNING: Server requests forwarding for unknown path %.100s", 3799295367Sdes path); 3800295367Sdes return NULL; 3801295367Sdes} 3802295367Sdes 380376262Sgreen/* Check if connecting to that port is permitted and connect. */ 3804181111SdesChannel * 3805295367Sdeschannel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) 380676262Sgreen{ 3807162856Sdes int i, permit, permit_adm = 1; 380876262Sgreen 380976262Sgreen permit = all_opens_permitted; 381076262Sgreen if (!permit) { 381176262Sgreen for (i = 0; i < num_permitted_opens; i++) 3812295367Sdes if (open_match(&permitted_opens[i], host, port)) { 381376262Sgreen permit = 1; 3814295367Sdes break; 3815295367Sdes } 3816162856Sdes } 381776262Sgreen 3818162856Sdes if (num_adm_permitted_opens > 0) { 3819162856Sdes permit_adm = 0; 3820162856Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3821295367Sdes if (open_match(&permitted_adm_opens[i], host, port)) { 3822162856Sdes permit_adm = 1; 3823295367Sdes break; 3824295367Sdes } 382576262Sgreen } 3826162856Sdes 3827162856Sdes if (!permit || !permit_adm) { 3828124207Sdes logit("Received request to connect to host %.100s port %d, " 382976262Sgreen "but the request was denied.", host, port); 3830181111Sdes return NULL; 383176262Sgreen } 3832181111Sdes return connect_to(host, port, ctype, rname); 383376262Sgreen} 383476262Sgreen 3835295367Sdes/* Check if connecting to that path is permitted and connect. */ 3836295367SdesChannel * 3837295367Sdeschannel_connect_to_path(const char *path, char *ctype, char *rname) 3838295367Sdes{ 3839295367Sdes int i, permit, permit_adm = 1; 3840295367Sdes 3841295367Sdes permit = all_opens_permitted; 3842295367Sdes if (!permit) { 3843295367Sdes for (i = 0; i < num_permitted_opens; i++) 3844295367Sdes if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { 3845295367Sdes permit = 1; 3846295367Sdes break; 3847295367Sdes } 3848295367Sdes } 3849295367Sdes 3850295367Sdes if (num_adm_permitted_opens > 0) { 3851295367Sdes permit_adm = 0; 3852295367Sdes for (i = 0; i < num_adm_permitted_opens; i++) 3853295367Sdes if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { 3854295367Sdes permit_adm = 1; 3855295367Sdes break; 3856295367Sdes } 3857295367Sdes } 3858295367Sdes 3859295367Sdes if (!permit || !permit_adm) { 3860295367Sdes logit("Received request to connect to path %.100s, " 3861295367Sdes "but the request was denied.", path); 3862295367Sdes return NULL; 3863295367Sdes } 3864295367Sdes return connect_to(path, PORT_STREAMLOCAL, ctype, rname); 3865295367Sdes} 3866295367Sdes 3867137019Sdesvoid 3868137019Sdeschannel_send_window_changes(void) 3869137019Sdes{ 3870137019Sdes u_int i; 3871137019Sdes struct winsize ws; 3872137019Sdes 3873137019Sdes for (i = 0; i < channels_alloc; i++) { 3874147005Sdes if (channels[i] == NULL || !channels[i]->client_tty || 3875137019Sdes channels[i]->type != SSH_CHANNEL_OPEN) 3876137019Sdes continue; 3877137019Sdes if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 3878137019Sdes continue; 3879137019Sdes channel_request_start(i, "window-change", 0); 3880162856Sdes packet_put_int((u_int)ws.ws_col); 3881162856Sdes packet_put_int((u_int)ws.ws_row); 3882162856Sdes packet_put_int((u_int)ws.ws_xpixel); 3883162856Sdes packet_put_int((u_int)ws.ws_ypixel); 3884137019Sdes packet_send(); 3885137019Sdes } 3886137019Sdes} 3887137019Sdes 388892559Sdes/* -- X11 forwarding */ 388957429Smarkm 389057429Smarkm/* 389157429Smarkm * Creates an internet domain socket for listening for X11 connections. 389299063Sdes * Returns 0 and a suitable display number for the DISPLAY variable 389399063Sdes * stored in display_numberp , or -1 if an error occurs. 389457429Smarkm */ 389592559Sdesint 389692559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost, 3897149753Sdes int single_connection, u_int *display_numberp, int **chanids) 389857429Smarkm{ 389992559Sdes Channel *nc = NULL; 390057429Smarkm int display_number, sock; 390157429Smarkm u_short port; 390257429Smarkm struct addrinfo hints, *ai, *aitop; 390357429Smarkm char strport[NI_MAXSERV]; 390457429Smarkm int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 390557429Smarkm 3906157019Sdes if (chanids == NULL) 3907157019Sdes return -1; 3908157019Sdes 390957429Smarkm for (display_number = x11_display_offset; 391092559Sdes display_number < MAX_DISPLAYS; 391192559Sdes display_number++) { 391257429Smarkm port = 6000 + display_number; 391357429Smarkm memset(&hints, 0, sizeof(hints)); 391457429Smarkm hints.ai_family = IPv4or6; 391592559Sdes hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 391657429Smarkm hints.ai_socktype = SOCK_STREAM; 391757429Smarkm snprintf(strport, sizeof strport, "%d", port); 391857429Smarkm if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 3919181111Sdes error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 392092559Sdes return -1; 392157429Smarkm } 392257429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 392357429Smarkm if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 392457429Smarkm continue; 3925124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, 3926124207Sdes ai->ai_protocol); 392757429Smarkm if (sock < 0) { 3928207319Sdes if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 3929207319Sdes#ifdef EPFNOSUPPORT 3930207319Sdes && (errno != EPFNOSUPPORT) 3931207319Sdes#endif 3932207319Sdes ) { 393398941Sdes error("socket: %.100s", strerror(errno)); 3934137019Sdes freeaddrinfo(aitop); 393598941Sdes return -1; 393698941Sdes } else { 393798941Sdes debug("x11_create_display_inet: Socket family %d not supported", 393898941Sdes ai->ai_family); 393998941Sdes continue; 394098941Sdes } 394157429Smarkm } 3942204917Sdes if (ai->ai_family == AF_INET6) 3943204917Sdes sock_set_v6only(sock); 3944181111Sdes if (x11_use_localhost) 3945181111Sdes channel_set_reuseaddr(sock); 394657429Smarkm if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3947124207Sdes debug2("bind port %d: %.100s", port, strerror(errno)); 394857429Smarkm close(sock); 394998941Sdes 395057429Smarkm for (n = 0; n < num_socks; n++) { 395157429Smarkm close(socks[n]); 395257429Smarkm } 395357429Smarkm num_socks = 0; 395457429Smarkm break; 395557429Smarkm } 395657429Smarkm socks[num_socks++] = sock; 395757429Smarkm if (num_socks == NUM_SOCKS) 395857429Smarkm break; 395957429Smarkm } 396076262Sgreen freeaddrinfo(aitop); 396157429Smarkm if (num_socks > 0) 396257429Smarkm break; 396357429Smarkm } 396457429Smarkm if (display_number >= MAX_DISPLAYS) { 396557429Smarkm error("Failed to allocate internet-domain X11 display socket."); 396692559Sdes return -1; 396757429Smarkm } 396857429Smarkm /* Start listening for connections on the socket. */ 396957429Smarkm for (n = 0; n < num_socks; n++) { 397057429Smarkm sock = socks[n]; 3971126273Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 397257429Smarkm error("listen: %.100s", strerror(errno)); 397357429Smarkm close(sock); 397492559Sdes return -1; 397557429Smarkm } 397657429Smarkm } 397757429Smarkm 397857429Smarkm /* Allocate a channel for each socket. */ 3979162856Sdes *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 398057429Smarkm for (n = 0; n < num_socks; n++) { 398157429Smarkm sock = socks[n]; 3982294693Sdes nc = channel_new("x11 listener", 3983294693Sdes SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 3984294693Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3985294693Sdes 0, "X11 inet listener", 1); 398692559Sdes nc->single_connection = single_connection; 3987157019Sdes (*chanids)[n] = nc->self; 398857429Smarkm } 3989157019Sdes (*chanids)[n] = -1; 399057429Smarkm 399192559Sdes /* Return the display number for the DISPLAY environment variable. */ 399299063Sdes *display_numberp = display_number; 399399063Sdes return (0); 399457429Smarkm} 399557429Smarkm 399692559Sdesstatic int 3997192595Sdesconnect_local_xsocket_path(const char *pathname) 399857429Smarkm{ 399957429Smarkm int sock; 400057429Smarkm struct sockaddr_un addr; 400157429Smarkm 400292559Sdes sock = socket(AF_UNIX, SOCK_STREAM, 0); 400392559Sdes if (sock < 0) 400492559Sdes error("socket: %.100s", strerror(errno)); 400592559Sdes memset(&addr, 0, sizeof(addr)); 400692559Sdes addr.sun_family = AF_UNIX; 4007192595Sdes strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 4008162856Sdes if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 400992559Sdes return sock; 401092559Sdes close(sock); 401157429Smarkm error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 401257429Smarkm return -1; 401357429Smarkm} 401457429Smarkm 4015192595Sdesstatic int 4016192595Sdesconnect_local_xsocket(u_int dnr) 4017192595Sdes{ 4018192595Sdes char buf[1024]; 4019192595Sdes snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 4020192595Sdes return connect_local_xsocket_path(buf); 4021192595Sdes} 4022192595Sdes 402360573Skrisint 402460573Skrisx11_connect_display(void) 402557429Smarkm{ 4026162856Sdes u_int display_number; 402757429Smarkm const char *display; 402860573Skris char buf[1024], *cp; 402957429Smarkm struct addrinfo hints, *ai, *aitop; 403057429Smarkm char strport[NI_MAXSERV]; 4031162856Sdes int gaierr, sock = 0; 403257429Smarkm 403357429Smarkm /* Try to open a socket for the local X server. */ 403457429Smarkm display = getenv("DISPLAY"); 403557429Smarkm if (!display) { 403657429Smarkm error("DISPLAY not set."); 403760573Skris return -1; 403857429Smarkm } 403957429Smarkm /* 404057429Smarkm * Now we decode the value of the DISPLAY variable and make a 404157429Smarkm * connection to the real X server. 404257429Smarkm */ 404357429Smarkm 4044192595Sdes /* Check if the display is from launchd. */ 4045192595Sdes#ifdef __APPLE__ 4046192595Sdes if (strncmp(display, "/tmp/launch", 11) == 0) { 4047192595Sdes sock = connect_local_xsocket_path(display); 4048192595Sdes if (sock < 0) 4049192595Sdes return -1; 4050192595Sdes 4051192595Sdes /* OK, we now have a connection to the display. */ 4052192595Sdes return sock; 4053192595Sdes } 4054192595Sdes#endif 405557429Smarkm /* 405657429Smarkm * Check if it is a unix domain socket. Unix domain displays are in 405757429Smarkm * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 405857429Smarkm */ 405957429Smarkm if (strncmp(display, "unix:", 5) == 0 || 406057429Smarkm display[0] == ':') { 406157429Smarkm /* Connect to the unix domain socket. */ 4062162856Sdes if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 406357429Smarkm error("Could not parse display number from DISPLAY: %.100s", 406492559Sdes display); 406560573Skris return -1; 406657429Smarkm } 406757429Smarkm /* Create a socket. */ 406857429Smarkm sock = connect_local_xsocket(display_number); 406957429Smarkm if (sock < 0) 407060573Skris return -1; 407157429Smarkm 407257429Smarkm /* OK, we now have a connection to the display. */ 407360573Skris return sock; 407457429Smarkm } 407557429Smarkm /* 407657429Smarkm * Connect to an inet socket. The DISPLAY value is supposedly 407757429Smarkm * hostname:d[.s], where hostname may also be numeric IP address. 407857429Smarkm */ 407992559Sdes strlcpy(buf, display, sizeof(buf)); 408057429Smarkm cp = strchr(buf, ':'); 408157429Smarkm if (!cp) { 408257429Smarkm error("Could not find ':' in DISPLAY: %.100s", display); 408360573Skris return -1; 408457429Smarkm } 408557429Smarkm *cp = 0; 408657429Smarkm /* buf now contains the host name. But first we parse the display number. */ 4087162856Sdes if (sscanf(cp + 1, "%u", &display_number) != 1) { 408857429Smarkm error("Could not parse display number from DISPLAY: %.100s", 408992559Sdes display); 409060573Skris return -1; 409157429Smarkm } 409257429Smarkm 409357429Smarkm /* Look up the host address */ 409457429Smarkm memset(&hints, 0, sizeof(hints)); 409557429Smarkm hints.ai_family = IPv4or6; 409657429Smarkm hints.ai_socktype = SOCK_STREAM; 4097162856Sdes snprintf(strport, sizeof strport, "%u", 6000 + display_number); 409857429Smarkm if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 4099181111Sdes error("%.100s: unknown host. (%s)", buf, 4100181111Sdes ssh_gai_strerror(gaierr)); 410160573Skris return -1; 410257429Smarkm } 410357429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 410457429Smarkm /* Create a socket. */ 4105124207Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 410657429Smarkm if (sock < 0) { 4107124207Sdes debug2("socket: %.100s", strerror(errno)); 410860573Skris continue; 410960573Skris } 411060573Skris /* Connect it to the display. */ 411160573Skris if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 4112162856Sdes debug2("connect %.100s port %u: %.100s", buf, 411360573Skris 6000 + display_number, strerror(errno)); 411460573Skris close(sock); 411560573Skris continue; 411660573Skris } 411760573Skris /* Success */ 411860573Skris break; 411957429Smarkm } 412057429Smarkm freeaddrinfo(aitop); 412157429Smarkm if (!ai) { 4122162856Sdes error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 412357429Smarkm strerror(errno)); 412460573Skris return -1; 412557429Smarkm } 412692559Sdes set_nodelay(sock); 412760573Skris return sock; 412860573Skris} 412957429Smarkm 413060573Skris/* 413160573Skris * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 413260573Skris * the remote channel number. We should do whatever we want, and respond 413360573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 413460573Skris */ 413557429Smarkm 4136162856Sdes/* ARGSUSED */ 4137295367Sdesint 413892559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt) 413960573Skris{ 414092559Sdes Channel *c = NULL; 414192559Sdes int remote_id, sock = 0; 414260573Skris char *remote_host; 414357429Smarkm 414492559Sdes debug("Received X11 open request."); 414557429Smarkm 414692559Sdes remote_id = packet_get_int(); 414792559Sdes 414892559Sdes if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 414992559Sdes remote_host = packet_get_string(NULL); 415060573Skris } else { 415160573Skris remote_host = xstrdup("unknown (remote did not supply name)"); 415260573Skris } 415392559Sdes packet_check_eom(); 415460573Skris 415560573Skris /* Obtain a connection to the real X display. */ 415660573Skris sock = x11_connect_display(); 415792559Sdes if (sock != -1) { 415892559Sdes /* Allocate a channel for this connection. */ 415992559Sdes c = channel_new("connected x11 socket", 416092559Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 416192559Sdes remote_host, 1); 416292559Sdes c->remote_id = remote_id; 416392559Sdes c->force_drain = 1; 416492559Sdes } 4165255767Sdes free(remote_host); 416692559Sdes if (c == NULL) { 416760573Skris /* Send refusal to the remote host. */ 416860573Skris packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 416992559Sdes packet_put_int(remote_id); 417060573Skris } else { 417160573Skris /* Send a confirmation to the remote host. */ 417260573Skris packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 417392559Sdes packet_put_int(remote_id); 417492559Sdes packet_put_int(c->self); 417560573Skris } 417692559Sdes packet_send(); 4177295367Sdes return 0; 417857429Smarkm} 417957429Smarkm 418069587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 4181162856Sdes/* ARGSUSED */ 4182295367Sdesint 418392559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt) 418469587Sgreen{ 418569587Sgreen int rchan = packet_get_int(); 4186106130Sdes 418792559Sdes switch (type) { 418869587Sgreen case SSH_SMSG_AGENT_OPEN: 418969587Sgreen error("Warning: ssh server tried agent forwarding."); 419069587Sgreen break; 419169587Sgreen case SSH_SMSG_X11_OPEN: 419269587Sgreen error("Warning: ssh server tried X11 forwarding."); 419369587Sgreen break; 419469587Sgreen default: 419592559Sdes error("deny_input_open: type %d", type); 419669587Sgreen break; 419769587Sgreen } 4198157019Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 419969587Sgreen packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 420069587Sgreen packet_put_int(rchan); 420169587Sgreen packet_send(); 4202295367Sdes return 0; 420369587Sgreen} 420469587Sgreen 420557429Smarkm/* 420657429Smarkm * Requests forwarding of X11 connections, generates fake authentication 420757429Smarkm * data, and enables authentication spoofing. 420892559Sdes * This should be called in the client only. 420957429Smarkm */ 421060573Skrisvoid 4211149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 4212226046Sdes const char *proto, const char *data, int want_reply) 421357429Smarkm{ 421476262Sgreen u_int data_len = (u_int) strlen(data) / 2; 4215149753Sdes u_int i, value; 421657429Smarkm char *new_data; 421757429Smarkm int screen_number; 421857429Smarkm const char *cp; 4219137019Sdes u_int32_t rnd = 0; 422057429Smarkm 4221149753Sdes if (x11_saved_display == NULL) 4222149753Sdes x11_saved_display = xstrdup(disp); 4223149753Sdes else if (strcmp(disp, x11_saved_display) != 0) { 4224149753Sdes error("x11_request_forwarding_with_spoofing: different " 4225149753Sdes "$DISPLAY already forwarded"); 4226149753Sdes return; 4227149753Sdes } 4228149753Sdes 4229162856Sdes cp = strchr(disp, ':'); 423057429Smarkm if (cp) 423157429Smarkm cp = strchr(cp, '.'); 423257429Smarkm if (cp) 4233162856Sdes screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 423457429Smarkm else 423557429Smarkm screen_number = 0; 423657429Smarkm 4237149753Sdes if (x11_saved_proto == NULL) { 4238149753Sdes /* Save protocol name. */ 4239149753Sdes x11_saved_proto = xstrdup(proto); 4240149753Sdes /* 4241149753Sdes * Extract real authentication data and generate fake data 4242149753Sdes * of the same length. 4243149753Sdes */ 4244149753Sdes x11_saved_data = xmalloc(data_len); 4245149753Sdes x11_fake_data = xmalloc(data_len); 4246149753Sdes for (i = 0; i < data_len; i++) { 4247149753Sdes if (sscanf(data + 2 * i, "%2x", &value) != 1) 4248149753Sdes fatal("x11_request_forwarding: bad " 4249149753Sdes "authentication data: %.100s", data); 4250149753Sdes if (i % 4 == 0) 4251149753Sdes rnd = arc4random(); 4252149753Sdes x11_saved_data[i] = value; 4253149753Sdes x11_fake_data[i] = rnd & 0xff; 4254149753Sdes rnd >>= 8; 4255149753Sdes } 4256149753Sdes x11_saved_data_len = data_len; 4257149753Sdes x11_fake_data_len = data_len; 425857429Smarkm } 425957429Smarkm 426057429Smarkm /* Convert the fake data into hex. */ 4261149753Sdes new_data = tohex(x11_fake_data, data_len); 426257429Smarkm 426357429Smarkm /* Send the request packet. */ 426460573Skris if (compat20) { 4265226046Sdes channel_request_start(client_session_id, "x11-req", want_reply); 426660573Skris packet_put_char(0); /* XXX bool single connection */ 426760573Skris } else { 426860573Skris packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 426960573Skris } 427060573Skris packet_put_cstring(proto); 427160573Skris packet_put_cstring(new_data); 427257429Smarkm packet_put_int(screen_number); 427357429Smarkm packet_send(); 427457429Smarkm packet_write_wait(); 4275255767Sdes free(new_data); 427657429Smarkm} 427757429Smarkm 427892559Sdes 427992559Sdes/* -- agent forwarding */ 428092559Sdes 428157429Smarkm/* Sends a message to the server to request authentication fd forwarding. */ 428257429Smarkm 428360573Skrisvoid 428492559Sdesauth_request_forwarding(void) 428557429Smarkm{ 428657429Smarkm packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 428757429Smarkm packet_send(); 428857429Smarkm packet_write_wait(); 428957429Smarkm} 4290