1511b41d2SMark Murray /* 2511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 3511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4511b41d2SMark Murray * All rights reserved 5511b41d2SMark Murray * This file contains functions for generic socket connection forwarding. 6511b41d2SMark Murray * There is also code for initiating connection forwarding for X11 connections, 7511b41d2SMark Murray * arbitrary tcp/ip connections, and the authentication agent connection. 8511b41d2SMark Murray * 9b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 10b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 11b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 12b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 13b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 14b66f2d16SKris Kennaway * 15a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 16af12a3e7SDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 17b66f2d16SKris Kennaway * Copyright (c) 1999 Dug Song. All rights reserved. 18b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 19b66f2d16SKris Kennaway * 20b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 21b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 22b66f2d16SKris Kennaway * are met: 23b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 24b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 25b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 26b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 27b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 28b66f2d16SKris Kennaway * 29b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 30b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 31b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 32b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 33b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 34b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 38b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39511b41d2SMark Murray */ 40511b41d2SMark Murray 41511b41d2SMark Murray #include "includes.h" 4280628bacSDag-Erling Smørgrav RCSID("$OpenBSD: channels.c,v 1.175 2002/06/10 22:28:41 markus Exp $"); 43a04a10f8SKris Kennaway 44ca3176e7SBrian Feldman #include "ssh.h" 45ca3176e7SBrian Feldman #include "ssh1.h" 46ca3176e7SBrian Feldman #include "ssh2.h" 47ca3176e7SBrian Feldman #include "packet.h" 48ca3176e7SBrian Feldman #include "xmalloc.h" 49ca3176e7SBrian Feldman #include "log.h" 50ca3176e7SBrian Feldman #include "misc.h" 51ca3176e7SBrian Feldman #include "channels.h" 52ca3176e7SBrian Feldman #include "compat.h" 53ca3176e7SBrian Feldman #include "canohost.h" 54b66f2d16SKris Kennaway #include "key.h" 55b66f2d16SKris Kennaway #include "authfd.h" 56af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 57b66f2d16SKris Kennaway 58511b41d2SMark Murray 59af12a3e7SDag-Erling Smørgrav /* -- channel core */ 60511b41d2SMark Murray 61511b41d2SMark Murray /* 62511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 63511b41d2SMark Murray * dynamically extended as needed. 64511b41d2SMark Murray */ 65af12a3e7SDag-Erling Smørgrav static Channel **channels = NULL; 66511b41d2SMark Murray 67511b41d2SMark Murray /* 68511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 69af12a3e7SDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 70511b41d2SMark Murray */ 71511b41d2SMark Murray static int channels_alloc = 0; 72511b41d2SMark Murray 73511b41d2SMark Murray /* 74511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 75af12a3e7SDag-Erling Smørgrav * updated in channel_new. 76511b41d2SMark Murray */ 77ca3176e7SBrian Feldman static int channel_max_fd = 0; 78511b41d2SMark Murray 79511b41d2SMark Murray 80af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 81511b41d2SMark Murray 82511b41d2SMark Murray /* 83511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 84511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 85511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 86511b41d2SMark Murray * network (which might be behind a firewall). 87511b41d2SMark Murray */ 88511b41d2SMark Murray typedef struct { 89a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 90a04a10f8SKris Kennaway u_short port_to_connect; /* Connect to 'port'. */ 91a04a10f8SKris Kennaway u_short listen_port; /* Remote side should listen port number. */ 92511b41d2SMark Murray } ForwardPermission; 93511b41d2SMark Murray 94511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */ 95511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 96af12a3e7SDag-Erling Smørgrav 97511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */ 98511b41d2SMark Murray static int num_permitted_opens = 0; 99511b41d2SMark Murray /* 100511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 101511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 102511b41d2SMark Murray * anything after logging in anyway. 103511b41d2SMark Murray */ 104511b41d2SMark Murray static int all_opens_permitted = 0; 105511b41d2SMark Murray 106af12a3e7SDag-Erling Smørgrav 107af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 108af12a3e7SDag-Erling Smørgrav 109af12a3e7SDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 110af12a3e7SDag-Erling Smørgrav #define MAX_DISPLAYS 1000 111af12a3e7SDag-Erling Smørgrav 112af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 113af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 114af12a3e7SDag-Erling Smørgrav 115af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 116af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 117af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 118af12a3e7SDag-Erling Smørgrav 119af12a3e7SDag-Erling Smørgrav /* 120af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 121af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 122af12a3e7SDag-Erling Smørgrav */ 123af12a3e7SDag-Erling Smørgrav static char *x11_fake_data = NULL; 124af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 125af12a3e7SDag-Erling Smørgrav 126af12a3e7SDag-Erling Smørgrav 127af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 128af12a3e7SDag-Erling Smørgrav 129af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 130af12a3e7SDag-Erling Smørgrav 131ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 132989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 133ca3176e7SBrian Feldman 134af12a3e7SDag-Erling Smørgrav /* helper */ 135af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 136ca3176e7SBrian Feldman 137af12a3e7SDag-Erling Smørgrav /* -- channel core */ 138a04a10f8SKris Kennaway 139a04a10f8SKris Kennaway Channel * 140a04a10f8SKris Kennaway channel_lookup(int id) 141a04a10f8SKris Kennaway { 142a04a10f8SKris Kennaway Channel *c; 143af12a3e7SDag-Erling Smørgrav 144d96f3089SJacques Vidrine if (id < 0 || id >= channels_alloc) { 145a04a10f8SKris Kennaway log("channel_lookup: %d: bad id", id); 146a04a10f8SKris Kennaway return NULL; 147a04a10f8SKris Kennaway } 148af12a3e7SDag-Erling Smørgrav c = channels[id]; 149af12a3e7SDag-Erling Smørgrav if (c == NULL) { 150a04a10f8SKris Kennaway log("channel_lookup: %d: bad id: channel free", id); 151a04a10f8SKris Kennaway return NULL; 152a04a10f8SKris Kennaway } 153a04a10f8SKris Kennaway return c; 154a04a10f8SKris Kennaway } 155a04a10f8SKris Kennaway 156a04a10f8SKris Kennaway /* 157a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 158a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 159a04a10f8SKris Kennaway */ 160a04a10f8SKris Kennaway 161af12a3e7SDag-Erling Smørgrav static void 1625b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1635b9b2fafSBrian Feldman int extusage, int nonblock) 164a04a10f8SKris Kennaway { 165a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 166ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, rfd); 167ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, wfd); 168ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, efd); 169ca3176e7SBrian Feldman 170a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 171a04a10f8SKris Kennaway 172a04a10f8SKris Kennaway c->rfd = rfd; 173a04a10f8SKris Kennaway c->wfd = wfd; 174a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 175a04a10f8SKris Kennaway c->efd = efd; 176a04a10f8SKris Kennaway c->extended_usage = extusage; 1775b9b2fafSBrian Feldman 178e0fbb1d2SBrian Feldman /* XXX ugly hack: nonblock is only set by the server */ 179e0fbb1d2SBrian Feldman if (nonblock && isatty(c->rfd)) { 180ca3176e7SBrian Feldman debug("channel %d: rfd %d isatty", c->self, c->rfd); 181e0fbb1d2SBrian Feldman c->isatty = 1; 182e0fbb1d2SBrian Feldman if (!isatty(c->wfd)) { 183ca3176e7SBrian Feldman error("channel %d: wfd %d is not a tty?", 184e0fbb1d2SBrian Feldman c->self, c->wfd); 185e0fbb1d2SBrian Feldman } 186e0fbb1d2SBrian Feldman } else { 187e0fbb1d2SBrian Feldman c->isatty = 0; 188e0fbb1d2SBrian Feldman } 189e0fbb1d2SBrian Feldman 1905b9b2fafSBrian Feldman /* enable nonblocking mode */ 1915b9b2fafSBrian Feldman if (nonblock) { 192a04a10f8SKris Kennaway if (rfd != -1) 193a04a10f8SKris Kennaway set_nonblock(rfd); 194a04a10f8SKris Kennaway if (wfd != -1) 195a04a10f8SKris Kennaway set_nonblock(wfd); 196a04a10f8SKris Kennaway if (efd != -1) 197a04a10f8SKris Kennaway set_nonblock(efd); 198a04a10f8SKris Kennaway } 1995b9b2fafSBrian Feldman } 200a04a10f8SKris Kennaway 201511b41d2SMark Murray /* 202511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 203511b41d2SMark Murray * remote_name to be freed. 204511b41d2SMark Murray */ 205511b41d2SMark Murray 206af12a3e7SDag-Erling Smørgrav Channel * 207a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 2085b9b2fafSBrian Feldman int window, int maxpack, int extusage, char *remote_name, int nonblock) 209511b41d2SMark Murray { 210511b41d2SMark Murray int i, found; 211511b41d2SMark Murray Channel *c; 212511b41d2SMark Murray 213511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 214511b41d2SMark Murray if (channels_alloc == 0) { 215511b41d2SMark Murray channels_alloc = 10; 216af12a3e7SDag-Erling Smørgrav channels = xmalloc(channels_alloc * sizeof(Channel *)); 217511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 218af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 219af12a3e7SDag-Erling Smørgrav fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); 220511b41d2SMark Murray } 221511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 222511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 223af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 224511b41d2SMark Murray /* Found a free slot. */ 225511b41d2SMark Murray found = i; 226511b41d2SMark Murray break; 227511b41d2SMark Murray } 228511b41d2SMark Murray if (found == -1) { 229511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 230511b41d2SMark Murray found = channels_alloc; 231511b41d2SMark Murray channels_alloc += 10; 2325b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 233af12a3e7SDag-Erling Smørgrav channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); 234511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 235af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 236511b41d2SMark Murray } 237af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 238af12a3e7SDag-Erling Smørgrav c = channels[found] = xmalloc(sizeof(Channel)); 239af12a3e7SDag-Erling Smørgrav memset(c, 0, sizeof(Channel)); 240511b41d2SMark Murray buffer_init(&c->input); 241511b41d2SMark Murray buffer_init(&c->output); 242a04a10f8SKris Kennaway buffer_init(&c->extended); 243af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 244af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 245af12a3e7SDag-Erling Smørgrav c->flags = 0; 2465b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 247511b41d2SMark Murray c->self = found; 248511b41d2SMark Murray c->type = type; 249a04a10f8SKris Kennaway c->ctype = ctype; 250a04a10f8SKris Kennaway c->local_window = window; 251a04a10f8SKris Kennaway c->local_window_max = window; 252a04a10f8SKris Kennaway c->local_consumed = 0; 253a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 254511b41d2SMark Murray c->remote_id = -1; 255511b41d2SMark Murray c->remote_name = remote_name; 256a04a10f8SKris Kennaway c->remote_window = 0; 257a04a10f8SKris Kennaway c->remote_maxpacket = 0; 258af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 259af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 260af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 261af12a3e7SDag-Erling Smørgrav c->confirm = NULL; 262b66f2d16SKris Kennaway c->input_filter = NULL; 263511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 264af12a3e7SDag-Erling Smørgrav return c; 265a04a10f8SKris Kennaway } 266511b41d2SMark Murray 267af12a3e7SDag-Erling Smørgrav static int 268af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 269af12a3e7SDag-Erling Smørgrav { 270af12a3e7SDag-Erling Smørgrav int i, max = 0; 271af12a3e7SDag-Erling Smørgrav Channel *c; 272af12a3e7SDag-Erling Smørgrav 273af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 274af12a3e7SDag-Erling Smørgrav c = channels[i]; 275af12a3e7SDag-Erling Smørgrav if (c != NULL) { 276af12a3e7SDag-Erling Smørgrav max = MAX(max, c->rfd); 277af12a3e7SDag-Erling Smørgrav max = MAX(max, c->wfd); 278af12a3e7SDag-Erling Smørgrav max = MAX(max, c->efd); 279af12a3e7SDag-Erling Smørgrav } 280af12a3e7SDag-Erling Smørgrav } 281af12a3e7SDag-Erling Smørgrav return max; 282af12a3e7SDag-Erling Smørgrav } 283af12a3e7SDag-Erling Smørgrav 284af12a3e7SDag-Erling Smørgrav int 285af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 286af12a3e7SDag-Erling Smørgrav { 287af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 288af12a3e7SDag-Erling Smørgrav 289af12a3e7SDag-Erling Smørgrav if (fd != -1) { 290af12a3e7SDag-Erling Smørgrav ret = close(fd); 291af12a3e7SDag-Erling Smørgrav *fdp = -1; 292af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 293af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 294af12a3e7SDag-Erling Smørgrav } 295af12a3e7SDag-Erling Smørgrav return ret; 296af12a3e7SDag-Erling Smørgrav } 297a04a10f8SKris Kennaway 298a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 299511b41d2SMark Murray 300af12a3e7SDag-Erling Smørgrav static void 301a04a10f8SKris Kennaway channel_close_fds(Channel *c) 302511b41d2SMark Murray { 303af12a3e7SDag-Erling Smørgrav debug3("channel_close_fds: channel %d: r %d w %d e %d", 304af12a3e7SDag-Erling Smørgrav c->self, c->rfd, c->wfd, c->efd); 305af12a3e7SDag-Erling Smørgrav 306af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 307af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 308af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 309af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 310a04a10f8SKris Kennaway } 311511b41d2SMark Murray 312a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 313a04a10f8SKris Kennaway 314a04a10f8SKris Kennaway void 315af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 316a04a10f8SKris Kennaway { 317af12a3e7SDag-Erling Smørgrav char *s; 318af12a3e7SDag-Erling Smørgrav int i, n; 319ca3176e7SBrian Feldman 320af12a3e7SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) 321af12a3e7SDag-Erling Smørgrav if (channels[i]) 322af12a3e7SDag-Erling Smørgrav n++; 323af12a3e7SDag-Erling Smørgrav debug("channel_free: channel %d: %s, nchannels %d", c->self, 324af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 325af12a3e7SDag-Erling Smørgrav 326af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 327af12a3e7SDag-Erling Smørgrav debug3("channel_free: status: %s", s); 328ca3176e7SBrian Feldman xfree(s); 329ca3176e7SBrian Feldman 330a04a10f8SKris Kennaway if (c->sock != -1) 331a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 332a04a10f8SKris Kennaway channel_close_fds(c); 333a04a10f8SKris Kennaway buffer_free(&c->input); 334a04a10f8SKris Kennaway buffer_free(&c->output); 335a04a10f8SKris Kennaway buffer_free(&c->extended); 336a04a10f8SKris Kennaway if (c->remote_name) { 337a04a10f8SKris Kennaway xfree(c->remote_name); 338a04a10f8SKris Kennaway c->remote_name = NULL; 339511b41d2SMark Murray } 340af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 341af12a3e7SDag-Erling Smørgrav xfree(c); 342af12a3e7SDag-Erling Smørgrav } 343af12a3e7SDag-Erling Smørgrav 344af12a3e7SDag-Erling Smørgrav void 345af12a3e7SDag-Erling Smørgrav channel_free_all(void) 346af12a3e7SDag-Erling Smørgrav { 347af12a3e7SDag-Erling Smørgrav int i; 348af12a3e7SDag-Erling Smørgrav 349af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 350af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 351af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 352af12a3e7SDag-Erling Smørgrav } 353af12a3e7SDag-Erling Smørgrav 354af12a3e7SDag-Erling Smørgrav /* 355af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 356af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 357af12a3e7SDag-Erling Smørgrav */ 358af12a3e7SDag-Erling Smørgrav 359af12a3e7SDag-Erling Smørgrav void 360af12a3e7SDag-Erling Smørgrav channel_close_all(void) 361af12a3e7SDag-Erling Smørgrav { 362af12a3e7SDag-Erling Smørgrav int i; 363af12a3e7SDag-Erling Smørgrav 364af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 365af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 366af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 367af12a3e7SDag-Erling Smørgrav } 368af12a3e7SDag-Erling Smørgrav 369af12a3e7SDag-Erling Smørgrav /* 370af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 371af12a3e7SDag-Erling Smørgrav */ 372af12a3e7SDag-Erling Smørgrav 373af12a3e7SDag-Erling Smørgrav void 374af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 375af12a3e7SDag-Erling Smørgrav { 376af12a3e7SDag-Erling Smørgrav int i; 377af12a3e7SDag-Erling Smørgrav Channel *c; 378af12a3e7SDag-Erling Smørgrav 379af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 380af12a3e7SDag-Erling Smørgrav c = channels[i]; 381af12a3e7SDag-Erling Smørgrav if (c != NULL) { 382af12a3e7SDag-Erling Smørgrav switch (c->type) { 383af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 384af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 385af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 386af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 387af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 388af12a3e7SDag-Erling Smørgrav channel_free(c); 389af12a3e7SDag-Erling Smørgrav break; 390af12a3e7SDag-Erling Smørgrav } 391af12a3e7SDag-Erling Smørgrav } 392af12a3e7SDag-Erling Smørgrav } 393af12a3e7SDag-Erling Smørgrav } 394af12a3e7SDag-Erling Smørgrav 395af12a3e7SDag-Erling Smørgrav /* 396af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 397af12a3e7SDag-Erling Smørgrav * more channel is overfull. 398af12a3e7SDag-Erling Smørgrav */ 399af12a3e7SDag-Erling Smørgrav 400af12a3e7SDag-Erling Smørgrav int 401af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 402af12a3e7SDag-Erling Smørgrav { 403af12a3e7SDag-Erling Smørgrav u_int i; 404af12a3e7SDag-Erling Smørgrav Channel *c; 405af12a3e7SDag-Erling Smørgrav 406af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 407af12a3e7SDag-Erling Smørgrav c = channels[i]; 408af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 409af12a3e7SDag-Erling Smørgrav #if 0 410af12a3e7SDag-Erling Smørgrav if (!compat20 && 411af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 412af12a3e7SDag-Erling Smørgrav debug("channel %d: big input buffer %d", 413af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 414af12a3e7SDag-Erling Smørgrav return 0; 415af12a3e7SDag-Erling Smørgrav } 416af12a3e7SDag-Erling Smørgrav #endif 417af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 418af12a3e7SDag-Erling Smørgrav debug("channel %d: big output buffer %d > %d", 419af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 420af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 421af12a3e7SDag-Erling Smørgrav return 0; 422af12a3e7SDag-Erling Smørgrav } 423af12a3e7SDag-Erling Smørgrav } 424af12a3e7SDag-Erling Smørgrav } 425af12a3e7SDag-Erling Smørgrav return 1; 426af12a3e7SDag-Erling Smørgrav } 427af12a3e7SDag-Erling Smørgrav 428af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 429af12a3e7SDag-Erling Smørgrav 430af12a3e7SDag-Erling Smørgrav int 431af12a3e7SDag-Erling Smørgrav channel_still_open(void) 432af12a3e7SDag-Erling Smørgrav { 433af12a3e7SDag-Erling Smørgrav int i; 434af12a3e7SDag-Erling Smørgrav Channel *c; 435af12a3e7SDag-Erling Smørgrav 436af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 437af12a3e7SDag-Erling Smørgrav c = channels[i]; 438af12a3e7SDag-Erling Smørgrav if (c == NULL) 439af12a3e7SDag-Erling Smørgrav continue; 440af12a3e7SDag-Erling Smørgrav switch (c->type) { 441af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 442af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 443af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 444af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 445af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 446af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 447af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 448af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 449af12a3e7SDag-Erling Smørgrav continue; 450af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 451af12a3e7SDag-Erling Smørgrav if (!compat20) 452af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 453af12a3e7SDag-Erling Smørgrav continue; 454af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 455af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 456af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 457af12a3e7SDag-Erling Smørgrav return 1; 458af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 459af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 460af12a3e7SDag-Erling Smørgrav if (!compat13) 461af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 462af12a3e7SDag-Erling Smørgrav return 1; 463af12a3e7SDag-Erling Smørgrav default: 464af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 465af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 466af12a3e7SDag-Erling Smørgrav } 467af12a3e7SDag-Erling Smørgrav } 468af12a3e7SDag-Erling Smørgrav return 0; 469af12a3e7SDag-Erling Smørgrav } 470af12a3e7SDag-Erling Smørgrav 471af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 472af12a3e7SDag-Erling Smørgrav 473af12a3e7SDag-Erling Smørgrav int 474af12a3e7SDag-Erling Smørgrav channel_find_open(void) 475af12a3e7SDag-Erling Smørgrav { 476af12a3e7SDag-Erling Smørgrav int i; 477af12a3e7SDag-Erling Smørgrav Channel *c; 478af12a3e7SDag-Erling Smørgrav 479af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 480af12a3e7SDag-Erling Smørgrav c = channels[i]; 481af12a3e7SDag-Erling Smørgrav if (c == NULL) 482af12a3e7SDag-Erling Smørgrav continue; 483af12a3e7SDag-Erling Smørgrav switch (c->type) { 484af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 485af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 486af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 487af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 488af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 489af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 490af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 491af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 492af12a3e7SDag-Erling Smørgrav continue; 493af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 494af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 495af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 496af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 497af12a3e7SDag-Erling Smørgrav return i; 498af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 499af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 500af12a3e7SDag-Erling Smørgrav if (!compat13) 501af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 502af12a3e7SDag-Erling Smørgrav return i; 503af12a3e7SDag-Erling Smørgrav default: 504af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 505af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 506af12a3e7SDag-Erling Smørgrav } 507af12a3e7SDag-Erling Smørgrav } 508af12a3e7SDag-Erling Smørgrav return -1; 509af12a3e7SDag-Erling Smørgrav } 510af12a3e7SDag-Erling Smørgrav 511af12a3e7SDag-Erling Smørgrav 512af12a3e7SDag-Erling Smørgrav /* 513af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 514af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 515af12a3e7SDag-Erling Smørgrav * newlines. 516af12a3e7SDag-Erling Smørgrav */ 517af12a3e7SDag-Erling Smørgrav 518af12a3e7SDag-Erling Smørgrav char * 519af12a3e7SDag-Erling Smørgrav channel_open_message(void) 520af12a3e7SDag-Erling Smørgrav { 521af12a3e7SDag-Erling Smørgrav Buffer buffer; 522af12a3e7SDag-Erling Smørgrav Channel *c; 523af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 524af12a3e7SDag-Erling Smørgrav int i; 525af12a3e7SDag-Erling Smørgrav 526af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 527af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 528af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 529af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 530af12a3e7SDag-Erling Smørgrav c = channels[i]; 531af12a3e7SDag-Erling Smørgrav if (c == NULL) 532af12a3e7SDag-Erling Smørgrav continue; 533af12a3e7SDag-Erling Smørgrav switch (c->type) { 534af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 535af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 536af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 537af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 538af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 539af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 540af12a3e7SDag-Erling Smørgrav continue; 541af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 542af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 543af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 544af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 545af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 546af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 547af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 548af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 549af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 550af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 551af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 552af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 553af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 554af12a3e7SDag-Erling Smørgrav c->rfd, c->wfd); 555af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 556af12a3e7SDag-Erling Smørgrav continue; 557af12a3e7SDag-Erling Smørgrav default: 558af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 559af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 560af12a3e7SDag-Erling Smørgrav } 561af12a3e7SDag-Erling Smørgrav } 562af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 563af12a3e7SDag-Erling Smørgrav cp = xstrdup(buffer_ptr(&buffer)); 564af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 565af12a3e7SDag-Erling Smørgrav return cp; 566af12a3e7SDag-Erling Smørgrav } 567af12a3e7SDag-Erling Smørgrav 568af12a3e7SDag-Erling Smørgrav void 569af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 570af12a3e7SDag-Erling Smørgrav { 571af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 572af12a3e7SDag-Erling Smørgrav if (c == NULL) { 573af12a3e7SDag-Erling Smørgrav log("channel_send_open: %d: bad id", id); 574af12a3e7SDag-Erling Smørgrav return; 575af12a3e7SDag-Erling Smørgrav } 576af12a3e7SDag-Erling Smørgrav debug("send channel open %d", id); 577af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 578af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 579af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 580af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 581af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 582af12a3e7SDag-Erling Smørgrav packet_send(); 583af12a3e7SDag-Erling Smørgrav } 584af12a3e7SDag-Erling Smørgrav 585af12a3e7SDag-Erling Smørgrav void 586af12a3e7SDag-Erling Smørgrav channel_request_start(int local_id, char *service, int wantconfirm) 587af12a3e7SDag-Erling Smørgrav { 588af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(local_id); 589af12a3e7SDag-Erling Smørgrav if (c == NULL) { 590af12a3e7SDag-Erling Smørgrav log("channel_request_start: %d: unknown channel id", local_id); 591af12a3e7SDag-Erling Smørgrav return; 592af12a3e7SDag-Erling Smørgrav } 593af12a3e7SDag-Erling Smørgrav debug("channel request %d: %s", local_id, service) ; 594af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 595af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 596af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 597af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 598af12a3e7SDag-Erling Smørgrav } 599af12a3e7SDag-Erling Smørgrav void 600af12a3e7SDag-Erling Smørgrav channel_register_confirm(int id, channel_callback_fn *fn) 601af12a3e7SDag-Erling Smørgrav { 602af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 603af12a3e7SDag-Erling Smørgrav if (c == NULL) { 604af12a3e7SDag-Erling Smørgrav log("channel_register_comfirm: %d: bad id", id); 605af12a3e7SDag-Erling Smørgrav return; 606af12a3e7SDag-Erling Smørgrav } 607af12a3e7SDag-Erling Smørgrav c->confirm = fn; 608af12a3e7SDag-Erling Smørgrav } 609af12a3e7SDag-Erling Smørgrav void 610af12a3e7SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn) 611af12a3e7SDag-Erling Smørgrav { 612af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 613af12a3e7SDag-Erling Smørgrav if (c == NULL) { 614af12a3e7SDag-Erling Smørgrav log("channel_register_cleanup: %d: bad id", id); 615af12a3e7SDag-Erling Smørgrav return; 616af12a3e7SDag-Erling Smørgrav } 617af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 618af12a3e7SDag-Erling Smørgrav } 619af12a3e7SDag-Erling Smørgrav void 620af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 621af12a3e7SDag-Erling Smørgrav { 622af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 623af12a3e7SDag-Erling Smørgrav if (c == NULL) { 624af12a3e7SDag-Erling Smørgrav log("channel_cancel_cleanup: %d: bad id", id); 625af12a3e7SDag-Erling Smørgrav return; 626af12a3e7SDag-Erling Smørgrav } 627af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 628af12a3e7SDag-Erling Smørgrav } 629af12a3e7SDag-Erling Smørgrav void 630af12a3e7SDag-Erling Smørgrav channel_register_filter(int id, channel_filter_fn *fn) 631af12a3e7SDag-Erling Smørgrav { 632af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 633af12a3e7SDag-Erling Smørgrav if (c == NULL) { 634af12a3e7SDag-Erling Smørgrav log("channel_register_filter: %d: bad id", id); 635af12a3e7SDag-Erling Smørgrav return; 636af12a3e7SDag-Erling Smørgrav } 637af12a3e7SDag-Erling Smørgrav c->input_filter = fn; 638af12a3e7SDag-Erling Smørgrav } 639af12a3e7SDag-Erling Smørgrav 640af12a3e7SDag-Erling Smørgrav void 641af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 642af12a3e7SDag-Erling Smørgrav int extusage, int nonblock, u_int window_max) 643af12a3e7SDag-Erling Smørgrav { 644af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 645af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 646af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 647af12a3e7SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 648af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 649af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 650af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 651af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 652af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 653af12a3e7SDag-Erling Smørgrav packet_send(); 654511b41d2SMark Murray } 655511b41d2SMark Murray 656511b41d2SMark Murray /* 657a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 658a04a10f8SKris Kennaway * channels in the select bitmasks. 659511b41d2SMark Murray */ 660a04a10f8SKris Kennaway /* 661a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 662a04a10f8SKris Kennaway * have events pending. 663a04a10f8SKris Kennaway */ 664a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 665a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 666a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 667511b41d2SMark Murray 668af12a3e7SDag-Erling Smørgrav static void 669a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 670511b41d2SMark Murray { 671a04a10f8SKris Kennaway FD_SET(c->sock, readset); 672a04a10f8SKris Kennaway } 673a04a10f8SKris Kennaway 674af12a3e7SDag-Erling Smørgrav static void 675ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) 676ca3176e7SBrian Feldman { 677ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 678ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 679ca3176e7SBrian Feldman } 680ca3176e7SBrian Feldman 681af12a3e7SDag-Erling Smørgrav static void 682a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 683a04a10f8SKris Kennaway { 684a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 685a04a10f8SKris Kennaway FD_SET(c->sock, readset); 686a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 687a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 688a04a10f8SKris Kennaway } 689a04a10f8SKris Kennaway 690af12a3e7SDag-Erling Smørgrav static void 691af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) 692a04a10f8SKris Kennaway { 693af12a3e7SDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 694a04a10f8SKris Kennaway 695a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 696af12a3e7SDag-Erling Smørgrav limit > 0 && 697af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) < limit) 698a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 699a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 700a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 701a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 702a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 703a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 70480628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 70580628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 70680628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 70780628bacSDag-Erling Smørgrav else 708a04a10f8SKris Kennaway chan_obuf_empty(c); 709a04a10f8SKris Kennaway } 710a04a10f8SKris Kennaway } 711a04a10f8SKris Kennaway /** XXX check close conditions, too */ 712af12a3e7SDag-Erling Smørgrav if (compat20 && c->efd != -1) { 713a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 714a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 715a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 71680628bacSDag-Erling Smørgrav else if (!(c->flags & CHAN_EOF_SENT) && 71780628bacSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ && 718a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 719a04a10f8SKris Kennaway FD_SET(c->efd, readset); 720a04a10f8SKris Kennaway } 721a04a10f8SKris Kennaway } 722a04a10f8SKris Kennaway 723af12a3e7SDag-Erling Smørgrav static void 724a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 725a04a10f8SKris Kennaway { 726a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 727a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 728a04a10f8SKris Kennaway packet_put_int(c->remote_id); 729a04a10f8SKris Kennaway packet_send(); 730a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 731ca3176e7SBrian Feldman debug("channel %d: closing after input drain.", c->self); 732a04a10f8SKris Kennaway } 733a04a10f8SKris Kennaway } 734a04a10f8SKris Kennaway 735af12a3e7SDag-Erling Smørgrav static void 736a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 737a04a10f8SKris Kennaway { 738a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 739af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 740a04a10f8SKris Kennaway else 741a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 742a04a10f8SKris Kennaway } 743a04a10f8SKris Kennaway 744a04a10f8SKris Kennaway /* 745a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 746a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 747a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 748a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 749a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 750a04a10f8SKris Kennaway * XXX All this happens at the client side. 751af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 752a04a10f8SKris Kennaway */ 753af12a3e7SDag-Erling Smørgrav static int 754af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 755a04a10f8SKris Kennaway { 756ca3176e7SBrian Feldman u_char *ucp; 757ca3176e7SBrian Feldman u_int proto_len, data_len; 758511b41d2SMark Murray 759511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 760af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 761a04a10f8SKris Kennaway return 0; 762511b41d2SMark Murray 763511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 764af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 765511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 766511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 767511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 768511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 769511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 770511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 771511b41d2SMark Murray } else { 772511b41d2SMark Murray debug("Initial X11 packet contains bad byte order byte: 0x%x", 773511b41d2SMark Murray ucp[0]); 774a04a10f8SKris Kennaway return -1; 775511b41d2SMark Murray } 776511b41d2SMark Murray 777511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 778af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 779511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 780a04a10f8SKris Kennaway return 0; 781511b41d2SMark Murray 782511b41d2SMark Murray /* Check if authentication protocol matches. */ 783511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 784511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 785511b41d2SMark Murray debug("X11 connection uses different authentication protocol."); 786a04a10f8SKris Kennaway return -1; 787511b41d2SMark Murray } 788511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 789511b41d2SMark Murray if (data_len != x11_fake_data_len || 790511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 791511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 792511b41d2SMark Murray debug("X11 auth data does not match fake data."); 793a04a10f8SKris Kennaway return -1; 794511b41d2SMark Murray } 795511b41d2SMark Murray /* Check fake data length */ 796511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 797511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 798511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 799a04a10f8SKris Kennaway return -1; 800511b41d2SMark Murray } 801511b41d2SMark Murray /* 802511b41d2SMark Murray * Received authentication protocol and data match 803511b41d2SMark Murray * our fake data. Substitute the fake data with real 804511b41d2SMark Murray * data. 805511b41d2SMark Murray */ 806511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 807511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 808a04a10f8SKris Kennaway return 1; 809a04a10f8SKris Kennaway } 810511b41d2SMark Murray 811af12a3e7SDag-Erling Smørgrav static void 812a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 813a04a10f8SKris Kennaway { 814af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 815a04a10f8SKris Kennaway if (ret == 1) { 816511b41d2SMark Murray /* Start normal processing for the channel. */ 817a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 818a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 819a04a10f8SKris Kennaway } else if (ret == -1) { 820511b41d2SMark Murray /* 821511b41d2SMark Murray * We have received an X11 connection that has bad 822511b41d2SMark Murray * authentication information. 823511b41d2SMark Murray */ 824ca3176e7SBrian Feldman log("X11 connection rejected because of wrong authentication."); 825a04a10f8SKris Kennaway buffer_clear(&c->input); 826a04a10f8SKris Kennaway buffer_clear(&c->output); 827af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 828a04a10f8SKris Kennaway c->sock = -1; 829a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 830511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 831a04a10f8SKris Kennaway packet_put_int(c->remote_id); 832511b41d2SMark Murray packet_send(); 833511b41d2SMark Murray } 834511b41d2SMark Murray } 835511b41d2SMark Murray 836af12a3e7SDag-Erling Smørgrav static void 837a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 838a04a10f8SKris Kennaway { 839af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 840af12a3e7SDag-Erling Smørgrav 841af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 842af12a3e7SDag-Erling Smørgrav 843a04a10f8SKris Kennaway if (ret == 1) { 844a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 845af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 846a04a10f8SKris Kennaway } else if (ret == -1) { 847af12a3e7SDag-Erling Smørgrav log("X11 connection rejected because of wrong authentication."); 848a04a10f8SKris Kennaway debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 849af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 850af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 851af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 852af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 853af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 854af12a3e7SDag-Erling Smørgrav if (compat20) 855a04a10f8SKris Kennaway chan_write_failed(c); 856af12a3e7SDag-Erling Smørgrav else 857af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 858a04a10f8SKris Kennaway debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 859a04a10f8SKris Kennaway } 860a04a10f8SKris Kennaway } 861a04a10f8SKris Kennaway 862ca3176e7SBrian Feldman /* try to decode a socks4 header */ 863af12a3e7SDag-Erling Smørgrav static int 864ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) 865ca3176e7SBrian Feldman { 866ca3176e7SBrian Feldman u_char *p, *host; 867ca3176e7SBrian Feldman int len, have, i, found; 868ca3176e7SBrian Feldman char username[256]; 869ca3176e7SBrian Feldman struct { 870ca3176e7SBrian Feldman u_int8_t version; 871ca3176e7SBrian Feldman u_int8_t command; 872ca3176e7SBrian Feldman u_int16_t dest_port; 873ca3176e7SBrian Feldman struct in_addr dest_addr; 874ca3176e7SBrian Feldman } s4_req, s4_rsp; 875ca3176e7SBrian Feldman 876ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 877ca3176e7SBrian Feldman 878ca3176e7SBrian Feldman have = buffer_len(&c->input); 879ca3176e7SBrian Feldman len = sizeof(s4_req); 880ca3176e7SBrian Feldman if (have < len) 881ca3176e7SBrian Feldman return 0; 882ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 883ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 884ca3176e7SBrian Feldman if (p[i] == '\0') { 885ca3176e7SBrian Feldman found = 1; 886ca3176e7SBrian Feldman break; 887ca3176e7SBrian Feldman } 888ca3176e7SBrian Feldman if (i > 1024) { 889ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 890ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 891ca3176e7SBrian Feldman c->self); 892ca3176e7SBrian Feldman return -1; 893ca3176e7SBrian Feldman } 894ca3176e7SBrian Feldman } 895ca3176e7SBrian Feldman if (!found) 896ca3176e7SBrian Feldman return 0; 897ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 898ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 899ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 900ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 901ca3176e7SBrian Feldman have = buffer_len(&c->input); 902ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 903ca3176e7SBrian Feldman len = strlen(p); 904ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 905ca3176e7SBrian Feldman if (len > have) 906ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 907ca3176e7SBrian Feldman c->self, len, have); 908ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 909ca3176e7SBrian Feldman buffer_consume(&c->input, len); 910ca3176e7SBrian Feldman buffer_consume(&c->input, 1); /* trailing '\0' */ 911ca3176e7SBrian Feldman 912ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 913ca3176e7SBrian Feldman strlcpy(c->path, host, sizeof(c->path)); 914ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 915ca3176e7SBrian Feldman 916ca3176e7SBrian Feldman debug("channel %d: dynamic request: socks4 host %s port %u command %u", 917ca3176e7SBrian Feldman c->self, host, c->host_port, s4_req.command); 918ca3176e7SBrian Feldman 919ca3176e7SBrian Feldman if (s4_req.command != 1) { 920ca3176e7SBrian Feldman debug("channel %d: cannot handle: socks4 cn %d", 921ca3176e7SBrian Feldman c->self, s4_req.command); 922ca3176e7SBrian Feldman return -1; 923ca3176e7SBrian Feldman } 924ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 925ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 926ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 927ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 928ca3176e7SBrian Feldman buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); 929ca3176e7SBrian Feldman return 1; 930ca3176e7SBrian Feldman } 931ca3176e7SBrian Feldman 932ca3176e7SBrian Feldman /* dynamic port forwarding */ 933af12a3e7SDag-Erling Smørgrav static void 934ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 935ca3176e7SBrian Feldman { 936ca3176e7SBrian Feldman u_char *p; 937ca3176e7SBrian Feldman int have, ret; 938ca3176e7SBrian Feldman 939ca3176e7SBrian Feldman have = buffer_len(&c->input); 940af12a3e7SDag-Erling Smørgrav c->delayed = 0; 941ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 942ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 943ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 944ca3176e7SBrian Feldman if (have < 4) { 945ca3176e7SBrian Feldman /* need more */ 946ca3176e7SBrian Feldman FD_SET(c->sock, readset); 947ca3176e7SBrian Feldman return; 948ca3176e7SBrian Feldman } 949ca3176e7SBrian Feldman /* try to guess the protocol */ 950ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 951ca3176e7SBrian Feldman switch (p[0]) { 952ca3176e7SBrian Feldman case 0x04: 953ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 954ca3176e7SBrian Feldman break; 955ca3176e7SBrian Feldman default: 956ca3176e7SBrian Feldman ret = -1; 957ca3176e7SBrian Feldman break; 958ca3176e7SBrian Feldman } 959ca3176e7SBrian Feldman if (ret < 0) { 960af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 961ca3176e7SBrian Feldman } else if (ret == 0) { 962ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 963ca3176e7SBrian Feldman /* need more */ 964ca3176e7SBrian Feldman FD_SET(c->sock, readset); 965ca3176e7SBrian Feldman } else { 966ca3176e7SBrian Feldman /* switch to the next state */ 967ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 968ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 969ca3176e7SBrian Feldman } 970ca3176e7SBrian Feldman } 971ca3176e7SBrian Feldman 972a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 973af12a3e7SDag-Erling Smørgrav static void 974a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 975511b41d2SMark Murray { 976af12a3e7SDag-Erling Smørgrav Channel *nc; 977511b41d2SMark Murray struct sockaddr addr; 978af12a3e7SDag-Erling Smørgrav int newsock; 979511b41d2SMark Murray socklen_t addrlen; 980ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 981a04a10f8SKris Kennaway int remote_port; 982511b41d2SMark Murray 983a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 984511b41d2SMark Murray debug("X11 connection requested."); 985511b41d2SMark Murray addrlen = sizeof(addr); 986a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 987af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 988af12a3e7SDag-Erling Smørgrav debug("single_connection: closing X11 listener."); 989af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 990af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 991af12a3e7SDag-Erling Smørgrav } 992511b41d2SMark Murray if (newsock < 0) { 993511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 994a04a10f8SKris Kennaway return; 995511b41d2SMark Murray } 996af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 997ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 998a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 999511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1000ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1001a04a10f8SKris Kennaway 1002af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1003a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1004a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 10055b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 1006a04a10f8SKris Kennaway if (compat20) { 1007a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1008a04a10f8SKris Kennaway packet_put_cstring("x11"); 1009af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1010af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1011af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1012ca3176e7SBrian Feldman /* originator ipaddr and port */ 1013ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1014a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1015a04a10f8SKris Kennaway debug("ssh2 x11 bug compat mode"); 1016a04a10f8SKris Kennaway } else { 1017a04a10f8SKris Kennaway packet_put_int(remote_port); 1018a04a10f8SKris Kennaway } 1019a04a10f8SKris Kennaway packet_send(); 1020a04a10f8SKris Kennaway } else { 1021511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1022af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1023af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1024af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1025af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1026511b41d2SMark Murray packet_send(); 1027511b41d2SMark Murray } 1028ca3176e7SBrian Feldman xfree(remote_ipaddr); 1029a04a10f8SKris Kennaway } 1030a04a10f8SKris Kennaway } 1031511b41d2SMark Murray 1032af12a3e7SDag-Erling Smørgrav static void 1033ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1034ca3176e7SBrian Feldman { 1035ca3176e7SBrian Feldman int direct; 1036ca3176e7SBrian Feldman char buf[1024]; 1037ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1038ca3176e7SBrian Feldman u_short remote_port = get_peer_port(c->sock); 1039ca3176e7SBrian Feldman 1040ca3176e7SBrian Feldman direct = (strcmp(rtype, "direct-tcpip") == 0); 1041ca3176e7SBrian Feldman 1042ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1043ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1044ca3176e7SBrian Feldman "connect from %.200s port %d", 1045ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1046ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1047ca3176e7SBrian Feldman 1048ca3176e7SBrian Feldman xfree(c->remote_name); 1049ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1050ca3176e7SBrian Feldman 1051ca3176e7SBrian Feldman if (compat20) { 1052ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1053ca3176e7SBrian Feldman packet_put_cstring(rtype); 1054ca3176e7SBrian Feldman packet_put_int(c->self); 1055ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1056ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1057ca3176e7SBrian Feldman if (direct) { 1058ca3176e7SBrian Feldman /* target host, port */ 1059ca3176e7SBrian Feldman packet_put_cstring(c->path); 1060ca3176e7SBrian Feldman packet_put_int(c->host_port); 1061ca3176e7SBrian Feldman } else { 1062ca3176e7SBrian Feldman /* listen address, port */ 1063ca3176e7SBrian Feldman packet_put_cstring(c->path); 1064ca3176e7SBrian Feldman packet_put_int(c->listening_port); 1065ca3176e7SBrian Feldman } 1066ca3176e7SBrian Feldman /* originator host and port */ 1067ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1068ca3176e7SBrian Feldman packet_put_int(remote_port); 1069ca3176e7SBrian Feldman packet_send(); 1070ca3176e7SBrian Feldman } else { 1071ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1072ca3176e7SBrian Feldman packet_put_int(c->self); 1073ca3176e7SBrian Feldman packet_put_cstring(c->path); 1074ca3176e7SBrian Feldman packet_put_int(c->host_port); 1075af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1076af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1077ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1078ca3176e7SBrian Feldman packet_send(); 1079ca3176e7SBrian Feldman } 1080ca3176e7SBrian Feldman xfree(remote_ipaddr); 1081ca3176e7SBrian Feldman } 1082ca3176e7SBrian Feldman 1083511b41d2SMark Murray /* 1084a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1085511b41d2SMark Murray */ 1086af12a3e7SDag-Erling Smørgrav static void 1087a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 1088a04a10f8SKris Kennaway { 1089ca3176e7SBrian Feldman Channel *nc; 1090a04a10f8SKris Kennaway struct sockaddr addr; 1091af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1092a04a10f8SKris Kennaway socklen_t addrlen; 1093ca3176e7SBrian Feldman char *rtype; 1094a04a10f8SKris Kennaway 1095a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1096a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1097a04a10f8SKris Kennaway "to %.100s port %d requested.", 1098a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1099ca3176e7SBrian Feldman 1100af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1101af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1102af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1103af12a3e7SDag-Erling Smørgrav } else { 1104af12a3e7SDag-Erling Smørgrav if (c->host_port == 0) { 1105af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1106af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1107af12a3e7SDag-Erling Smørgrav } else { 1108af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1109af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1110af12a3e7SDag-Erling Smørgrav } 1111af12a3e7SDag-Erling Smørgrav } 1112ca3176e7SBrian Feldman 1113511b41d2SMark Murray addrlen = sizeof(addr); 1114a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1115511b41d2SMark Murray if (newsock < 0) { 1116511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1117a04a10f8SKris Kennaway return; 1118511b41d2SMark Murray } 1119af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1120af12a3e7SDag-Erling Smørgrav nc = channel_new(rtype, 1121ca3176e7SBrian Feldman nextstate, newsock, newsock, -1, 1122a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 1123ca3176e7SBrian Feldman 0, xstrdup(rtype), 1); 1124ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1125ca3176e7SBrian Feldman nc->host_port = c->host_port; 1126ca3176e7SBrian Feldman strlcpy(nc->path, c->path, sizeof(nc->path)); 1127ca3176e7SBrian Feldman 1128af12a3e7SDag-Erling Smørgrav if (nextstate == SSH_CHANNEL_DYNAMIC) { 1129af12a3e7SDag-Erling Smørgrav /* 1130af12a3e7SDag-Erling Smørgrav * do not call the channel_post handler until 1131af12a3e7SDag-Erling Smørgrav * this flag has been reset by a pre-handler. 1132af12a3e7SDag-Erling Smørgrav * otherwise the FD_ISSET calls might overflow 1133af12a3e7SDag-Erling Smørgrav */ 1134af12a3e7SDag-Erling Smørgrav nc->delayed = 1; 1135af12a3e7SDag-Erling Smørgrav } else { 1136ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1137a04a10f8SKris Kennaway } 1138a04a10f8SKris Kennaway } 1139af12a3e7SDag-Erling Smørgrav } 1140511b41d2SMark Murray 1141511b41d2SMark Murray /* 1142a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1143a04a10f8SKris Kennaway * clients. 1144511b41d2SMark Murray */ 1145af12a3e7SDag-Erling Smørgrav static void 1146a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 1147a04a10f8SKris Kennaway { 1148af12a3e7SDag-Erling Smørgrav Channel *nc; 1149af12a3e7SDag-Erling Smørgrav char *name; 1150af12a3e7SDag-Erling Smørgrav int newsock; 1151a04a10f8SKris Kennaway struct sockaddr addr; 1152a04a10f8SKris Kennaway socklen_t addrlen; 1153a04a10f8SKris Kennaway 1154a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1155511b41d2SMark Murray addrlen = sizeof(addr); 1156a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1157511b41d2SMark Murray if (newsock < 0) { 1158511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 1159a04a10f8SKris Kennaway return; 1160511b41d2SMark Murray } 1161af12a3e7SDag-Erling Smørgrav name = xstrdup("accepted auth socket"); 1162af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1163ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1164ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1165af12a3e7SDag-Erling Smørgrav 0, name, 1); 1166ca3176e7SBrian Feldman if (compat20) { 1167ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1168ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1169af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1170ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1171ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1172ca3176e7SBrian Feldman } else { 1173511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1174af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1175ca3176e7SBrian Feldman } 1176511b41d2SMark Murray packet_send(); 1177511b41d2SMark Murray } 1178a04a10f8SKris Kennaway } 1179511b41d2SMark Murray 1180af12a3e7SDag-Erling Smørgrav static void 1181ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 1182ca3176e7SBrian Feldman { 1183ca3176e7SBrian Feldman int err = 0; 1184af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1185af12a3e7SDag-Erling Smørgrav 1186af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1187af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1188af12a3e7SDag-Erling Smørgrav err = errno; 1189af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1190af12a3e7SDag-Erling Smørgrav } 1191ca3176e7SBrian Feldman if (err == 0) { 1192af12a3e7SDag-Erling Smørgrav debug("channel %d: connected", c->self); 1193af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1194af12a3e7SDag-Erling Smørgrav if (compat20) { 1195af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1196af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1197af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1198af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1199af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1200af12a3e7SDag-Erling Smørgrav } else { 1201af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1202af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1203af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1204af12a3e7SDag-Erling Smørgrav } 1205ca3176e7SBrian Feldman } else { 1206ca3176e7SBrian Feldman debug("channel %d: not connected: %s", 1207ca3176e7SBrian Feldman c->self, strerror(err)); 1208af12a3e7SDag-Erling Smørgrav if (compat20) { 1209af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1210af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1211af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1212af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1213af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1214af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1215ca3176e7SBrian Feldman } 1216af12a3e7SDag-Erling Smørgrav } else { 1217af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1218af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1219ca3176e7SBrian Feldman } 1220af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1221af12a3e7SDag-Erling Smørgrav } 1222af12a3e7SDag-Erling Smørgrav packet_send(); 1223ca3176e7SBrian Feldman } 1224ca3176e7SBrian Feldman } 1225ca3176e7SBrian Feldman 1226af12a3e7SDag-Erling Smørgrav static int 1227a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 1228a04a10f8SKris Kennaway { 1229a04a10f8SKris Kennaway char buf[16*1024]; 1230a04a10f8SKris Kennaway int len; 1231511b41d2SMark Murray 1232a04a10f8SKris Kennaway if (c->rfd != -1 && 1233a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 1234a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1235a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1236a04a10f8SKris Kennaway return 1; 12370c82706bSBrian Feldman if (len <= 0) { 12380c82706bSBrian Feldman debug("channel %d: read<=0 rfd %d len %d", 1239a04a10f8SKris Kennaway c->self, c->rfd, len); 1240ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1241ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1242af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1243ca3176e7SBrian Feldman return -1; 1244ca3176e7SBrian Feldman } else if (compat13) { 1245af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1246a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1247af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1248a04a10f8SKris Kennaway } else { 1249a04a10f8SKris Kennaway chan_read_failed(c); 1250a04a10f8SKris Kennaway } 1251a04a10f8SKris Kennaway return -1; 1252a04a10f8SKris Kennaway } 1253b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1254b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1255ca3176e7SBrian Feldman debug("channel %d: filter stops", c->self); 1256b66f2d16SKris Kennaway chan_read_failed(c); 1257b66f2d16SKris Kennaway } 1258b66f2d16SKris Kennaway } else { 1259a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1260a04a10f8SKris Kennaway } 1261b66f2d16SKris Kennaway } 1262a04a10f8SKris Kennaway return 1; 1263a04a10f8SKris Kennaway } 1264af12a3e7SDag-Erling Smørgrav static int 1265a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 1266a04a10f8SKris Kennaway { 1267ca3176e7SBrian Feldman struct termios tio; 1268af12a3e7SDag-Erling Smørgrav u_char *data; 1269af12a3e7SDag-Erling Smørgrav u_int dlen; 1270a04a10f8SKris Kennaway int len; 1271a04a10f8SKris Kennaway 1272a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1273a04a10f8SKris Kennaway if (c->wfd != -1 && 1274a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1275a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1276af12a3e7SDag-Erling Smørgrav data = buffer_ptr(&c->output); 1277af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1278af12a3e7SDag-Erling Smørgrav len = write(c->wfd, data, dlen); 1279a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1280a04a10f8SKris Kennaway return 1; 1281511b41d2SMark Murray if (len <= 0) { 1282ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1283ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1284af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1285ca3176e7SBrian Feldman return -1; 1286ca3176e7SBrian Feldman } else if (compat13) { 1287af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1288af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1289a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1290511b41d2SMark Murray } else { 1291a04a10f8SKris Kennaway chan_write_failed(c); 1292511b41d2SMark Murray } 1293a04a10f8SKris Kennaway return -1; 1294511b41d2SMark Murray } 1295af12a3e7SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { 1296e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1297e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1298e0fbb1d2SBrian Feldman /* 1299e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1300ca3176e7SBrian Feldman * traffic analysis. We need to match the 1301ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1302ca3176e7SBrian Feldman * (4 byte channel id + data) 1303e0fbb1d2SBrian Feldman */ 1304ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1305e0fbb1d2SBrian Feldman packet_send(); 1306e0fbb1d2SBrian Feldman } 1307e0fbb1d2SBrian Feldman } 1308a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1309a04a10f8SKris Kennaway if (compat20 && len > 0) { 1310a04a10f8SKris Kennaway c->local_consumed += len; 1311511b41d2SMark Murray } 1312511b41d2SMark Murray } 1313a04a10f8SKris Kennaway return 1; 1314511b41d2SMark Murray } 1315af12a3e7SDag-Erling Smørgrav static int 1316a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 1317a04a10f8SKris Kennaway { 1318a04a10f8SKris Kennaway char buf[16*1024]; 1319a04a10f8SKris Kennaway int len; 1320511b41d2SMark Murray 1321a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1322a04a10f8SKris Kennaway if (c->efd != -1) { 1323a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1324a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1325a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1326a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1327a04a10f8SKris Kennaway buffer_len(&c->extended)); 13285b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1329a04a10f8SKris Kennaway c->self, len, c->efd); 1330ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1331ca3176e7SBrian Feldman return 1; 1332ca3176e7SBrian Feldman if (len <= 0) { 1333ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1334ca3176e7SBrian Feldman c->self, c->efd); 1335af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1336ca3176e7SBrian Feldman } else { 1337a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1338a04a10f8SKris Kennaway c->local_consumed += len; 1339a04a10f8SKris Kennaway } 1340a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 1341a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 1342a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 13435b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1344a04a10f8SKris Kennaway c->self, len, c->efd); 1345ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1346ca3176e7SBrian Feldman return 1; 1347ca3176e7SBrian Feldman if (len <= 0) { 1348ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1349a04a10f8SKris Kennaway c->self, c->efd); 1350af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1351ca3176e7SBrian Feldman } else { 1352a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1353a04a10f8SKris Kennaway } 1354a04a10f8SKris Kennaway } 1355ca3176e7SBrian Feldman } 1356a04a10f8SKris Kennaway return 1; 1357a04a10f8SKris Kennaway } 1358af12a3e7SDag-Erling Smørgrav static int 1359ca3176e7SBrian Feldman channel_check_window(Channel *c) 1360a04a10f8SKris Kennaway { 1361ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1362ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1363a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 1364a04a10f8SKris Kennaway c->local_consumed > 0) { 1365a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1366a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1367a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 1368a04a10f8SKris Kennaway packet_send(); 13695b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1370a04a10f8SKris Kennaway c->self, c->local_window, 1371a04a10f8SKris Kennaway c->local_consumed); 1372a04a10f8SKris Kennaway c->local_window += c->local_consumed; 1373a04a10f8SKris Kennaway c->local_consumed = 0; 1374a04a10f8SKris Kennaway } 1375a04a10f8SKris Kennaway return 1; 1376a04a10f8SKris Kennaway } 1377a04a10f8SKris Kennaway 1378af12a3e7SDag-Erling Smørgrav static void 1379af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 1380a04a10f8SKris Kennaway { 1381af12a3e7SDag-Erling Smørgrav if (c->delayed) 1382af12a3e7SDag-Erling Smørgrav return; 1383a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1384a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1385af12a3e7SDag-Erling Smørgrav if (!compat20) 1386af12a3e7SDag-Erling Smørgrav return; 1387a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1388ca3176e7SBrian Feldman channel_check_window(c); 1389a04a10f8SKris Kennaway } 1390a04a10f8SKris Kennaway 1391af12a3e7SDag-Erling Smørgrav static void 1392a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 1393a04a10f8SKris Kennaway { 1394a04a10f8SKris Kennaway int len; 1395511b41d2SMark Murray /* Send buffered output data to the socket. */ 1396a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 1397a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 1398a04a10f8SKris Kennaway buffer_len(&c->output)); 1399511b41d2SMark Murray if (len <= 0) 1400af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1401511b41d2SMark Murray else 1402a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1403511b41d2SMark Murray } 1404a04a10f8SKris Kennaway } 1405511b41d2SMark Murray 1406af12a3e7SDag-Erling Smørgrav static void 1407a04a10f8SKris Kennaway channel_handler_init_20(void) 1408a04a10f8SKris Kennaway { 1409af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1410a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1411a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1412ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 1413a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1414ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1415ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1416ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1417a04a10f8SKris Kennaway 1418af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1419a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1420ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 1421a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1422ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1423ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1424af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1425a04a10f8SKris Kennaway } 1426a04a10f8SKris Kennaway 1427af12a3e7SDag-Erling Smørgrav static void 1428a04a10f8SKris Kennaway channel_handler_init_13(void) 1429a04a10f8SKris Kennaway { 1430a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 1431a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 1432a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1433a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1434a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1435a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 1436a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 1437ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1438ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1439a04a10f8SKris Kennaway 1440af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1441a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1442a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1443a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1444a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 1445ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1446af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1447a04a10f8SKris Kennaway } 1448a04a10f8SKris Kennaway 1449af12a3e7SDag-Erling Smørgrav static void 1450a04a10f8SKris Kennaway channel_handler_init_15(void) 1451a04a10f8SKris Kennaway { 1452af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1453a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1454a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1455a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1456a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1457ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1458ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1459a04a10f8SKris Kennaway 1460a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1461a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1462a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1463af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1464ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1465af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1466a04a10f8SKris Kennaway } 1467a04a10f8SKris Kennaway 1468af12a3e7SDag-Erling Smørgrav static void 1469a04a10f8SKris Kennaway channel_handler_init(void) 1470a04a10f8SKris Kennaway { 1471a04a10f8SKris Kennaway int i; 1472a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 1473a04a10f8SKris Kennaway channel_pre[i] = NULL; 1474a04a10f8SKris Kennaway channel_post[i] = NULL; 1475a04a10f8SKris Kennaway } 1476a04a10f8SKris Kennaway if (compat20) 1477a04a10f8SKris Kennaway channel_handler_init_20(); 1478a04a10f8SKris Kennaway else if (compat13) 1479a04a10f8SKris Kennaway channel_handler_init_13(); 1480a04a10f8SKris Kennaway else 1481a04a10f8SKris Kennaway channel_handler_init_15(); 1482a04a10f8SKris Kennaway } 1483a04a10f8SKris Kennaway 1484af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 1485af12a3e7SDag-Erling Smørgrav static void 1486af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 1487af12a3e7SDag-Erling Smørgrav { 1488af12a3e7SDag-Erling Smørgrav if (c == NULL) 1489af12a3e7SDag-Erling Smørgrav return; 1490af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 1491af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 0)) 1492af12a3e7SDag-Erling Smørgrav return; 1493af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: notify user", c->self); 1494af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 1495af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 1496af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 1497af12a3e7SDag-Erling Smørgrav return; 1498af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: user detached", c->self); 1499af12a3e7SDag-Erling Smørgrav } 1500af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 1501af12a3e7SDag-Erling Smørgrav return; 1502af12a3e7SDag-Erling Smørgrav debug("channel %d: garbage collecting", c->self); 1503af12a3e7SDag-Erling Smørgrav channel_free(c); 1504af12a3e7SDag-Erling Smørgrav } 1505af12a3e7SDag-Erling Smørgrav 1506af12a3e7SDag-Erling Smørgrav static void 1507a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 1508a04a10f8SKris Kennaway { 1509a04a10f8SKris Kennaway static int did_init = 0; 1510a04a10f8SKris Kennaway int i; 1511a04a10f8SKris Kennaway Channel *c; 1512a04a10f8SKris Kennaway 1513a04a10f8SKris Kennaway if (!did_init) { 1514a04a10f8SKris Kennaway channel_handler_init(); 1515a04a10f8SKris Kennaway did_init = 1; 1516a04a10f8SKris Kennaway } 1517a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 1518af12a3e7SDag-Erling Smørgrav c = channels[i]; 1519af12a3e7SDag-Erling Smørgrav if (c == NULL) 1520511b41d2SMark Murray continue; 1521af12a3e7SDag-Erling Smørgrav if (ftab[c->type] != NULL) 1522a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 1523af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 1524511b41d2SMark Murray } 1525511b41d2SMark Murray } 1526a04a10f8SKris Kennaway 1527af12a3e7SDag-Erling Smørgrav /* 1528af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 1529af12a3e7SDag-Erling Smørgrav * select bitmasks. 1530af12a3e7SDag-Erling Smørgrav */ 1531a04a10f8SKris Kennaway void 1532ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1533af12a3e7SDag-Erling Smørgrav int *nallocp, int rekeying) 1534a04a10f8SKris Kennaway { 1535ca3176e7SBrian Feldman int n; 1536ca3176e7SBrian Feldman u_int sz; 1537ca3176e7SBrian Feldman 1538ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 1539ca3176e7SBrian Feldman 1540ca3176e7SBrian Feldman sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 1541af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 1542af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 1543af12a3e7SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, sz); 1544af12a3e7SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, sz); 1545af12a3e7SDag-Erling Smørgrav *nallocp = sz; 1546ca3176e7SBrian Feldman } 1547af12a3e7SDag-Erling Smørgrav *maxfdp = n; 1548ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 1549ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 1550ca3176e7SBrian Feldman 1551ca3176e7SBrian Feldman if (!rekeying) 1552ca3176e7SBrian Feldman channel_handler(channel_pre, *readsetp, *writesetp); 1553a04a10f8SKris Kennaway } 1554a04a10f8SKris Kennaway 1555af12a3e7SDag-Erling Smørgrav /* 1556af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 1557af12a3e7SDag-Erling Smørgrav * events pending. 1558af12a3e7SDag-Erling Smørgrav */ 1559a04a10f8SKris Kennaway void 1560a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 1561a04a10f8SKris Kennaway { 1562a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 1563511b41d2SMark Murray } 1564511b41d2SMark Murray 1565af12a3e7SDag-Erling Smørgrav 1566ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 1567511b41d2SMark Murray 1568511b41d2SMark Murray void 1569af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 1570511b41d2SMark Murray { 1571511b41d2SMark Murray int len, i; 1572a04a10f8SKris Kennaway Channel *c; 1573511b41d2SMark Murray 1574511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1575af12a3e7SDag-Erling Smørgrav c = channels[i]; 1576af12a3e7SDag-Erling Smørgrav if (c == NULL) 1577af12a3e7SDag-Erling Smørgrav continue; 1578511b41d2SMark Murray 1579af12a3e7SDag-Erling Smørgrav /* 1580af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 1581af12a3e7SDag-Erling Smørgrav * incoming data. 1582af12a3e7SDag-Erling Smørgrav */ 1583511b41d2SMark Murray if (compat13) { 1584a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1585a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 1586511b41d2SMark Murray continue; 1587511b41d2SMark Murray } else { 1588a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 1589511b41d2SMark Murray continue; 1590a04a10f8SKris Kennaway } 1591a04a10f8SKris Kennaway if (compat20 && 1592a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 1593ca3176e7SBrian Feldman /* XXX is this true? */ 1594af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 1595511b41d2SMark Murray continue; 1596511b41d2SMark Murray } 1597511b41d2SMark Murray 1598511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 1599ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 1600ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 1601ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 1602af12a3e7SDag-Erling Smørgrav /* 1603af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 1604af12a3e7SDag-Erling Smørgrav * connection. 1605af12a3e7SDag-Erling Smørgrav */ 1606a04a10f8SKris Kennaway if (compat20) { 1607a04a10f8SKris Kennaway if (len > c->remote_window) 1608a04a10f8SKris Kennaway len = c->remote_window; 1609a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1610a04a10f8SKris Kennaway len = c->remote_maxpacket; 1611a04a10f8SKris Kennaway } else { 1612511b41d2SMark Murray if (packet_is_interactive()) { 1613511b41d2SMark Murray if (len > 1024) 1614511b41d2SMark Murray len = 512; 1615511b41d2SMark Murray } else { 1616511b41d2SMark Murray /* Keep the packets at reasonable size. */ 1617511b41d2SMark Murray if (len > packet_get_maxsize()/2) 1618511b41d2SMark Murray len = packet_get_maxsize()/2; 1619511b41d2SMark Murray } 1620a04a10f8SKris Kennaway } 1621a04a10f8SKris Kennaway if (len > 0) { 1622a04a10f8SKris Kennaway packet_start(compat20 ? 1623a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 1624a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1625a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 1626511b41d2SMark Murray packet_send(); 1627a04a10f8SKris Kennaway buffer_consume(&c->input, len); 1628a04a10f8SKris Kennaway c->remote_window -= len; 1629a04a10f8SKris Kennaway } 1630a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1631511b41d2SMark Murray if (compat13) 1632511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1633511b41d2SMark Murray /* 1634511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 163580628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 163680628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 1637511b41d2SMark Murray */ 163880628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 163980628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 164080628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 164180628bacSDag-Erling Smørgrav else 1642a04a10f8SKris Kennaway chan_ibuf_empty(c); 1643a04a10f8SKris Kennaway } 1644a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1645a04a10f8SKris Kennaway if (compat20 && 164680628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 1647a04a10f8SKris Kennaway c->remote_window > 0 && 1648a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1649a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1650ca3176e7SBrian Feldman debug2("channel %d: rwin %d elen %d euse %d", 1651ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 1652ca3176e7SBrian Feldman c->extended_usage); 1653a04a10f8SKris Kennaway if (len > c->remote_window) 1654a04a10f8SKris Kennaway len = c->remote_window; 1655a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1656a04a10f8SKris Kennaway len = c->remote_maxpacket; 1657a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1658a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1659a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1660a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1661a04a10f8SKris Kennaway packet_send(); 1662a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1663a04a10f8SKris Kennaway c->remote_window -= len; 1664ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 1665511b41d2SMark Murray } 1666511b41d2SMark Murray } 1667511b41d2SMark Murray } 1668511b41d2SMark Murray 1669af12a3e7SDag-Erling Smørgrav 1670af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 1671511b41d2SMark Murray 1672511b41d2SMark Murray void 1673af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 1674511b41d2SMark Murray { 1675511b41d2SMark Murray int id; 1676511b41d2SMark Murray char *data; 1677ca3176e7SBrian Feldman u_int data_len; 1678a04a10f8SKris Kennaway Channel *c; 1679511b41d2SMark Murray 1680511b41d2SMark Murray /* Get the channel number and verify it. */ 1681511b41d2SMark Murray id = packet_get_int(); 1682a04a10f8SKris Kennaway c = channel_lookup(id); 1683a04a10f8SKris Kennaway if (c == NULL) 1684511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1685511b41d2SMark Murray 1686511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1687a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1688a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1689511b41d2SMark Murray return; 1690511b41d2SMark Murray 1691511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1692a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1693511b41d2SMark Murray return; 1694511b41d2SMark Murray 1695511b41d2SMark Murray /* Get the data. */ 1696511b41d2SMark Murray data = packet_get_string(&data_len); 1697a04a10f8SKris Kennaway 1698a04a10f8SKris Kennaway if (compat20) { 1699a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1700a04a10f8SKris Kennaway log("channel %d: rcvd big packet %d, maxpack %d", 1701a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1702a04a10f8SKris Kennaway } 1703a04a10f8SKris Kennaway if (data_len > c->local_window) { 1704a04a10f8SKris Kennaway log("channel %d: rcvd too much data %d, win %d", 1705a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1706a04a10f8SKris Kennaway xfree(data); 1707a04a10f8SKris Kennaway return; 1708a04a10f8SKris Kennaway } 1709a04a10f8SKris Kennaway c->local_window -= data_len; 1710a04a10f8SKris Kennaway } 1711af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1712a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1713511b41d2SMark Murray xfree(data); 1714511b41d2SMark Murray } 1715af12a3e7SDag-Erling Smørgrav 1716a04a10f8SKris Kennaway void 1717af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 1718a04a10f8SKris Kennaway { 1719a04a10f8SKris Kennaway int id; 1720a04a10f8SKris Kennaway int tcode; 1721a04a10f8SKris Kennaway char *data; 1722ca3176e7SBrian Feldman u_int data_len; 1723a04a10f8SKris Kennaway Channel *c; 1724a04a10f8SKris Kennaway 1725a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1726a04a10f8SKris Kennaway id = packet_get_int(); 1727a04a10f8SKris Kennaway c = channel_lookup(id); 1728a04a10f8SKris Kennaway 1729a04a10f8SKris Kennaway if (c == NULL) 1730a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1731a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1732a04a10f8SKris Kennaway log("channel %d: ext data for non open", id); 1733a04a10f8SKris Kennaway return; 1734a04a10f8SKris Kennaway } 173580628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 173680628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 173780628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 173880628bacSDag-Erling Smørgrav else 173980628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 174080628bacSDag-Erling Smørgrav "on channel %d.", id); 174180628bacSDag-Erling Smørgrav } 1742a04a10f8SKris Kennaway tcode = packet_get_int(); 1743a04a10f8SKris Kennaway if (c->efd == -1 || 1744a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1745a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1746a04a10f8SKris Kennaway log("channel %d: bad ext data", c->self); 1747a04a10f8SKris Kennaway return; 1748a04a10f8SKris Kennaway } 1749a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1750af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1751a04a10f8SKris Kennaway if (data_len > c->local_window) { 1752a04a10f8SKris Kennaway log("channel %d: rcvd too much extended_data %d, win %d", 1753a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1754a04a10f8SKris Kennaway xfree(data); 1755a04a10f8SKris Kennaway return; 1756a04a10f8SKris Kennaway } 17575b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1758a04a10f8SKris Kennaway c->local_window -= data_len; 1759a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1760a04a10f8SKris Kennaway xfree(data); 1761a04a10f8SKris Kennaway } 1762a04a10f8SKris Kennaway 1763a04a10f8SKris Kennaway void 1764af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 1765a04a10f8SKris Kennaway { 1766a04a10f8SKris Kennaway int id; 1767a04a10f8SKris Kennaway Channel *c; 1768a04a10f8SKris Kennaway 1769a04a10f8SKris Kennaway id = packet_get_int(); 1770af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1771a04a10f8SKris Kennaway c = channel_lookup(id); 1772a04a10f8SKris Kennaway if (c == NULL) 1773a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1774a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1775af12a3e7SDag-Erling Smørgrav 1776af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 1777af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 1778af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 1779af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 1780af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 1781af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1782af12a3e7SDag-Erling Smørgrav } 1783af12a3e7SDag-Erling Smørgrav 1784a04a10f8SKris Kennaway } 1785511b41d2SMark Murray 1786511b41d2SMark Murray void 1787af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 1788511b41d2SMark Murray { 1789a04a10f8SKris Kennaway int id; 1790a04a10f8SKris Kennaway Channel *c; 1791511b41d2SMark Murray 1792a04a10f8SKris Kennaway id = packet_get_int(); 1793af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1794a04a10f8SKris Kennaway c = channel_lookup(id); 1795a04a10f8SKris Kennaway if (c == NULL) 1796a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1797511b41d2SMark Murray 1798511b41d2SMark Murray /* 1799511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1800511b41d2SMark Murray * data is coming for it. 1801511b41d2SMark Murray */ 1802511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1803a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1804511b41d2SMark Murray packet_send(); 1805511b41d2SMark Murray 1806511b41d2SMark Murray /* 1807511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1808511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1809511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1810511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1811511b41d2SMark Murray * the confirmation arrives. 1812511b41d2SMark Murray */ 1813a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1814511b41d2SMark Murray /* 1815511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1816511b41d2SMark Murray * cause it to be freed later. 1817511b41d2SMark Murray */ 1818af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1819a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1820511b41d2SMark Murray } 1821511b41d2SMark Murray } 1822511b41d2SMark Murray 1823a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1824a04a10f8SKris Kennaway void 1825af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 1826a04a10f8SKris Kennaway { 1827a04a10f8SKris Kennaway int id = packet_get_int(); 1828a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1829af12a3e7SDag-Erling Smørgrav 1830af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1831a04a10f8SKris Kennaway if (c == NULL) 1832a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1833a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1834a04a10f8SKris Kennaway } 1835511b41d2SMark Murray 1836511b41d2SMark Murray void 1837af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 1838511b41d2SMark Murray { 1839a04a10f8SKris Kennaway int id = packet_get_int(); 1840a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1841a04a10f8SKris Kennaway 1842af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1843a04a10f8SKris Kennaway if (c == NULL) 1844a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1845a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1846a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1847a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1848a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1849af12a3e7SDag-Erling Smørgrav channel_free(c); 1850a04a10f8SKris Kennaway } 1851a04a10f8SKris Kennaway 1852a04a10f8SKris Kennaway void 1853af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 1854a04a10f8SKris Kennaway { 1855a04a10f8SKris Kennaway int id, remote_id; 1856a04a10f8SKris Kennaway Channel *c; 1857a04a10f8SKris Kennaway 1858a04a10f8SKris Kennaway id = packet_get_int(); 1859a04a10f8SKris Kennaway c = channel_lookup(id); 1860a04a10f8SKris Kennaway 1861a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1862a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1863a04a10f8SKris Kennaway "non-opening channel %d.", id); 1864a04a10f8SKris Kennaway remote_id = packet_get_int(); 1865a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1866a04a10f8SKris Kennaway c->remote_id = remote_id; 1867a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1868a04a10f8SKris Kennaway 1869a04a10f8SKris Kennaway if (compat20) { 1870a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 1871a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 1872af12a3e7SDag-Erling Smørgrav if (c->confirm) { 18735b9b2fafSBrian Feldman debug2("callback start"); 1874af12a3e7SDag-Erling Smørgrav c->confirm(c->self, NULL); 18755b9b2fafSBrian Feldman debug2("callback done"); 1876a04a10f8SKris Kennaway } 1877a04a10f8SKris Kennaway debug("channel %d: open confirm rwindow %d rmax %d", c->self, 1878a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 1879a04a10f8SKris Kennaway } 1880af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1881af12a3e7SDag-Erling Smørgrav } 1882af12a3e7SDag-Erling Smørgrav 1883af12a3e7SDag-Erling Smørgrav static char * 1884af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 1885af12a3e7SDag-Erling Smørgrav { 1886af12a3e7SDag-Erling Smørgrav switch (reason) { 1887af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 1888af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 1889af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 1890af12a3e7SDag-Erling Smørgrav return "connect failed"; 1891af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 1892af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 1893af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 1894af12a3e7SDag-Erling Smørgrav return "resource shortage"; 1895af12a3e7SDag-Erling Smørgrav } 1896af12a3e7SDag-Erling Smørgrav return "unknown reason"; 1897a04a10f8SKris Kennaway } 1898a04a10f8SKris Kennaway 1899a04a10f8SKris Kennaway void 1900af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 1901a04a10f8SKris Kennaway { 1902ca3176e7SBrian Feldman int id, reason; 1903ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 1904a04a10f8SKris Kennaway Channel *c; 1905a04a10f8SKris Kennaway 1906a04a10f8SKris Kennaway id = packet_get_int(); 1907a04a10f8SKris Kennaway c = channel_lookup(id); 1908a04a10f8SKris Kennaway 1909a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1910a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 1911a04a10f8SKris Kennaway "non-opening channel %d.", id); 1912a04a10f8SKris Kennaway if (compat20) { 1913ca3176e7SBrian Feldman reason = packet_get_int(); 1914af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1915ca3176e7SBrian Feldman msg = packet_get_string(NULL); 1916ca3176e7SBrian Feldman lang = packet_get_string(NULL); 1917ca3176e7SBrian Feldman } 1918af12a3e7SDag-Erling Smørgrav log("channel %d: open failed: %s%s%s", id, 1919af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 1920ca3176e7SBrian Feldman if (msg != NULL) 1921a04a10f8SKris Kennaway xfree(msg); 1922ca3176e7SBrian Feldman if (lang != NULL) 1923a04a10f8SKris Kennaway xfree(lang); 1924a04a10f8SKris Kennaway } 1925af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1926a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 1927af12a3e7SDag-Erling Smørgrav channel_free(c); 1928a04a10f8SKris Kennaway } 1929a04a10f8SKris Kennaway 1930a04a10f8SKris Kennaway void 1931af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 1932a04a10f8SKris Kennaway { 1933a04a10f8SKris Kennaway Channel *c; 1934a04a10f8SKris Kennaway int id, adjust; 1935a04a10f8SKris Kennaway 1936a04a10f8SKris Kennaway if (!compat20) 1937a04a10f8SKris Kennaway return; 1938511b41d2SMark Murray 1939511b41d2SMark Murray /* Get the channel number and verify it. */ 1940a04a10f8SKris Kennaway id = packet_get_int(); 1941a04a10f8SKris Kennaway c = channel_lookup(id); 1942511b41d2SMark Murray 1943a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 1944a04a10f8SKris Kennaway log("Received window adjust for " 1945a04a10f8SKris Kennaway "non-open channel %d.", id); 1946511b41d2SMark Murray return; 1947511b41d2SMark Murray } 1948a04a10f8SKris Kennaway adjust = packet_get_int(); 1949af12a3e7SDag-Erling Smørgrav packet_check_eom(); 19505b9b2fafSBrian Feldman debug2("channel %d: rcvd adjust %d", id, adjust); 1951a04a10f8SKris Kennaway c->remote_window += adjust; 1952511b41d2SMark Murray } 1953511b41d2SMark Murray 1954af12a3e7SDag-Erling Smørgrav void 1955af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 1956af12a3e7SDag-Erling Smørgrav { 1957af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 1958af12a3e7SDag-Erling Smørgrav u_short host_port; 1959af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 1960af12a3e7SDag-Erling Smørgrav int remote_id, sock = -1; 1961af12a3e7SDag-Erling Smørgrav 1962af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 1963af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 1964af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 1965af12a3e7SDag-Erling Smørgrav 1966af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 1967af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 1968af12a3e7SDag-Erling Smørgrav } else { 1969af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 1970af12a3e7SDag-Erling Smørgrav } 1971af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1972af12a3e7SDag-Erling Smørgrav sock = channel_connect_to(host, host_port); 1973af12a3e7SDag-Erling Smørgrav if (sock != -1) { 1974af12a3e7SDag-Erling Smørgrav c = channel_new("connected socket", 1975af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 1976af12a3e7SDag-Erling Smørgrav originator_string, 1); 1977af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 1978af12a3e7SDag-Erling Smørgrav } 1979af12a3e7SDag-Erling Smørgrav if (c == NULL) { 1980af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1981af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 1982af12a3e7SDag-Erling Smørgrav packet_send(); 1983af12a3e7SDag-Erling Smørgrav } 1984af12a3e7SDag-Erling Smørgrav xfree(host); 1985af12a3e7SDag-Erling Smørgrav } 1986af12a3e7SDag-Erling Smørgrav 1987af12a3e7SDag-Erling Smørgrav 1988af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 1989511b41d2SMark Murray 1990511b41d2SMark Murray void 1991af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 1992511b41d2SMark Murray { 1993af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 1994511b41d2SMark Murray } 1995511b41d2SMark Murray 1996af12a3e7SDag-Erling Smørgrav static int 1997af12a3e7SDag-Erling Smørgrav channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 1998af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 1999511b41d2SMark Murray { 2000af12a3e7SDag-Erling Smørgrav Channel *c; 2001af12a3e7SDag-Erling Smørgrav int success, sock, on = 1; 2002511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2003ca3176e7SBrian Feldman const char *host; 2004af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2005511b41d2SMark Murray struct linger linger; 2006511b41d2SMark Murray 2007ca3176e7SBrian Feldman success = 0; 2008af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2009af12a3e7SDag-Erling Smørgrav listen_addr : host_to_connect; 2010511b41d2SMark Murray 2011af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2012af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2013af12a3e7SDag-Erling Smørgrav return success; 2014ca3176e7SBrian Feldman } 2015af12a3e7SDag-Erling Smørgrav if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 2016ca3176e7SBrian Feldman error("Forward host name too long."); 2017ca3176e7SBrian Feldman return success; 2018ca3176e7SBrian Feldman } 2019ca3176e7SBrian Feldman 2020511b41d2SMark Murray /* 2021511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2022511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2023511b41d2SMark Murray */ 2024511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2025511b41d2SMark Murray hints.ai_family = IPv4or6; 2026511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 2027511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2028ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", listen_port); 2029511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 2030511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 2031511b41d2SMark Murray 2032511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2033511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2034511b41d2SMark Murray continue; 2035511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2036511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2037af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: getnameinfo failed"); 2038511b41d2SMark Murray continue; 2039511b41d2SMark Murray } 2040511b41d2SMark Murray /* Create a port to listen for the host. */ 2041511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2042511b41d2SMark Murray if (sock < 0) { 2043511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2044511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2045511b41d2SMark Murray continue; 2046511b41d2SMark Murray } 2047511b41d2SMark Murray /* 2048511b41d2SMark Murray * Set socket options. We would like the socket to disappear 2049511b41d2SMark Murray * as soon as it has been closed for whatever reason. 2050511b41d2SMark Murray */ 2051af12a3e7SDag-Erling Smørgrav setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 2052511b41d2SMark Murray linger.l_onoff = 1; 2053511b41d2SMark Murray linger.l_linger = 5; 2054af12a3e7SDag-Erling Smørgrav setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); 2055511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 2056511b41d2SMark Murray 2057511b41d2SMark Murray /* Bind the socket to the address. */ 2058511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2059511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2060989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2061989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2062989dd127SDag-Erling Smørgrav else 2063511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2064989dd127SDag-Erling Smørgrav 2065511b41d2SMark Murray close(sock); 2066511b41d2SMark Murray continue; 2067511b41d2SMark Murray } 2068511b41d2SMark Murray /* Start listening for connections on the socket. */ 2069511b41d2SMark Murray if (listen(sock, 5) < 0) { 2070511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2071511b41d2SMark Murray close(sock); 2072511b41d2SMark Murray continue; 2073511b41d2SMark Murray } 2074511b41d2SMark Murray /* Allocate a channel number for the socket. */ 2075af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2076a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 20775b9b2fafSBrian Feldman 0, xstrdup("port listener"), 1); 2078af12a3e7SDag-Erling Smørgrav strlcpy(c->path, host, sizeof(c->path)); 2079af12a3e7SDag-Erling Smørgrav c->host_port = port_to_connect; 2080af12a3e7SDag-Erling Smørgrav c->listening_port = listen_port; 2081511b41d2SMark Murray success = 1; 2082511b41d2SMark Murray } 2083511b41d2SMark Murray if (success == 0) 2084af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: cannot listen to port: %d", 2085ca3176e7SBrian Feldman listen_port); 2086511b41d2SMark Murray freeaddrinfo(aitop); 2087ca3176e7SBrian Feldman return success; 2088511b41d2SMark Murray } 2089511b41d2SMark Murray 2090af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 2091af12a3e7SDag-Erling Smørgrav int 2092af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(u_short listen_port, 2093af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2094af12a3e7SDag-Erling Smørgrav { 2095af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2096af12a3e7SDag-Erling Smørgrav NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); 2097af12a3e7SDag-Erling Smørgrav } 2098af12a3e7SDag-Erling Smørgrav 2099af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 2100af12a3e7SDag-Erling Smørgrav int 2101af12a3e7SDag-Erling Smørgrav channel_setup_remote_fwd_listener(const char *listen_address, 2102af12a3e7SDag-Erling Smørgrav u_short listen_port, int gateway_ports) 2103af12a3e7SDag-Erling Smørgrav { 2104af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2105af12a3e7SDag-Erling Smørgrav listen_address, listen_port, NULL, 0, gateway_ports); 2106af12a3e7SDag-Erling Smørgrav } 2107af12a3e7SDag-Erling Smørgrav 2108511b41d2SMark Murray /* 2109511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 2110511b41d2SMark Murray * the secure channel to host:port from local side. 2111511b41d2SMark Murray */ 2112511b41d2SMark Murray 2113511b41d2SMark Murray void 2114ca3176e7SBrian Feldman channel_request_remote_forwarding(u_short listen_port, 2115ca3176e7SBrian Feldman const char *host_to_connect, u_short port_to_connect) 2116511b41d2SMark Murray { 2117af12a3e7SDag-Erling Smørgrav int type, success = 0; 2118ca3176e7SBrian Feldman 2119511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 2120511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2121511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 2122511b41d2SMark Murray 2123511b41d2SMark Murray /* Send the forward request to the remote side. */ 2124a04a10f8SKris Kennaway if (compat20) { 2125a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 2126a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 2127a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 212880628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2129a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 2130a04a10f8SKris Kennaway packet_put_int(listen_port); 2131ca3176e7SBrian Feldman packet_send(); 2132ca3176e7SBrian Feldman packet_write_wait(); 2133ca3176e7SBrian Feldman /* Assume that server accepts the request */ 2134ca3176e7SBrian Feldman success = 1; 2135a04a10f8SKris Kennaway } else { 2136511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 2137a04a10f8SKris Kennaway packet_put_int(listen_port); 2138a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 2139a04a10f8SKris Kennaway packet_put_int(port_to_connect); 2140511b41d2SMark Murray packet_send(); 2141511b41d2SMark Murray packet_write_wait(); 2142ca3176e7SBrian Feldman 2143ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 2144af12a3e7SDag-Erling Smørgrav type = packet_read(); 2145ca3176e7SBrian Feldman switch (type) { 2146ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 2147ca3176e7SBrian Feldman success = 1; 2148ca3176e7SBrian Feldman break; 2149ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 2150ca3176e7SBrian Feldman log("Warning: Server denied remote port forwarding."); 2151ca3176e7SBrian Feldman break; 2152ca3176e7SBrian Feldman default: 2153ca3176e7SBrian Feldman /* Unknown packet */ 2154ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 2155ca3176e7SBrian Feldman "received packet type %d.", type); 2156ca3176e7SBrian Feldman } 2157ca3176e7SBrian Feldman } 2158ca3176e7SBrian Feldman if (success) { 2159ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 2160ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 2161ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].listen_port = listen_port; 2162ca3176e7SBrian Feldman num_permitted_opens++; 2163511b41d2SMark Murray } 2164a04a10f8SKris Kennaway } 2165511b41d2SMark Murray 2166511b41d2SMark Murray /* 2167511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 2168511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 2169511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 2170511b41d2SMark Murray */ 2171511b41d2SMark Murray 2172511b41d2SMark Murray void 2173a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 2174511b41d2SMark Murray { 2175511b41d2SMark Murray u_short port, host_port; 2176511b41d2SMark Murray char *hostname; 2177511b41d2SMark Murray 2178511b41d2SMark Murray /* Get arguments from the packet. */ 2179511b41d2SMark Murray port = packet_get_int(); 2180511b41d2SMark Murray hostname = packet_get_string(NULL); 2181511b41d2SMark Murray host_port = packet_get_int(); 2182511b41d2SMark Murray 2183989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 2184511b41d2SMark Murray /* 2185511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 2186511b41d2SMark Murray * privileged port. 2187511b41d2SMark Murray */ 2188511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 2189511b41d2SMark Murray packet_disconnect("Requested forwarding of port %d but user is not root.", 2190511b41d2SMark Murray port); 2191989dd127SDag-Erling Smørgrav #endif 2192ca3176e7SBrian Feldman /* Initiate forwarding */ 2193af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); 2194511b41d2SMark Murray 2195511b41d2SMark Murray /* Free the argument string. */ 2196511b41d2SMark Murray xfree(hostname); 2197511b41d2SMark Murray } 2198511b41d2SMark Murray 2199ca3176e7SBrian Feldman /* 2200ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 2201ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 2202ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 2203ca3176e7SBrian Feldman */ 2204ca3176e7SBrian Feldman void 2205af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 2206ca3176e7SBrian Feldman { 2207ca3176e7SBrian Feldman if (num_permitted_opens == 0) 2208ca3176e7SBrian Feldman all_opens_permitted = 1; 2209ca3176e7SBrian Feldman } 2210ca3176e7SBrian Feldman 2211ca3176e7SBrian Feldman void 2212ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 2213ca3176e7SBrian Feldman { 2214ca3176e7SBrian Feldman if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2215ca3176e7SBrian Feldman fatal("channel_request_remote_forwarding: too many forwards"); 2216ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 2217ca3176e7SBrian Feldman 2218ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 2219ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 2220ca3176e7SBrian Feldman num_permitted_opens++; 2221ca3176e7SBrian Feldman 2222ca3176e7SBrian Feldman all_opens_permitted = 0; 2223ca3176e7SBrian Feldman } 2224ca3176e7SBrian Feldman 2225ca3176e7SBrian Feldman void 2226ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 2227ca3176e7SBrian Feldman { 2228ca3176e7SBrian Feldman int i; 2229ca3176e7SBrian Feldman 2230ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2231ca3176e7SBrian Feldman xfree(permitted_opens[i].host_to_connect); 2232ca3176e7SBrian Feldman num_permitted_opens = 0; 2233ca3176e7SBrian Feldman 2234ca3176e7SBrian Feldman } 2235ca3176e7SBrian Feldman 2236ca3176e7SBrian Feldman 2237ca3176e7SBrian Feldman /* return socket to remote host, port */ 2238af12a3e7SDag-Erling Smørgrav static int 2239ca3176e7SBrian Feldman connect_to(const char *host, u_short port) 2240511b41d2SMark Murray { 2241511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2242511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2243511b41d2SMark Murray int gaierr; 2244a04a10f8SKris Kennaway int sock = -1; 2245511b41d2SMark Murray 2246511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2247511b41d2SMark Murray hints.ai_family = IPv4or6; 2248511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2249ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", port); 2250511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 2251ca3176e7SBrian Feldman error("connect_to %.100s: unknown host (%s)", host, 2252ca3176e7SBrian Feldman gai_strerror(gaierr)); 2253a04a10f8SKris Kennaway return -1; 2254511b41d2SMark Murray } 2255511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2256511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2257511b41d2SMark Murray continue; 2258511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2259511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2260ca3176e7SBrian Feldman error("connect_to: getnameinfo failed"); 2261511b41d2SMark Murray continue; 2262511b41d2SMark Murray } 2263511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2264511b41d2SMark Murray if (sock < 0) { 2265511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2266511b41d2SMark Murray continue; 2267511b41d2SMark Murray } 2268ca3176e7SBrian Feldman if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) 2269ca3176e7SBrian Feldman fatal("connect_to: F_SETFL: %s", strerror(errno)); 2270ca3176e7SBrian Feldman if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2271ca3176e7SBrian Feldman errno != EINPROGRESS) { 2272ca3176e7SBrian Feldman error("connect_to %.100s port %s: %.100s", ntop, strport, 2273511b41d2SMark Murray strerror(errno)); 2274511b41d2SMark Murray close(sock); 2275511b41d2SMark Murray continue; /* fail -- try next */ 2276511b41d2SMark Murray } 2277511b41d2SMark Murray break; /* success */ 2278511b41d2SMark Murray 2279511b41d2SMark Murray } 2280511b41d2SMark Murray freeaddrinfo(aitop); 2281511b41d2SMark Murray if (!ai) { 2282ca3176e7SBrian Feldman error("connect_to %.100s port %d: failed.", host, port); 2283a04a10f8SKris Kennaway return -1; 2284a04a10f8SKris Kennaway } 2285a04a10f8SKris Kennaway /* success */ 2286af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2287a04a10f8SKris Kennaway return sock; 2288a04a10f8SKris Kennaway } 2289ca3176e7SBrian Feldman 2290ca3176e7SBrian Feldman int 2291af12a3e7SDag-Erling Smørgrav channel_connect_by_listen_address(u_short listen_port) 2292ca3176e7SBrian Feldman { 2293ca3176e7SBrian Feldman int i; 2294ca3176e7SBrian Feldman 2295ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2296ca3176e7SBrian Feldman if (permitted_opens[i].listen_port == listen_port) 2297ca3176e7SBrian Feldman return connect_to( 2298ca3176e7SBrian Feldman permitted_opens[i].host_to_connect, 2299ca3176e7SBrian Feldman permitted_opens[i].port_to_connect); 2300ca3176e7SBrian Feldman error("WARNING: Server requests forwarding for unknown listen_port %d", 2301ca3176e7SBrian Feldman listen_port); 2302ca3176e7SBrian Feldman return -1; 2303ca3176e7SBrian Feldman } 2304ca3176e7SBrian Feldman 2305ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 2306ca3176e7SBrian Feldman int 2307ca3176e7SBrian Feldman channel_connect_to(const char *host, u_short port) 2308ca3176e7SBrian Feldman { 2309ca3176e7SBrian Feldman int i, permit; 2310ca3176e7SBrian Feldman 2311ca3176e7SBrian Feldman permit = all_opens_permitted; 2312ca3176e7SBrian Feldman if (!permit) { 2313ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2314ca3176e7SBrian Feldman if (permitted_opens[i].port_to_connect == port && 2315ca3176e7SBrian Feldman strcmp(permitted_opens[i].host_to_connect, host) == 0) 2316ca3176e7SBrian Feldman permit = 1; 2317ca3176e7SBrian Feldman 2318ca3176e7SBrian Feldman } 2319ca3176e7SBrian Feldman if (!permit) { 2320ca3176e7SBrian Feldman log("Received request to connect to host %.100s port %d, " 2321ca3176e7SBrian Feldman "but the request was denied.", host, port); 2322ca3176e7SBrian Feldman return -1; 2323ca3176e7SBrian Feldman } 2324ca3176e7SBrian Feldman return connect_to(host, port); 2325ca3176e7SBrian Feldman } 2326ca3176e7SBrian Feldman 2327af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 2328511b41d2SMark Murray 2329511b41d2SMark Murray /* 2330511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 2331af12a3e7SDag-Erling Smørgrav * Returns a suitable display number for the DISPLAY variable, or -1 if 2332af12a3e7SDag-Erling Smørgrav * an error occurs. 2333511b41d2SMark Murray */ 2334af12a3e7SDag-Erling Smørgrav int 2335af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2336af12a3e7SDag-Erling Smørgrav int single_connection) 2337511b41d2SMark Murray { 2338af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 2339511b41d2SMark Murray int display_number, sock; 2340511b41d2SMark Murray u_short port; 2341511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2342511b41d2SMark Murray char strport[NI_MAXSERV]; 2343511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 2344511b41d2SMark Murray 2345511b41d2SMark Murray for (display_number = x11_display_offset; 2346511b41d2SMark Murray display_number < MAX_DISPLAYS; 2347511b41d2SMark Murray display_number++) { 2348511b41d2SMark Murray port = 6000 + display_number; 2349511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2350511b41d2SMark Murray hints.ai_family = IPv4or6; 2351af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 2352511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2353511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2354511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2355511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 2356af12a3e7SDag-Erling Smørgrav return -1; 2357511b41d2SMark Murray } 2358511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2359511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2360511b41d2SMark Murray continue; 2361511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2362511b41d2SMark Murray if (sock < 0) { 2363989dd127SDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 2364511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2365af12a3e7SDag-Erling Smørgrav return -1; 2366989dd127SDag-Erling Smørgrav } else { 2367989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 2368989dd127SDag-Erling Smørgrav ai->ai_family); 2369989dd127SDag-Erling Smørgrav continue; 2370511b41d2SMark Murray } 2371989dd127SDag-Erling Smørgrav } 2372989dd127SDag-Erling Smørgrav #ifdef IPV6_V6ONLY 2373989dd127SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 2374989dd127SDag-Erling Smørgrav int on = 1; 2375989dd127SDag-Erling Smørgrav if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 2376989dd127SDag-Erling Smørgrav error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 2377989dd127SDag-Erling Smørgrav } 2378989dd127SDag-Erling Smørgrav #endif 2379511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2380511b41d2SMark Murray debug("bind port %d: %.100s", port, strerror(errno)); 2381511b41d2SMark Murray close(sock); 2382989dd127SDag-Erling Smørgrav 2383989dd127SDag-Erling Smørgrav if (ai->ai_next) 2384989dd127SDag-Erling Smørgrav continue; 2385989dd127SDag-Erling Smørgrav 2386511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2387511b41d2SMark Murray close(socks[n]); 2388511b41d2SMark Murray } 2389511b41d2SMark Murray num_socks = 0; 2390511b41d2SMark Murray break; 2391511b41d2SMark Murray } 2392511b41d2SMark Murray socks[num_socks++] = sock; 2393989dd127SDag-Erling Smørgrav #ifndef DONT_TRY_OTHER_AF 2394511b41d2SMark Murray if (num_socks == NUM_SOCKS) 2395511b41d2SMark Murray break; 2396989dd127SDag-Erling Smørgrav #else 2397989dd127SDag-Erling Smørgrav if (x11_use_localhost) { 2398989dd127SDag-Erling Smørgrav if (num_socks == NUM_SOCKS) 2399989dd127SDag-Erling Smørgrav break; 2400989dd127SDag-Erling Smørgrav } else { 2401989dd127SDag-Erling Smørgrav break; 2402989dd127SDag-Erling Smørgrav } 2403989dd127SDag-Erling Smørgrav #endif 2404511b41d2SMark Murray } 2405ca3176e7SBrian Feldman freeaddrinfo(aitop); 2406511b41d2SMark Murray if (num_socks > 0) 2407511b41d2SMark Murray break; 2408511b41d2SMark Murray } 2409511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 2410511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 2411af12a3e7SDag-Erling Smørgrav return -1; 2412511b41d2SMark Murray } 2413511b41d2SMark Murray /* Start listening for connections on the socket. */ 2414511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2415511b41d2SMark Murray sock = socks[n]; 2416511b41d2SMark Murray if (listen(sock, 5) < 0) { 2417511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2418511b41d2SMark Murray close(sock); 2419af12a3e7SDag-Erling Smørgrav return -1; 2420511b41d2SMark Murray } 2421511b41d2SMark Murray } 2422511b41d2SMark Murray 2423511b41d2SMark Murray /* Allocate a channel for each socket. */ 2424511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2425511b41d2SMark Murray sock = socks[n]; 2426af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 2427a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 2428a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 24295b9b2fafSBrian Feldman 0, xstrdup("X11 inet listener"), 1); 2430af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 2431511b41d2SMark Murray } 2432511b41d2SMark Murray 2433af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 2434af12a3e7SDag-Erling Smørgrav return display_number; 2435511b41d2SMark Murray } 2436511b41d2SMark Murray 2437af12a3e7SDag-Erling Smørgrav static int 2438ca3176e7SBrian Feldman connect_local_xsocket(u_int dnr) 2439511b41d2SMark Murray { 2440511b41d2SMark Murray int sock; 2441511b41d2SMark Murray struct sockaddr_un addr; 2442511b41d2SMark Murray 2443511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2444511b41d2SMark Murray if (sock < 0) 2445511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2446511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 2447511b41d2SMark Murray addr.sun_family = AF_UNIX; 2448af12a3e7SDag-Erling Smørgrav snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 2449511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 2450511b41d2SMark Murray return sock; 2451511b41d2SMark Murray close(sock); 2452511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 2453511b41d2SMark Murray return -1; 2454511b41d2SMark Murray } 2455511b41d2SMark Murray 2456a04a10f8SKris Kennaway int 2457a04a10f8SKris Kennaway x11_connect_display(void) 2458511b41d2SMark Murray { 2459a04a10f8SKris Kennaway int display_number, sock = 0; 2460511b41d2SMark Murray const char *display; 2461a04a10f8SKris Kennaway char buf[1024], *cp; 2462511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2463511b41d2SMark Murray char strport[NI_MAXSERV]; 2464511b41d2SMark Murray int gaierr; 2465511b41d2SMark Murray 2466511b41d2SMark Murray /* Try to open a socket for the local X server. */ 2467511b41d2SMark Murray display = getenv("DISPLAY"); 2468511b41d2SMark Murray if (!display) { 2469511b41d2SMark Murray error("DISPLAY not set."); 2470a04a10f8SKris Kennaway return -1; 2471511b41d2SMark Murray } 2472511b41d2SMark Murray /* 2473511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 2474511b41d2SMark Murray * connection to the real X server. 2475511b41d2SMark Murray */ 2476511b41d2SMark Murray 2477511b41d2SMark Murray /* 2478511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 2479511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 2480511b41d2SMark Murray */ 2481511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 2482511b41d2SMark Murray display[0] == ':') { 2483511b41d2SMark Murray /* Connect to the unix domain socket. */ 2484511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 2485511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2486511b41d2SMark Murray display); 2487a04a10f8SKris Kennaway return -1; 2488511b41d2SMark Murray } 2489511b41d2SMark Murray /* Create a socket. */ 2490511b41d2SMark Murray sock = connect_local_xsocket(display_number); 2491511b41d2SMark Murray if (sock < 0) 2492a04a10f8SKris Kennaway return -1; 2493511b41d2SMark Murray 2494511b41d2SMark Murray /* OK, we now have a connection to the display. */ 2495a04a10f8SKris Kennaway return sock; 2496511b41d2SMark Murray } 2497511b41d2SMark Murray /* 2498511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 2499511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 2500511b41d2SMark Murray */ 2501af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 2502511b41d2SMark Murray cp = strchr(buf, ':'); 2503511b41d2SMark Murray if (!cp) { 2504511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 2505a04a10f8SKris Kennaway return -1; 2506511b41d2SMark Murray } 2507511b41d2SMark Murray *cp = 0; 2508511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 2509511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 2510511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2511511b41d2SMark Murray display); 2512a04a10f8SKris Kennaway return -1; 2513511b41d2SMark Murray } 2514511b41d2SMark Murray 2515511b41d2SMark Murray /* Look up the host address */ 2516511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2517511b41d2SMark Murray hints.ai_family = IPv4or6; 2518511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2519511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 2520511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 2521511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 2522a04a10f8SKris Kennaway return -1; 2523511b41d2SMark Murray } 2524511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2525511b41d2SMark Murray /* Create a socket. */ 2526511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2527511b41d2SMark Murray if (sock < 0) { 2528511b41d2SMark Murray debug("socket: %.100s", strerror(errno)); 2529511b41d2SMark Murray continue; 2530511b41d2SMark Murray } 2531511b41d2SMark Murray /* Connect it to the display. */ 2532511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2533a04a10f8SKris Kennaway debug("connect %.100s port %d: %.100s", buf, 2534a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 2535511b41d2SMark Murray close(sock); 2536511b41d2SMark Murray continue; 2537511b41d2SMark Murray } 2538511b41d2SMark Murray /* Success */ 2539511b41d2SMark Murray break; 2540a04a10f8SKris Kennaway } 2541511b41d2SMark Murray freeaddrinfo(aitop); 2542511b41d2SMark Murray if (!ai) { 2543511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 2544511b41d2SMark Murray strerror(errno)); 2545a04a10f8SKris Kennaway return -1; 2546511b41d2SMark Murray } 2547af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2548a04a10f8SKris Kennaway return sock; 2549a04a10f8SKris Kennaway } 2550511b41d2SMark Murray 2551a04a10f8SKris Kennaway /* 2552a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 2553a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 2554a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 2555a04a10f8SKris Kennaway */ 2556a04a10f8SKris Kennaway 2557a04a10f8SKris Kennaway void 2558af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 2559a04a10f8SKris Kennaway { 2560af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2561af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 2562a04a10f8SKris Kennaway char *remote_host; 2563a04a10f8SKris Kennaway 2564a04a10f8SKris Kennaway debug("Received X11 open request."); 2565af12a3e7SDag-Erling Smørgrav 2566af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2567af12a3e7SDag-Erling Smørgrav 2568af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2569af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 2570af12a3e7SDag-Erling Smørgrav } else { 2571af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 2572af12a3e7SDag-Erling Smørgrav } 2573af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2574a04a10f8SKris Kennaway 2575a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 2576a04a10f8SKris Kennaway sock = x11_connect_display(); 2577af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2578af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 2579af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 2580af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 2581af12a3e7SDag-Erling Smørgrav remote_host, 1); 2582af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2583af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2584af12a3e7SDag-Erling Smørgrav } 2585af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2586a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 2587a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2588af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2589a04a10f8SKris Kennaway } else { 2590511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2591511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2592af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2593af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 2594a04a10f8SKris Kennaway } 2595af12a3e7SDag-Erling Smørgrav packet_send(); 2596511b41d2SMark Murray } 2597511b41d2SMark Murray 25985b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 25995b9b2fafSBrian Feldman void 2600af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 26015b9b2fafSBrian Feldman { 26025b9b2fafSBrian Feldman int rchan = packet_get_int(); 26035b9b2fafSBrian Feldman switch (type) { 26045b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 26055b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 26065b9b2fafSBrian Feldman break; 26075b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 26085b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 26095b9b2fafSBrian Feldman break; 26105b9b2fafSBrian Feldman default: 2611af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 26125b9b2fafSBrian Feldman break; 26135b9b2fafSBrian Feldman } 26145b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 26155b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 26165b9b2fafSBrian Feldman packet_put_int(rchan); 26175b9b2fafSBrian Feldman packet_send(); 26185b9b2fafSBrian Feldman } 26195b9b2fafSBrian Feldman 2620511b41d2SMark Murray /* 2621511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2622511b41d2SMark Murray * data, and enables authentication spoofing. 2623af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 2624511b41d2SMark Murray */ 2625511b41d2SMark Murray void 2626a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2627a04a10f8SKris Kennaway const char *proto, const char *data) 2628511b41d2SMark Murray { 2629ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 2630ca3176e7SBrian Feldman u_int i, value, len; 2631511b41d2SMark Murray char *new_data; 2632511b41d2SMark Murray int screen_number; 2633511b41d2SMark Murray const char *cp; 2634511b41d2SMark Murray u_int32_t rand = 0; 2635511b41d2SMark Murray 2636511b41d2SMark Murray cp = getenv("DISPLAY"); 2637511b41d2SMark Murray if (cp) 2638511b41d2SMark Murray cp = strchr(cp, ':'); 2639511b41d2SMark Murray if (cp) 2640511b41d2SMark Murray cp = strchr(cp, '.'); 2641511b41d2SMark Murray if (cp) 2642511b41d2SMark Murray screen_number = atoi(cp + 1); 2643511b41d2SMark Murray else 2644511b41d2SMark Murray screen_number = 0; 2645511b41d2SMark Murray 2646511b41d2SMark Murray /* Save protocol name. */ 2647511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2648511b41d2SMark Murray 2649511b41d2SMark Murray /* 2650511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2651511b41d2SMark Murray * same length. 2652511b41d2SMark Murray */ 2653511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2654511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2655511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2656511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2657511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2658511b41d2SMark Murray if (i % 4 == 0) 2659511b41d2SMark Murray rand = arc4random(); 2660511b41d2SMark Murray x11_saved_data[i] = value; 2661511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2662511b41d2SMark Murray rand >>= 8; 2663511b41d2SMark Murray } 2664511b41d2SMark Murray x11_saved_data_len = data_len; 2665511b41d2SMark Murray x11_fake_data_len = data_len; 2666511b41d2SMark Murray 2667511b41d2SMark Murray /* Convert the fake data into hex. */ 2668ca3176e7SBrian Feldman len = 2 * data_len + 1; 2669ca3176e7SBrian Feldman new_data = xmalloc(len); 2670511b41d2SMark Murray for (i = 0; i < data_len; i++) 2671ca3176e7SBrian Feldman snprintf(new_data + 2 * i, len - 2 * i, 2672ca3176e7SBrian Feldman "%02x", (u_char) x11_fake_data[i]); 2673511b41d2SMark Murray 2674511b41d2SMark Murray /* Send the request packet. */ 2675a04a10f8SKris Kennaway if (compat20) { 2676a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2677a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2678a04a10f8SKris Kennaway } else { 2679511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2680a04a10f8SKris Kennaway } 2681a04a10f8SKris Kennaway packet_put_cstring(proto); 2682a04a10f8SKris Kennaway packet_put_cstring(new_data); 2683511b41d2SMark Murray packet_put_int(screen_number); 2684511b41d2SMark Murray packet_send(); 2685511b41d2SMark Murray packet_write_wait(); 2686511b41d2SMark Murray xfree(new_data); 2687511b41d2SMark Murray } 2688511b41d2SMark Murray 2689af12a3e7SDag-Erling Smørgrav 2690af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 2691af12a3e7SDag-Erling Smørgrav 2692511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2693511b41d2SMark Murray 2694511b41d2SMark Murray void 2695af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 2696511b41d2SMark Murray { 2697511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2698511b41d2SMark Murray packet_send(); 2699511b41d2SMark Murray packet_write_wait(); 2700511b41d2SMark Murray } 2701511b41d2SMark Murray 2702511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2703511b41d2SMark Murray 2704511b41d2SMark Murray void 2705af12a3e7SDag-Erling Smørgrav auth_input_open_request(int type, u_int32_t seq, void *ctxt) 2706511b41d2SMark Murray { 2707af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2708af12a3e7SDag-Erling Smørgrav int remote_id, sock; 2709af12a3e7SDag-Erling Smørgrav char *name; 2710a04a10f8SKris Kennaway 2711511b41d2SMark Murray /* Read the remote channel number from the message. */ 2712af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2713af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2714511b41d2SMark Murray 2715511b41d2SMark Murray /* 2716511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2717511b41d2SMark Murray * get forwarded). 2718511b41d2SMark Murray */ 2719511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2720511b41d2SMark Murray 2721511b41d2SMark Murray /* 2722511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2723511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2724511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2725511b41d2SMark Murray * agent. 2726511b41d2SMark Murray */ 2727af12a3e7SDag-Erling Smørgrav if (sock >= 0) { 2728af12a3e7SDag-Erling Smørgrav name = xstrdup("authentication agent connection"); 2729af12a3e7SDag-Erling Smørgrav c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 2730af12a3e7SDag-Erling Smørgrav -1, 0, 0, 0, name, 1); 2731af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2732af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2733af12a3e7SDag-Erling Smørgrav } 2734af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2735511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2736af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2737af12a3e7SDag-Erling Smørgrav } else { 2738511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2739af12a3e7SDag-Erling Smørgrav debug("Forwarding authentication connection."); 2740511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2741af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2742a04a10f8SKris Kennaway packet_put_int(c->self); 2743a04a10f8SKris Kennaway } 2744a04a10f8SKris Kennaway packet_send(); 2745a04a10f8SKris Kennaway } 2746