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" 42a82e551fSDag-Erling Smørgrav RCSID("$OpenBSD: channels.c,v 1.179 2002/06/26 08:55:02 markus Exp $"); 43a82e551fSDag-Erling Smørgrav RCSID("$FreeBSD$"); 44a04a10f8SKris Kennaway 45ca3176e7SBrian Feldman #include "ssh.h" 46ca3176e7SBrian Feldman #include "ssh1.h" 47ca3176e7SBrian Feldman #include "ssh2.h" 48ca3176e7SBrian Feldman #include "packet.h" 49ca3176e7SBrian Feldman #include "xmalloc.h" 50ca3176e7SBrian Feldman #include "log.h" 51ca3176e7SBrian Feldman #include "misc.h" 52ca3176e7SBrian Feldman #include "channels.h" 53ca3176e7SBrian Feldman #include "compat.h" 54ca3176e7SBrian Feldman #include "canohost.h" 55b66f2d16SKris Kennaway #include "key.h" 56b66f2d16SKris Kennaway #include "authfd.h" 57af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 58b66f2d16SKris Kennaway 59511b41d2SMark Murray 60af12a3e7SDag-Erling Smørgrav /* -- channel core */ 61511b41d2SMark Murray 62511b41d2SMark Murray /* 63511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 64511b41d2SMark Murray * dynamically extended as needed. 65511b41d2SMark Murray */ 66af12a3e7SDag-Erling Smørgrav static Channel **channels = NULL; 67511b41d2SMark Murray 68511b41d2SMark Murray /* 69511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 70af12a3e7SDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 71511b41d2SMark Murray */ 72511b41d2SMark Murray static int channels_alloc = 0; 73511b41d2SMark Murray 74511b41d2SMark Murray /* 75511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 76af12a3e7SDag-Erling Smørgrav * updated in channel_new. 77511b41d2SMark Murray */ 78ca3176e7SBrian Feldman static int channel_max_fd = 0; 79511b41d2SMark Murray 80511b41d2SMark Murray 81af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 82511b41d2SMark Murray 83511b41d2SMark Murray /* 84511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 85511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 86511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 87511b41d2SMark Murray * network (which might be behind a firewall). 88511b41d2SMark Murray */ 89511b41d2SMark Murray typedef struct { 90a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 91a04a10f8SKris Kennaway u_short port_to_connect; /* Connect to 'port'. */ 92a04a10f8SKris Kennaway u_short listen_port; /* Remote side should listen port number. */ 93511b41d2SMark Murray } ForwardPermission; 94511b41d2SMark Murray 95511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */ 96511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 97af12a3e7SDag-Erling Smørgrav 98511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */ 99511b41d2SMark Murray static int num_permitted_opens = 0; 100511b41d2SMark Murray /* 101511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 102511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 103511b41d2SMark Murray * anything after logging in anyway. 104511b41d2SMark Murray */ 105511b41d2SMark Murray static int all_opens_permitted = 0; 106511b41d2SMark Murray 107af12a3e7SDag-Erling Smørgrav 108af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 109af12a3e7SDag-Erling Smørgrav 110af12a3e7SDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 111af12a3e7SDag-Erling Smørgrav #define MAX_DISPLAYS 1000 112af12a3e7SDag-Erling Smørgrav 113af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 114af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 115af12a3e7SDag-Erling Smørgrav 116af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 117af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 118af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 119af12a3e7SDag-Erling Smørgrav 120af12a3e7SDag-Erling Smørgrav /* 121af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 122af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 123af12a3e7SDag-Erling Smørgrav */ 124af12a3e7SDag-Erling Smørgrav static char *x11_fake_data = NULL; 125af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 126af12a3e7SDag-Erling Smørgrav 127af12a3e7SDag-Erling Smørgrav 128af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 129af12a3e7SDag-Erling Smørgrav 130af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 131af12a3e7SDag-Erling Smørgrav 132ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 133989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 134ca3176e7SBrian Feldman 135af12a3e7SDag-Erling Smørgrav /* helper */ 136af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 137ca3176e7SBrian Feldman 138af12a3e7SDag-Erling Smørgrav /* -- channel core */ 139a04a10f8SKris Kennaway 140a04a10f8SKris Kennaway Channel * 141a04a10f8SKris Kennaway channel_lookup(int id) 142a04a10f8SKris Kennaway { 143a04a10f8SKris Kennaway Channel *c; 144af12a3e7SDag-Erling Smørgrav 145d96f3089SJacques Vidrine if (id < 0 || id >= channels_alloc) { 146a04a10f8SKris Kennaway log("channel_lookup: %d: bad id", id); 147a04a10f8SKris Kennaway return NULL; 148a04a10f8SKris Kennaway } 149af12a3e7SDag-Erling Smørgrav c = channels[id]; 150af12a3e7SDag-Erling Smørgrav if (c == NULL) { 151a04a10f8SKris Kennaway log("channel_lookup: %d: bad id: channel free", id); 152a04a10f8SKris Kennaway return NULL; 153a04a10f8SKris Kennaway } 154a04a10f8SKris Kennaway return c; 155a04a10f8SKris Kennaway } 156a04a10f8SKris Kennaway 157a04a10f8SKris Kennaway /* 158a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 159a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 160a04a10f8SKris Kennaway */ 161a04a10f8SKris Kennaway 162af12a3e7SDag-Erling Smørgrav static void 1635b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1645b9b2fafSBrian Feldman int extusage, int nonblock) 165a04a10f8SKris Kennaway { 166a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 167ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, rfd); 168ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, wfd); 169ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, efd); 170ca3176e7SBrian Feldman 171a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 172a04a10f8SKris Kennaway 173a04a10f8SKris Kennaway c->rfd = rfd; 174a04a10f8SKris Kennaway c->wfd = wfd; 175a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 176a04a10f8SKris Kennaway c->efd = efd; 177a04a10f8SKris Kennaway c->extended_usage = extusage; 1785b9b2fafSBrian Feldman 179e0fbb1d2SBrian Feldman /* XXX ugly hack: nonblock is only set by the server */ 180e0fbb1d2SBrian Feldman if (nonblock && isatty(c->rfd)) { 181ca3176e7SBrian Feldman debug("channel %d: rfd %d isatty", c->self, c->rfd); 182e0fbb1d2SBrian Feldman c->isatty = 1; 183e0fbb1d2SBrian Feldman if (!isatty(c->wfd)) { 184ca3176e7SBrian Feldman error("channel %d: wfd %d is not a tty?", 185e0fbb1d2SBrian Feldman c->self, c->wfd); 186e0fbb1d2SBrian Feldman } 187e0fbb1d2SBrian Feldman } else { 188e0fbb1d2SBrian Feldman c->isatty = 0; 189e0fbb1d2SBrian Feldman } 190e0fbb1d2SBrian Feldman 1915b9b2fafSBrian Feldman /* enable nonblocking mode */ 1925b9b2fafSBrian Feldman if (nonblock) { 193a04a10f8SKris Kennaway if (rfd != -1) 194a04a10f8SKris Kennaway set_nonblock(rfd); 195a04a10f8SKris Kennaway if (wfd != -1) 196a04a10f8SKris Kennaway set_nonblock(wfd); 197a04a10f8SKris Kennaway if (efd != -1) 198a04a10f8SKris Kennaway set_nonblock(efd); 199a04a10f8SKris Kennaway } 2005b9b2fafSBrian Feldman } 201a04a10f8SKris Kennaway 202511b41d2SMark Murray /* 203511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 204511b41d2SMark Murray * remote_name to be freed. 205511b41d2SMark Murray */ 206511b41d2SMark Murray 207af12a3e7SDag-Erling Smørgrav Channel * 208a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 209a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 210511b41d2SMark Murray { 211511b41d2SMark Murray int i, found; 212511b41d2SMark Murray Channel *c; 213511b41d2SMark Murray 214511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 215511b41d2SMark Murray if (channels_alloc == 0) { 216511b41d2SMark Murray channels_alloc = 10; 217af12a3e7SDag-Erling Smørgrav channels = xmalloc(channels_alloc * sizeof(Channel *)); 218511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 219af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 220af12a3e7SDag-Erling Smørgrav fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); 221511b41d2SMark Murray } 222511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 223511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 224af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 225511b41d2SMark Murray /* Found a free slot. */ 226511b41d2SMark Murray found = i; 227511b41d2SMark Murray break; 228511b41d2SMark Murray } 229511b41d2SMark Murray if (found == -1) { 230511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 231511b41d2SMark Murray found = channels_alloc; 232511b41d2SMark Murray channels_alloc += 10; 233a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 234a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 235a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 2365b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 237af12a3e7SDag-Erling Smørgrav channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); 238511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 239af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 240511b41d2SMark Murray } 241af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 242af12a3e7SDag-Erling Smørgrav c = channels[found] = xmalloc(sizeof(Channel)); 243af12a3e7SDag-Erling Smørgrav memset(c, 0, sizeof(Channel)); 244511b41d2SMark Murray buffer_init(&c->input); 245511b41d2SMark Murray buffer_init(&c->output); 246a04a10f8SKris Kennaway buffer_init(&c->extended); 247af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 248af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 249af12a3e7SDag-Erling Smørgrav c->flags = 0; 2505b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 251511b41d2SMark Murray c->self = found; 252511b41d2SMark Murray c->type = type; 253a04a10f8SKris Kennaway c->ctype = ctype; 254a04a10f8SKris Kennaway c->local_window = window; 255a04a10f8SKris Kennaway c->local_window_max = window; 256a04a10f8SKris Kennaway c->local_consumed = 0; 257a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 258511b41d2SMark Murray c->remote_id = -1; 259511b41d2SMark Murray c->remote_name = remote_name; 260a04a10f8SKris Kennaway c->remote_window = 0; 261a04a10f8SKris Kennaway c->remote_maxpacket = 0; 262af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 263af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 264af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 265af12a3e7SDag-Erling Smørgrav c->confirm = NULL; 266b66f2d16SKris Kennaway c->input_filter = NULL; 267511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 268af12a3e7SDag-Erling Smørgrav return c; 269a04a10f8SKris Kennaway } 270511b41d2SMark Murray 271af12a3e7SDag-Erling Smørgrav static int 272af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 273af12a3e7SDag-Erling Smørgrav { 274af12a3e7SDag-Erling Smørgrav int i, max = 0; 275af12a3e7SDag-Erling Smørgrav Channel *c; 276af12a3e7SDag-Erling Smørgrav 277af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 278af12a3e7SDag-Erling Smørgrav c = channels[i]; 279af12a3e7SDag-Erling Smørgrav if (c != NULL) { 280af12a3e7SDag-Erling Smørgrav max = MAX(max, c->rfd); 281af12a3e7SDag-Erling Smørgrav max = MAX(max, c->wfd); 282af12a3e7SDag-Erling Smørgrav max = MAX(max, c->efd); 283af12a3e7SDag-Erling Smørgrav } 284af12a3e7SDag-Erling Smørgrav } 285af12a3e7SDag-Erling Smørgrav return max; 286af12a3e7SDag-Erling Smørgrav } 287af12a3e7SDag-Erling Smørgrav 288af12a3e7SDag-Erling Smørgrav int 289af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 290af12a3e7SDag-Erling Smørgrav { 291af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 292af12a3e7SDag-Erling Smørgrav 293af12a3e7SDag-Erling Smørgrav if (fd != -1) { 294af12a3e7SDag-Erling Smørgrav ret = close(fd); 295af12a3e7SDag-Erling Smørgrav *fdp = -1; 296af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 297af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 298af12a3e7SDag-Erling Smørgrav } 299af12a3e7SDag-Erling Smørgrav return ret; 300af12a3e7SDag-Erling Smørgrav } 301a04a10f8SKris Kennaway 302a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 303511b41d2SMark Murray 304af12a3e7SDag-Erling Smørgrav static void 305a04a10f8SKris Kennaway channel_close_fds(Channel *c) 306511b41d2SMark Murray { 307af12a3e7SDag-Erling Smørgrav debug3("channel_close_fds: channel %d: r %d w %d e %d", 308af12a3e7SDag-Erling Smørgrav c->self, c->rfd, c->wfd, c->efd); 309af12a3e7SDag-Erling Smørgrav 310af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 311af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 312af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 313af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 314a04a10f8SKris Kennaway } 315511b41d2SMark Murray 316a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 317a04a10f8SKris Kennaway 318a04a10f8SKris Kennaway void 319af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 320a04a10f8SKris Kennaway { 321af12a3e7SDag-Erling Smørgrav char *s; 322af12a3e7SDag-Erling Smørgrav int i, n; 323ca3176e7SBrian Feldman 324af12a3e7SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) 325af12a3e7SDag-Erling Smørgrav if (channels[i]) 326af12a3e7SDag-Erling Smørgrav n++; 327af12a3e7SDag-Erling Smørgrav debug("channel_free: channel %d: %s, nchannels %d", c->self, 328af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 329af12a3e7SDag-Erling Smørgrav 330af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 331af12a3e7SDag-Erling Smørgrav debug3("channel_free: status: %s", s); 332ca3176e7SBrian Feldman xfree(s); 333ca3176e7SBrian Feldman 334a04a10f8SKris Kennaway if (c->sock != -1) 335a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 336a04a10f8SKris Kennaway channel_close_fds(c); 337a04a10f8SKris Kennaway buffer_free(&c->input); 338a04a10f8SKris Kennaway buffer_free(&c->output); 339a04a10f8SKris Kennaway buffer_free(&c->extended); 340a04a10f8SKris Kennaway if (c->remote_name) { 341a04a10f8SKris Kennaway xfree(c->remote_name); 342a04a10f8SKris Kennaway c->remote_name = NULL; 343511b41d2SMark Murray } 344af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 345af12a3e7SDag-Erling Smørgrav xfree(c); 346af12a3e7SDag-Erling Smørgrav } 347af12a3e7SDag-Erling Smørgrav 348af12a3e7SDag-Erling Smørgrav void 349af12a3e7SDag-Erling Smørgrav channel_free_all(void) 350af12a3e7SDag-Erling Smørgrav { 351af12a3e7SDag-Erling Smørgrav int i; 352af12a3e7SDag-Erling Smørgrav 353af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 354af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 355af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 356af12a3e7SDag-Erling Smørgrav } 357af12a3e7SDag-Erling Smørgrav 358af12a3e7SDag-Erling Smørgrav /* 359af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 360af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 361af12a3e7SDag-Erling Smørgrav */ 362af12a3e7SDag-Erling Smørgrav 363af12a3e7SDag-Erling Smørgrav void 364af12a3e7SDag-Erling Smørgrav channel_close_all(void) 365af12a3e7SDag-Erling Smørgrav { 366af12a3e7SDag-Erling Smørgrav int i; 367af12a3e7SDag-Erling Smørgrav 368af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 369af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 370af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 371af12a3e7SDag-Erling Smørgrav } 372af12a3e7SDag-Erling Smørgrav 373af12a3e7SDag-Erling Smørgrav /* 374af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 375af12a3e7SDag-Erling Smørgrav */ 376af12a3e7SDag-Erling Smørgrav 377af12a3e7SDag-Erling Smørgrav void 378af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 379af12a3e7SDag-Erling Smørgrav { 380af12a3e7SDag-Erling Smørgrav int i; 381af12a3e7SDag-Erling Smørgrav Channel *c; 382af12a3e7SDag-Erling Smørgrav 383af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 384af12a3e7SDag-Erling Smørgrav c = channels[i]; 385af12a3e7SDag-Erling Smørgrav if (c != NULL) { 386af12a3e7SDag-Erling Smørgrav switch (c->type) { 387af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 388af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 389af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 390af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 391af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 392af12a3e7SDag-Erling Smørgrav channel_free(c); 393af12a3e7SDag-Erling Smørgrav break; 394af12a3e7SDag-Erling Smørgrav } 395af12a3e7SDag-Erling Smørgrav } 396af12a3e7SDag-Erling Smørgrav } 397af12a3e7SDag-Erling Smørgrav } 398af12a3e7SDag-Erling Smørgrav 399af12a3e7SDag-Erling Smørgrav /* 400af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 401af12a3e7SDag-Erling Smørgrav * more channel is overfull. 402af12a3e7SDag-Erling Smørgrav */ 403af12a3e7SDag-Erling Smørgrav 404af12a3e7SDag-Erling Smørgrav int 405af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 406af12a3e7SDag-Erling Smørgrav { 407af12a3e7SDag-Erling Smørgrav u_int i; 408af12a3e7SDag-Erling Smørgrav Channel *c; 409af12a3e7SDag-Erling Smørgrav 410af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 411af12a3e7SDag-Erling Smørgrav c = channels[i]; 412af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 413af12a3e7SDag-Erling Smørgrav #if 0 414af12a3e7SDag-Erling Smørgrav if (!compat20 && 415af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 416af12a3e7SDag-Erling Smørgrav debug("channel %d: big input buffer %d", 417af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 418af12a3e7SDag-Erling Smørgrav return 0; 419af12a3e7SDag-Erling Smørgrav } 420af12a3e7SDag-Erling Smørgrav #endif 421af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 422af12a3e7SDag-Erling Smørgrav debug("channel %d: big output buffer %d > %d", 423af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 424af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 425af12a3e7SDag-Erling Smørgrav return 0; 426af12a3e7SDag-Erling Smørgrav } 427af12a3e7SDag-Erling Smørgrav } 428af12a3e7SDag-Erling Smørgrav } 429af12a3e7SDag-Erling Smørgrav return 1; 430af12a3e7SDag-Erling Smørgrav } 431af12a3e7SDag-Erling Smørgrav 432af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 433af12a3e7SDag-Erling Smørgrav 434af12a3e7SDag-Erling Smørgrav int 435af12a3e7SDag-Erling Smørgrav channel_still_open(void) 436af12a3e7SDag-Erling Smørgrav { 437af12a3e7SDag-Erling Smørgrav int i; 438af12a3e7SDag-Erling Smørgrav Channel *c; 439af12a3e7SDag-Erling Smørgrav 440af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 441af12a3e7SDag-Erling Smørgrav c = channels[i]; 442af12a3e7SDag-Erling Smørgrav if (c == NULL) 443af12a3e7SDag-Erling Smørgrav continue; 444af12a3e7SDag-Erling Smørgrav switch (c->type) { 445af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 446af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 447af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 448af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 449af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 450af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 451af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 452af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 453af12a3e7SDag-Erling Smørgrav continue; 454af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 455af12a3e7SDag-Erling Smørgrav if (!compat20) 456af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 457af12a3e7SDag-Erling Smørgrav continue; 458af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 459af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 460af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 461af12a3e7SDag-Erling Smørgrav return 1; 462af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 463af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 464af12a3e7SDag-Erling Smørgrav if (!compat13) 465af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 466af12a3e7SDag-Erling Smørgrav return 1; 467af12a3e7SDag-Erling Smørgrav default: 468af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 469af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 470af12a3e7SDag-Erling Smørgrav } 471af12a3e7SDag-Erling Smørgrav } 472af12a3e7SDag-Erling Smørgrav return 0; 473af12a3e7SDag-Erling Smørgrav } 474af12a3e7SDag-Erling Smørgrav 475af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 476af12a3e7SDag-Erling Smørgrav 477af12a3e7SDag-Erling Smørgrav int 478af12a3e7SDag-Erling Smørgrav channel_find_open(void) 479af12a3e7SDag-Erling Smørgrav { 480af12a3e7SDag-Erling Smørgrav int i; 481af12a3e7SDag-Erling Smørgrav Channel *c; 482af12a3e7SDag-Erling Smørgrav 483af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 484af12a3e7SDag-Erling Smørgrav c = channels[i]; 485af12a3e7SDag-Erling Smørgrav if (c == NULL) 486af12a3e7SDag-Erling Smørgrav continue; 487af12a3e7SDag-Erling Smørgrav switch (c->type) { 488af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 489af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 490af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 491af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 492af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 493af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 494af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 495af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 496af12a3e7SDag-Erling Smørgrav continue; 497af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 498af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 499af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 500af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 501af12a3e7SDag-Erling Smørgrav return i; 502af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 503af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 504af12a3e7SDag-Erling Smørgrav if (!compat13) 505af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 506af12a3e7SDag-Erling Smørgrav return i; 507af12a3e7SDag-Erling Smørgrav default: 508af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 509af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 510af12a3e7SDag-Erling Smørgrav } 511af12a3e7SDag-Erling Smørgrav } 512af12a3e7SDag-Erling Smørgrav return -1; 513af12a3e7SDag-Erling Smørgrav } 514af12a3e7SDag-Erling Smørgrav 515af12a3e7SDag-Erling Smørgrav 516af12a3e7SDag-Erling Smørgrav /* 517af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 518af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 519af12a3e7SDag-Erling Smørgrav * newlines. 520af12a3e7SDag-Erling Smørgrav */ 521af12a3e7SDag-Erling Smørgrav 522af12a3e7SDag-Erling Smørgrav char * 523af12a3e7SDag-Erling Smørgrav channel_open_message(void) 524af12a3e7SDag-Erling Smørgrav { 525af12a3e7SDag-Erling Smørgrav Buffer buffer; 526af12a3e7SDag-Erling Smørgrav Channel *c; 527af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 528af12a3e7SDag-Erling Smørgrav int i; 529af12a3e7SDag-Erling Smørgrav 530af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 531af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 532af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 533af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 534af12a3e7SDag-Erling Smørgrav c = channels[i]; 535af12a3e7SDag-Erling Smørgrav if (c == NULL) 536af12a3e7SDag-Erling Smørgrav continue; 537af12a3e7SDag-Erling Smørgrav switch (c->type) { 538af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 539af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 540af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 541af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 542af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 543af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 544af12a3e7SDag-Erling Smørgrav continue; 545af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 546af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 547af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 548af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 549af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 550af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 551af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 552af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 553af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 554af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 555af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 556af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 557af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 558af12a3e7SDag-Erling Smørgrav c->rfd, c->wfd); 559af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 560af12a3e7SDag-Erling Smørgrav continue; 561af12a3e7SDag-Erling Smørgrav default: 562af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 563af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 564af12a3e7SDag-Erling Smørgrav } 565af12a3e7SDag-Erling Smørgrav } 566af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 567af12a3e7SDag-Erling Smørgrav cp = xstrdup(buffer_ptr(&buffer)); 568af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 569af12a3e7SDag-Erling Smørgrav return cp; 570af12a3e7SDag-Erling Smørgrav } 571af12a3e7SDag-Erling Smørgrav 572af12a3e7SDag-Erling Smørgrav void 573af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 574af12a3e7SDag-Erling Smørgrav { 575af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 576af12a3e7SDag-Erling Smørgrav if (c == NULL) { 577af12a3e7SDag-Erling Smørgrav log("channel_send_open: %d: bad id", id); 578af12a3e7SDag-Erling Smørgrav return; 579af12a3e7SDag-Erling Smørgrav } 580af12a3e7SDag-Erling Smørgrav debug("send channel open %d", id); 581af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 582af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 583af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 584af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 585af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 586af12a3e7SDag-Erling Smørgrav packet_send(); 587af12a3e7SDag-Erling Smørgrav } 588af12a3e7SDag-Erling Smørgrav 589af12a3e7SDag-Erling Smørgrav void 590af12a3e7SDag-Erling Smørgrav channel_request_start(int local_id, char *service, int wantconfirm) 591af12a3e7SDag-Erling Smørgrav { 592af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(local_id); 593af12a3e7SDag-Erling Smørgrav if (c == NULL) { 594af12a3e7SDag-Erling Smørgrav log("channel_request_start: %d: unknown channel id", local_id); 595af12a3e7SDag-Erling Smørgrav return; 596af12a3e7SDag-Erling Smørgrav } 597af12a3e7SDag-Erling Smørgrav debug("channel request %d: %s", local_id, service) ; 598af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 599af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 600af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 601af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 602af12a3e7SDag-Erling Smørgrav } 603af12a3e7SDag-Erling Smørgrav void 604af12a3e7SDag-Erling Smørgrav channel_register_confirm(int id, channel_callback_fn *fn) 605af12a3e7SDag-Erling Smørgrav { 606af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 607af12a3e7SDag-Erling Smørgrav if (c == NULL) { 608af12a3e7SDag-Erling Smørgrav log("channel_register_comfirm: %d: bad id", id); 609af12a3e7SDag-Erling Smørgrav return; 610af12a3e7SDag-Erling Smørgrav } 611af12a3e7SDag-Erling Smørgrav c->confirm = fn; 612af12a3e7SDag-Erling Smørgrav } 613af12a3e7SDag-Erling Smørgrav void 614af12a3e7SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn) 615af12a3e7SDag-Erling Smørgrav { 616af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 617af12a3e7SDag-Erling Smørgrav if (c == NULL) { 618af12a3e7SDag-Erling Smørgrav log("channel_register_cleanup: %d: bad id", id); 619af12a3e7SDag-Erling Smørgrav return; 620af12a3e7SDag-Erling Smørgrav } 621af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 622af12a3e7SDag-Erling Smørgrav } 623af12a3e7SDag-Erling Smørgrav void 624af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 625af12a3e7SDag-Erling Smørgrav { 626af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 627af12a3e7SDag-Erling Smørgrav if (c == NULL) { 628af12a3e7SDag-Erling Smørgrav log("channel_cancel_cleanup: %d: bad id", id); 629af12a3e7SDag-Erling Smørgrav return; 630af12a3e7SDag-Erling Smørgrav } 631af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 632af12a3e7SDag-Erling Smørgrav } 633af12a3e7SDag-Erling Smørgrav void 634af12a3e7SDag-Erling Smørgrav channel_register_filter(int id, channel_filter_fn *fn) 635af12a3e7SDag-Erling Smørgrav { 636af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 637af12a3e7SDag-Erling Smørgrav if (c == NULL) { 638af12a3e7SDag-Erling Smørgrav log("channel_register_filter: %d: bad id", id); 639af12a3e7SDag-Erling Smørgrav return; 640af12a3e7SDag-Erling Smørgrav } 641af12a3e7SDag-Erling Smørgrav c->input_filter = fn; 642af12a3e7SDag-Erling Smørgrav } 643af12a3e7SDag-Erling Smørgrav 644af12a3e7SDag-Erling Smørgrav void 645af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 646af12a3e7SDag-Erling Smørgrav int extusage, int nonblock, u_int window_max) 647af12a3e7SDag-Erling Smørgrav { 648af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 649af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 650af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 651af12a3e7SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 652af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 653af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 654af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 655af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 656af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 657af12a3e7SDag-Erling Smørgrav packet_send(); 658511b41d2SMark Murray } 659511b41d2SMark Murray 660511b41d2SMark Murray /* 661a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 662a04a10f8SKris Kennaway * channels in the select bitmasks. 663511b41d2SMark Murray */ 664a04a10f8SKris Kennaway /* 665a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 666a04a10f8SKris Kennaway * have events pending. 667a04a10f8SKris Kennaway */ 668a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 669a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 670a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 671511b41d2SMark Murray 672af12a3e7SDag-Erling Smørgrav static void 673a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 674511b41d2SMark Murray { 675a04a10f8SKris Kennaway FD_SET(c->sock, readset); 676a04a10f8SKris Kennaway } 677a04a10f8SKris Kennaway 678af12a3e7SDag-Erling Smørgrav static void 679ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) 680ca3176e7SBrian Feldman { 681ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 682ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 683ca3176e7SBrian Feldman } 684ca3176e7SBrian Feldman 685af12a3e7SDag-Erling Smørgrav static void 686a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 687a04a10f8SKris Kennaway { 688a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 689a04a10f8SKris Kennaway FD_SET(c->sock, readset); 690a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 691a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 692a04a10f8SKris Kennaway } 693a04a10f8SKris Kennaway 694af12a3e7SDag-Erling Smørgrav static void 695af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) 696a04a10f8SKris Kennaway { 697af12a3e7SDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 698a04a10f8SKris Kennaway 699a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 700af12a3e7SDag-Erling Smørgrav limit > 0 && 701af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) < limit) 702a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 703a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 704a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 705a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 706a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 707a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 70880628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 70980628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 71080628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 71180628bacSDag-Erling Smørgrav else 712a04a10f8SKris Kennaway chan_obuf_empty(c); 713a04a10f8SKris Kennaway } 714a04a10f8SKris Kennaway } 715a04a10f8SKris Kennaway /** XXX check close conditions, too */ 716af12a3e7SDag-Erling Smørgrav if (compat20 && c->efd != -1) { 717a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 718a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 719a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 72080628bacSDag-Erling Smørgrav else if (!(c->flags & CHAN_EOF_SENT) && 72180628bacSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ && 722a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 723a04a10f8SKris Kennaway FD_SET(c->efd, readset); 724a04a10f8SKris Kennaway } 725a04a10f8SKris Kennaway } 726a04a10f8SKris Kennaway 727af12a3e7SDag-Erling Smørgrav static void 728a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 729a04a10f8SKris Kennaway { 730a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 731a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 732a04a10f8SKris Kennaway packet_put_int(c->remote_id); 733a04a10f8SKris Kennaway packet_send(); 734a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 735ca3176e7SBrian Feldman debug("channel %d: closing after input drain.", c->self); 736a04a10f8SKris Kennaway } 737a04a10f8SKris Kennaway } 738a04a10f8SKris Kennaway 739af12a3e7SDag-Erling Smørgrav static void 740a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 741a04a10f8SKris Kennaway { 742a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 743af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 744a04a10f8SKris Kennaway else 745a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 746a04a10f8SKris Kennaway } 747a04a10f8SKris Kennaway 748a04a10f8SKris Kennaway /* 749a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 750a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 751a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 752a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 753a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 754a04a10f8SKris Kennaway * XXX All this happens at the client side. 755af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 756a04a10f8SKris Kennaway */ 757af12a3e7SDag-Erling Smørgrav static int 758af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 759a04a10f8SKris Kennaway { 760ca3176e7SBrian Feldman u_char *ucp; 761ca3176e7SBrian Feldman u_int proto_len, data_len; 762511b41d2SMark Murray 763511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 764af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 765a04a10f8SKris Kennaway return 0; 766511b41d2SMark Murray 767511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 768af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 769511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 770511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 771511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 772511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 773511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 774511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 775511b41d2SMark Murray } else { 776511b41d2SMark Murray debug("Initial X11 packet contains bad byte order byte: 0x%x", 777511b41d2SMark Murray ucp[0]); 778a04a10f8SKris Kennaway return -1; 779511b41d2SMark Murray } 780511b41d2SMark Murray 781511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 782af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 783511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 784a04a10f8SKris Kennaway return 0; 785511b41d2SMark Murray 786511b41d2SMark Murray /* Check if authentication protocol matches. */ 787511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 788511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 789511b41d2SMark Murray debug("X11 connection uses different authentication protocol."); 790a04a10f8SKris Kennaway return -1; 791511b41d2SMark Murray } 792511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 793511b41d2SMark Murray if (data_len != x11_fake_data_len || 794511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 795511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 796511b41d2SMark Murray debug("X11 auth data does not match fake data."); 797a04a10f8SKris Kennaway return -1; 798511b41d2SMark Murray } 799511b41d2SMark Murray /* Check fake data length */ 800511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 801511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 802511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 803a04a10f8SKris Kennaway return -1; 804511b41d2SMark Murray } 805511b41d2SMark Murray /* 806511b41d2SMark Murray * Received authentication protocol and data match 807511b41d2SMark Murray * our fake data. Substitute the fake data with real 808511b41d2SMark Murray * data. 809511b41d2SMark Murray */ 810511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 811511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 812a04a10f8SKris Kennaway return 1; 813a04a10f8SKris Kennaway } 814511b41d2SMark Murray 815af12a3e7SDag-Erling Smørgrav static void 816a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 817a04a10f8SKris Kennaway { 818af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 819a04a10f8SKris Kennaway if (ret == 1) { 820511b41d2SMark Murray /* Start normal processing for the channel. */ 821a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 822a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 823a04a10f8SKris Kennaway } else if (ret == -1) { 824511b41d2SMark Murray /* 825511b41d2SMark Murray * We have received an X11 connection that has bad 826511b41d2SMark Murray * authentication information. 827511b41d2SMark Murray */ 828ca3176e7SBrian Feldman log("X11 connection rejected because of wrong authentication."); 829a04a10f8SKris Kennaway buffer_clear(&c->input); 830a04a10f8SKris Kennaway buffer_clear(&c->output); 831af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 832a04a10f8SKris Kennaway c->sock = -1; 833a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 834511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 835a04a10f8SKris Kennaway packet_put_int(c->remote_id); 836511b41d2SMark Murray packet_send(); 837511b41d2SMark Murray } 838511b41d2SMark Murray } 839511b41d2SMark Murray 840af12a3e7SDag-Erling Smørgrav static void 841a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 842a04a10f8SKris Kennaway { 843af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 844af12a3e7SDag-Erling Smørgrav 845af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 846af12a3e7SDag-Erling Smørgrav 847a04a10f8SKris Kennaway if (ret == 1) { 848a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 849af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 850a04a10f8SKris Kennaway } else if (ret == -1) { 851af12a3e7SDag-Erling Smørgrav log("X11 connection rejected because of wrong authentication."); 852a04a10f8SKris Kennaway debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 853af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 854af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 855af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 856af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 857af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 858af12a3e7SDag-Erling Smørgrav if (compat20) 859a04a10f8SKris Kennaway chan_write_failed(c); 860af12a3e7SDag-Erling Smørgrav else 861af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 862a04a10f8SKris Kennaway debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 863a04a10f8SKris Kennaway } 864a04a10f8SKris Kennaway } 865a04a10f8SKris Kennaway 866ca3176e7SBrian Feldman /* try to decode a socks4 header */ 867af12a3e7SDag-Erling Smørgrav static int 868ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) 869ca3176e7SBrian Feldman { 870ca3176e7SBrian Feldman u_char *p, *host; 871ca3176e7SBrian Feldman int len, have, i, found; 872ca3176e7SBrian Feldman char username[256]; 873ca3176e7SBrian Feldman struct { 874ca3176e7SBrian Feldman u_int8_t version; 875ca3176e7SBrian Feldman u_int8_t command; 876ca3176e7SBrian Feldman u_int16_t dest_port; 877ca3176e7SBrian Feldman struct in_addr dest_addr; 878ca3176e7SBrian Feldman } s4_req, s4_rsp; 879ca3176e7SBrian Feldman 880ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 881ca3176e7SBrian Feldman 882ca3176e7SBrian Feldman have = buffer_len(&c->input); 883ca3176e7SBrian Feldman len = sizeof(s4_req); 884ca3176e7SBrian Feldman if (have < len) 885ca3176e7SBrian Feldman return 0; 886ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 887ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 888ca3176e7SBrian Feldman if (p[i] == '\0') { 889ca3176e7SBrian Feldman found = 1; 890ca3176e7SBrian Feldman break; 891ca3176e7SBrian Feldman } 892ca3176e7SBrian Feldman if (i > 1024) { 893ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 894ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 895ca3176e7SBrian Feldman c->self); 896ca3176e7SBrian Feldman return -1; 897ca3176e7SBrian Feldman } 898ca3176e7SBrian Feldman } 899ca3176e7SBrian Feldman if (!found) 900ca3176e7SBrian Feldman return 0; 901ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 902ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 903ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 904ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 905ca3176e7SBrian Feldman have = buffer_len(&c->input); 906ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 907ca3176e7SBrian Feldman len = strlen(p); 908ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 909ca3176e7SBrian Feldman if (len > have) 910ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 911ca3176e7SBrian Feldman c->self, len, have); 912ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 913ca3176e7SBrian Feldman buffer_consume(&c->input, len); 914ca3176e7SBrian Feldman buffer_consume(&c->input, 1); /* trailing '\0' */ 915ca3176e7SBrian Feldman 916ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 917ca3176e7SBrian Feldman strlcpy(c->path, host, sizeof(c->path)); 918ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 919ca3176e7SBrian Feldman 920ca3176e7SBrian Feldman debug("channel %d: dynamic request: socks4 host %s port %u command %u", 921ca3176e7SBrian Feldman c->self, host, c->host_port, s4_req.command); 922ca3176e7SBrian Feldman 923ca3176e7SBrian Feldman if (s4_req.command != 1) { 924ca3176e7SBrian Feldman debug("channel %d: cannot handle: socks4 cn %d", 925ca3176e7SBrian Feldman c->self, s4_req.command); 926ca3176e7SBrian Feldman return -1; 927ca3176e7SBrian Feldman } 928ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 929ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 930ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 931ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 932ca3176e7SBrian Feldman buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); 933ca3176e7SBrian Feldman return 1; 934ca3176e7SBrian Feldman } 935ca3176e7SBrian Feldman 936ca3176e7SBrian Feldman /* dynamic port forwarding */ 937af12a3e7SDag-Erling Smørgrav static void 938ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 939ca3176e7SBrian Feldman { 940ca3176e7SBrian Feldman u_char *p; 941ca3176e7SBrian Feldman int have, ret; 942ca3176e7SBrian Feldman 943ca3176e7SBrian Feldman have = buffer_len(&c->input); 944af12a3e7SDag-Erling Smørgrav c->delayed = 0; 945ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 946ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 947ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 948ca3176e7SBrian Feldman if (have < 4) { 949ca3176e7SBrian Feldman /* need more */ 950ca3176e7SBrian Feldman FD_SET(c->sock, readset); 951ca3176e7SBrian Feldman return; 952ca3176e7SBrian Feldman } 953ca3176e7SBrian Feldman /* try to guess the protocol */ 954ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 955ca3176e7SBrian Feldman switch (p[0]) { 956ca3176e7SBrian Feldman case 0x04: 957ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 958ca3176e7SBrian Feldman break; 959ca3176e7SBrian Feldman default: 960ca3176e7SBrian Feldman ret = -1; 961ca3176e7SBrian Feldman break; 962ca3176e7SBrian Feldman } 963ca3176e7SBrian Feldman if (ret < 0) { 964af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 965ca3176e7SBrian Feldman } else if (ret == 0) { 966ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 967ca3176e7SBrian Feldman /* need more */ 968ca3176e7SBrian Feldman FD_SET(c->sock, readset); 969ca3176e7SBrian Feldman } else { 970ca3176e7SBrian Feldman /* switch to the next state */ 971ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 972ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 973ca3176e7SBrian Feldman } 974ca3176e7SBrian Feldman } 975ca3176e7SBrian Feldman 976a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 977af12a3e7SDag-Erling Smørgrav static void 978a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 979511b41d2SMark Murray { 980af12a3e7SDag-Erling Smørgrav Channel *nc; 981511b41d2SMark Murray struct sockaddr addr; 982af12a3e7SDag-Erling Smørgrav int newsock; 983511b41d2SMark Murray socklen_t addrlen; 984ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 985a04a10f8SKris Kennaway int remote_port; 986511b41d2SMark Murray 987a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 988511b41d2SMark Murray debug("X11 connection requested."); 989511b41d2SMark Murray addrlen = sizeof(addr); 990a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 991af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 992af12a3e7SDag-Erling Smørgrav debug("single_connection: closing X11 listener."); 993af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 994af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 995af12a3e7SDag-Erling Smørgrav } 996511b41d2SMark Murray if (newsock < 0) { 997511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 998a04a10f8SKris Kennaway return; 999511b41d2SMark Murray } 1000af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1001ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1002a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1003511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1004ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1005a04a10f8SKris Kennaway 1006af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1007a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1008a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 10095b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 1010a04a10f8SKris Kennaway if (compat20) { 1011a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1012a04a10f8SKris Kennaway packet_put_cstring("x11"); 1013af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1014af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1015af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1016ca3176e7SBrian Feldman /* originator ipaddr and port */ 1017ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1018a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1019a04a10f8SKris Kennaway debug("ssh2 x11 bug compat mode"); 1020a04a10f8SKris Kennaway } else { 1021a04a10f8SKris Kennaway packet_put_int(remote_port); 1022a04a10f8SKris Kennaway } 1023a04a10f8SKris Kennaway packet_send(); 1024a04a10f8SKris Kennaway } else { 1025511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1026af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1027af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1028af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1029af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1030511b41d2SMark Murray packet_send(); 1031511b41d2SMark Murray } 1032ca3176e7SBrian Feldman xfree(remote_ipaddr); 1033a04a10f8SKris Kennaway } 1034a04a10f8SKris Kennaway } 1035511b41d2SMark Murray 1036af12a3e7SDag-Erling Smørgrav static void 1037ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1038ca3176e7SBrian Feldman { 1039ca3176e7SBrian Feldman int direct; 1040ca3176e7SBrian Feldman char buf[1024]; 1041ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1042ca3176e7SBrian Feldman u_short remote_port = get_peer_port(c->sock); 1043ca3176e7SBrian Feldman 1044ca3176e7SBrian Feldman direct = (strcmp(rtype, "direct-tcpip") == 0); 1045ca3176e7SBrian Feldman 1046ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1047ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1048ca3176e7SBrian Feldman "connect from %.200s port %d", 1049ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1050ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1051ca3176e7SBrian Feldman 1052ca3176e7SBrian Feldman xfree(c->remote_name); 1053ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1054ca3176e7SBrian Feldman 1055ca3176e7SBrian Feldman if (compat20) { 1056ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1057ca3176e7SBrian Feldman packet_put_cstring(rtype); 1058ca3176e7SBrian Feldman packet_put_int(c->self); 1059ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1060ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1061ca3176e7SBrian Feldman if (direct) { 1062ca3176e7SBrian Feldman /* target host, port */ 1063ca3176e7SBrian Feldman packet_put_cstring(c->path); 1064ca3176e7SBrian Feldman packet_put_int(c->host_port); 1065ca3176e7SBrian Feldman } else { 1066ca3176e7SBrian Feldman /* listen address, port */ 1067ca3176e7SBrian Feldman packet_put_cstring(c->path); 1068ca3176e7SBrian Feldman packet_put_int(c->listening_port); 1069ca3176e7SBrian Feldman } 1070ca3176e7SBrian Feldman /* originator host and port */ 1071ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1072ca3176e7SBrian Feldman packet_put_int(remote_port); 1073ca3176e7SBrian Feldman packet_send(); 1074ca3176e7SBrian Feldman } else { 1075ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1076ca3176e7SBrian Feldman packet_put_int(c->self); 1077ca3176e7SBrian Feldman packet_put_cstring(c->path); 1078ca3176e7SBrian Feldman packet_put_int(c->host_port); 1079af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1080af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1081ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1082ca3176e7SBrian Feldman packet_send(); 1083ca3176e7SBrian Feldman } 1084ca3176e7SBrian Feldman xfree(remote_ipaddr); 1085ca3176e7SBrian Feldman } 1086ca3176e7SBrian Feldman 1087511b41d2SMark Murray /* 1088a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1089511b41d2SMark Murray */ 1090af12a3e7SDag-Erling Smørgrav static void 1091a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 1092a04a10f8SKris Kennaway { 1093ca3176e7SBrian Feldman Channel *nc; 1094a04a10f8SKris Kennaway struct sockaddr addr; 1095af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1096a04a10f8SKris Kennaway socklen_t addrlen; 1097ca3176e7SBrian Feldman char *rtype; 1098a04a10f8SKris Kennaway 1099a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1100a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1101a04a10f8SKris Kennaway "to %.100s port %d requested.", 1102a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1103ca3176e7SBrian Feldman 1104af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1105af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1106af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1107af12a3e7SDag-Erling Smørgrav } else { 1108af12a3e7SDag-Erling Smørgrav if (c->host_port == 0) { 1109af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1110af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1111af12a3e7SDag-Erling Smørgrav } else { 1112af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1113af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1114af12a3e7SDag-Erling Smørgrav } 1115af12a3e7SDag-Erling Smørgrav } 1116ca3176e7SBrian Feldman 1117511b41d2SMark Murray addrlen = sizeof(addr); 1118a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1119511b41d2SMark Murray if (newsock < 0) { 1120511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1121a04a10f8SKris Kennaway return; 1122511b41d2SMark Murray } 1123af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1124af12a3e7SDag-Erling Smørgrav nc = channel_new(rtype, 1125ca3176e7SBrian Feldman nextstate, newsock, newsock, -1, 1126a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 1127ca3176e7SBrian Feldman 0, xstrdup(rtype), 1); 1128ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1129ca3176e7SBrian Feldman nc->host_port = c->host_port; 1130ca3176e7SBrian Feldman strlcpy(nc->path, c->path, sizeof(nc->path)); 1131ca3176e7SBrian Feldman 1132af12a3e7SDag-Erling Smørgrav if (nextstate == SSH_CHANNEL_DYNAMIC) { 1133af12a3e7SDag-Erling Smørgrav /* 1134af12a3e7SDag-Erling Smørgrav * do not call the channel_post handler until 1135af12a3e7SDag-Erling Smørgrav * this flag has been reset by a pre-handler. 1136af12a3e7SDag-Erling Smørgrav * otherwise the FD_ISSET calls might overflow 1137af12a3e7SDag-Erling Smørgrav */ 1138af12a3e7SDag-Erling Smørgrav nc->delayed = 1; 1139af12a3e7SDag-Erling Smørgrav } else { 1140ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1141a04a10f8SKris Kennaway } 1142a04a10f8SKris Kennaway } 1143af12a3e7SDag-Erling Smørgrav } 1144511b41d2SMark Murray 1145511b41d2SMark Murray /* 1146a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1147a04a10f8SKris Kennaway * clients. 1148511b41d2SMark Murray */ 1149af12a3e7SDag-Erling Smørgrav static void 1150a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 1151a04a10f8SKris Kennaway { 1152af12a3e7SDag-Erling Smørgrav Channel *nc; 1153af12a3e7SDag-Erling Smørgrav char *name; 1154af12a3e7SDag-Erling Smørgrav int newsock; 1155a04a10f8SKris Kennaway struct sockaddr addr; 1156a04a10f8SKris Kennaway socklen_t addrlen; 1157a04a10f8SKris Kennaway 1158a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1159511b41d2SMark Murray addrlen = sizeof(addr); 1160a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1161511b41d2SMark Murray if (newsock < 0) { 1162511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 1163a04a10f8SKris Kennaway return; 1164511b41d2SMark Murray } 1165af12a3e7SDag-Erling Smørgrav name = xstrdup("accepted auth socket"); 1166af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1167ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1168ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1169af12a3e7SDag-Erling Smørgrav 0, name, 1); 1170ca3176e7SBrian Feldman if (compat20) { 1171ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1172ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1173af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1174ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1175ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1176ca3176e7SBrian Feldman } else { 1177511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1178af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1179ca3176e7SBrian Feldman } 1180511b41d2SMark Murray packet_send(); 1181511b41d2SMark Murray } 1182a04a10f8SKris Kennaway } 1183511b41d2SMark Murray 1184af12a3e7SDag-Erling Smørgrav static void 1185ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 1186ca3176e7SBrian Feldman { 1187ca3176e7SBrian Feldman int err = 0; 1188af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1189af12a3e7SDag-Erling Smørgrav 1190af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1191af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1192af12a3e7SDag-Erling Smørgrav err = errno; 1193af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1194af12a3e7SDag-Erling Smørgrav } 1195ca3176e7SBrian Feldman if (err == 0) { 1196af12a3e7SDag-Erling Smørgrav debug("channel %d: connected", c->self); 1197af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1198af12a3e7SDag-Erling Smørgrav if (compat20) { 1199af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1200af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1201af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1202af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1203af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1204af12a3e7SDag-Erling Smørgrav } else { 1205af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1206af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1207af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1208af12a3e7SDag-Erling Smørgrav } 1209ca3176e7SBrian Feldman } else { 1210ca3176e7SBrian Feldman debug("channel %d: not connected: %s", 1211ca3176e7SBrian Feldman c->self, strerror(err)); 1212af12a3e7SDag-Erling Smørgrav if (compat20) { 1213af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1214af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1215af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1216af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1217af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1218af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1219ca3176e7SBrian Feldman } 1220af12a3e7SDag-Erling Smørgrav } else { 1221af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1222af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1223ca3176e7SBrian Feldman } 1224af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1225af12a3e7SDag-Erling Smørgrav } 1226af12a3e7SDag-Erling Smørgrav packet_send(); 1227ca3176e7SBrian Feldman } 1228ca3176e7SBrian Feldman } 1229ca3176e7SBrian Feldman 1230af12a3e7SDag-Erling Smørgrav static int 1231a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 1232a04a10f8SKris Kennaway { 1233a04a10f8SKris Kennaway char buf[16*1024]; 1234a04a10f8SKris Kennaway int len; 1235511b41d2SMark Murray 1236a04a10f8SKris Kennaway if (c->rfd != -1 && 1237a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 1238a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1239a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1240a04a10f8SKris Kennaway return 1; 12410c82706bSBrian Feldman if (len <= 0) { 12420c82706bSBrian Feldman debug("channel %d: read<=0 rfd %d len %d", 1243a04a10f8SKris Kennaway c->self, c->rfd, len); 1244ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1245ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1246af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1247ca3176e7SBrian Feldman return -1; 1248ca3176e7SBrian Feldman } else if (compat13) { 1249af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1250a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1251af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1252a04a10f8SKris Kennaway } else { 1253a04a10f8SKris Kennaway chan_read_failed(c); 1254a04a10f8SKris Kennaway } 1255a04a10f8SKris Kennaway return -1; 1256a04a10f8SKris Kennaway } 1257b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1258b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1259ca3176e7SBrian Feldman debug("channel %d: filter stops", c->self); 1260b66f2d16SKris Kennaway chan_read_failed(c); 1261b66f2d16SKris Kennaway } 1262b66f2d16SKris Kennaway } else { 1263a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1264a04a10f8SKris Kennaway } 1265b66f2d16SKris Kennaway } 1266a04a10f8SKris Kennaway return 1; 1267a04a10f8SKris Kennaway } 1268af12a3e7SDag-Erling Smørgrav static int 1269a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 1270a04a10f8SKris Kennaway { 1271ca3176e7SBrian Feldman struct termios tio; 1272af12a3e7SDag-Erling Smørgrav u_char *data; 1273af12a3e7SDag-Erling Smørgrav u_int dlen; 1274a04a10f8SKris Kennaway int len; 1275a04a10f8SKris Kennaway 1276a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1277a04a10f8SKris Kennaway if (c->wfd != -1 && 1278a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1279a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1280af12a3e7SDag-Erling Smørgrav data = buffer_ptr(&c->output); 1281af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1282af12a3e7SDag-Erling Smørgrav len = write(c->wfd, data, dlen); 1283a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1284a04a10f8SKris Kennaway return 1; 1285511b41d2SMark Murray if (len <= 0) { 1286ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1287ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1288af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1289ca3176e7SBrian Feldman return -1; 1290ca3176e7SBrian Feldman } else if (compat13) { 1291af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1292af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1293a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1294511b41d2SMark Murray } else { 1295a04a10f8SKris Kennaway chan_write_failed(c); 1296511b41d2SMark Murray } 1297a04a10f8SKris Kennaway return -1; 1298511b41d2SMark Murray } 1299af12a3e7SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { 1300e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1301e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1302e0fbb1d2SBrian Feldman /* 1303e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1304ca3176e7SBrian Feldman * traffic analysis. We need to match the 1305ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1306ca3176e7SBrian Feldman * (4 byte channel id + data) 1307e0fbb1d2SBrian Feldman */ 1308ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1309e0fbb1d2SBrian Feldman packet_send(); 1310e0fbb1d2SBrian Feldman } 1311e0fbb1d2SBrian Feldman } 1312a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1313a04a10f8SKris Kennaway if (compat20 && len > 0) { 1314a04a10f8SKris Kennaway c->local_consumed += len; 1315511b41d2SMark Murray } 1316511b41d2SMark Murray } 1317a04a10f8SKris Kennaway return 1; 1318511b41d2SMark Murray } 1319af12a3e7SDag-Erling Smørgrav static int 1320a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 1321a04a10f8SKris Kennaway { 1322a04a10f8SKris Kennaway char buf[16*1024]; 1323a04a10f8SKris Kennaway int len; 1324511b41d2SMark Murray 1325a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1326a04a10f8SKris Kennaway if (c->efd != -1) { 1327a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1328a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1329a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1330a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1331a04a10f8SKris Kennaway buffer_len(&c->extended)); 13325b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1333a04a10f8SKris Kennaway c->self, len, c->efd); 1334ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1335ca3176e7SBrian Feldman return 1; 1336ca3176e7SBrian Feldman if (len <= 0) { 1337ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1338ca3176e7SBrian Feldman c->self, c->efd); 1339af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1340ca3176e7SBrian Feldman } else { 1341a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1342a04a10f8SKris Kennaway c->local_consumed += len; 1343a04a10f8SKris Kennaway } 1344a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 1345a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 1346a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 13475b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1348a04a10f8SKris Kennaway c->self, len, c->efd); 1349ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1350ca3176e7SBrian Feldman return 1; 1351ca3176e7SBrian Feldman if (len <= 0) { 1352ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1353a04a10f8SKris Kennaway c->self, c->efd); 1354af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1355ca3176e7SBrian Feldman } else { 1356a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1357a04a10f8SKris Kennaway } 1358a04a10f8SKris Kennaway } 1359ca3176e7SBrian Feldman } 1360a04a10f8SKris Kennaway return 1; 1361a04a10f8SKris Kennaway } 1362af12a3e7SDag-Erling Smørgrav static int 1363ca3176e7SBrian Feldman channel_check_window(Channel *c) 1364a04a10f8SKris Kennaway { 1365ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1366ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1367a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 1368a04a10f8SKris Kennaway c->local_consumed > 0) { 1369a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1370a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1371a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 1372a04a10f8SKris Kennaway packet_send(); 13735b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1374a04a10f8SKris Kennaway c->self, c->local_window, 1375a04a10f8SKris Kennaway c->local_consumed); 1376a04a10f8SKris Kennaway c->local_window += c->local_consumed; 1377a04a10f8SKris Kennaway c->local_consumed = 0; 1378a04a10f8SKris Kennaway } 1379a04a10f8SKris Kennaway return 1; 1380a04a10f8SKris Kennaway } 1381a04a10f8SKris Kennaway 1382af12a3e7SDag-Erling Smørgrav static void 1383af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 1384a04a10f8SKris Kennaway { 1385af12a3e7SDag-Erling Smørgrav if (c->delayed) 1386af12a3e7SDag-Erling Smørgrav return; 1387a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1388a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1389af12a3e7SDag-Erling Smørgrav if (!compat20) 1390af12a3e7SDag-Erling Smørgrav return; 1391a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1392ca3176e7SBrian Feldman channel_check_window(c); 1393a04a10f8SKris Kennaway } 1394a04a10f8SKris Kennaway 1395af12a3e7SDag-Erling Smørgrav static void 1396a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 1397a04a10f8SKris Kennaway { 1398a04a10f8SKris Kennaway int len; 1399511b41d2SMark Murray /* Send buffered output data to the socket. */ 1400a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 1401a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 1402a04a10f8SKris Kennaway buffer_len(&c->output)); 1403511b41d2SMark Murray if (len <= 0) 1404af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1405511b41d2SMark Murray else 1406a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1407511b41d2SMark Murray } 1408a04a10f8SKris Kennaway } 1409511b41d2SMark Murray 1410af12a3e7SDag-Erling Smørgrav static void 1411a04a10f8SKris Kennaway channel_handler_init_20(void) 1412a04a10f8SKris Kennaway { 1413af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1414a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1415a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1416ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 1417a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1418ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1419ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1420ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1421a04a10f8SKris Kennaway 1422af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1423a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1424ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 1425a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1426ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1427ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1428af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1429a04a10f8SKris Kennaway } 1430a04a10f8SKris Kennaway 1431af12a3e7SDag-Erling Smørgrav static void 1432a04a10f8SKris Kennaway channel_handler_init_13(void) 1433a04a10f8SKris Kennaway { 1434a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 1435a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 1436a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1437a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1438a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1439a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 1440a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 1441ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1442ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1443a04a10f8SKris Kennaway 1444af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1445a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1446a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1447a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1448a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 1449ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1450af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1451a04a10f8SKris Kennaway } 1452a04a10f8SKris Kennaway 1453af12a3e7SDag-Erling Smørgrav static void 1454a04a10f8SKris Kennaway channel_handler_init_15(void) 1455a04a10f8SKris Kennaway { 1456af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1457a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1458a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1459a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1460a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1461ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1462ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1463a04a10f8SKris Kennaway 1464a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1465a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1466a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1467af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1468ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1469af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1470a04a10f8SKris Kennaway } 1471a04a10f8SKris Kennaway 1472af12a3e7SDag-Erling Smørgrav static void 1473a04a10f8SKris Kennaway channel_handler_init(void) 1474a04a10f8SKris Kennaway { 1475a04a10f8SKris Kennaway int i; 1476a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 1477a04a10f8SKris Kennaway channel_pre[i] = NULL; 1478a04a10f8SKris Kennaway channel_post[i] = NULL; 1479a04a10f8SKris Kennaway } 1480a04a10f8SKris Kennaway if (compat20) 1481a04a10f8SKris Kennaway channel_handler_init_20(); 1482a04a10f8SKris Kennaway else if (compat13) 1483a04a10f8SKris Kennaway channel_handler_init_13(); 1484a04a10f8SKris Kennaway else 1485a04a10f8SKris Kennaway channel_handler_init_15(); 1486a04a10f8SKris Kennaway } 1487a04a10f8SKris Kennaway 1488af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 1489af12a3e7SDag-Erling Smørgrav static void 1490af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 1491af12a3e7SDag-Erling Smørgrav { 1492af12a3e7SDag-Erling Smørgrav if (c == NULL) 1493af12a3e7SDag-Erling Smørgrav return; 1494af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 1495af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 0)) 1496af12a3e7SDag-Erling Smørgrav return; 1497af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: notify user", c->self); 1498af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 1499af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 1500af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 1501af12a3e7SDag-Erling Smørgrav return; 1502af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: user detached", c->self); 1503af12a3e7SDag-Erling Smørgrav } 1504af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 1505af12a3e7SDag-Erling Smørgrav return; 1506af12a3e7SDag-Erling Smørgrav debug("channel %d: garbage collecting", c->self); 1507af12a3e7SDag-Erling Smørgrav channel_free(c); 1508af12a3e7SDag-Erling Smørgrav } 1509af12a3e7SDag-Erling Smørgrav 1510af12a3e7SDag-Erling Smørgrav static void 1511a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 1512a04a10f8SKris Kennaway { 1513a04a10f8SKris Kennaway static int did_init = 0; 1514a04a10f8SKris Kennaway int i; 1515a04a10f8SKris Kennaway Channel *c; 1516a04a10f8SKris Kennaway 1517a04a10f8SKris Kennaway if (!did_init) { 1518a04a10f8SKris Kennaway channel_handler_init(); 1519a04a10f8SKris Kennaway did_init = 1; 1520a04a10f8SKris Kennaway } 1521a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 1522af12a3e7SDag-Erling Smørgrav c = channels[i]; 1523af12a3e7SDag-Erling Smørgrav if (c == NULL) 1524511b41d2SMark Murray continue; 1525af12a3e7SDag-Erling Smørgrav if (ftab[c->type] != NULL) 1526a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 1527af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 1528511b41d2SMark Murray } 1529511b41d2SMark Murray } 1530a04a10f8SKris Kennaway 1531af12a3e7SDag-Erling Smørgrav /* 1532af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 1533af12a3e7SDag-Erling Smørgrav * select bitmasks. 1534af12a3e7SDag-Erling Smørgrav */ 1535a04a10f8SKris Kennaway void 1536ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1537af12a3e7SDag-Erling Smørgrav int *nallocp, int rekeying) 1538a04a10f8SKris Kennaway { 1539ca3176e7SBrian Feldman int n; 1540ca3176e7SBrian Feldman u_int sz; 1541ca3176e7SBrian Feldman 1542ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 1543ca3176e7SBrian Feldman 1544ca3176e7SBrian Feldman sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 1545af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 1546af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 1547af12a3e7SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, sz); 1548af12a3e7SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, sz); 1549af12a3e7SDag-Erling Smørgrav *nallocp = sz; 1550ca3176e7SBrian Feldman } 1551af12a3e7SDag-Erling Smørgrav *maxfdp = n; 1552ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 1553ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 1554ca3176e7SBrian Feldman 1555ca3176e7SBrian Feldman if (!rekeying) 1556ca3176e7SBrian Feldman channel_handler(channel_pre, *readsetp, *writesetp); 1557a04a10f8SKris Kennaway } 1558a04a10f8SKris Kennaway 1559af12a3e7SDag-Erling Smørgrav /* 1560af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 1561af12a3e7SDag-Erling Smørgrav * events pending. 1562af12a3e7SDag-Erling Smørgrav */ 1563a04a10f8SKris Kennaway void 1564a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 1565a04a10f8SKris Kennaway { 1566a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 1567511b41d2SMark Murray } 1568511b41d2SMark Murray 1569af12a3e7SDag-Erling Smørgrav 1570ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 1571511b41d2SMark Murray 1572511b41d2SMark Murray void 1573af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 1574511b41d2SMark Murray { 1575a04a10f8SKris Kennaway Channel *c; 1576a82e551fSDag-Erling Smørgrav int i; 1577a82e551fSDag-Erling Smørgrav u_int len; 1578511b41d2SMark Murray 1579511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1580af12a3e7SDag-Erling Smørgrav c = channels[i]; 1581af12a3e7SDag-Erling Smørgrav if (c == NULL) 1582af12a3e7SDag-Erling Smørgrav continue; 1583511b41d2SMark Murray 1584af12a3e7SDag-Erling Smørgrav /* 1585af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 1586af12a3e7SDag-Erling Smørgrav * incoming data. 1587af12a3e7SDag-Erling Smørgrav */ 1588511b41d2SMark Murray if (compat13) { 1589a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1590a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 1591511b41d2SMark Murray continue; 1592511b41d2SMark Murray } else { 1593a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 1594511b41d2SMark Murray continue; 1595a04a10f8SKris Kennaway } 1596a04a10f8SKris Kennaway if (compat20 && 1597a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 1598ca3176e7SBrian Feldman /* XXX is this true? */ 1599af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 1600511b41d2SMark Murray continue; 1601511b41d2SMark Murray } 1602511b41d2SMark Murray 1603511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 1604ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 1605ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 1606ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 1607af12a3e7SDag-Erling Smørgrav /* 1608af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 1609af12a3e7SDag-Erling Smørgrav * connection. 1610af12a3e7SDag-Erling Smørgrav */ 1611a04a10f8SKris Kennaway if (compat20) { 1612a04a10f8SKris Kennaway if (len > c->remote_window) 1613a04a10f8SKris Kennaway len = c->remote_window; 1614a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1615a04a10f8SKris Kennaway len = c->remote_maxpacket; 1616a04a10f8SKris Kennaway } else { 1617511b41d2SMark Murray if (packet_is_interactive()) { 1618511b41d2SMark Murray if (len > 1024) 1619511b41d2SMark Murray len = 512; 1620511b41d2SMark Murray } else { 1621511b41d2SMark Murray /* Keep the packets at reasonable size. */ 1622511b41d2SMark Murray if (len > packet_get_maxsize()/2) 1623511b41d2SMark Murray len = packet_get_maxsize()/2; 1624511b41d2SMark Murray } 1625a04a10f8SKris Kennaway } 1626a04a10f8SKris Kennaway if (len > 0) { 1627a04a10f8SKris Kennaway packet_start(compat20 ? 1628a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 1629a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1630a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 1631511b41d2SMark Murray packet_send(); 1632a04a10f8SKris Kennaway buffer_consume(&c->input, len); 1633a04a10f8SKris Kennaway c->remote_window -= len; 1634a04a10f8SKris Kennaway } 1635a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1636511b41d2SMark Murray if (compat13) 1637511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1638511b41d2SMark Murray /* 1639511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 164080628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 164180628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 1642511b41d2SMark Murray */ 164380628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 164480628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 164580628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 164680628bacSDag-Erling Smørgrav else 1647a04a10f8SKris Kennaway chan_ibuf_empty(c); 1648a04a10f8SKris Kennaway } 1649a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1650a04a10f8SKris Kennaway if (compat20 && 165180628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 1652a04a10f8SKris Kennaway c->remote_window > 0 && 1653a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1654a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1655a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 1656ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 1657ca3176e7SBrian Feldman c->extended_usage); 1658a04a10f8SKris Kennaway if (len > c->remote_window) 1659a04a10f8SKris Kennaway len = c->remote_window; 1660a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1661a04a10f8SKris Kennaway len = c->remote_maxpacket; 1662a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1663a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1664a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1665a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1666a04a10f8SKris Kennaway packet_send(); 1667a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1668a04a10f8SKris Kennaway c->remote_window -= len; 1669ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 1670511b41d2SMark Murray } 1671511b41d2SMark Murray } 1672511b41d2SMark Murray } 1673511b41d2SMark Murray 1674af12a3e7SDag-Erling Smørgrav 1675af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 1676511b41d2SMark Murray 1677511b41d2SMark Murray void 1678af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 1679511b41d2SMark Murray { 1680511b41d2SMark Murray int id; 1681511b41d2SMark Murray char *data; 1682ca3176e7SBrian Feldman u_int data_len; 1683a04a10f8SKris Kennaway Channel *c; 1684511b41d2SMark Murray 1685511b41d2SMark Murray /* Get the channel number and verify it. */ 1686511b41d2SMark Murray id = packet_get_int(); 1687a04a10f8SKris Kennaway c = channel_lookup(id); 1688a04a10f8SKris Kennaway if (c == NULL) 1689511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1690511b41d2SMark Murray 1691511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1692a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1693a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1694511b41d2SMark Murray return; 1695511b41d2SMark Murray 1696511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1697a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1698511b41d2SMark Murray return; 1699511b41d2SMark Murray 1700511b41d2SMark Murray /* Get the data. */ 1701511b41d2SMark Murray data = packet_get_string(&data_len); 1702a04a10f8SKris Kennaway 1703a04a10f8SKris Kennaway if (compat20) { 1704a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1705a04a10f8SKris Kennaway log("channel %d: rcvd big packet %d, maxpack %d", 1706a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1707a04a10f8SKris Kennaway } 1708a04a10f8SKris Kennaway if (data_len > c->local_window) { 1709a04a10f8SKris Kennaway log("channel %d: rcvd too much data %d, win %d", 1710a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1711a04a10f8SKris Kennaway xfree(data); 1712a04a10f8SKris Kennaway return; 1713a04a10f8SKris Kennaway } 1714a04a10f8SKris Kennaway c->local_window -= data_len; 1715a04a10f8SKris Kennaway } 1716af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1717a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1718511b41d2SMark Murray xfree(data); 1719511b41d2SMark Murray } 1720af12a3e7SDag-Erling Smørgrav 1721a04a10f8SKris Kennaway void 1722af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 1723a04a10f8SKris Kennaway { 1724a04a10f8SKris Kennaway int id; 1725a04a10f8SKris Kennaway char *data; 1726a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 1727a04a10f8SKris Kennaway Channel *c; 1728a04a10f8SKris Kennaway 1729a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1730a04a10f8SKris Kennaway id = packet_get_int(); 1731a04a10f8SKris Kennaway c = channel_lookup(id); 1732a04a10f8SKris Kennaway 1733a04a10f8SKris Kennaway if (c == NULL) 1734a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1735a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1736a04a10f8SKris Kennaway log("channel %d: ext data for non open", id); 1737a04a10f8SKris Kennaway return; 1738a04a10f8SKris Kennaway } 173980628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 174080628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 174180628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 174280628bacSDag-Erling Smørgrav else 174380628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 174480628bacSDag-Erling Smørgrav "on channel %d.", id); 174580628bacSDag-Erling Smørgrav } 1746a04a10f8SKris Kennaway tcode = packet_get_int(); 1747a04a10f8SKris Kennaway if (c->efd == -1 || 1748a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1749a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1750a04a10f8SKris Kennaway log("channel %d: bad ext data", c->self); 1751a04a10f8SKris Kennaway return; 1752a04a10f8SKris Kennaway } 1753a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1754af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1755a04a10f8SKris Kennaway if (data_len > c->local_window) { 1756a04a10f8SKris Kennaway log("channel %d: rcvd too much extended_data %d, win %d", 1757a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1758a04a10f8SKris Kennaway xfree(data); 1759a04a10f8SKris Kennaway return; 1760a04a10f8SKris Kennaway } 17615b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1762a04a10f8SKris Kennaway c->local_window -= data_len; 1763a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1764a04a10f8SKris Kennaway xfree(data); 1765a04a10f8SKris Kennaway } 1766a04a10f8SKris Kennaway 1767a04a10f8SKris Kennaway void 1768af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 1769a04a10f8SKris Kennaway { 1770a04a10f8SKris Kennaway int id; 1771a04a10f8SKris Kennaway Channel *c; 1772a04a10f8SKris Kennaway 1773a04a10f8SKris Kennaway id = packet_get_int(); 1774af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1775a04a10f8SKris Kennaway c = channel_lookup(id); 1776a04a10f8SKris Kennaway if (c == NULL) 1777a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1778a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1779af12a3e7SDag-Erling Smørgrav 1780af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 1781af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 1782af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 1783af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 1784af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 1785af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1786af12a3e7SDag-Erling Smørgrav } 1787af12a3e7SDag-Erling Smørgrav 1788a04a10f8SKris Kennaway } 1789511b41d2SMark Murray 1790511b41d2SMark Murray void 1791af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 1792511b41d2SMark Murray { 1793a04a10f8SKris Kennaway int id; 1794a04a10f8SKris Kennaway Channel *c; 1795511b41d2SMark Murray 1796a04a10f8SKris Kennaway id = packet_get_int(); 1797af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1798a04a10f8SKris Kennaway c = channel_lookup(id); 1799a04a10f8SKris Kennaway if (c == NULL) 1800a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1801511b41d2SMark Murray 1802511b41d2SMark Murray /* 1803511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1804511b41d2SMark Murray * data is coming for it. 1805511b41d2SMark Murray */ 1806511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1807a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1808511b41d2SMark Murray packet_send(); 1809511b41d2SMark Murray 1810511b41d2SMark Murray /* 1811511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1812511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1813511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1814511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1815511b41d2SMark Murray * the confirmation arrives. 1816511b41d2SMark Murray */ 1817a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1818511b41d2SMark Murray /* 1819511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1820511b41d2SMark Murray * cause it to be freed later. 1821511b41d2SMark Murray */ 1822af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1823a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1824511b41d2SMark Murray } 1825511b41d2SMark Murray } 1826511b41d2SMark Murray 1827a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1828a04a10f8SKris Kennaway void 1829af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 1830a04a10f8SKris Kennaway { 1831a04a10f8SKris Kennaway int id = packet_get_int(); 1832a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1833af12a3e7SDag-Erling Smørgrav 1834af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1835a04a10f8SKris Kennaway if (c == NULL) 1836a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1837a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1838a04a10f8SKris Kennaway } 1839511b41d2SMark Murray 1840511b41d2SMark Murray void 1841af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 1842511b41d2SMark Murray { 1843a04a10f8SKris Kennaway int id = packet_get_int(); 1844a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1845a04a10f8SKris Kennaway 1846af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1847a04a10f8SKris Kennaway if (c == NULL) 1848a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1849a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1850a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1851a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1852a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1853af12a3e7SDag-Erling Smørgrav channel_free(c); 1854a04a10f8SKris Kennaway } 1855a04a10f8SKris Kennaway 1856a04a10f8SKris Kennaway void 1857af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 1858a04a10f8SKris Kennaway { 1859a04a10f8SKris Kennaway int id, remote_id; 1860a04a10f8SKris Kennaway Channel *c; 1861a04a10f8SKris Kennaway 1862a04a10f8SKris Kennaway id = packet_get_int(); 1863a04a10f8SKris Kennaway c = channel_lookup(id); 1864a04a10f8SKris Kennaway 1865a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1866a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1867a04a10f8SKris Kennaway "non-opening channel %d.", id); 1868a04a10f8SKris Kennaway remote_id = packet_get_int(); 1869a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1870a04a10f8SKris Kennaway c->remote_id = remote_id; 1871a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1872a04a10f8SKris Kennaway 1873a04a10f8SKris Kennaway if (compat20) { 1874a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 1875a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 1876af12a3e7SDag-Erling Smørgrav if (c->confirm) { 18775b9b2fafSBrian Feldman debug2("callback start"); 1878af12a3e7SDag-Erling Smørgrav c->confirm(c->self, NULL); 18795b9b2fafSBrian Feldman debug2("callback done"); 1880a04a10f8SKris Kennaway } 1881a82e551fSDag-Erling Smørgrav debug("channel %d: open confirm rwindow %u rmax %u", c->self, 1882a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 1883a04a10f8SKris Kennaway } 1884af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1885af12a3e7SDag-Erling Smørgrav } 1886af12a3e7SDag-Erling Smørgrav 1887af12a3e7SDag-Erling Smørgrav static char * 1888af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 1889af12a3e7SDag-Erling Smørgrav { 1890af12a3e7SDag-Erling Smørgrav switch (reason) { 1891af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 1892af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 1893af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 1894af12a3e7SDag-Erling Smørgrav return "connect failed"; 1895af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 1896af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 1897af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 1898af12a3e7SDag-Erling Smørgrav return "resource shortage"; 1899af12a3e7SDag-Erling Smørgrav } 1900af12a3e7SDag-Erling Smørgrav return "unknown reason"; 1901a04a10f8SKris Kennaway } 1902a04a10f8SKris Kennaway 1903a04a10f8SKris Kennaway void 1904af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 1905a04a10f8SKris Kennaway { 1906ca3176e7SBrian Feldman int id, reason; 1907ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 1908a04a10f8SKris Kennaway Channel *c; 1909a04a10f8SKris Kennaway 1910a04a10f8SKris Kennaway id = packet_get_int(); 1911a04a10f8SKris Kennaway c = channel_lookup(id); 1912a04a10f8SKris Kennaway 1913a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1914a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 1915a04a10f8SKris Kennaway "non-opening channel %d.", id); 1916a04a10f8SKris Kennaway if (compat20) { 1917ca3176e7SBrian Feldman reason = packet_get_int(); 1918af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1919ca3176e7SBrian Feldman msg = packet_get_string(NULL); 1920ca3176e7SBrian Feldman lang = packet_get_string(NULL); 1921ca3176e7SBrian Feldman } 1922af12a3e7SDag-Erling Smørgrav log("channel %d: open failed: %s%s%s", id, 1923af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 1924ca3176e7SBrian Feldman if (msg != NULL) 1925a04a10f8SKris Kennaway xfree(msg); 1926ca3176e7SBrian Feldman if (lang != NULL) 1927a04a10f8SKris Kennaway xfree(lang); 1928a04a10f8SKris Kennaway } 1929af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1930a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 1931af12a3e7SDag-Erling Smørgrav channel_free(c); 1932a04a10f8SKris Kennaway } 1933a04a10f8SKris Kennaway 1934a04a10f8SKris Kennaway void 1935af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 1936a04a10f8SKris Kennaway { 1937a04a10f8SKris Kennaway Channel *c; 1938a82e551fSDag-Erling Smørgrav int id; 1939a82e551fSDag-Erling Smørgrav u_int adjust; 1940a04a10f8SKris Kennaway 1941a04a10f8SKris Kennaway if (!compat20) 1942a04a10f8SKris Kennaway return; 1943511b41d2SMark Murray 1944511b41d2SMark Murray /* Get the channel number and verify it. */ 1945a04a10f8SKris Kennaway id = packet_get_int(); 1946a04a10f8SKris Kennaway c = channel_lookup(id); 1947511b41d2SMark Murray 1948a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 1949a04a10f8SKris Kennaway log("Received window adjust for " 1950a04a10f8SKris Kennaway "non-open channel %d.", id); 1951511b41d2SMark Murray return; 1952511b41d2SMark Murray } 1953a04a10f8SKris Kennaway adjust = packet_get_int(); 1954af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1955a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 1956a04a10f8SKris Kennaway c->remote_window += adjust; 1957511b41d2SMark Murray } 1958511b41d2SMark Murray 1959af12a3e7SDag-Erling Smørgrav void 1960af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 1961af12a3e7SDag-Erling Smørgrav { 1962af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 1963af12a3e7SDag-Erling Smørgrav u_short host_port; 1964af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 1965af12a3e7SDag-Erling Smørgrav int remote_id, sock = -1; 1966af12a3e7SDag-Erling Smørgrav 1967af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 1968af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 1969af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 1970af12a3e7SDag-Erling Smørgrav 1971af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 1972af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 1973af12a3e7SDag-Erling Smørgrav } else { 1974af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 1975af12a3e7SDag-Erling Smørgrav } 1976af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1977af12a3e7SDag-Erling Smørgrav sock = channel_connect_to(host, host_port); 1978af12a3e7SDag-Erling Smørgrav if (sock != -1) { 1979af12a3e7SDag-Erling Smørgrav c = channel_new("connected socket", 1980af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 1981af12a3e7SDag-Erling Smørgrav originator_string, 1); 1982af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 1983af12a3e7SDag-Erling Smørgrav } 1984af12a3e7SDag-Erling Smørgrav if (c == NULL) { 1985af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1986af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 1987af12a3e7SDag-Erling Smørgrav packet_send(); 1988af12a3e7SDag-Erling Smørgrav } 1989af12a3e7SDag-Erling Smørgrav xfree(host); 1990af12a3e7SDag-Erling Smørgrav } 1991af12a3e7SDag-Erling Smørgrav 1992af12a3e7SDag-Erling Smørgrav 1993af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 1994511b41d2SMark Murray 1995511b41d2SMark Murray void 1996af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 1997511b41d2SMark Murray { 1998af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 1999511b41d2SMark Murray } 2000511b41d2SMark Murray 2001af12a3e7SDag-Erling Smørgrav static int 2002af12a3e7SDag-Erling Smørgrav channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 2003af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2004511b41d2SMark Murray { 2005af12a3e7SDag-Erling Smørgrav Channel *c; 2006af12a3e7SDag-Erling Smørgrav int success, sock, on = 1; 2007511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2008ca3176e7SBrian Feldman const char *host; 2009af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2010511b41d2SMark Murray struct linger linger; 2011511b41d2SMark Murray 2012ca3176e7SBrian Feldman success = 0; 2013af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2014af12a3e7SDag-Erling Smørgrav listen_addr : host_to_connect; 2015511b41d2SMark Murray 2016af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2017af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2018af12a3e7SDag-Erling Smørgrav return success; 2019ca3176e7SBrian Feldman } 2020af12a3e7SDag-Erling Smørgrav if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 2021ca3176e7SBrian Feldman error("Forward host name too long."); 2022ca3176e7SBrian Feldman return success; 2023ca3176e7SBrian Feldman } 2024ca3176e7SBrian Feldman 2025511b41d2SMark Murray /* 2026511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2027511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2028511b41d2SMark Murray */ 2029511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2030511b41d2SMark Murray hints.ai_family = IPv4or6; 2031511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 2032511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2033ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", listen_port); 2034511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 2035511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 2036511b41d2SMark Murray 2037511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2038511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2039511b41d2SMark Murray continue; 2040511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2041511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2042af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: getnameinfo failed"); 2043511b41d2SMark Murray continue; 2044511b41d2SMark Murray } 2045511b41d2SMark Murray /* Create a port to listen for the host. */ 2046511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2047511b41d2SMark Murray if (sock < 0) { 2048511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2049511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2050511b41d2SMark Murray continue; 2051511b41d2SMark Murray } 2052511b41d2SMark Murray /* 2053511b41d2SMark Murray * Set socket options. We would like the socket to disappear 2054511b41d2SMark Murray * as soon as it has been closed for whatever reason. 2055511b41d2SMark Murray */ 2056af12a3e7SDag-Erling Smørgrav setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 2057511b41d2SMark Murray linger.l_onoff = 1; 2058511b41d2SMark Murray linger.l_linger = 5; 2059af12a3e7SDag-Erling Smørgrav setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); 2060511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 2061511b41d2SMark Murray 2062511b41d2SMark Murray /* Bind the socket to the address. */ 2063511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2064511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2065989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2066989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2067989dd127SDag-Erling Smørgrav else 2068511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2069989dd127SDag-Erling Smørgrav 2070511b41d2SMark Murray close(sock); 2071511b41d2SMark Murray continue; 2072511b41d2SMark Murray } 2073511b41d2SMark Murray /* Start listening for connections on the socket. */ 2074511b41d2SMark Murray if (listen(sock, 5) < 0) { 2075511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2076511b41d2SMark Murray close(sock); 2077511b41d2SMark Murray continue; 2078511b41d2SMark Murray } 2079511b41d2SMark Murray /* Allocate a channel number for the socket. */ 2080af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2081a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 20825b9b2fafSBrian Feldman 0, xstrdup("port listener"), 1); 2083af12a3e7SDag-Erling Smørgrav strlcpy(c->path, host, sizeof(c->path)); 2084af12a3e7SDag-Erling Smørgrav c->host_port = port_to_connect; 2085af12a3e7SDag-Erling Smørgrav c->listening_port = listen_port; 2086511b41d2SMark Murray success = 1; 2087511b41d2SMark Murray } 2088511b41d2SMark Murray if (success == 0) 2089af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: cannot listen to port: %d", 2090ca3176e7SBrian Feldman listen_port); 2091511b41d2SMark Murray freeaddrinfo(aitop); 2092ca3176e7SBrian Feldman return success; 2093511b41d2SMark Murray } 2094511b41d2SMark Murray 2095af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 2096af12a3e7SDag-Erling Smørgrav int 2097af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(u_short listen_port, 2098af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2099af12a3e7SDag-Erling Smørgrav { 2100af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2101af12a3e7SDag-Erling Smørgrav NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); 2102af12a3e7SDag-Erling Smørgrav } 2103af12a3e7SDag-Erling Smørgrav 2104af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 2105af12a3e7SDag-Erling Smørgrav int 2106af12a3e7SDag-Erling Smørgrav channel_setup_remote_fwd_listener(const char *listen_address, 2107af12a3e7SDag-Erling Smørgrav u_short listen_port, int gateway_ports) 2108af12a3e7SDag-Erling Smørgrav { 2109af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2110af12a3e7SDag-Erling Smørgrav listen_address, listen_port, NULL, 0, gateway_ports); 2111af12a3e7SDag-Erling Smørgrav } 2112af12a3e7SDag-Erling Smørgrav 2113511b41d2SMark Murray /* 2114511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 2115511b41d2SMark Murray * the secure channel to host:port from local side. 2116511b41d2SMark Murray */ 2117511b41d2SMark Murray 2118511b41d2SMark Murray void 2119ca3176e7SBrian Feldman channel_request_remote_forwarding(u_short listen_port, 2120ca3176e7SBrian Feldman const char *host_to_connect, u_short port_to_connect) 2121511b41d2SMark Murray { 2122af12a3e7SDag-Erling Smørgrav int type, success = 0; 2123ca3176e7SBrian Feldman 2124511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 2125511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2126511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 2127511b41d2SMark Murray 2128511b41d2SMark Murray /* Send the forward request to the remote side. */ 2129a04a10f8SKris Kennaway if (compat20) { 2130a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 2131a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 2132a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 213380628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2134a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 2135a04a10f8SKris Kennaway packet_put_int(listen_port); 2136ca3176e7SBrian Feldman packet_send(); 2137ca3176e7SBrian Feldman packet_write_wait(); 2138ca3176e7SBrian Feldman /* Assume that server accepts the request */ 2139ca3176e7SBrian Feldman success = 1; 2140a04a10f8SKris Kennaway } else { 2141511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 2142a04a10f8SKris Kennaway packet_put_int(listen_port); 2143a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 2144a04a10f8SKris Kennaway packet_put_int(port_to_connect); 2145511b41d2SMark Murray packet_send(); 2146511b41d2SMark Murray packet_write_wait(); 2147ca3176e7SBrian Feldman 2148ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 2149af12a3e7SDag-Erling Smørgrav type = packet_read(); 2150ca3176e7SBrian Feldman switch (type) { 2151ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 2152ca3176e7SBrian Feldman success = 1; 2153ca3176e7SBrian Feldman break; 2154ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 2155ca3176e7SBrian Feldman log("Warning: Server denied remote port forwarding."); 2156ca3176e7SBrian Feldman break; 2157ca3176e7SBrian Feldman default: 2158ca3176e7SBrian Feldman /* Unknown packet */ 2159ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 2160ca3176e7SBrian Feldman "received packet type %d.", type); 2161ca3176e7SBrian Feldman } 2162ca3176e7SBrian Feldman } 2163ca3176e7SBrian Feldman if (success) { 2164ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 2165ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 2166ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].listen_port = listen_port; 2167ca3176e7SBrian Feldman num_permitted_opens++; 2168511b41d2SMark Murray } 2169a04a10f8SKris Kennaway } 2170511b41d2SMark Murray 2171511b41d2SMark Murray /* 2172511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 2173511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 2174511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 2175511b41d2SMark Murray */ 2176511b41d2SMark Murray 2177511b41d2SMark Murray void 2178a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 2179511b41d2SMark Murray { 2180511b41d2SMark Murray u_short port, host_port; 2181511b41d2SMark Murray char *hostname; 2182511b41d2SMark Murray 2183511b41d2SMark Murray /* Get arguments from the packet. */ 2184511b41d2SMark Murray port = packet_get_int(); 2185511b41d2SMark Murray hostname = packet_get_string(NULL); 2186511b41d2SMark Murray host_port = packet_get_int(); 2187511b41d2SMark Murray 2188989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 2189511b41d2SMark Murray /* 2190511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 2191511b41d2SMark Murray * privileged port. 2192511b41d2SMark Murray */ 2193511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 2194511b41d2SMark Murray packet_disconnect("Requested forwarding of port %d but user is not root.", 2195511b41d2SMark Murray port); 2196989dd127SDag-Erling Smørgrav #endif 2197ca3176e7SBrian Feldman /* Initiate forwarding */ 2198af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); 2199511b41d2SMark Murray 2200511b41d2SMark Murray /* Free the argument string. */ 2201511b41d2SMark Murray xfree(hostname); 2202511b41d2SMark Murray } 2203511b41d2SMark Murray 2204ca3176e7SBrian Feldman /* 2205ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 2206ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 2207ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 2208ca3176e7SBrian Feldman */ 2209ca3176e7SBrian Feldman void 2210af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 2211ca3176e7SBrian Feldman { 2212ca3176e7SBrian Feldman if (num_permitted_opens == 0) 2213ca3176e7SBrian Feldman all_opens_permitted = 1; 2214ca3176e7SBrian Feldman } 2215ca3176e7SBrian Feldman 2216ca3176e7SBrian Feldman void 2217ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 2218ca3176e7SBrian Feldman { 2219ca3176e7SBrian Feldman if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2220ca3176e7SBrian Feldman fatal("channel_request_remote_forwarding: too many forwards"); 2221ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 2222ca3176e7SBrian Feldman 2223ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 2224ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 2225ca3176e7SBrian Feldman num_permitted_opens++; 2226ca3176e7SBrian Feldman 2227ca3176e7SBrian Feldman all_opens_permitted = 0; 2228ca3176e7SBrian Feldman } 2229ca3176e7SBrian Feldman 2230ca3176e7SBrian Feldman void 2231ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 2232ca3176e7SBrian Feldman { 2233ca3176e7SBrian Feldman int i; 2234ca3176e7SBrian Feldman 2235ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2236ca3176e7SBrian Feldman xfree(permitted_opens[i].host_to_connect); 2237ca3176e7SBrian Feldman num_permitted_opens = 0; 2238ca3176e7SBrian Feldman 2239ca3176e7SBrian Feldman } 2240ca3176e7SBrian Feldman 2241ca3176e7SBrian Feldman 2242ca3176e7SBrian Feldman /* return socket to remote host, port */ 2243af12a3e7SDag-Erling Smørgrav static int 2244ca3176e7SBrian Feldman connect_to(const char *host, u_short port) 2245511b41d2SMark Murray { 2246511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2247511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2248511b41d2SMark Murray int gaierr; 2249a04a10f8SKris Kennaway int sock = -1; 2250511b41d2SMark Murray 2251511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2252511b41d2SMark Murray hints.ai_family = IPv4or6; 2253511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2254ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", port); 2255511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 2256ca3176e7SBrian Feldman error("connect_to %.100s: unknown host (%s)", host, 2257ca3176e7SBrian Feldman gai_strerror(gaierr)); 2258a04a10f8SKris Kennaway return -1; 2259511b41d2SMark Murray } 2260511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2261511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2262511b41d2SMark Murray continue; 2263511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2264511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2265ca3176e7SBrian Feldman error("connect_to: getnameinfo failed"); 2266511b41d2SMark Murray continue; 2267511b41d2SMark Murray } 2268511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2269511b41d2SMark Murray if (sock < 0) { 2270511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2271511b41d2SMark Murray continue; 2272511b41d2SMark Murray } 2273ca3176e7SBrian Feldman if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) 2274ca3176e7SBrian Feldman fatal("connect_to: F_SETFL: %s", strerror(errno)); 2275ca3176e7SBrian Feldman if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2276ca3176e7SBrian Feldman errno != EINPROGRESS) { 2277ca3176e7SBrian Feldman error("connect_to %.100s port %s: %.100s", ntop, strport, 2278511b41d2SMark Murray strerror(errno)); 2279511b41d2SMark Murray close(sock); 2280511b41d2SMark Murray continue; /* fail -- try next */ 2281511b41d2SMark Murray } 2282511b41d2SMark Murray break; /* success */ 2283511b41d2SMark Murray 2284511b41d2SMark Murray } 2285511b41d2SMark Murray freeaddrinfo(aitop); 2286511b41d2SMark Murray if (!ai) { 2287ca3176e7SBrian Feldman error("connect_to %.100s port %d: failed.", host, port); 2288a04a10f8SKris Kennaway return -1; 2289a04a10f8SKris Kennaway } 2290a04a10f8SKris Kennaway /* success */ 2291af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2292a04a10f8SKris Kennaway return sock; 2293a04a10f8SKris Kennaway } 2294ca3176e7SBrian Feldman 2295ca3176e7SBrian Feldman int 2296af12a3e7SDag-Erling Smørgrav channel_connect_by_listen_address(u_short listen_port) 2297ca3176e7SBrian Feldman { 2298ca3176e7SBrian Feldman int i; 2299ca3176e7SBrian Feldman 2300ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2301ca3176e7SBrian Feldman if (permitted_opens[i].listen_port == listen_port) 2302ca3176e7SBrian Feldman return connect_to( 2303ca3176e7SBrian Feldman permitted_opens[i].host_to_connect, 2304ca3176e7SBrian Feldman permitted_opens[i].port_to_connect); 2305ca3176e7SBrian Feldman error("WARNING: Server requests forwarding for unknown listen_port %d", 2306ca3176e7SBrian Feldman listen_port); 2307ca3176e7SBrian Feldman return -1; 2308ca3176e7SBrian Feldman } 2309ca3176e7SBrian Feldman 2310ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 2311ca3176e7SBrian Feldman int 2312ca3176e7SBrian Feldman channel_connect_to(const char *host, u_short port) 2313ca3176e7SBrian Feldman { 2314ca3176e7SBrian Feldman int i, permit; 2315ca3176e7SBrian Feldman 2316ca3176e7SBrian Feldman permit = all_opens_permitted; 2317ca3176e7SBrian Feldman if (!permit) { 2318ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2319ca3176e7SBrian Feldman if (permitted_opens[i].port_to_connect == port && 2320ca3176e7SBrian Feldman strcmp(permitted_opens[i].host_to_connect, host) == 0) 2321ca3176e7SBrian Feldman permit = 1; 2322ca3176e7SBrian Feldman 2323ca3176e7SBrian Feldman } 2324ca3176e7SBrian Feldman if (!permit) { 2325ca3176e7SBrian Feldman log("Received request to connect to host %.100s port %d, " 2326ca3176e7SBrian Feldman "but the request was denied.", host, port); 2327ca3176e7SBrian Feldman return -1; 2328ca3176e7SBrian Feldman } 2329ca3176e7SBrian Feldman return connect_to(host, port); 2330ca3176e7SBrian Feldman } 2331ca3176e7SBrian Feldman 2332af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 2333511b41d2SMark Murray 2334511b41d2SMark Murray /* 2335511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 2336a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 2337a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 2338511b41d2SMark Murray */ 2339af12a3e7SDag-Erling Smørgrav int 2340af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2341a82e551fSDag-Erling Smørgrav int single_connection, u_int *display_numberp) 2342511b41d2SMark Murray { 2343af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 2344511b41d2SMark Murray int display_number, sock; 2345511b41d2SMark Murray u_short port; 2346511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2347511b41d2SMark Murray char strport[NI_MAXSERV]; 2348511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 2349511b41d2SMark Murray 2350511b41d2SMark Murray for (display_number = x11_display_offset; 2351511b41d2SMark Murray display_number < MAX_DISPLAYS; 2352511b41d2SMark Murray display_number++) { 2353511b41d2SMark Murray port = 6000 + display_number; 2354511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2355511b41d2SMark Murray hints.ai_family = IPv4or6; 2356af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 2357511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2358511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2359511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2360511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 2361af12a3e7SDag-Erling Smørgrav return -1; 2362511b41d2SMark Murray } 2363511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2364511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2365511b41d2SMark Murray continue; 2366511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2367511b41d2SMark Murray if (sock < 0) { 2368989dd127SDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 2369511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2370af12a3e7SDag-Erling Smørgrav return -1; 2371989dd127SDag-Erling Smørgrav } else { 2372989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 2373989dd127SDag-Erling Smørgrav ai->ai_family); 2374989dd127SDag-Erling Smørgrav continue; 2375511b41d2SMark Murray } 2376989dd127SDag-Erling Smørgrav } 2377989dd127SDag-Erling Smørgrav #ifdef IPV6_V6ONLY 2378989dd127SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 2379989dd127SDag-Erling Smørgrav int on = 1; 2380989dd127SDag-Erling Smørgrav if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 2381989dd127SDag-Erling Smørgrav error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 2382989dd127SDag-Erling Smørgrav } 2383989dd127SDag-Erling Smørgrav #endif 2384511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2385511b41d2SMark Murray debug("bind port %d: %.100s", port, strerror(errno)); 2386511b41d2SMark Murray close(sock); 2387989dd127SDag-Erling Smørgrav 2388989dd127SDag-Erling Smørgrav if (ai->ai_next) 2389989dd127SDag-Erling Smørgrav continue; 2390989dd127SDag-Erling Smørgrav 2391511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2392511b41d2SMark Murray close(socks[n]); 2393511b41d2SMark Murray } 2394511b41d2SMark Murray num_socks = 0; 2395511b41d2SMark Murray break; 2396511b41d2SMark Murray } 2397511b41d2SMark Murray socks[num_socks++] = sock; 2398989dd127SDag-Erling Smørgrav #ifndef DONT_TRY_OTHER_AF 2399511b41d2SMark Murray if (num_socks == NUM_SOCKS) 2400511b41d2SMark Murray break; 2401989dd127SDag-Erling Smørgrav #else 2402989dd127SDag-Erling Smørgrav if (x11_use_localhost) { 2403989dd127SDag-Erling Smørgrav if (num_socks == NUM_SOCKS) 2404989dd127SDag-Erling Smørgrav break; 2405989dd127SDag-Erling Smørgrav } else { 2406989dd127SDag-Erling Smørgrav break; 2407989dd127SDag-Erling Smørgrav } 2408989dd127SDag-Erling Smørgrav #endif 2409511b41d2SMark Murray } 2410ca3176e7SBrian Feldman freeaddrinfo(aitop); 2411511b41d2SMark Murray if (num_socks > 0) 2412511b41d2SMark Murray break; 2413511b41d2SMark Murray } 2414511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 2415511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 2416af12a3e7SDag-Erling Smørgrav return -1; 2417511b41d2SMark Murray } 2418511b41d2SMark Murray /* Start listening for connections on the socket. */ 2419511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2420511b41d2SMark Murray sock = socks[n]; 2421511b41d2SMark Murray if (listen(sock, 5) < 0) { 2422511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2423511b41d2SMark Murray close(sock); 2424af12a3e7SDag-Erling Smørgrav return -1; 2425511b41d2SMark Murray } 2426511b41d2SMark Murray } 2427511b41d2SMark Murray 2428511b41d2SMark Murray /* Allocate a channel for each socket. */ 2429511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2430511b41d2SMark Murray sock = socks[n]; 2431af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 2432a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 2433a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 24345b9b2fafSBrian Feldman 0, xstrdup("X11 inet listener"), 1); 2435af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 2436511b41d2SMark Murray } 2437511b41d2SMark Murray 2438af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 2439a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 2440a82e551fSDag-Erling Smørgrav return (0); 2441511b41d2SMark Murray } 2442511b41d2SMark Murray 2443af12a3e7SDag-Erling Smørgrav static int 2444ca3176e7SBrian Feldman connect_local_xsocket(u_int dnr) 2445511b41d2SMark Murray { 2446511b41d2SMark Murray int sock; 2447511b41d2SMark Murray struct sockaddr_un addr; 2448511b41d2SMark Murray 2449511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2450511b41d2SMark Murray if (sock < 0) 2451511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2452511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 2453511b41d2SMark Murray addr.sun_family = AF_UNIX; 2454af12a3e7SDag-Erling Smørgrav snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 2455511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 2456511b41d2SMark Murray return sock; 2457511b41d2SMark Murray close(sock); 2458511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 2459511b41d2SMark Murray return -1; 2460511b41d2SMark Murray } 2461511b41d2SMark Murray 2462a04a10f8SKris Kennaway int 2463a04a10f8SKris Kennaway x11_connect_display(void) 2464511b41d2SMark Murray { 2465a04a10f8SKris Kennaway int display_number, sock = 0; 2466511b41d2SMark Murray const char *display; 2467a04a10f8SKris Kennaway char buf[1024], *cp; 2468511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2469511b41d2SMark Murray char strport[NI_MAXSERV]; 2470511b41d2SMark Murray int gaierr; 2471511b41d2SMark Murray 2472511b41d2SMark Murray /* Try to open a socket for the local X server. */ 2473511b41d2SMark Murray display = getenv("DISPLAY"); 2474511b41d2SMark Murray if (!display) { 2475511b41d2SMark Murray error("DISPLAY not set."); 2476a04a10f8SKris Kennaway return -1; 2477511b41d2SMark Murray } 2478511b41d2SMark Murray /* 2479511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 2480511b41d2SMark Murray * connection to the real X server. 2481511b41d2SMark Murray */ 2482511b41d2SMark Murray 2483511b41d2SMark Murray /* 2484511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 2485511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 2486511b41d2SMark Murray */ 2487511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 2488511b41d2SMark Murray display[0] == ':') { 2489511b41d2SMark Murray /* Connect to the unix domain socket. */ 2490511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 2491511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2492511b41d2SMark Murray display); 2493a04a10f8SKris Kennaway return -1; 2494511b41d2SMark Murray } 2495511b41d2SMark Murray /* Create a socket. */ 2496511b41d2SMark Murray sock = connect_local_xsocket(display_number); 2497511b41d2SMark Murray if (sock < 0) 2498a04a10f8SKris Kennaway return -1; 2499511b41d2SMark Murray 2500511b41d2SMark Murray /* OK, we now have a connection to the display. */ 2501a04a10f8SKris Kennaway return sock; 2502511b41d2SMark Murray } 2503511b41d2SMark Murray /* 2504511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 2505511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 2506511b41d2SMark Murray */ 2507af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 2508511b41d2SMark Murray cp = strchr(buf, ':'); 2509511b41d2SMark Murray if (!cp) { 2510511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 2511a04a10f8SKris Kennaway return -1; 2512511b41d2SMark Murray } 2513511b41d2SMark Murray *cp = 0; 2514511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 2515511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 2516511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2517511b41d2SMark Murray display); 2518a04a10f8SKris Kennaway return -1; 2519511b41d2SMark Murray } 2520511b41d2SMark Murray 2521511b41d2SMark Murray /* Look up the host address */ 2522511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2523511b41d2SMark Murray hints.ai_family = IPv4or6; 2524511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2525511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 2526511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 2527511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 2528a04a10f8SKris Kennaway return -1; 2529511b41d2SMark Murray } 2530511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2531511b41d2SMark Murray /* Create a socket. */ 2532511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2533511b41d2SMark Murray if (sock < 0) { 2534511b41d2SMark Murray debug("socket: %.100s", strerror(errno)); 2535511b41d2SMark Murray continue; 2536511b41d2SMark Murray } 2537511b41d2SMark Murray /* Connect it to the display. */ 2538511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2539a04a10f8SKris Kennaway debug("connect %.100s port %d: %.100s", buf, 2540a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 2541511b41d2SMark Murray close(sock); 2542511b41d2SMark Murray continue; 2543511b41d2SMark Murray } 2544511b41d2SMark Murray /* Success */ 2545511b41d2SMark Murray break; 2546a04a10f8SKris Kennaway } 2547511b41d2SMark Murray freeaddrinfo(aitop); 2548511b41d2SMark Murray if (!ai) { 2549511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 2550511b41d2SMark Murray strerror(errno)); 2551a04a10f8SKris Kennaway return -1; 2552511b41d2SMark Murray } 2553af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2554a04a10f8SKris Kennaway return sock; 2555a04a10f8SKris Kennaway } 2556511b41d2SMark Murray 2557a04a10f8SKris Kennaway /* 2558a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 2559a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 2560a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 2561a04a10f8SKris Kennaway */ 2562a04a10f8SKris Kennaway 2563a04a10f8SKris Kennaway void 2564af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 2565a04a10f8SKris Kennaway { 2566af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2567af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 2568a04a10f8SKris Kennaway char *remote_host; 2569a04a10f8SKris Kennaway 2570a04a10f8SKris Kennaway debug("Received X11 open request."); 2571af12a3e7SDag-Erling Smørgrav 2572af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2573af12a3e7SDag-Erling Smørgrav 2574af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2575af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 2576af12a3e7SDag-Erling Smørgrav } else { 2577af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 2578af12a3e7SDag-Erling Smørgrav } 2579af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2580a04a10f8SKris Kennaway 2581a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 2582a04a10f8SKris Kennaway sock = x11_connect_display(); 2583af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2584af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 2585af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 2586af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 2587af12a3e7SDag-Erling Smørgrav remote_host, 1); 2588af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2589af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2590af12a3e7SDag-Erling Smørgrav } 2591af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2592a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 2593a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2594af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2595a04a10f8SKris Kennaway } else { 2596511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2597511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2598af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2599af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 2600a04a10f8SKris Kennaway } 2601af12a3e7SDag-Erling Smørgrav packet_send(); 2602511b41d2SMark Murray } 2603511b41d2SMark Murray 26045b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 26055b9b2fafSBrian Feldman void 2606af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 26075b9b2fafSBrian Feldman { 26085b9b2fafSBrian Feldman int rchan = packet_get_int(); 26095b9b2fafSBrian Feldman switch (type) { 26105b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 26115b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 26125b9b2fafSBrian Feldman break; 26135b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 26145b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 26155b9b2fafSBrian Feldman break; 26165b9b2fafSBrian Feldman default: 2617af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 26185b9b2fafSBrian Feldman break; 26195b9b2fafSBrian Feldman } 26205b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 26215b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 26225b9b2fafSBrian Feldman packet_put_int(rchan); 26235b9b2fafSBrian Feldman packet_send(); 26245b9b2fafSBrian Feldman } 26255b9b2fafSBrian Feldman 2626511b41d2SMark Murray /* 2627511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2628511b41d2SMark Murray * data, and enables authentication spoofing. 2629af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 2630511b41d2SMark Murray */ 2631511b41d2SMark Murray void 2632a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2633a04a10f8SKris Kennaway const char *proto, const char *data) 2634511b41d2SMark Murray { 2635ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 2636ca3176e7SBrian Feldman u_int i, value, len; 2637511b41d2SMark Murray char *new_data; 2638511b41d2SMark Murray int screen_number; 2639511b41d2SMark Murray const char *cp; 2640511b41d2SMark Murray u_int32_t rand = 0; 2641511b41d2SMark Murray 2642511b41d2SMark Murray cp = getenv("DISPLAY"); 2643511b41d2SMark Murray if (cp) 2644511b41d2SMark Murray cp = strchr(cp, ':'); 2645511b41d2SMark Murray if (cp) 2646511b41d2SMark Murray cp = strchr(cp, '.'); 2647511b41d2SMark Murray if (cp) 2648511b41d2SMark Murray screen_number = atoi(cp + 1); 2649511b41d2SMark Murray else 2650511b41d2SMark Murray screen_number = 0; 2651511b41d2SMark Murray 2652511b41d2SMark Murray /* Save protocol name. */ 2653511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2654511b41d2SMark Murray 2655511b41d2SMark Murray /* 2656511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2657511b41d2SMark Murray * same length. 2658511b41d2SMark Murray */ 2659511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2660511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2661511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2662511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2663511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2664511b41d2SMark Murray if (i % 4 == 0) 2665511b41d2SMark Murray rand = arc4random(); 2666511b41d2SMark Murray x11_saved_data[i] = value; 2667511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2668511b41d2SMark Murray rand >>= 8; 2669511b41d2SMark Murray } 2670511b41d2SMark Murray x11_saved_data_len = data_len; 2671511b41d2SMark Murray x11_fake_data_len = data_len; 2672511b41d2SMark Murray 2673511b41d2SMark Murray /* Convert the fake data into hex. */ 2674ca3176e7SBrian Feldman len = 2 * data_len + 1; 2675ca3176e7SBrian Feldman new_data = xmalloc(len); 2676511b41d2SMark Murray for (i = 0; i < data_len; i++) 2677ca3176e7SBrian Feldman snprintf(new_data + 2 * i, len - 2 * i, 2678ca3176e7SBrian Feldman "%02x", (u_char) x11_fake_data[i]); 2679511b41d2SMark Murray 2680511b41d2SMark Murray /* Send the request packet. */ 2681a04a10f8SKris Kennaway if (compat20) { 2682a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2683a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2684a04a10f8SKris Kennaway } else { 2685511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2686a04a10f8SKris Kennaway } 2687a04a10f8SKris Kennaway packet_put_cstring(proto); 2688a04a10f8SKris Kennaway packet_put_cstring(new_data); 2689511b41d2SMark Murray packet_put_int(screen_number); 2690511b41d2SMark Murray packet_send(); 2691511b41d2SMark Murray packet_write_wait(); 2692511b41d2SMark Murray xfree(new_data); 2693511b41d2SMark Murray } 2694511b41d2SMark Murray 2695af12a3e7SDag-Erling Smørgrav 2696af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 2697af12a3e7SDag-Erling Smørgrav 2698511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2699511b41d2SMark Murray 2700511b41d2SMark Murray void 2701af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 2702511b41d2SMark Murray { 2703511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2704511b41d2SMark Murray packet_send(); 2705511b41d2SMark Murray packet_write_wait(); 2706511b41d2SMark Murray } 2707511b41d2SMark Murray 2708511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2709511b41d2SMark Murray 2710511b41d2SMark Murray void 2711af12a3e7SDag-Erling Smørgrav auth_input_open_request(int type, u_int32_t seq, void *ctxt) 2712511b41d2SMark Murray { 2713af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2714af12a3e7SDag-Erling Smørgrav int remote_id, sock; 2715af12a3e7SDag-Erling Smørgrav char *name; 2716a04a10f8SKris Kennaway 2717511b41d2SMark Murray /* Read the remote channel number from the message. */ 2718af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2719af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2720511b41d2SMark Murray 2721511b41d2SMark Murray /* 2722511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2723511b41d2SMark Murray * get forwarded). 2724511b41d2SMark Murray */ 2725511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2726511b41d2SMark Murray 2727511b41d2SMark Murray /* 2728511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2729511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2730511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2731511b41d2SMark Murray * agent. 2732511b41d2SMark Murray */ 2733af12a3e7SDag-Erling Smørgrav if (sock >= 0) { 2734af12a3e7SDag-Erling Smørgrav name = xstrdup("authentication agent connection"); 2735af12a3e7SDag-Erling Smørgrav c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 2736af12a3e7SDag-Erling Smørgrav -1, 0, 0, 0, name, 1); 2737af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2738af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2739af12a3e7SDag-Erling Smørgrav } 2740af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2741511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2742af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2743af12a3e7SDag-Erling Smørgrav } else { 2744511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2745af12a3e7SDag-Erling Smørgrav debug("Forwarding authentication connection."); 2746511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2747af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2748a04a10f8SKris Kennaway packet_put_int(c->self); 2749a04a10f8SKris Kennaway } 2750a04a10f8SKris Kennaway packet_send(); 2751a04a10f8SKris Kennaway } 2752