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" 42f388f5efSDag-Erling Smørgrav RCSID("$OpenBSD: channels.c,v 1.183 2002/09/17 07:47:02 itojun 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 } 190f388f5efSDag-Erling Smørgrav c->wfd_isatty = isatty(c->wfd); 191e0fbb1d2SBrian Feldman 1925b9b2fafSBrian Feldman /* enable nonblocking mode */ 1935b9b2fafSBrian Feldman if (nonblock) { 194a04a10f8SKris Kennaway if (rfd != -1) 195a04a10f8SKris Kennaway set_nonblock(rfd); 196a04a10f8SKris Kennaway if (wfd != -1) 197a04a10f8SKris Kennaway set_nonblock(wfd); 198a04a10f8SKris Kennaway if (efd != -1) 199a04a10f8SKris Kennaway set_nonblock(efd); 200a04a10f8SKris Kennaway } 2015b9b2fafSBrian Feldman } 202a04a10f8SKris Kennaway 203511b41d2SMark Murray /* 204511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 205511b41d2SMark Murray * remote_name to be freed. 206511b41d2SMark Murray */ 207511b41d2SMark Murray 208af12a3e7SDag-Erling Smørgrav Channel * 209a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 210a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 211511b41d2SMark Murray { 212511b41d2SMark Murray int i, found; 213511b41d2SMark Murray Channel *c; 214511b41d2SMark Murray 215511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 216511b41d2SMark Murray if (channels_alloc == 0) { 217511b41d2SMark Murray channels_alloc = 10; 218af12a3e7SDag-Erling Smørgrav channels = xmalloc(channels_alloc * sizeof(Channel *)); 219511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 220af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 221af12a3e7SDag-Erling Smørgrav fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); 222511b41d2SMark Murray } 223511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 224511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 225af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 226511b41d2SMark Murray /* Found a free slot. */ 227511b41d2SMark Murray found = i; 228511b41d2SMark Murray break; 229511b41d2SMark Murray } 230511b41d2SMark Murray if (found == -1) { 231511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 232511b41d2SMark Murray found = channels_alloc; 233511b41d2SMark Murray channels_alloc += 10; 234a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 235a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 236a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 2375b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 238af12a3e7SDag-Erling Smørgrav channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); 239511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 240af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 241511b41d2SMark Murray } 242af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 243af12a3e7SDag-Erling Smørgrav c = channels[found] = xmalloc(sizeof(Channel)); 244af12a3e7SDag-Erling Smørgrav memset(c, 0, sizeof(Channel)); 245511b41d2SMark Murray buffer_init(&c->input); 246511b41d2SMark Murray buffer_init(&c->output); 247a04a10f8SKris Kennaway buffer_init(&c->extended); 248af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 249af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 250af12a3e7SDag-Erling Smørgrav c->flags = 0; 2515b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 252511b41d2SMark Murray c->self = found; 253511b41d2SMark Murray c->type = type; 254a04a10f8SKris Kennaway c->ctype = ctype; 255a04a10f8SKris Kennaway c->local_window = window; 256a04a10f8SKris Kennaway c->local_window_max = window; 257a04a10f8SKris Kennaway c->local_consumed = 0; 258a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 259511b41d2SMark Murray c->remote_id = -1; 260511b41d2SMark Murray c->remote_name = remote_name; 261a04a10f8SKris Kennaway c->remote_window = 0; 262a04a10f8SKris Kennaway c->remote_maxpacket = 0; 263af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 264af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 265af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 266af12a3e7SDag-Erling Smørgrav c->confirm = NULL; 267b66f2d16SKris Kennaway c->input_filter = NULL; 268511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 269af12a3e7SDag-Erling Smørgrav return c; 270a04a10f8SKris Kennaway } 271511b41d2SMark Murray 272af12a3e7SDag-Erling Smørgrav static int 273af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 274af12a3e7SDag-Erling Smørgrav { 275af12a3e7SDag-Erling Smørgrav int i, max = 0; 276af12a3e7SDag-Erling Smørgrav Channel *c; 277af12a3e7SDag-Erling Smørgrav 278af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 279af12a3e7SDag-Erling Smørgrav c = channels[i]; 280af12a3e7SDag-Erling Smørgrav if (c != NULL) { 281af12a3e7SDag-Erling Smørgrav max = MAX(max, c->rfd); 282af12a3e7SDag-Erling Smørgrav max = MAX(max, c->wfd); 283af12a3e7SDag-Erling Smørgrav max = MAX(max, c->efd); 284af12a3e7SDag-Erling Smørgrav } 285af12a3e7SDag-Erling Smørgrav } 286af12a3e7SDag-Erling Smørgrav return max; 287af12a3e7SDag-Erling Smørgrav } 288af12a3e7SDag-Erling Smørgrav 289af12a3e7SDag-Erling Smørgrav int 290af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 291af12a3e7SDag-Erling Smørgrav { 292af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 293af12a3e7SDag-Erling Smørgrav 294af12a3e7SDag-Erling Smørgrav if (fd != -1) { 295af12a3e7SDag-Erling Smørgrav ret = close(fd); 296af12a3e7SDag-Erling Smørgrav *fdp = -1; 297af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 298af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 299af12a3e7SDag-Erling Smørgrav } 300af12a3e7SDag-Erling Smørgrav return ret; 301af12a3e7SDag-Erling Smørgrav } 302a04a10f8SKris Kennaway 303a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 304511b41d2SMark Murray 305af12a3e7SDag-Erling Smørgrav static void 306a04a10f8SKris Kennaway channel_close_fds(Channel *c) 307511b41d2SMark Murray { 308af12a3e7SDag-Erling Smørgrav debug3("channel_close_fds: channel %d: r %d w %d e %d", 309af12a3e7SDag-Erling Smørgrav c->self, c->rfd, c->wfd, c->efd); 310af12a3e7SDag-Erling Smørgrav 311af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 312af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 313af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 314af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 315a04a10f8SKris Kennaway } 316511b41d2SMark Murray 317a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 318a04a10f8SKris Kennaway 319a04a10f8SKris Kennaway void 320af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 321a04a10f8SKris Kennaway { 322af12a3e7SDag-Erling Smørgrav char *s; 323af12a3e7SDag-Erling Smørgrav int i, n; 324ca3176e7SBrian Feldman 325af12a3e7SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) 326af12a3e7SDag-Erling Smørgrav if (channels[i]) 327af12a3e7SDag-Erling Smørgrav n++; 328af12a3e7SDag-Erling Smørgrav debug("channel_free: channel %d: %s, nchannels %d", c->self, 329af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 330af12a3e7SDag-Erling Smørgrav 331af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 332af12a3e7SDag-Erling Smørgrav debug3("channel_free: status: %s", s); 333ca3176e7SBrian Feldman xfree(s); 334ca3176e7SBrian Feldman 335a04a10f8SKris Kennaway if (c->sock != -1) 336a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 337a04a10f8SKris Kennaway channel_close_fds(c); 338a04a10f8SKris Kennaway buffer_free(&c->input); 339a04a10f8SKris Kennaway buffer_free(&c->output); 340a04a10f8SKris Kennaway buffer_free(&c->extended); 341a04a10f8SKris Kennaway if (c->remote_name) { 342a04a10f8SKris Kennaway xfree(c->remote_name); 343a04a10f8SKris Kennaway c->remote_name = NULL; 344511b41d2SMark Murray } 345af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 346af12a3e7SDag-Erling Smørgrav xfree(c); 347af12a3e7SDag-Erling Smørgrav } 348af12a3e7SDag-Erling Smørgrav 349af12a3e7SDag-Erling Smørgrav void 350af12a3e7SDag-Erling Smørgrav channel_free_all(void) 351af12a3e7SDag-Erling Smørgrav { 352af12a3e7SDag-Erling Smørgrav int i; 353af12a3e7SDag-Erling Smørgrav 354af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 355af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 356af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 357af12a3e7SDag-Erling Smørgrav } 358af12a3e7SDag-Erling Smørgrav 359af12a3e7SDag-Erling Smørgrav /* 360af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 361af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 362af12a3e7SDag-Erling Smørgrav */ 363af12a3e7SDag-Erling Smørgrav 364af12a3e7SDag-Erling Smørgrav void 365af12a3e7SDag-Erling Smørgrav channel_close_all(void) 366af12a3e7SDag-Erling Smørgrav { 367af12a3e7SDag-Erling Smørgrav int i; 368af12a3e7SDag-Erling Smørgrav 369af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 370af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 371af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 372af12a3e7SDag-Erling Smørgrav } 373af12a3e7SDag-Erling Smørgrav 374af12a3e7SDag-Erling Smørgrav /* 375af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 376af12a3e7SDag-Erling Smørgrav */ 377af12a3e7SDag-Erling Smørgrav 378af12a3e7SDag-Erling Smørgrav void 379af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 380af12a3e7SDag-Erling Smørgrav { 381af12a3e7SDag-Erling Smørgrav int i; 382af12a3e7SDag-Erling Smørgrav Channel *c; 383af12a3e7SDag-Erling Smørgrav 384af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 385af12a3e7SDag-Erling Smørgrav c = channels[i]; 386af12a3e7SDag-Erling Smørgrav if (c != NULL) { 387af12a3e7SDag-Erling Smørgrav switch (c->type) { 388af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 389af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 390af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 391af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 392af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 393af12a3e7SDag-Erling Smørgrav channel_free(c); 394af12a3e7SDag-Erling Smørgrav break; 395af12a3e7SDag-Erling Smørgrav } 396af12a3e7SDag-Erling Smørgrav } 397af12a3e7SDag-Erling Smørgrav } 398af12a3e7SDag-Erling Smørgrav } 399af12a3e7SDag-Erling Smørgrav 400af12a3e7SDag-Erling Smørgrav /* 401af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 402af12a3e7SDag-Erling Smørgrav * more channel is overfull. 403af12a3e7SDag-Erling Smørgrav */ 404af12a3e7SDag-Erling Smørgrav 405af12a3e7SDag-Erling Smørgrav int 406af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 407af12a3e7SDag-Erling Smørgrav { 408af12a3e7SDag-Erling Smørgrav u_int i; 409af12a3e7SDag-Erling Smørgrav Channel *c; 410af12a3e7SDag-Erling Smørgrav 411af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 412af12a3e7SDag-Erling Smørgrav c = channels[i]; 413af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 414af12a3e7SDag-Erling Smørgrav #if 0 415af12a3e7SDag-Erling Smørgrav if (!compat20 && 416af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 417af12a3e7SDag-Erling Smørgrav debug("channel %d: big input buffer %d", 418af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 419af12a3e7SDag-Erling Smørgrav return 0; 420af12a3e7SDag-Erling Smørgrav } 421af12a3e7SDag-Erling Smørgrav #endif 422af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 423af12a3e7SDag-Erling Smørgrav debug("channel %d: big output buffer %d > %d", 424af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 425af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 426af12a3e7SDag-Erling Smørgrav return 0; 427af12a3e7SDag-Erling Smørgrav } 428af12a3e7SDag-Erling Smørgrav } 429af12a3e7SDag-Erling Smørgrav } 430af12a3e7SDag-Erling Smørgrav return 1; 431af12a3e7SDag-Erling Smørgrav } 432af12a3e7SDag-Erling Smørgrav 433af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 434af12a3e7SDag-Erling Smørgrav 435af12a3e7SDag-Erling Smørgrav int 436af12a3e7SDag-Erling Smørgrav channel_still_open(void) 437af12a3e7SDag-Erling Smørgrav { 438af12a3e7SDag-Erling Smørgrav int i; 439af12a3e7SDag-Erling Smørgrav Channel *c; 440af12a3e7SDag-Erling Smørgrav 441af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 442af12a3e7SDag-Erling Smørgrav c = channels[i]; 443af12a3e7SDag-Erling Smørgrav if (c == NULL) 444af12a3e7SDag-Erling Smørgrav continue; 445af12a3e7SDag-Erling Smørgrav switch (c->type) { 446af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 447af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 448af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 449af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 450af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 451af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 452af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 453af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 454af12a3e7SDag-Erling Smørgrav continue; 455af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 456af12a3e7SDag-Erling Smørgrav if (!compat20) 457af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 458af12a3e7SDag-Erling Smørgrav continue; 459af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 460af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 461af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 462af12a3e7SDag-Erling Smørgrav return 1; 463af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 464af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 465af12a3e7SDag-Erling Smørgrav if (!compat13) 466af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 467af12a3e7SDag-Erling Smørgrav return 1; 468af12a3e7SDag-Erling Smørgrav default: 469af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 470af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 471af12a3e7SDag-Erling Smørgrav } 472af12a3e7SDag-Erling Smørgrav } 473af12a3e7SDag-Erling Smørgrav return 0; 474af12a3e7SDag-Erling Smørgrav } 475af12a3e7SDag-Erling Smørgrav 476af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 477af12a3e7SDag-Erling Smørgrav 478af12a3e7SDag-Erling Smørgrav int 479af12a3e7SDag-Erling Smørgrav channel_find_open(void) 480af12a3e7SDag-Erling Smørgrav { 481af12a3e7SDag-Erling Smørgrav int i; 482af12a3e7SDag-Erling Smørgrav Channel *c; 483af12a3e7SDag-Erling Smørgrav 484af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 485af12a3e7SDag-Erling Smørgrav c = channels[i]; 486af12a3e7SDag-Erling Smørgrav if (c == NULL) 487af12a3e7SDag-Erling Smørgrav continue; 488af12a3e7SDag-Erling Smørgrav switch (c->type) { 489af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 490af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 491af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 492af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 493af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 494af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 495af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 496af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 497af12a3e7SDag-Erling Smørgrav continue; 498af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 499af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 500af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 501af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 502af12a3e7SDag-Erling Smørgrav return i; 503af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 504af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 505af12a3e7SDag-Erling Smørgrav if (!compat13) 506af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 507af12a3e7SDag-Erling Smørgrav return i; 508af12a3e7SDag-Erling Smørgrav default: 509af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 510af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 511af12a3e7SDag-Erling Smørgrav } 512af12a3e7SDag-Erling Smørgrav } 513af12a3e7SDag-Erling Smørgrav return -1; 514af12a3e7SDag-Erling Smørgrav } 515af12a3e7SDag-Erling Smørgrav 516af12a3e7SDag-Erling Smørgrav 517af12a3e7SDag-Erling Smørgrav /* 518af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 519af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 520af12a3e7SDag-Erling Smørgrav * newlines. 521af12a3e7SDag-Erling Smørgrav */ 522af12a3e7SDag-Erling Smørgrav 523af12a3e7SDag-Erling Smørgrav char * 524af12a3e7SDag-Erling Smørgrav channel_open_message(void) 525af12a3e7SDag-Erling Smørgrav { 526af12a3e7SDag-Erling Smørgrav Buffer buffer; 527af12a3e7SDag-Erling Smørgrav Channel *c; 528af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 529af12a3e7SDag-Erling Smørgrav int i; 530af12a3e7SDag-Erling Smørgrav 531af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 532af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 533af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 534af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 535af12a3e7SDag-Erling Smørgrav c = channels[i]; 536af12a3e7SDag-Erling Smørgrav if (c == NULL) 537af12a3e7SDag-Erling Smørgrav continue; 538af12a3e7SDag-Erling Smørgrav switch (c->type) { 539af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 540af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 541af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 542af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 543af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 544af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 545af12a3e7SDag-Erling Smørgrav continue; 546af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 547af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 548af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 549af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 550af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 551af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 552af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 553af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 554af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 555af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 556af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 557af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 558af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 559af12a3e7SDag-Erling Smørgrav c->rfd, c->wfd); 560af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 561af12a3e7SDag-Erling Smørgrav continue; 562af12a3e7SDag-Erling Smørgrav default: 563af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 564af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 565af12a3e7SDag-Erling Smørgrav } 566af12a3e7SDag-Erling Smørgrav } 567af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 568af12a3e7SDag-Erling Smørgrav cp = xstrdup(buffer_ptr(&buffer)); 569af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 570af12a3e7SDag-Erling Smørgrav return cp; 571af12a3e7SDag-Erling Smørgrav } 572af12a3e7SDag-Erling Smørgrav 573af12a3e7SDag-Erling Smørgrav void 574af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 575af12a3e7SDag-Erling Smørgrav { 576af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 577f388f5efSDag-Erling Smørgrav 578af12a3e7SDag-Erling Smørgrav if (c == NULL) { 579af12a3e7SDag-Erling Smørgrav log("channel_send_open: %d: bad id", id); 580af12a3e7SDag-Erling Smørgrav return; 581af12a3e7SDag-Erling Smørgrav } 582af12a3e7SDag-Erling Smørgrav debug("send channel open %d", id); 583af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 584af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 585af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 586af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 587af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 588af12a3e7SDag-Erling Smørgrav packet_send(); 589af12a3e7SDag-Erling Smørgrav } 590af12a3e7SDag-Erling Smørgrav 591af12a3e7SDag-Erling Smørgrav void 592af12a3e7SDag-Erling Smørgrav channel_request_start(int local_id, char *service, int wantconfirm) 593af12a3e7SDag-Erling Smørgrav { 594af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(local_id); 595f388f5efSDag-Erling Smørgrav 596af12a3e7SDag-Erling Smørgrav if (c == NULL) { 597af12a3e7SDag-Erling Smørgrav log("channel_request_start: %d: unknown channel id", local_id); 598af12a3e7SDag-Erling Smørgrav return; 599af12a3e7SDag-Erling Smørgrav } 600af12a3e7SDag-Erling Smørgrav debug("channel request %d: %s", local_id, service) ; 601af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 602af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 603af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 604af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 605af12a3e7SDag-Erling Smørgrav } 606af12a3e7SDag-Erling Smørgrav void 607af12a3e7SDag-Erling Smørgrav channel_register_confirm(int id, channel_callback_fn *fn) 608af12a3e7SDag-Erling Smørgrav { 609af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 610f388f5efSDag-Erling Smørgrav 611af12a3e7SDag-Erling Smørgrav if (c == NULL) { 612af12a3e7SDag-Erling Smørgrav log("channel_register_comfirm: %d: bad id", id); 613af12a3e7SDag-Erling Smørgrav return; 614af12a3e7SDag-Erling Smørgrav } 615af12a3e7SDag-Erling Smørgrav c->confirm = fn; 616af12a3e7SDag-Erling Smørgrav } 617af12a3e7SDag-Erling Smørgrav void 618af12a3e7SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn) 619af12a3e7SDag-Erling Smørgrav { 620af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 621f388f5efSDag-Erling Smørgrav 622af12a3e7SDag-Erling Smørgrav if (c == NULL) { 623af12a3e7SDag-Erling Smørgrav log("channel_register_cleanup: %d: bad id", id); 624af12a3e7SDag-Erling Smørgrav return; 625af12a3e7SDag-Erling Smørgrav } 626af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 627af12a3e7SDag-Erling Smørgrav } 628af12a3e7SDag-Erling Smørgrav void 629af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 630af12a3e7SDag-Erling Smørgrav { 631af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 632f388f5efSDag-Erling Smørgrav 633af12a3e7SDag-Erling Smørgrav if (c == NULL) { 634af12a3e7SDag-Erling Smørgrav log("channel_cancel_cleanup: %d: bad id", id); 635af12a3e7SDag-Erling Smørgrav return; 636af12a3e7SDag-Erling Smørgrav } 637af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 638af12a3e7SDag-Erling Smørgrav } 639af12a3e7SDag-Erling Smørgrav void 640af12a3e7SDag-Erling Smørgrav channel_register_filter(int id, channel_filter_fn *fn) 641af12a3e7SDag-Erling Smørgrav { 642af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 643f388f5efSDag-Erling Smørgrav 644af12a3e7SDag-Erling Smørgrav if (c == NULL) { 645af12a3e7SDag-Erling Smørgrav log("channel_register_filter: %d: bad id", id); 646af12a3e7SDag-Erling Smørgrav return; 647af12a3e7SDag-Erling Smørgrav } 648af12a3e7SDag-Erling Smørgrav c->input_filter = fn; 649af12a3e7SDag-Erling Smørgrav } 650af12a3e7SDag-Erling Smørgrav 651af12a3e7SDag-Erling Smørgrav void 652af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 653af12a3e7SDag-Erling Smørgrav int extusage, int nonblock, u_int window_max) 654af12a3e7SDag-Erling Smørgrav { 655af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 656f388f5efSDag-Erling Smørgrav 657af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 658af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 659af12a3e7SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 660af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 661af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 662af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 663af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 664af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 665af12a3e7SDag-Erling Smørgrav packet_send(); 666511b41d2SMark Murray } 667511b41d2SMark Murray 668511b41d2SMark Murray /* 669a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 670a04a10f8SKris Kennaway * channels in the select bitmasks. 671511b41d2SMark Murray */ 672a04a10f8SKris Kennaway /* 673a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 674a04a10f8SKris Kennaway * have events pending. 675a04a10f8SKris Kennaway */ 676a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 677a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 678a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 679511b41d2SMark Murray 680af12a3e7SDag-Erling Smørgrav static void 681a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 682511b41d2SMark Murray { 683a04a10f8SKris Kennaway FD_SET(c->sock, readset); 684a04a10f8SKris Kennaway } 685a04a10f8SKris Kennaway 686af12a3e7SDag-Erling Smørgrav static void 687ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) 688ca3176e7SBrian Feldman { 689ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 690ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 691ca3176e7SBrian Feldman } 692ca3176e7SBrian Feldman 693af12a3e7SDag-Erling Smørgrav static void 694a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 695a04a10f8SKris Kennaway { 696a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 697a04a10f8SKris Kennaway FD_SET(c->sock, readset); 698a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 699a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 700a04a10f8SKris Kennaway } 701a04a10f8SKris Kennaway 702af12a3e7SDag-Erling Smørgrav static void 703af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) 704a04a10f8SKris Kennaway { 705af12a3e7SDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 706a04a10f8SKris Kennaway 707a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 708af12a3e7SDag-Erling Smørgrav limit > 0 && 709af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) < limit) 710a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 711a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 712a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 713a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 714a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 715a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 71680628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 71780628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 71880628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 71980628bacSDag-Erling Smørgrav else 720a04a10f8SKris Kennaway chan_obuf_empty(c); 721a04a10f8SKris Kennaway } 722a04a10f8SKris Kennaway } 723a04a10f8SKris Kennaway /** XXX check close conditions, too */ 724af12a3e7SDag-Erling Smørgrav if (compat20 && c->efd != -1) { 725a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 726a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 727a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 72880628bacSDag-Erling Smørgrav else if (!(c->flags & CHAN_EOF_SENT) && 72980628bacSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ && 730a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 731a04a10f8SKris Kennaway FD_SET(c->efd, readset); 732a04a10f8SKris Kennaway } 733a04a10f8SKris Kennaway } 734a04a10f8SKris Kennaway 735af12a3e7SDag-Erling Smørgrav static void 736a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 737a04a10f8SKris Kennaway { 738a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 739a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 740a04a10f8SKris Kennaway packet_put_int(c->remote_id); 741a04a10f8SKris Kennaway packet_send(); 742a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 743ca3176e7SBrian Feldman debug("channel %d: closing after input drain.", c->self); 744a04a10f8SKris Kennaway } 745a04a10f8SKris Kennaway } 746a04a10f8SKris Kennaway 747af12a3e7SDag-Erling Smørgrav static void 748a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 749a04a10f8SKris Kennaway { 750a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 751af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 752a04a10f8SKris Kennaway else 753a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 754a04a10f8SKris Kennaway } 755a04a10f8SKris Kennaway 756a04a10f8SKris Kennaway /* 757a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 758a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 759a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 760a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 761a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 762a04a10f8SKris Kennaway * XXX All this happens at the client side. 763af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 764a04a10f8SKris Kennaway */ 765af12a3e7SDag-Erling Smørgrav static int 766af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 767a04a10f8SKris Kennaway { 768ca3176e7SBrian Feldman u_char *ucp; 769ca3176e7SBrian Feldman u_int proto_len, data_len; 770511b41d2SMark Murray 771511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 772af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 773a04a10f8SKris Kennaway return 0; 774511b41d2SMark Murray 775511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 776af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 777511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 778511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 779511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 780511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 781511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 782511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 783511b41d2SMark Murray } else { 784511b41d2SMark Murray debug("Initial X11 packet contains bad byte order byte: 0x%x", 785511b41d2SMark Murray ucp[0]); 786a04a10f8SKris Kennaway return -1; 787511b41d2SMark Murray } 788511b41d2SMark Murray 789511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 790af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 791511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 792a04a10f8SKris Kennaway return 0; 793511b41d2SMark Murray 794511b41d2SMark Murray /* Check if authentication protocol matches. */ 795511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 796511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 797511b41d2SMark Murray debug("X11 connection uses different authentication protocol."); 798a04a10f8SKris Kennaway return -1; 799511b41d2SMark Murray } 800511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 801511b41d2SMark Murray if (data_len != x11_fake_data_len || 802511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 803511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 804511b41d2SMark Murray debug("X11 auth data does not match fake data."); 805a04a10f8SKris Kennaway return -1; 806511b41d2SMark Murray } 807511b41d2SMark Murray /* Check fake data length */ 808511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 809511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 810511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 811a04a10f8SKris Kennaway return -1; 812511b41d2SMark Murray } 813511b41d2SMark Murray /* 814511b41d2SMark Murray * Received authentication protocol and data match 815511b41d2SMark Murray * our fake data. Substitute the fake data with real 816511b41d2SMark Murray * data. 817511b41d2SMark Murray */ 818511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 819511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 820a04a10f8SKris Kennaway return 1; 821a04a10f8SKris Kennaway } 822511b41d2SMark Murray 823af12a3e7SDag-Erling Smørgrav static void 824a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 825a04a10f8SKris Kennaway { 826af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 827f388f5efSDag-Erling Smørgrav 828a04a10f8SKris Kennaway if (ret == 1) { 829511b41d2SMark Murray /* Start normal processing for the channel. */ 830a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 831a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 832a04a10f8SKris Kennaway } else if (ret == -1) { 833511b41d2SMark Murray /* 834511b41d2SMark Murray * We have received an X11 connection that has bad 835511b41d2SMark Murray * authentication information. 836511b41d2SMark Murray */ 837ca3176e7SBrian Feldman log("X11 connection rejected because of wrong authentication."); 838a04a10f8SKris Kennaway buffer_clear(&c->input); 839a04a10f8SKris Kennaway buffer_clear(&c->output); 840af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 841a04a10f8SKris Kennaway c->sock = -1; 842a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 843511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 844a04a10f8SKris Kennaway packet_put_int(c->remote_id); 845511b41d2SMark Murray packet_send(); 846511b41d2SMark Murray } 847511b41d2SMark Murray } 848511b41d2SMark Murray 849af12a3e7SDag-Erling Smørgrav static void 850a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 851a04a10f8SKris Kennaway { 852af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 853af12a3e7SDag-Erling Smørgrav 854af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 855af12a3e7SDag-Erling Smørgrav 856a04a10f8SKris Kennaway if (ret == 1) { 857a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 858af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 859a04a10f8SKris Kennaway } else if (ret == -1) { 860af12a3e7SDag-Erling Smørgrav log("X11 connection rejected because of wrong authentication."); 861a04a10f8SKris Kennaway debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 862af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 863af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 864af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 865af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 866af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 867af12a3e7SDag-Erling Smørgrav if (compat20) 868a04a10f8SKris Kennaway chan_write_failed(c); 869af12a3e7SDag-Erling Smørgrav else 870af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 871a04a10f8SKris Kennaway debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 872a04a10f8SKris Kennaway } 873a04a10f8SKris Kennaway } 874a04a10f8SKris Kennaway 875ca3176e7SBrian Feldman /* try to decode a socks4 header */ 876af12a3e7SDag-Erling Smørgrav static int 877ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) 878ca3176e7SBrian Feldman { 879f388f5efSDag-Erling Smørgrav char *p, *host; 880ca3176e7SBrian Feldman int len, have, i, found; 881ca3176e7SBrian Feldman char username[256]; 882ca3176e7SBrian Feldman struct { 883ca3176e7SBrian Feldman u_int8_t version; 884ca3176e7SBrian Feldman u_int8_t command; 885ca3176e7SBrian Feldman u_int16_t dest_port; 886ca3176e7SBrian Feldman struct in_addr dest_addr; 887ca3176e7SBrian Feldman } s4_req, s4_rsp; 888ca3176e7SBrian Feldman 889ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 890ca3176e7SBrian Feldman 891ca3176e7SBrian Feldman have = buffer_len(&c->input); 892ca3176e7SBrian Feldman len = sizeof(s4_req); 893ca3176e7SBrian Feldman if (have < len) 894ca3176e7SBrian Feldman return 0; 895ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 896ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 897ca3176e7SBrian Feldman if (p[i] == '\0') { 898ca3176e7SBrian Feldman found = 1; 899ca3176e7SBrian Feldman break; 900ca3176e7SBrian Feldman } 901ca3176e7SBrian Feldman if (i > 1024) { 902ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 903ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 904ca3176e7SBrian Feldman c->self); 905ca3176e7SBrian Feldman return -1; 906ca3176e7SBrian Feldman } 907ca3176e7SBrian Feldman } 908ca3176e7SBrian Feldman if (!found) 909ca3176e7SBrian Feldman return 0; 910ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 911ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 912ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 913ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 914ca3176e7SBrian Feldman have = buffer_len(&c->input); 915ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 916ca3176e7SBrian Feldman len = strlen(p); 917ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 918ca3176e7SBrian Feldman if (len > have) 919ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 920ca3176e7SBrian Feldman c->self, len, have); 921ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 922ca3176e7SBrian Feldman buffer_consume(&c->input, len); 923ca3176e7SBrian Feldman buffer_consume(&c->input, 1); /* trailing '\0' */ 924ca3176e7SBrian Feldman 925ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 926ca3176e7SBrian Feldman strlcpy(c->path, host, sizeof(c->path)); 927ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 928ca3176e7SBrian Feldman 929ca3176e7SBrian Feldman debug("channel %d: dynamic request: socks4 host %s port %u command %u", 930ca3176e7SBrian Feldman c->self, host, c->host_port, s4_req.command); 931ca3176e7SBrian Feldman 932ca3176e7SBrian Feldman if (s4_req.command != 1) { 933ca3176e7SBrian Feldman debug("channel %d: cannot handle: socks4 cn %d", 934ca3176e7SBrian Feldman c->self, s4_req.command); 935ca3176e7SBrian Feldman return -1; 936ca3176e7SBrian Feldman } 937ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 938ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 939ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 940ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 941ca3176e7SBrian Feldman buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); 942ca3176e7SBrian Feldman return 1; 943ca3176e7SBrian Feldman } 944ca3176e7SBrian Feldman 945ca3176e7SBrian Feldman /* dynamic port forwarding */ 946af12a3e7SDag-Erling Smørgrav static void 947ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 948ca3176e7SBrian Feldman { 949ca3176e7SBrian Feldman u_char *p; 950ca3176e7SBrian Feldman int have, ret; 951ca3176e7SBrian Feldman 952ca3176e7SBrian Feldman have = buffer_len(&c->input); 953af12a3e7SDag-Erling Smørgrav c->delayed = 0; 954ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 955ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 956ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 957ca3176e7SBrian Feldman if (have < 4) { 958ca3176e7SBrian Feldman /* need more */ 959ca3176e7SBrian Feldman FD_SET(c->sock, readset); 960ca3176e7SBrian Feldman return; 961ca3176e7SBrian Feldman } 962ca3176e7SBrian Feldman /* try to guess the protocol */ 963ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 964ca3176e7SBrian Feldman switch (p[0]) { 965ca3176e7SBrian Feldman case 0x04: 966ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 967ca3176e7SBrian Feldman break; 968ca3176e7SBrian Feldman default: 969ca3176e7SBrian Feldman ret = -1; 970ca3176e7SBrian Feldman break; 971ca3176e7SBrian Feldman } 972ca3176e7SBrian Feldman if (ret < 0) { 973af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 974ca3176e7SBrian Feldman } else if (ret == 0) { 975ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 976ca3176e7SBrian Feldman /* need more */ 977ca3176e7SBrian Feldman FD_SET(c->sock, readset); 978ca3176e7SBrian Feldman } else { 979ca3176e7SBrian Feldman /* switch to the next state */ 980ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 981ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 982ca3176e7SBrian Feldman } 983ca3176e7SBrian Feldman } 984ca3176e7SBrian Feldman 985a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 986af12a3e7SDag-Erling Smørgrav static void 987a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 988511b41d2SMark Murray { 989af12a3e7SDag-Erling Smørgrav Channel *nc; 990511b41d2SMark Murray struct sockaddr addr; 991af12a3e7SDag-Erling Smørgrav int newsock; 992511b41d2SMark Murray socklen_t addrlen; 993ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 994a04a10f8SKris Kennaway int remote_port; 995511b41d2SMark Murray 996a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 997511b41d2SMark Murray debug("X11 connection requested."); 998511b41d2SMark Murray addrlen = sizeof(addr); 999a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1000af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1001af12a3e7SDag-Erling Smørgrav debug("single_connection: closing X11 listener."); 1002af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1003af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1004af12a3e7SDag-Erling Smørgrav } 1005511b41d2SMark Murray if (newsock < 0) { 1006511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1007a04a10f8SKris Kennaway return; 1008511b41d2SMark Murray } 1009af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1010ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1011a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1012511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1013ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1014a04a10f8SKris Kennaway 1015af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1016a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1017a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 10185b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 1019a04a10f8SKris Kennaway if (compat20) { 1020a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1021a04a10f8SKris Kennaway packet_put_cstring("x11"); 1022af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1023af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1024af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1025ca3176e7SBrian Feldman /* originator ipaddr and port */ 1026ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1027a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1028a04a10f8SKris Kennaway debug("ssh2 x11 bug compat mode"); 1029a04a10f8SKris Kennaway } else { 1030a04a10f8SKris Kennaway packet_put_int(remote_port); 1031a04a10f8SKris Kennaway } 1032a04a10f8SKris Kennaway packet_send(); 1033a04a10f8SKris Kennaway } else { 1034511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1035af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1036af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1037af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1038af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1039511b41d2SMark Murray packet_send(); 1040511b41d2SMark Murray } 1041ca3176e7SBrian Feldman xfree(remote_ipaddr); 1042a04a10f8SKris Kennaway } 1043a04a10f8SKris Kennaway } 1044511b41d2SMark Murray 1045af12a3e7SDag-Erling Smørgrav static void 1046ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1047ca3176e7SBrian Feldman { 1048ca3176e7SBrian Feldman int direct; 1049ca3176e7SBrian Feldman char buf[1024]; 1050ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1051ca3176e7SBrian Feldman u_short remote_port = get_peer_port(c->sock); 1052ca3176e7SBrian Feldman 1053ca3176e7SBrian Feldman direct = (strcmp(rtype, "direct-tcpip") == 0); 1054ca3176e7SBrian Feldman 1055ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1056ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1057ca3176e7SBrian Feldman "connect from %.200s port %d", 1058ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1059ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1060ca3176e7SBrian Feldman 1061ca3176e7SBrian Feldman xfree(c->remote_name); 1062ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1063ca3176e7SBrian Feldman 1064ca3176e7SBrian Feldman if (compat20) { 1065ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1066ca3176e7SBrian Feldman packet_put_cstring(rtype); 1067ca3176e7SBrian Feldman packet_put_int(c->self); 1068ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1069ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1070ca3176e7SBrian Feldman if (direct) { 1071ca3176e7SBrian Feldman /* target host, port */ 1072ca3176e7SBrian Feldman packet_put_cstring(c->path); 1073ca3176e7SBrian Feldman packet_put_int(c->host_port); 1074ca3176e7SBrian Feldman } else { 1075ca3176e7SBrian Feldman /* listen address, port */ 1076ca3176e7SBrian Feldman packet_put_cstring(c->path); 1077ca3176e7SBrian Feldman packet_put_int(c->listening_port); 1078ca3176e7SBrian Feldman } 1079ca3176e7SBrian Feldman /* originator host and port */ 1080ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1081ca3176e7SBrian Feldman packet_put_int(remote_port); 1082ca3176e7SBrian Feldman packet_send(); 1083ca3176e7SBrian Feldman } else { 1084ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1085ca3176e7SBrian Feldman packet_put_int(c->self); 1086ca3176e7SBrian Feldman packet_put_cstring(c->path); 1087ca3176e7SBrian Feldman packet_put_int(c->host_port); 1088af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1089af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1090ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1091ca3176e7SBrian Feldman packet_send(); 1092ca3176e7SBrian Feldman } 1093ca3176e7SBrian Feldman xfree(remote_ipaddr); 1094ca3176e7SBrian Feldman } 1095ca3176e7SBrian Feldman 1096511b41d2SMark Murray /* 1097a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1098511b41d2SMark Murray */ 1099af12a3e7SDag-Erling Smørgrav static void 1100a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 1101a04a10f8SKris Kennaway { 1102ca3176e7SBrian Feldman Channel *nc; 1103a04a10f8SKris Kennaway struct sockaddr addr; 1104af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1105a04a10f8SKris Kennaway socklen_t addrlen; 1106ca3176e7SBrian Feldman char *rtype; 1107a04a10f8SKris Kennaway 1108a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1109a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1110a04a10f8SKris Kennaway "to %.100s port %d requested.", 1111a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1112ca3176e7SBrian Feldman 1113af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1114af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1115af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1116af12a3e7SDag-Erling Smørgrav } else { 1117af12a3e7SDag-Erling Smørgrav if (c->host_port == 0) { 1118af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1119af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1120af12a3e7SDag-Erling Smørgrav } else { 1121af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1122af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1123af12a3e7SDag-Erling Smørgrav } 1124af12a3e7SDag-Erling Smørgrav } 1125ca3176e7SBrian Feldman 1126511b41d2SMark Murray addrlen = sizeof(addr); 1127a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1128511b41d2SMark Murray if (newsock < 0) { 1129511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1130a04a10f8SKris Kennaway return; 1131511b41d2SMark Murray } 1132af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1133af12a3e7SDag-Erling Smørgrav nc = channel_new(rtype, 1134ca3176e7SBrian Feldman nextstate, newsock, newsock, -1, 1135a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 1136ca3176e7SBrian Feldman 0, xstrdup(rtype), 1); 1137ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1138ca3176e7SBrian Feldman nc->host_port = c->host_port; 1139ca3176e7SBrian Feldman strlcpy(nc->path, c->path, sizeof(nc->path)); 1140ca3176e7SBrian Feldman 1141af12a3e7SDag-Erling Smørgrav if (nextstate == SSH_CHANNEL_DYNAMIC) { 1142af12a3e7SDag-Erling Smørgrav /* 1143af12a3e7SDag-Erling Smørgrav * do not call the channel_post handler until 1144af12a3e7SDag-Erling Smørgrav * this flag has been reset by a pre-handler. 1145af12a3e7SDag-Erling Smørgrav * otherwise the FD_ISSET calls might overflow 1146af12a3e7SDag-Erling Smørgrav */ 1147af12a3e7SDag-Erling Smørgrav nc->delayed = 1; 1148af12a3e7SDag-Erling Smørgrav } else { 1149ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1150a04a10f8SKris Kennaway } 1151a04a10f8SKris Kennaway } 1152af12a3e7SDag-Erling Smørgrav } 1153511b41d2SMark Murray 1154511b41d2SMark Murray /* 1155a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1156a04a10f8SKris Kennaway * clients. 1157511b41d2SMark Murray */ 1158af12a3e7SDag-Erling Smørgrav static void 1159a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 1160a04a10f8SKris Kennaway { 1161af12a3e7SDag-Erling Smørgrav Channel *nc; 1162af12a3e7SDag-Erling Smørgrav char *name; 1163af12a3e7SDag-Erling Smørgrav int newsock; 1164a04a10f8SKris Kennaway struct sockaddr addr; 1165a04a10f8SKris Kennaway socklen_t addrlen; 1166a04a10f8SKris Kennaway 1167a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1168511b41d2SMark Murray addrlen = sizeof(addr); 1169a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1170511b41d2SMark Murray if (newsock < 0) { 1171511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 1172a04a10f8SKris Kennaway return; 1173511b41d2SMark Murray } 1174af12a3e7SDag-Erling Smørgrav name = xstrdup("accepted auth socket"); 1175af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1176ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1177ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1178af12a3e7SDag-Erling Smørgrav 0, name, 1); 1179ca3176e7SBrian Feldman if (compat20) { 1180ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1181ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1182af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1183ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1184ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1185ca3176e7SBrian Feldman } else { 1186511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1187af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1188ca3176e7SBrian Feldman } 1189511b41d2SMark Murray packet_send(); 1190511b41d2SMark Murray } 1191a04a10f8SKris Kennaway } 1192511b41d2SMark Murray 1193af12a3e7SDag-Erling Smørgrav static void 1194ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 1195ca3176e7SBrian Feldman { 1196ca3176e7SBrian Feldman int err = 0; 1197af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1198af12a3e7SDag-Erling Smørgrav 1199af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1200af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1201af12a3e7SDag-Erling Smørgrav err = errno; 1202af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1203af12a3e7SDag-Erling Smørgrav } 1204ca3176e7SBrian Feldman if (err == 0) { 1205af12a3e7SDag-Erling Smørgrav debug("channel %d: connected", c->self); 1206af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1207af12a3e7SDag-Erling Smørgrav if (compat20) { 1208af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1209af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1210af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1211af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1212af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1213af12a3e7SDag-Erling Smørgrav } else { 1214af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1215af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1216af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1217af12a3e7SDag-Erling Smørgrav } 1218ca3176e7SBrian Feldman } else { 1219ca3176e7SBrian Feldman debug("channel %d: not connected: %s", 1220ca3176e7SBrian Feldman c->self, strerror(err)); 1221af12a3e7SDag-Erling Smørgrav if (compat20) { 1222af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1223af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1224af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1225af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1226af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1227af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1228ca3176e7SBrian Feldman } 1229af12a3e7SDag-Erling Smørgrav } else { 1230af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1231af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1232ca3176e7SBrian Feldman } 1233af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1234af12a3e7SDag-Erling Smørgrav } 1235af12a3e7SDag-Erling Smørgrav packet_send(); 1236ca3176e7SBrian Feldman } 1237ca3176e7SBrian Feldman } 1238ca3176e7SBrian Feldman 1239af12a3e7SDag-Erling Smørgrav static int 1240a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 1241a04a10f8SKris Kennaway { 1242a04a10f8SKris Kennaway char buf[16*1024]; 1243a04a10f8SKris Kennaway int len; 1244511b41d2SMark Murray 1245a04a10f8SKris Kennaway if (c->rfd != -1 && 1246a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 1247a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1248a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1249a04a10f8SKris Kennaway return 1; 12500c82706bSBrian Feldman if (len <= 0) { 12510c82706bSBrian Feldman debug("channel %d: read<=0 rfd %d len %d", 1252a04a10f8SKris Kennaway c->self, c->rfd, len); 1253ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1254ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1255af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1256ca3176e7SBrian Feldman return -1; 1257ca3176e7SBrian Feldman } else if (compat13) { 1258af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1259a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1260af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1261a04a10f8SKris Kennaway } else { 1262a04a10f8SKris Kennaway chan_read_failed(c); 1263a04a10f8SKris Kennaway } 1264a04a10f8SKris Kennaway return -1; 1265a04a10f8SKris Kennaway } 1266b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1267b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1268ca3176e7SBrian Feldman debug("channel %d: filter stops", c->self); 1269b66f2d16SKris Kennaway chan_read_failed(c); 1270b66f2d16SKris Kennaway } 1271b66f2d16SKris Kennaway } else { 1272a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1273a04a10f8SKris Kennaway } 1274b66f2d16SKris Kennaway } 1275a04a10f8SKris Kennaway return 1; 1276a04a10f8SKris Kennaway } 1277af12a3e7SDag-Erling Smørgrav static int 1278a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 1279a04a10f8SKris Kennaway { 1280ca3176e7SBrian Feldman struct termios tio; 1281af12a3e7SDag-Erling Smørgrav u_char *data; 1282af12a3e7SDag-Erling Smørgrav u_int dlen; 1283a04a10f8SKris Kennaway int len; 1284a04a10f8SKris Kennaway 1285a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1286a04a10f8SKris Kennaway if (c->wfd != -1 && 1287a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1288a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1289af12a3e7SDag-Erling Smørgrav data = buffer_ptr(&c->output); 1290af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1291f388f5efSDag-Erling Smørgrav #ifdef _AIX 1292f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 1293f388f5efSDag-Erling Smørgrav if (compat20 && c->wfd_isatty && dlen > 8*1024) 1294f388f5efSDag-Erling Smørgrav dlen = 8*1024; 1295f388f5efSDag-Erling Smørgrav #endif 1296af12a3e7SDag-Erling Smørgrav len = write(c->wfd, data, dlen); 1297a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1298a04a10f8SKris Kennaway return 1; 1299511b41d2SMark Murray if (len <= 0) { 1300ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1301ca3176e7SBrian Feldman debug("channel %d: not open", c->self); 1302af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1303ca3176e7SBrian Feldman return -1; 1304ca3176e7SBrian Feldman } else if (compat13) { 1305af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1306af12a3e7SDag-Erling Smørgrav debug("channel %d: input draining.", c->self); 1307a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1308511b41d2SMark Murray } else { 1309a04a10f8SKris Kennaway chan_write_failed(c); 1310511b41d2SMark Murray } 1311a04a10f8SKris Kennaway return -1; 1312511b41d2SMark Murray } 1313af12a3e7SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { 1314e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1315e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1316e0fbb1d2SBrian Feldman /* 1317e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1318ca3176e7SBrian Feldman * traffic analysis. We need to match the 1319ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1320ca3176e7SBrian Feldman * (4 byte channel id + data) 1321e0fbb1d2SBrian Feldman */ 1322ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1323e0fbb1d2SBrian Feldman packet_send(); 1324e0fbb1d2SBrian Feldman } 1325e0fbb1d2SBrian Feldman } 1326a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1327a04a10f8SKris Kennaway if (compat20 && len > 0) { 1328a04a10f8SKris Kennaway c->local_consumed += len; 1329511b41d2SMark Murray } 1330511b41d2SMark Murray } 1331a04a10f8SKris Kennaway return 1; 1332511b41d2SMark Murray } 1333af12a3e7SDag-Erling Smørgrav static int 1334a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 1335a04a10f8SKris Kennaway { 1336a04a10f8SKris Kennaway char buf[16*1024]; 1337a04a10f8SKris Kennaway int len; 1338511b41d2SMark Murray 1339a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1340a04a10f8SKris Kennaway if (c->efd != -1) { 1341a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1342a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1343a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1344a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1345a04a10f8SKris Kennaway buffer_len(&c->extended)); 13465b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1347a04a10f8SKris Kennaway c->self, len, c->efd); 1348ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1349ca3176e7SBrian Feldman return 1; 1350ca3176e7SBrian Feldman if (len <= 0) { 1351ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1352ca3176e7SBrian Feldman c->self, c->efd); 1353af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1354ca3176e7SBrian Feldman } else { 1355a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1356a04a10f8SKris Kennaway c->local_consumed += len; 1357a04a10f8SKris Kennaway } 1358a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 1359a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 1360a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 13615b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1362a04a10f8SKris Kennaway c->self, len, c->efd); 1363ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1364ca3176e7SBrian Feldman return 1; 1365ca3176e7SBrian Feldman if (len <= 0) { 1366ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1367a04a10f8SKris Kennaway c->self, c->efd); 1368af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1369ca3176e7SBrian Feldman } else { 1370a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1371a04a10f8SKris Kennaway } 1372a04a10f8SKris Kennaway } 1373ca3176e7SBrian Feldman } 1374a04a10f8SKris Kennaway return 1; 1375a04a10f8SKris Kennaway } 1376af12a3e7SDag-Erling Smørgrav static int 1377ca3176e7SBrian Feldman channel_check_window(Channel *c) 1378a04a10f8SKris Kennaway { 1379ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1380ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1381a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 1382a04a10f8SKris Kennaway c->local_consumed > 0) { 1383a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1384a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1385a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 1386a04a10f8SKris Kennaway packet_send(); 13875b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1388a04a10f8SKris Kennaway c->self, c->local_window, 1389a04a10f8SKris Kennaway c->local_consumed); 1390a04a10f8SKris Kennaway c->local_window += c->local_consumed; 1391a04a10f8SKris Kennaway c->local_consumed = 0; 1392a04a10f8SKris Kennaway } 1393a04a10f8SKris Kennaway return 1; 1394a04a10f8SKris Kennaway } 1395a04a10f8SKris Kennaway 1396af12a3e7SDag-Erling Smørgrav static void 1397af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 1398a04a10f8SKris Kennaway { 1399af12a3e7SDag-Erling Smørgrav if (c->delayed) 1400af12a3e7SDag-Erling Smørgrav return; 1401a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1402a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1403af12a3e7SDag-Erling Smørgrav if (!compat20) 1404af12a3e7SDag-Erling Smørgrav return; 1405a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1406ca3176e7SBrian Feldman channel_check_window(c); 1407a04a10f8SKris Kennaway } 1408a04a10f8SKris Kennaway 1409af12a3e7SDag-Erling Smørgrav static void 1410a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 1411a04a10f8SKris Kennaway { 1412a04a10f8SKris Kennaway int len; 1413f388f5efSDag-Erling Smørgrav 1414511b41d2SMark Murray /* Send buffered output data to the socket. */ 1415a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 1416a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 1417a04a10f8SKris Kennaway buffer_len(&c->output)); 1418511b41d2SMark Murray if (len <= 0) 1419af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1420511b41d2SMark Murray else 1421a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1422511b41d2SMark Murray } 1423a04a10f8SKris Kennaway } 1424511b41d2SMark Murray 1425af12a3e7SDag-Erling Smørgrav static void 1426a04a10f8SKris Kennaway channel_handler_init_20(void) 1427a04a10f8SKris Kennaway { 1428af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1429a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1430a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1431ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 1432a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1433ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1434ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1435ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1436a04a10f8SKris Kennaway 1437af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1438a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1439ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 1440a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1441ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1442ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1443af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1444a04a10f8SKris Kennaway } 1445a04a10f8SKris Kennaway 1446af12a3e7SDag-Erling Smørgrav static void 1447a04a10f8SKris Kennaway channel_handler_init_13(void) 1448a04a10f8SKris Kennaway { 1449a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 1450a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 1451a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1452a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1453a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1454a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 1455a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 1456ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1457ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1458a04a10f8SKris Kennaway 1459af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1460a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1461a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1462a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1463a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 1464ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1465af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1466a04a10f8SKris Kennaway } 1467a04a10f8SKris Kennaway 1468af12a3e7SDag-Erling Smørgrav static void 1469a04a10f8SKris Kennaway channel_handler_init_15(void) 1470a04a10f8SKris Kennaway { 1471af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1472a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1473a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1474a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1475a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1476ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1477ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1478a04a10f8SKris Kennaway 1479a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1480a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1481a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1482af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1483ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1484af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1485a04a10f8SKris Kennaway } 1486a04a10f8SKris Kennaway 1487af12a3e7SDag-Erling Smørgrav static void 1488a04a10f8SKris Kennaway channel_handler_init(void) 1489a04a10f8SKris Kennaway { 1490a04a10f8SKris Kennaway int i; 1491f388f5efSDag-Erling Smørgrav 1492a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 1493a04a10f8SKris Kennaway channel_pre[i] = NULL; 1494a04a10f8SKris Kennaway channel_post[i] = NULL; 1495a04a10f8SKris Kennaway } 1496a04a10f8SKris Kennaway if (compat20) 1497a04a10f8SKris Kennaway channel_handler_init_20(); 1498a04a10f8SKris Kennaway else if (compat13) 1499a04a10f8SKris Kennaway channel_handler_init_13(); 1500a04a10f8SKris Kennaway else 1501a04a10f8SKris Kennaway channel_handler_init_15(); 1502a04a10f8SKris Kennaway } 1503a04a10f8SKris Kennaway 1504af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 1505af12a3e7SDag-Erling Smørgrav static void 1506af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 1507af12a3e7SDag-Erling Smørgrav { 1508af12a3e7SDag-Erling Smørgrav if (c == NULL) 1509af12a3e7SDag-Erling Smørgrav return; 1510af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 1511af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 0)) 1512af12a3e7SDag-Erling Smørgrav return; 1513af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: notify user", c->self); 1514af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 1515af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 1516af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 1517af12a3e7SDag-Erling Smørgrav return; 1518af12a3e7SDag-Erling Smørgrav debug("channel %d: gc: user detached", c->self); 1519af12a3e7SDag-Erling Smørgrav } 1520af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 1521af12a3e7SDag-Erling Smørgrav return; 1522af12a3e7SDag-Erling Smørgrav debug("channel %d: garbage collecting", c->self); 1523af12a3e7SDag-Erling Smørgrav channel_free(c); 1524af12a3e7SDag-Erling Smørgrav } 1525af12a3e7SDag-Erling Smørgrav 1526af12a3e7SDag-Erling Smørgrav static void 1527a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 1528a04a10f8SKris Kennaway { 1529a04a10f8SKris Kennaway static int did_init = 0; 1530a04a10f8SKris Kennaway int i; 1531a04a10f8SKris Kennaway Channel *c; 1532a04a10f8SKris Kennaway 1533a04a10f8SKris Kennaway if (!did_init) { 1534a04a10f8SKris Kennaway channel_handler_init(); 1535a04a10f8SKris Kennaway did_init = 1; 1536a04a10f8SKris Kennaway } 1537a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 1538af12a3e7SDag-Erling Smørgrav c = channels[i]; 1539af12a3e7SDag-Erling Smørgrav if (c == NULL) 1540511b41d2SMark Murray continue; 1541af12a3e7SDag-Erling Smørgrav if (ftab[c->type] != NULL) 1542a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 1543af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 1544511b41d2SMark Murray } 1545511b41d2SMark Murray } 1546a04a10f8SKris Kennaway 1547af12a3e7SDag-Erling Smørgrav /* 1548af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 1549af12a3e7SDag-Erling Smørgrav * select bitmasks. 1550af12a3e7SDag-Erling Smørgrav */ 1551a04a10f8SKris Kennaway void 1552ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1553af12a3e7SDag-Erling Smørgrav int *nallocp, int rekeying) 1554a04a10f8SKris Kennaway { 1555ca3176e7SBrian Feldman int n; 1556ca3176e7SBrian Feldman u_int sz; 1557ca3176e7SBrian Feldman 1558ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 1559ca3176e7SBrian Feldman 1560ca3176e7SBrian Feldman sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 1561af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 1562af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 1563af12a3e7SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, sz); 1564af12a3e7SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, sz); 1565af12a3e7SDag-Erling Smørgrav *nallocp = sz; 1566ca3176e7SBrian Feldman } 1567af12a3e7SDag-Erling Smørgrav *maxfdp = n; 1568ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 1569ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 1570ca3176e7SBrian Feldman 1571ca3176e7SBrian Feldman if (!rekeying) 1572ca3176e7SBrian Feldman channel_handler(channel_pre, *readsetp, *writesetp); 1573a04a10f8SKris Kennaway } 1574a04a10f8SKris Kennaway 1575af12a3e7SDag-Erling Smørgrav /* 1576af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 1577af12a3e7SDag-Erling Smørgrav * events pending. 1578af12a3e7SDag-Erling Smørgrav */ 1579a04a10f8SKris Kennaway void 1580a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 1581a04a10f8SKris Kennaway { 1582a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 1583511b41d2SMark Murray } 1584511b41d2SMark Murray 1585af12a3e7SDag-Erling Smørgrav 1586ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 1587511b41d2SMark Murray 1588511b41d2SMark Murray void 1589af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 1590511b41d2SMark Murray { 1591a04a10f8SKris Kennaway Channel *c; 1592a82e551fSDag-Erling Smørgrav int i; 1593a82e551fSDag-Erling Smørgrav u_int len; 1594511b41d2SMark Murray 1595511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1596af12a3e7SDag-Erling Smørgrav c = channels[i]; 1597af12a3e7SDag-Erling Smørgrav if (c == NULL) 1598af12a3e7SDag-Erling Smørgrav continue; 1599511b41d2SMark Murray 1600af12a3e7SDag-Erling Smørgrav /* 1601af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 1602af12a3e7SDag-Erling Smørgrav * incoming data. 1603af12a3e7SDag-Erling Smørgrav */ 1604511b41d2SMark Murray if (compat13) { 1605a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1606a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 1607511b41d2SMark Murray continue; 1608511b41d2SMark Murray } else { 1609a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 1610511b41d2SMark Murray continue; 1611a04a10f8SKris Kennaway } 1612a04a10f8SKris Kennaway if (compat20 && 1613a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 1614ca3176e7SBrian Feldman /* XXX is this true? */ 1615af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 1616511b41d2SMark Murray continue; 1617511b41d2SMark Murray } 1618511b41d2SMark Murray 1619511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 1620ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 1621ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 1622ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 1623af12a3e7SDag-Erling Smørgrav /* 1624af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 1625af12a3e7SDag-Erling Smørgrav * connection. 1626af12a3e7SDag-Erling Smørgrav */ 1627a04a10f8SKris Kennaway if (compat20) { 1628a04a10f8SKris Kennaway if (len > c->remote_window) 1629a04a10f8SKris Kennaway len = c->remote_window; 1630a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1631a04a10f8SKris Kennaway len = c->remote_maxpacket; 1632a04a10f8SKris Kennaway } else { 1633511b41d2SMark Murray if (packet_is_interactive()) { 1634511b41d2SMark Murray if (len > 1024) 1635511b41d2SMark Murray len = 512; 1636511b41d2SMark Murray } else { 1637511b41d2SMark Murray /* Keep the packets at reasonable size. */ 1638511b41d2SMark Murray if (len > packet_get_maxsize()/2) 1639511b41d2SMark Murray len = packet_get_maxsize()/2; 1640511b41d2SMark Murray } 1641a04a10f8SKris Kennaway } 1642a04a10f8SKris Kennaway if (len > 0) { 1643a04a10f8SKris Kennaway packet_start(compat20 ? 1644a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 1645a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1646a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 1647511b41d2SMark Murray packet_send(); 1648a04a10f8SKris Kennaway buffer_consume(&c->input, len); 1649a04a10f8SKris Kennaway c->remote_window -= len; 1650a04a10f8SKris Kennaway } 1651a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1652511b41d2SMark Murray if (compat13) 1653511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1654511b41d2SMark Murray /* 1655511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 165680628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 165780628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 1658511b41d2SMark Murray */ 165980628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 166080628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 166180628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 166280628bacSDag-Erling Smørgrav else 1663a04a10f8SKris Kennaway chan_ibuf_empty(c); 1664a04a10f8SKris Kennaway } 1665a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1666a04a10f8SKris Kennaway if (compat20 && 166780628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 1668a04a10f8SKris Kennaway c->remote_window > 0 && 1669a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1670a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1671a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 1672ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 1673ca3176e7SBrian Feldman c->extended_usage); 1674a04a10f8SKris Kennaway if (len > c->remote_window) 1675a04a10f8SKris Kennaway len = c->remote_window; 1676a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1677a04a10f8SKris Kennaway len = c->remote_maxpacket; 1678a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1679a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1680a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1681a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1682a04a10f8SKris Kennaway packet_send(); 1683a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1684a04a10f8SKris Kennaway c->remote_window -= len; 1685ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 1686511b41d2SMark Murray } 1687511b41d2SMark Murray } 1688511b41d2SMark Murray } 1689511b41d2SMark Murray 1690af12a3e7SDag-Erling Smørgrav 1691af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 1692511b41d2SMark Murray 1693511b41d2SMark Murray void 1694af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 1695511b41d2SMark Murray { 1696511b41d2SMark Murray int id; 1697511b41d2SMark Murray char *data; 1698ca3176e7SBrian Feldman u_int data_len; 1699a04a10f8SKris Kennaway Channel *c; 1700511b41d2SMark Murray 1701511b41d2SMark Murray /* Get the channel number and verify it. */ 1702511b41d2SMark Murray id = packet_get_int(); 1703a04a10f8SKris Kennaway c = channel_lookup(id); 1704a04a10f8SKris Kennaway if (c == NULL) 1705511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1706511b41d2SMark Murray 1707511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1708a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1709a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1710511b41d2SMark Murray return; 1711511b41d2SMark Murray 1712511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1713a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1714511b41d2SMark Murray return; 1715511b41d2SMark Murray 1716511b41d2SMark Murray /* Get the data. */ 1717511b41d2SMark Murray data = packet_get_string(&data_len); 1718a04a10f8SKris Kennaway 1719a04a10f8SKris Kennaway if (compat20) { 1720a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1721a04a10f8SKris Kennaway log("channel %d: rcvd big packet %d, maxpack %d", 1722a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1723a04a10f8SKris Kennaway } 1724a04a10f8SKris Kennaway if (data_len > c->local_window) { 1725a04a10f8SKris Kennaway log("channel %d: rcvd too much data %d, win %d", 1726a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1727a04a10f8SKris Kennaway xfree(data); 1728a04a10f8SKris Kennaway return; 1729a04a10f8SKris Kennaway } 1730a04a10f8SKris Kennaway c->local_window -= data_len; 1731a04a10f8SKris Kennaway } 1732af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1733a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1734511b41d2SMark Murray xfree(data); 1735511b41d2SMark Murray } 1736af12a3e7SDag-Erling Smørgrav 1737a04a10f8SKris Kennaway void 1738af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 1739a04a10f8SKris Kennaway { 1740a04a10f8SKris Kennaway int id; 1741a04a10f8SKris Kennaway char *data; 1742a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 1743a04a10f8SKris Kennaway Channel *c; 1744a04a10f8SKris Kennaway 1745a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1746a04a10f8SKris Kennaway id = packet_get_int(); 1747a04a10f8SKris Kennaway c = channel_lookup(id); 1748a04a10f8SKris Kennaway 1749a04a10f8SKris Kennaway if (c == NULL) 1750a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1751a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1752a04a10f8SKris Kennaway log("channel %d: ext data for non open", id); 1753a04a10f8SKris Kennaway return; 1754a04a10f8SKris Kennaway } 175580628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 175680628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 175780628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 175880628bacSDag-Erling Smørgrav else 175980628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 176080628bacSDag-Erling Smørgrav "on channel %d.", id); 176180628bacSDag-Erling Smørgrav } 1762a04a10f8SKris Kennaway tcode = packet_get_int(); 1763a04a10f8SKris Kennaway if (c->efd == -1 || 1764a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1765a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1766a04a10f8SKris Kennaway log("channel %d: bad ext data", c->self); 1767a04a10f8SKris Kennaway return; 1768a04a10f8SKris Kennaway } 1769a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1770af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1771a04a10f8SKris Kennaway if (data_len > c->local_window) { 1772a04a10f8SKris Kennaway log("channel %d: rcvd too much extended_data %d, win %d", 1773a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1774a04a10f8SKris Kennaway xfree(data); 1775a04a10f8SKris Kennaway return; 1776a04a10f8SKris Kennaway } 17775b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1778a04a10f8SKris Kennaway c->local_window -= data_len; 1779a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1780a04a10f8SKris Kennaway xfree(data); 1781a04a10f8SKris Kennaway } 1782a04a10f8SKris Kennaway 1783a04a10f8SKris Kennaway void 1784af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 1785a04a10f8SKris Kennaway { 1786a04a10f8SKris Kennaway int id; 1787a04a10f8SKris Kennaway Channel *c; 1788a04a10f8SKris Kennaway 1789a04a10f8SKris Kennaway id = packet_get_int(); 1790af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1791a04a10f8SKris Kennaway c = channel_lookup(id); 1792a04a10f8SKris Kennaway if (c == NULL) 1793a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1794a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1795af12a3e7SDag-Erling Smørgrav 1796af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 1797af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 1798af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 1799af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 1800af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 1801af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1802af12a3e7SDag-Erling Smørgrav } 1803af12a3e7SDag-Erling Smørgrav 1804a04a10f8SKris Kennaway } 1805511b41d2SMark Murray 1806511b41d2SMark Murray void 1807af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 1808511b41d2SMark Murray { 1809a04a10f8SKris Kennaway int id; 1810a04a10f8SKris Kennaway Channel *c; 1811511b41d2SMark Murray 1812a04a10f8SKris Kennaway id = packet_get_int(); 1813af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1814a04a10f8SKris Kennaway c = channel_lookup(id); 1815a04a10f8SKris Kennaway if (c == NULL) 1816a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1817511b41d2SMark Murray 1818511b41d2SMark Murray /* 1819511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1820511b41d2SMark Murray * data is coming for it. 1821511b41d2SMark Murray */ 1822511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1823a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1824511b41d2SMark Murray packet_send(); 1825511b41d2SMark Murray 1826511b41d2SMark Murray /* 1827511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1828511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1829511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1830511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1831511b41d2SMark Murray * the confirmation arrives. 1832511b41d2SMark Murray */ 1833a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1834511b41d2SMark Murray /* 1835511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1836511b41d2SMark Murray * cause it to be freed later. 1837511b41d2SMark Murray */ 1838af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1839a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1840511b41d2SMark Murray } 1841511b41d2SMark Murray } 1842511b41d2SMark Murray 1843a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1844a04a10f8SKris Kennaway void 1845af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 1846a04a10f8SKris Kennaway { 1847a04a10f8SKris Kennaway int id = packet_get_int(); 1848a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1849af12a3e7SDag-Erling Smørgrav 1850af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1851a04a10f8SKris Kennaway if (c == NULL) 1852a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1853a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1854a04a10f8SKris Kennaway } 1855511b41d2SMark Murray 1856511b41d2SMark Murray void 1857af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 1858511b41d2SMark Murray { 1859a04a10f8SKris Kennaway int id = packet_get_int(); 1860a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1861a04a10f8SKris Kennaway 1862af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1863a04a10f8SKris Kennaway if (c == NULL) 1864a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1865a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1866a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1867a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1868a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1869af12a3e7SDag-Erling Smørgrav channel_free(c); 1870a04a10f8SKris Kennaway } 1871a04a10f8SKris Kennaway 1872a04a10f8SKris Kennaway void 1873af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 1874a04a10f8SKris Kennaway { 1875a04a10f8SKris Kennaway int id, remote_id; 1876a04a10f8SKris Kennaway Channel *c; 1877a04a10f8SKris Kennaway 1878a04a10f8SKris Kennaway id = packet_get_int(); 1879a04a10f8SKris Kennaway c = channel_lookup(id); 1880a04a10f8SKris Kennaway 1881a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1882a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1883a04a10f8SKris Kennaway "non-opening channel %d.", id); 1884a04a10f8SKris Kennaway remote_id = packet_get_int(); 1885a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1886a04a10f8SKris Kennaway c->remote_id = remote_id; 1887a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1888a04a10f8SKris Kennaway 1889a04a10f8SKris Kennaway if (compat20) { 1890a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 1891a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 1892af12a3e7SDag-Erling Smørgrav if (c->confirm) { 18935b9b2fafSBrian Feldman debug2("callback start"); 1894af12a3e7SDag-Erling Smørgrav c->confirm(c->self, NULL); 18955b9b2fafSBrian Feldman debug2("callback done"); 1896a04a10f8SKris Kennaway } 1897a82e551fSDag-Erling Smørgrav debug("channel %d: open confirm rwindow %u rmax %u", c->self, 1898a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 1899a04a10f8SKris Kennaway } 1900af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1901af12a3e7SDag-Erling Smørgrav } 1902af12a3e7SDag-Erling Smørgrav 1903af12a3e7SDag-Erling Smørgrav static char * 1904af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 1905af12a3e7SDag-Erling Smørgrav { 1906af12a3e7SDag-Erling Smørgrav switch (reason) { 1907af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 1908af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 1909af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 1910af12a3e7SDag-Erling Smørgrav return "connect failed"; 1911af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 1912af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 1913af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 1914af12a3e7SDag-Erling Smørgrav return "resource shortage"; 1915af12a3e7SDag-Erling Smørgrav } 1916af12a3e7SDag-Erling Smørgrav return "unknown reason"; 1917a04a10f8SKris Kennaway } 1918a04a10f8SKris Kennaway 1919a04a10f8SKris Kennaway void 1920af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 1921a04a10f8SKris Kennaway { 1922ca3176e7SBrian Feldman int id, reason; 1923ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 1924a04a10f8SKris Kennaway Channel *c; 1925a04a10f8SKris Kennaway 1926a04a10f8SKris Kennaway id = packet_get_int(); 1927a04a10f8SKris Kennaway c = channel_lookup(id); 1928a04a10f8SKris Kennaway 1929a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1930a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 1931a04a10f8SKris Kennaway "non-opening channel %d.", id); 1932a04a10f8SKris Kennaway if (compat20) { 1933ca3176e7SBrian Feldman reason = packet_get_int(); 1934af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1935ca3176e7SBrian Feldman msg = packet_get_string(NULL); 1936ca3176e7SBrian Feldman lang = packet_get_string(NULL); 1937ca3176e7SBrian Feldman } 1938af12a3e7SDag-Erling Smørgrav log("channel %d: open failed: %s%s%s", id, 1939af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 1940ca3176e7SBrian Feldman if (msg != NULL) 1941a04a10f8SKris Kennaway xfree(msg); 1942ca3176e7SBrian Feldman if (lang != NULL) 1943a04a10f8SKris Kennaway xfree(lang); 1944a04a10f8SKris Kennaway } 1945af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1946a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 1947af12a3e7SDag-Erling Smørgrav channel_free(c); 1948a04a10f8SKris Kennaway } 1949a04a10f8SKris Kennaway 1950a04a10f8SKris Kennaway void 1951af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 1952a04a10f8SKris Kennaway { 1953a04a10f8SKris Kennaway Channel *c; 1954a82e551fSDag-Erling Smørgrav int id; 1955a82e551fSDag-Erling Smørgrav u_int adjust; 1956a04a10f8SKris Kennaway 1957a04a10f8SKris Kennaway if (!compat20) 1958a04a10f8SKris Kennaway return; 1959511b41d2SMark Murray 1960511b41d2SMark Murray /* Get the channel number and verify it. */ 1961a04a10f8SKris Kennaway id = packet_get_int(); 1962a04a10f8SKris Kennaway c = channel_lookup(id); 1963511b41d2SMark Murray 1964a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 1965a04a10f8SKris Kennaway log("Received window adjust for " 1966a04a10f8SKris Kennaway "non-open channel %d.", id); 1967511b41d2SMark Murray return; 1968511b41d2SMark Murray } 1969a04a10f8SKris Kennaway adjust = packet_get_int(); 1970af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1971a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 1972a04a10f8SKris Kennaway c->remote_window += adjust; 1973511b41d2SMark Murray } 1974511b41d2SMark Murray 1975af12a3e7SDag-Erling Smørgrav void 1976af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 1977af12a3e7SDag-Erling Smørgrav { 1978af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 1979af12a3e7SDag-Erling Smørgrav u_short host_port; 1980af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 1981af12a3e7SDag-Erling Smørgrav int remote_id, sock = -1; 1982af12a3e7SDag-Erling Smørgrav 1983af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 1984af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 1985af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 1986af12a3e7SDag-Erling Smørgrav 1987af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 1988af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 1989af12a3e7SDag-Erling Smørgrav } else { 1990af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 1991af12a3e7SDag-Erling Smørgrav } 1992af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1993af12a3e7SDag-Erling Smørgrav sock = channel_connect_to(host, host_port); 1994af12a3e7SDag-Erling Smørgrav if (sock != -1) { 1995af12a3e7SDag-Erling Smørgrav c = channel_new("connected socket", 1996af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 1997af12a3e7SDag-Erling Smørgrav originator_string, 1); 1998af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 1999af12a3e7SDag-Erling Smørgrav } 2000af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2001af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2002af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2003af12a3e7SDag-Erling Smørgrav packet_send(); 2004af12a3e7SDag-Erling Smørgrav } 2005af12a3e7SDag-Erling Smørgrav xfree(host); 2006af12a3e7SDag-Erling Smørgrav } 2007af12a3e7SDag-Erling Smørgrav 2008af12a3e7SDag-Erling Smørgrav 2009af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 2010511b41d2SMark Murray 2011511b41d2SMark Murray void 2012af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 2013511b41d2SMark Murray { 2014af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 2015511b41d2SMark Murray } 2016511b41d2SMark Murray 2017af12a3e7SDag-Erling Smørgrav static int 2018af12a3e7SDag-Erling Smørgrav channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 2019af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2020511b41d2SMark Murray { 2021af12a3e7SDag-Erling Smørgrav Channel *c; 2022af12a3e7SDag-Erling Smørgrav int success, sock, on = 1; 2023511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2024ca3176e7SBrian Feldman const char *host; 2025af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2026511b41d2SMark Murray 2027ca3176e7SBrian Feldman success = 0; 2028af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2029af12a3e7SDag-Erling Smørgrav listen_addr : host_to_connect; 2030511b41d2SMark Murray 2031af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2032af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2033af12a3e7SDag-Erling Smørgrav return success; 2034ca3176e7SBrian Feldman } 2035af12a3e7SDag-Erling Smørgrav if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 2036ca3176e7SBrian Feldman error("Forward host name too long."); 2037ca3176e7SBrian Feldman return success; 2038ca3176e7SBrian Feldman } 2039ca3176e7SBrian Feldman 2040511b41d2SMark Murray /* 2041511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2042511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2043511b41d2SMark Murray */ 2044511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2045511b41d2SMark Murray hints.ai_family = IPv4or6; 2046511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 2047511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2048ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", listen_port); 2049511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 2050511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 2051511b41d2SMark Murray 2052511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2053511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2054511b41d2SMark Murray continue; 2055511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2056511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2057af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: getnameinfo failed"); 2058511b41d2SMark Murray continue; 2059511b41d2SMark Murray } 2060511b41d2SMark Murray /* Create a port to listen for the host. */ 2061511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2062511b41d2SMark Murray if (sock < 0) { 2063511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2064511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2065511b41d2SMark Murray continue; 2066511b41d2SMark Murray } 2067511b41d2SMark Murray /* 2068f388f5efSDag-Erling Smørgrav * Set socket options. 2069f388f5efSDag-Erling Smørgrav * Allow local port reuse in TIME_WAIT. 2070511b41d2SMark Murray */ 2071f388f5efSDag-Erling Smørgrav if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, 2072f388f5efSDag-Erling Smørgrav sizeof(on)) == -1) 2073f388f5efSDag-Erling Smørgrav error("setsockopt SO_REUSEADDR: %s", strerror(errno)); 2074f388f5efSDag-Erling Smørgrav 2075511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 2076511b41d2SMark Murray 2077511b41d2SMark Murray /* Bind the socket to the address. */ 2078511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2079511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2080989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2081989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2082989dd127SDag-Erling Smørgrav else 2083511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2084989dd127SDag-Erling Smørgrav 2085511b41d2SMark Murray close(sock); 2086511b41d2SMark Murray continue; 2087511b41d2SMark Murray } 2088511b41d2SMark Murray /* Start listening for connections on the socket. */ 2089511b41d2SMark Murray if (listen(sock, 5) < 0) { 2090511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2091511b41d2SMark Murray close(sock); 2092511b41d2SMark Murray continue; 2093511b41d2SMark Murray } 2094511b41d2SMark Murray /* Allocate a channel number for the socket. */ 2095af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2096a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 20975b9b2fafSBrian Feldman 0, xstrdup("port listener"), 1); 2098af12a3e7SDag-Erling Smørgrav strlcpy(c->path, host, sizeof(c->path)); 2099af12a3e7SDag-Erling Smørgrav c->host_port = port_to_connect; 2100af12a3e7SDag-Erling Smørgrav c->listening_port = listen_port; 2101511b41d2SMark Murray success = 1; 2102511b41d2SMark Murray } 2103511b41d2SMark Murray if (success == 0) 2104af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: cannot listen to port: %d", 2105ca3176e7SBrian Feldman listen_port); 2106511b41d2SMark Murray freeaddrinfo(aitop); 2107ca3176e7SBrian Feldman return success; 2108511b41d2SMark Murray } 2109511b41d2SMark Murray 2110af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 2111af12a3e7SDag-Erling Smørgrav int 2112af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(u_short listen_port, 2113af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2114af12a3e7SDag-Erling Smørgrav { 2115af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2116af12a3e7SDag-Erling Smørgrav NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); 2117af12a3e7SDag-Erling Smørgrav } 2118af12a3e7SDag-Erling Smørgrav 2119af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 2120af12a3e7SDag-Erling Smørgrav int 2121af12a3e7SDag-Erling Smørgrav channel_setup_remote_fwd_listener(const char *listen_address, 2122af12a3e7SDag-Erling Smørgrav u_short listen_port, int gateway_ports) 2123af12a3e7SDag-Erling Smørgrav { 2124af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2125af12a3e7SDag-Erling Smørgrav listen_address, listen_port, NULL, 0, gateway_ports); 2126af12a3e7SDag-Erling Smørgrav } 2127af12a3e7SDag-Erling Smørgrav 2128511b41d2SMark Murray /* 2129511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 2130511b41d2SMark Murray * the secure channel to host:port from local side. 2131511b41d2SMark Murray */ 2132511b41d2SMark Murray 2133511b41d2SMark Murray void 2134ca3176e7SBrian Feldman channel_request_remote_forwarding(u_short listen_port, 2135ca3176e7SBrian Feldman const char *host_to_connect, u_short port_to_connect) 2136511b41d2SMark Murray { 2137af12a3e7SDag-Erling Smørgrav int type, success = 0; 2138ca3176e7SBrian Feldman 2139511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 2140511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2141511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 2142511b41d2SMark Murray 2143511b41d2SMark Murray /* Send the forward request to the remote side. */ 2144a04a10f8SKris Kennaway if (compat20) { 2145a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 2146a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 2147a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 214880628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2149a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 2150a04a10f8SKris Kennaway packet_put_int(listen_port); 2151ca3176e7SBrian Feldman packet_send(); 2152ca3176e7SBrian Feldman packet_write_wait(); 2153ca3176e7SBrian Feldman /* Assume that server accepts the request */ 2154ca3176e7SBrian Feldman success = 1; 2155a04a10f8SKris Kennaway } else { 2156511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 2157a04a10f8SKris Kennaway packet_put_int(listen_port); 2158a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 2159a04a10f8SKris Kennaway packet_put_int(port_to_connect); 2160511b41d2SMark Murray packet_send(); 2161511b41d2SMark Murray packet_write_wait(); 2162ca3176e7SBrian Feldman 2163ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 2164af12a3e7SDag-Erling Smørgrav type = packet_read(); 2165ca3176e7SBrian Feldman switch (type) { 2166ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 2167ca3176e7SBrian Feldman success = 1; 2168ca3176e7SBrian Feldman break; 2169ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 2170ca3176e7SBrian Feldman log("Warning: Server denied remote port forwarding."); 2171ca3176e7SBrian Feldman break; 2172ca3176e7SBrian Feldman default: 2173ca3176e7SBrian Feldman /* Unknown packet */ 2174ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 2175ca3176e7SBrian Feldman "received packet type %d.", type); 2176ca3176e7SBrian Feldman } 2177ca3176e7SBrian Feldman } 2178ca3176e7SBrian Feldman if (success) { 2179ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 2180ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 2181ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].listen_port = listen_port; 2182ca3176e7SBrian Feldman num_permitted_opens++; 2183511b41d2SMark Murray } 2184a04a10f8SKris Kennaway } 2185511b41d2SMark Murray 2186511b41d2SMark Murray /* 2187511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 2188511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 2189511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 2190511b41d2SMark Murray */ 2191511b41d2SMark Murray 2192511b41d2SMark Murray void 2193a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 2194511b41d2SMark Murray { 2195511b41d2SMark Murray u_short port, host_port; 2196511b41d2SMark Murray char *hostname; 2197511b41d2SMark Murray 2198511b41d2SMark Murray /* Get arguments from the packet. */ 2199511b41d2SMark Murray port = packet_get_int(); 2200511b41d2SMark Murray hostname = packet_get_string(NULL); 2201511b41d2SMark Murray host_port = packet_get_int(); 2202511b41d2SMark Murray 2203989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 2204511b41d2SMark Murray /* 2205511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 2206511b41d2SMark Murray * privileged port. 2207511b41d2SMark Murray */ 2208511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 2209511b41d2SMark Murray packet_disconnect("Requested forwarding of port %d but user is not root.", 2210511b41d2SMark Murray port); 2211989dd127SDag-Erling Smørgrav #endif 2212ca3176e7SBrian Feldman /* Initiate forwarding */ 2213af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); 2214511b41d2SMark Murray 2215511b41d2SMark Murray /* Free the argument string. */ 2216511b41d2SMark Murray xfree(hostname); 2217511b41d2SMark Murray } 2218511b41d2SMark Murray 2219ca3176e7SBrian Feldman /* 2220ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 2221ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 2222ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 2223ca3176e7SBrian Feldman */ 2224ca3176e7SBrian Feldman void 2225af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 2226ca3176e7SBrian Feldman { 2227ca3176e7SBrian Feldman if (num_permitted_opens == 0) 2228ca3176e7SBrian Feldman all_opens_permitted = 1; 2229ca3176e7SBrian Feldman } 2230ca3176e7SBrian Feldman 2231ca3176e7SBrian Feldman void 2232ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 2233ca3176e7SBrian Feldman { 2234ca3176e7SBrian Feldman if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2235ca3176e7SBrian Feldman fatal("channel_request_remote_forwarding: too many forwards"); 2236ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 2237ca3176e7SBrian Feldman 2238ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 2239ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 2240ca3176e7SBrian Feldman num_permitted_opens++; 2241ca3176e7SBrian Feldman 2242ca3176e7SBrian Feldman all_opens_permitted = 0; 2243ca3176e7SBrian Feldman } 2244ca3176e7SBrian Feldman 2245ca3176e7SBrian Feldman void 2246ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 2247ca3176e7SBrian Feldman { 2248ca3176e7SBrian Feldman int i; 2249ca3176e7SBrian Feldman 2250ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2251ca3176e7SBrian Feldman xfree(permitted_opens[i].host_to_connect); 2252ca3176e7SBrian Feldman num_permitted_opens = 0; 2253ca3176e7SBrian Feldman 2254ca3176e7SBrian Feldman } 2255ca3176e7SBrian Feldman 2256ca3176e7SBrian Feldman 2257ca3176e7SBrian Feldman /* return socket to remote host, port */ 2258af12a3e7SDag-Erling Smørgrav static int 2259ca3176e7SBrian Feldman connect_to(const char *host, u_short port) 2260511b41d2SMark Murray { 2261511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2262511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2263511b41d2SMark Murray int gaierr; 2264a04a10f8SKris Kennaway int sock = -1; 2265511b41d2SMark Murray 2266511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2267511b41d2SMark Murray hints.ai_family = IPv4or6; 2268511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2269ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", port); 2270511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 2271ca3176e7SBrian Feldman error("connect_to %.100s: unknown host (%s)", host, 2272ca3176e7SBrian Feldman gai_strerror(gaierr)); 2273a04a10f8SKris Kennaway return -1; 2274511b41d2SMark Murray } 2275511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2276511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2277511b41d2SMark Murray continue; 2278511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2279511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2280ca3176e7SBrian Feldman error("connect_to: getnameinfo failed"); 2281511b41d2SMark Murray continue; 2282511b41d2SMark Murray } 2283511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2284511b41d2SMark Murray if (sock < 0) { 2285511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2286511b41d2SMark Murray continue; 2287511b41d2SMark Murray } 2288ca3176e7SBrian Feldman if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) 2289ca3176e7SBrian Feldman fatal("connect_to: F_SETFL: %s", strerror(errno)); 2290ca3176e7SBrian Feldman if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2291ca3176e7SBrian Feldman errno != EINPROGRESS) { 2292ca3176e7SBrian Feldman error("connect_to %.100s port %s: %.100s", ntop, strport, 2293511b41d2SMark Murray strerror(errno)); 2294511b41d2SMark Murray close(sock); 2295511b41d2SMark Murray continue; /* fail -- try next */ 2296511b41d2SMark Murray } 2297511b41d2SMark Murray break; /* success */ 2298511b41d2SMark Murray 2299511b41d2SMark Murray } 2300511b41d2SMark Murray freeaddrinfo(aitop); 2301511b41d2SMark Murray if (!ai) { 2302ca3176e7SBrian Feldman error("connect_to %.100s port %d: failed.", host, port); 2303a04a10f8SKris Kennaway return -1; 2304a04a10f8SKris Kennaway } 2305a04a10f8SKris Kennaway /* success */ 2306af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2307a04a10f8SKris Kennaway return sock; 2308a04a10f8SKris Kennaway } 2309ca3176e7SBrian Feldman 2310ca3176e7SBrian Feldman int 2311af12a3e7SDag-Erling Smørgrav channel_connect_by_listen_address(u_short listen_port) 2312ca3176e7SBrian Feldman { 2313ca3176e7SBrian Feldman int i; 2314ca3176e7SBrian Feldman 2315ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2316ca3176e7SBrian Feldman if (permitted_opens[i].listen_port == listen_port) 2317ca3176e7SBrian Feldman return connect_to( 2318ca3176e7SBrian Feldman permitted_opens[i].host_to_connect, 2319ca3176e7SBrian Feldman permitted_opens[i].port_to_connect); 2320ca3176e7SBrian Feldman error("WARNING: Server requests forwarding for unknown listen_port %d", 2321ca3176e7SBrian Feldman listen_port); 2322ca3176e7SBrian Feldman return -1; 2323ca3176e7SBrian Feldman } 2324ca3176e7SBrian Feldman 2325ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 2326ca3176e7SBrian Feldman int 2327ca3176e7SBrian Feldman channel_connect_to(const char *host, u_short port) 2328ca3176e7SBrian Feldman { 2329ca3176e7SBrian Feldman int i, permit; 2330ca3176e7SBrian Feldman 2331ca3176e7SBrian Feldman permit = all_opens_permitted; 2332ca3176e7SBrian Feldman if (!permit) { 2333ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2334ca3176e7SBrian Feldman if (permitted_opens[i].port_to_connect == port && 2335ca3176e7SBrian Feldman strcmp(permitted_opens[i].host_to_connect, host) == 0) 2336ca3176e7SBrian Feldman permit = 1; 2337ca3176e7SBrian Feldman 2338ca3176e7SBrian Feldman } 2339ca3176e7SBrian Feldman if (!permit) { 2340ca3176e7SBrian Feldman log("Received request to connect to host %.100s port %d, " 2341ca3176e7SBrian Feldman "but the request was denied.", host, port); 2342ca3176e7SBrian Feldman return -1; 2343ca3176e7SBrian Feldman } 2344ca3176e7SBrian Feldman return connect_to(host, port); 2345ca3176e7SBrian Feldman } 2346ca3176e7SBrian Feldman 2347af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 2348511b41d2SMark Murray 2349511b41d2SMark Murray /* 2350511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 2351a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 2352a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 2353511b41d2SMark Murray */ 2354af12a3e7SDag-Erling Smørgrav int 2355af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2356a82e551fSDag-Erling Smørgrav int single_connection, u_int *display_numberp) 2357511b41d2SMark Murray { 2358af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 2359511b41d2SMark Murray int display_number, sock; 2360511b41d2SMark Murray u_short port; 2361511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2362511b41d2SMark Murray char strport[NI_MAXSERV]; 2363511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 2364511b41d2SMark Murray 2365511b41d2SMark Murray for (display_number = x11_display_offset; 2366511b41d2SMark Murray display_number < MAX_DISPLAYS; 2367511b41d2SMark Murray display_number++) { 2368511b41d2SMark Murray port = 6000 + display_number; 2369511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2370511b41d2SMark Murray hints.ai_family = IPv4or6; 2371af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 2372511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2373511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2374511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2375511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 2376af12a3e7SDag-Erling Smørgrav return -1; 2377511b41d2SMark Murray } 2378511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2379511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2380511b41d2SMark Murray continue; 2381511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2382511b41d2SMark Murray if (sock < 0) { 2383989dd127SDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 2384511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2385af12a3e7SDag-Erling Smørgrav return -1; 2386989dd127SDag-Erling Smørgrav } else { 2387989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 2388989dd127SDag-Erling Smørgrav ai->ai_family); 2389989dd127SDag-Erling Smørgrav continue; 2390511b41d2SMark Murray } 2391989dd127SDag-Erling Smørgrav } 2392989dd127SDag-Erling Smørgrav #ifdef IPV6_V6ONLY 2393989dd127SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 2394989dd127SDag-Erling Smørgrav int on = 1; 2395989dd127SDag-Erling Smørgrav if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 2396989dd127SDag-Erling Smørgrav error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 2397989dd127SDag-Erling Smørgrav } 2398989dd127SDag-Erling Smørgrav #endif 2399511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2400511b41d2SMark Murray debug("bind port %d: %.100s", port, strerror(errno)); 2401511b41d2SMark Murray close(sock); 2402989dd127SDag-Erling Smørgrav 2403989dd127SDag-Erling Smørgrav if (ai->ai_next) 2404989dd127SDag-Erling Smørgrav continue; 2405989dd127SDag-Erling Smørgrav 2406511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2407511b41d2SMark Murray close(socks[n]); 2408511b41d2SMark Murray } 2409511b41d2SMark Murray num_socks = 0; 2410511b41d2SMark Murray break; 2411511b41d2SMark Murray } 2412511b41d2SMark Murray socks[num_socks++] = sock; 2413989dd127SDag-Erling Smørgrav #ifndef DONT_TRY_OTHER_AF 2414511b41d2SMark Murray if (num_socks == NUM_SOCKS) 2415511b41d2SMark Murray break; 2416989dd127SDag-Erling Smørgrav #else 2417989dd127SDag-Erling Smørgrav if (x11_use_localhost) { 2418989dd127SDag-Erling Smørgrav if (num_socks == NUM_SOCKS) 2419989dd127SDag-Erling Smørgrav break; 2420989dd127SDag-Erling Smørgrav } else { 2421989dd127SDag-Erling Smørgrav break; 2422989dd127SDag-Erling Smørgrav } 2423989dd127SDag-Erling Smørgrav #endif 2424511b41d2SMark Murray } 2425ca3176e7SBrian Feldman freeaddrinfo(aitop); 2426511b41d2SMark Murray if (num_socks > 0) 2427511b41d2SMark Murray break; 2428511b41d2SMark Murray } 2429511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 2430511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 2431af12a3e7SDag-Erling Smørgrav return -1; 2432511b41d2SMark Murray } 2433511b41d2SMark Murray /* Start listening for connections on the socket. */ 2434511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2435511b41d2SMark Murray sock = socks[n]; 2436511b41d2SMark Murray if (listen(sock, 5) < 0) { 2437511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2438511b41d2SMark Murray close(sock); 2439af12a3e7SDag-Erling Smørgrav return -1; 2440511b41d2SMark Murray } 2441511b41d2SMark Murray } 2442511b41d2SMark Murray 2443511b41d2SMark Murray /* Allocate a channel for each socket. */ 2444511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2445511b41d2SMark Murray sock = socks[n]; 2446af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 2447a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 2448a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 24495b9b2fafSBrian Feldman 0, xstrdup("X11 inet listener"), 1); 2450af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 2451511b41d2SMark Murray } 2452511b41d2SMark Murray 2453af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 2454a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 2455a82e551fSDag-Erling Smørgrav return (0); 2456511b41d2SMark Murray } 2457511b41d2SMark Murray 2458af12a3e7SDag-Erling Smørgrav static int 2459ca3176e7SBrian Feldman connect_local_xsocket(u_int dnr) 2460511b41d2SMark Murray { 2461511b41d2SMark Murray int sock; 2462511b41d2SMark Murray struct sockaddr_un addr; 2463511b41d2SMark Murray 2464511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2465511b41d2SMark Murray if (sock < 0) 2466511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2467511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 2468511b41d2SMark Murray addr.sun_family = AF_UNIX; 2469af12a3e7SDag-Erling Smørgrav snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 2470511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 2471511b41d2SMark Murray return sock; 2472511b41d2SMark Murray close(sock); 2473511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 2474511b41d2SMark Murray return -1; 2475511b41d2SMark Murray } 2476511b41d2SMark Murray 2477a04a10f8SKris Kennaway int 2478a04a10f8SKris Kennaway x11_connect_display(void) 2479511b41d2SMark Murray { 2480a04a10f8SKris Kennaway int display_number, sock = 0; 2481511b41d2SMark Murray const char *display; 2482a04a10f8SKris Kennaway char buf[1024], *cp; 2483511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2484511b41d2SMark Murray char strport[NI_MAXSERV]; 2485511b41d2SMark Murray int gaierr; 2486511b41d2SMark Murray 2487511b41d2SMark Murray /* Try to open a socket for the local X server. */ 2488511b41d2SMark Murray display = getenv("DISPLAY"); 2489511b41d2SMark Murray if (!display) { 2490511b41d2SMark Murray error("DISPLAY not set."); 2491a04a10f8SKris Kennaway return -1; 2492511b41d2SMark Murray } 2493511b41d2SMark Murray /* 2494511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 2495511b41d2SMark Murray * connection to the real X server. 2496511b41d2SMark Murray */ 2497511b41d2SMark Murray 2498511b41d2SMark Murray /* 2499511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 2500511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 2501511b41d2SMark Murray */ 2502511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 2503511b41d2SMark Murray display[0] == ':') { 2504511b41d2SMark Murray /* Connect to the unix domain socket. */ 2505511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 2506511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2507511b41d2SMark Murray display); 2508a04a10f8SKris Kennaway return -1; 2509511b41d2SMark Murray } 2510511b41d2SMark Murray /* Create a socket. */ 2511511b41d2SMark Murray sock = connect_local_xsocket(display_number); 2512511b41d2SMark Murray if (sock < 0) 2513a04a10f8SKris Kennaway return -1; 2514511b41d2SMark Murray 2515511b41d2SMark Murray /* OK, we now have a connection to the display. */ 2516a04a10f8SKris Kennaway return sock; 2517511b41d2SMark Murray } 2518511b41d2SMark Murray /* 2519511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 2520511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 2521511b41d2SMark Murray */ 2522af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 2523511b41d2SMark Murray cp = strchr(buf, ':'); 2524511b41d2SMark Murray if (!cp) { 2525511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 2526a04a10f8SKris Kennaway return -1; 2527511b41d2SMark Murray } 2528511b41d2SMark Murray *cp = 0; 2529511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 2530511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 2531511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2532511b41d2SMark Murray display); 2533a04a10f8SKris Kennaway return -1; 2534511b41d2SMark Murray } 2535511b41d2SMark Murray 2536511b41d2SMark Murray /* Look up the host address */ 2537511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2538511b41d2SMark Murray hints.ai_family = IPv4or6; 2539511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2540511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 2541511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 2542511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 2543a04a10f8SKris Kennaway return -1; 2544511b41d2SMark Murray } 2545511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2546511b41d2SMark Murray /* Create a socket. */ 2547511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 2548511b41d2SMark Murray if (sock < 0) { 2549511b41d2SMark Murray debug("socket: %.100s", strerror(errno)); 2550511b41d2SMark Murray continue; 2551511b41d2SMark Murray } 2552511b41d2SMark Murray /* Connect it to the display. */ 2553511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2554a04a10f8SKris Kennaway debug("connect %.100s port %d: %.100s", buf, 2555a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 2556511b41d2SMark Murray close(sock); 2557511b41d2SMark Murray continue; 2558511b41d2SMark Murray } 2559511b41d2SMark Murray /* Success */ 2560511b41d2SMark Murray break; 2561a04a10f8SKris Kennaway } 2562511b41d2SMark Murray freeaddrinfo(aitop); 2563511b41d2SMark Murray if (!ai) { 2564511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 2565511b41d2SMark Murray strerror(errno)); 2566a04a10f8SKris Kennaway return -1; 2567511b41d2SMark Murray } 2568af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2569a04a10f8SKris Kennaway return sock; 2570a04a10f8SKris Kennaway } 2571511b41d2SMark Murray 2572a04a10f8SKris Kennaway /* 2573a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 2574a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 2575a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 2576a04a10f8SKris Kennaway */ 2577a04a10f8SKris Kennaway 2578a04a10f8SKris Kennaway void 2579af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 2580a04a10f8SKris Kennaway { 2581af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2582af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 2583a04a10f8SKris Kennaway char *remote_host; 2584a04a10f8SKris Kennaway 2585a04a10f8SKris Kennaway debug("Received X11 open request."); 2586af12a3e7SDag-Erling Smørgrav 2587af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2588af12a3e7SDag-Erling Smørgrav 2589af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2590af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 2591af12a3e7SDag-Erling Smørgrav } else { 2592af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 2593af12a3e7SDag-Erling Smørgrav } 2594af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2595a04a10f8SKris Kennaway 2596a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 2597a04a10f8SKris Kennaway sock = x11_connect_display(); 2598af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2599af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 2600af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 2601af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 2602af12a3e7SDag-Erling Smørgrav remote_host, 1); 2603af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2604af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2605af12a3e7SDag-Erling Smørgrav } 2606af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2607a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 2608a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2609af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2610a04a10f8SKris Kennaway } else { 2611511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2612511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2613af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2614af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 2615a04a10f8SKris Kennaway } 2616af12a3e7SDag-Erling Smørgrav packet_send(); 2617511b41d2SMark Murray } 2618511b41d2SMark Murray 26195b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 26205b9b2fafSBrian Feldman void 2621af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 26225b9b2fafSBrian Feldman { 26235b9b2fafSBrian Feldman int rchan = packet_get_int(); 2624f388f5efSDag-Erling Smørgrav 26255b9b2fafSBrian Feldman switch (type) { 26265b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 26275b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 26285b9b2fafSBrian Feldman break; 26295b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 26305b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 26315b9b2fafSBrian Feldman break; 26325b9b2fafSBrian Feldman default: 2633af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 26345b9b2fafSBrian Feldman break; 26355b9b2fafSBrian Feldman } 26365b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 26375b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 26385b9b2fafSBrian Feldman packet_put_int(rchan); 26395b9b2fafSBrian Feldman packet_send(); 26405b9b2fafSBrian Feldman } 26415b9b2fafSBrian Feldman 2642511b41d2SMark Murray /* 2643511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2644511b41d2SMark Murray * data, and enables authentication spoofing. 2645af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 2646511b41d2SMark Murray */ 2647511b41d2SMark Murray void 2648a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2649a04a10f8SKris Kennaway const char *proto, const char *data) 2650511b41d2SMark Murray { 2651ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 2652ca3176e7SBrian Feldman u_int i, value, len; 2653511b41d2SMark Murray char *new_data; 2654511b41d2SMark Murray int screen_number; 2655511b41d2SMark Murray const char *cp; 2656511b41d2SMark Murray u_int32_t rand = 0; 2657511b41d2SMark Murray 2658511b41d2SMark Murray cp = getenv("DISPLAY"); 2659511b41d2SMark Murray if (cp) 2660511b41d2SMark Murray cp = strchr(cp, ':'); 2661511b41d2SMark Murray if (cp) 2662511b41d2SMark Murray cp = strchr(cp, '.'); 2663511b41d2SMark Murray if (cp) 2664511b41d2SMark Murray screen_number = atoi(cp + 1); 2665511b41d2SMark Murray else 2666511b41d2SMark Murray screen_number = 0; 2667511b41d2SMark Murray 2668511b41d2SMark Murray /* Save protocol name. */ 2669511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2670511b41d2SMark Murray 2671511b41d2SMark Murray /* 2672511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2673511b41d2SMark Murray * same length. 2674511b41d2SMark Murray */ 2675511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2676511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2677511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2678511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2679511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2680511b41d2SMark Murray if (i % 4 == 0) 2681511b41d2SMark Murray rand = arc4random(); 2682511b41d2SMark Murray x11_saved_data[i] = value; 2683511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2684511b41d2SMark Murray rand >>= 8; 2685511b41d2SMark Murray } 2686511b41d2SMark Murray x11_saved_data_len = data_len; 2687511b41d2SMark Murray x11_fake_data_len = data_len; 2688511b41d2SMark Murray 2689511b41d2SMark Murray /* Convert the fake data into hex. */ 2690ca3176e7SBrian Feldman len = 2 * data_len + 1; 2691ca3176e7SBrian Feldman new_data = xmalloc(len); 2692511b41d2SMark Murray for (i = 0; i < data_len; i++) 2693ca3176e7SBrian Feldman snprintf(new_data + 2 * i, len - 2 * i, 2694ca3176e7SBrian Feldman "%02x", (u_char) x11_fake_data[i]); 2695511b41d2SMark Murray 2696511b41d2SMark Murray /* Send the request packet. */ 2697a04a10f8SKris Kennaway if (compat20) { 2698a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2699a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2700a04a10f8SKris Kennaway } else { 2701511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2702a04a10f8SKris Kennaway } 2703a04a10f8SKris Kennaway packet_put_cstring(proto); 2704a04a10f8SKris Kennaway packet_put_cstring(new_data); 2705511b41d2SMark Murray packet_put_int(screen_number); 2706511b41d2SMark Murray packet_send(); 2707511b41d2SMark Murray packet_write_wait(); 2708511b41d2SMark Murray xfree(new_data); 2709511b41d2SMark Murray } 2710511b41d2SMark Murray 2711af12a3e7SDag-Erling Smørgrav 2712af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 2713af12a3e7SDag-Erling Smørgrav 2714511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2715511b41d2SMark Murray 2716511b41d2SMark Murray void 2717af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 2718511b41d2SMark Murray { 2719511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2720511b41d2SMark Murray packet_send(); 2721511b41d2SMark Murray packet_write_wait(); 2722511b41d2SMark Murray } 2723511b41d2SMark Murray 2724511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2725511b41d2SMark Murray 2726511b41d2SMark Murray void 2727af12a3e7SDag-Erling Smørgrav auth_input_open_request(int type, u_int32_t seq, void *ctxt) 2728511b41d2SMark Murray { 2729af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2730af12a3e7SDag-Erling Smørgrav int remote_id, sock; 2731af12a3e7SDag-Erling Smørgrav char *name; 2732a04a10f8SKris Kennaway 2733511b41d2SMark Murray /* Read the remote channel number from the message. */ 2734af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2735af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2736511b41d2SMark Murray 2737511b41d2SMark Murray /* 2738511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2739511b41d2SMark Murray * get forwarded). 2740511b41d2SMark Murray */ 2741511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2742511b41d2SMark Murray 2743511b41d2SMark Murray /* 2744511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2745511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2746511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2747511b41d2SMark Murray * agent. 2748511b41d2SMark Murray */ 2749af12a3e7SDag-Erling Smørgrav if (sock >= 0) { 2750af12a3e7SDag-Erling Smørgrav name = xstrdup("authentication agent connection"); 2751af12a3e7SDag-Erling Smørgrav c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 2752af12a3e7SDag-Erling Smørgrav -1, 0, 0, 0, name, 1); 2753af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2754af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2755af12a3e7SDag-Erling Smørgrav } 2756af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2757511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2758af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2759af12a3e7SDag-Erling Smørgrav } else { 2760511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2761af12a3e7SDag-Erling Smørgrav debug("Forwarding authentication connection."); 2762511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2763af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2764a04a10f8SKris Kennaway packet_put_int(c->self); 2765a04a10f8SKris Kennaway } 2766a04a10f8SKris Kennaway packet_send(); 2767a04a10f8SKris Kennaway } 2768