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" 42221552e4SDag-Erling Smørgrav RCSID("$OpenBSD: channels.c,v 1.195 2003/09/16 21:02:40 markus Exp $"); 43a04a10f8SKris Kennaway 44ca3176e7SBrian Feldman #include "ssh.h" 45ca3176e7SBrian Feldman #include "ssh1.h" 46ca3176e7SBrian Feldman #include "ssh2.h" 47ca3176e7SBrian Feldman #include "packet.h" 48ca3176e7SBrian Feldman #include "xmalloc.h" 49ca3176e7SBrian Feldman #include "log.h" 50ca3176e7SBrian Feldman #include "misc.h" 51ca3176e7SBrian Feldman #include "channels.h" 52ca3176e7SBrian Feldman #include "compat.h" 53ca3176e7SBrian Feldman #include "canohost.h" 54b66f2d16SKris Kennaway #include "key.h" 55b66f2d16SKris Kennaway #include "authfd.h" 56af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 57221552e4SDag-Erling Smørgrav #include "bufaux.h" 58511b41d2SMark Murray 59af12a3e7SDag-Erling Smørgrav /* -- channel core */ 60511b41d2SMark Murray 61511b41d2SMark Murray /* 62511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 63511b41d2SMark Murray * dynamically extended as needed. 64511b41d2SMark Murray */ 65af12a3e7SDag-Erling Smørgrav static Channel **channels = NULL; 66511b41d2SMark Murray 67511b41d2SMark Murray /* 68511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 69af12a3e7SDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 70511b41d2SMark Murray */ 71511b41d2SMark Murray static int channels_alloc = 0; 72511b41d2SMark Murray 73511b41d2SMark Murray /* 74511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 75af12a3e7SDag-Erling Smørgrav * updated in channel_new. 76511b41d2SMark Murray */ 77ca3176e7SBrian Feldman static int channel_max_fd = 0; 78511b41d2SMark Murray 79511b41d2SMark Murray 80af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 81511b41d2SMark Murray 82511b41d2SMark Murray /* 83511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 84511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 85511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 86511b41d2SMark Murray * network (which might be behind a firewall). 87511b41d2SMark Murray */ 88511b41d2SMark Murray typedef struct { 89a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 90a04a10f8SKris Kennaway u_short port_to_connect; /* Connect to 'port'. */ 91a04a10f8SKris Kennaway u_short listen_port; /* Remote side should listen port number. */ 92511b41d2SMark Murray } ForwardPermission; 93511b41d2SMark Murray 94511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */ 95511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 96af12a3e7SDag-Erling Smørgrav 97511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */ 98511b41d2SMark Murray static int num_permitted_opens = 0; 99511b41d2SMark Murray /* 100511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 101511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 102511b41d2SMark Murray * anything after logging in anyway. 103511b41d2SMark Murray */ 104511b41d2SMark Murray static int all_opens_permitted = 0; 105511b41d2SMark Murray 106af12a3e7SDag-Erling Smørgrav 107af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 108af12a3e7SDag-Erling Smørgrav 109af12a3e7SDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 110af12a3e7SDag-Erling Smørgrav #define MAX_DISPLAYS 1000 111af12a3e7SDag-Erling Smørgrav 112af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 113af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 114af12a3e7SDag-Erling Smørgrav 115af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 116af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 117af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 118af12a3e7SDag-Erling Smørgrav 119af12a3e7SDag-Erling Smørgrav /* 120af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 121af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 122af12a3e7SDag-Erling Smørgrav */ 123af12a3e7SDag-Erling Smørgrav static char *x11_fake_data = NULL; 124af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 125af12a3e7SDag-Erling Smørgrav 126af12a3e7SDag-Erling Smørgrav 127af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 128af12a3e7SDag-Erling Smørgrav 129af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 130af12a3e7SDag-Erling Smørgrav 131ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 132989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 133ca3176e7SBrian Feldman 134af12a3e7SDag-Erling Smørgrav /* helper */ 135af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 136ca3176e7SBrian Feldman 137af12a3e7SDag-Erling Smørgrav /* -- channel core */ 138a04a10f8SKris Kennaway 139a04a10f8SKris Kennaway Channel * 140a04a10f8SKris Kennaway channel_lookup(int id) 141a04a10f8SKris Kennaway { 142a04a10f8SKris Kennaway Channel *c; 143af12a3e7SDag-Erling Smørgrav 144d96f3089SJacques Vidrine if (id < 0 || id >= channels_alloc) { 145221552e4SDag-Erling Smørgrav logit("channel_lookup: %d: bad id", id); 146a04a10f8SKris Kennaway return NULL; 147a04a10f8SKris Kennaway } 148af12a3e7SDag-Erling Smørgrav c = channels[id]; 149af12a3e7SDag-Erling Smørgrav if (c == NULL) { 150221552e4SDag-Erling Smørgrav logit("channel_lookup: %d: bad id: channel free", id); 151a04a10f8SKris Kennaway return NULL; 152a04a10f8SKris Kennaway } 153a04a10f8SKris Kennaway return c; 154a04a10f8SKris Kennaway } 155a04a10f8SKris Kennaway 156a04a10f8SKris Kennaway /* 157a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 158a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 159a04a10f8SKris Kennaway */ 160a04a10f8SKris Kennaway 161af12a3e7SDag-Erling Smørgrav static void 1625b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1635b9b2fafSBrian Feldman int extusage, int nonblock) 164a04a10f8SKris Kennaway { 165a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 166ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, rfd); 167ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, wfd); 168ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, efd); 169ca3176e7SBrian Feldman 170a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 171a04a10f8SKris Kennaway 172a04a10f8SKris Kennaway c->rfd = rfd; 173a04a10f8SKris Kennaway c->wfd = wfd; 174a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 175a04a10f8SKris Kennaway c->efd = efd; 176a04a10f8SKris Kennaway c->extended_usage = extusage; 1775b9b2fafSBrian Feldman 178e0fbb1d2SBrian Feldman /* XXX ugly hack: nonblock is only set by the server */ 179e0fbb1d2SBrian Feldman if (nonblock && isatty(c->rfd)) { 180221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 181e0fbb1d2SBrian Feldman c->isatty = 1; 182e0fbb1d2SBrian Feldman if (!isatty(c->wfd)) { 183ca3176e7SBrian Feldman error("channel %d: wfd %d is not a tty?", 184e0fbb1d2SBrian Feldman c->self, c->wfd); 185e0fbb1d2SBrian Feldman } 186e0fbb1d2SBrian Feldman } else { 187e0fbb1d2SBrian Feldman c->isatty = 0; 188e0fbb1d2SBrian Feldman } 189f388f5efSDag-Erling Smørgrav c->wfd_isatty = isatty(c->wfd); 190e0fbb1d2SBrian Feldman 1915b9b2fafSBrian Feldman /* enable nonblocking mode */ 1925b9b2fafSBrian Feldman if (nonblock) { 193a04a10f8SKris Kennaway if (rfd != -1) 194a04a10f8SKris Kennaway set_nonblock(rfd); 195a04a10f8SKris Kennaway if (wfd != -1) 196a04a10f8SKris Kennaway set_nonblock(wfd); 197a04a10f8SKris Kennaway if (efd != -1) 198a04a10f8SKris Kennaway set_nonblock(efd); 199a04a10f8SKris Kennaway } 2005b9b2fafSBrian Feldman } 201a04a10f8SKris Kennaway 202511b41d2SMark Murray /* 203511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 204511b41d2SMark Murray * remote_name to be freed. 205511b41d2SMark Murray */ 206511b41d2SMark Murray 207af12a3e7SDag-Erling Smørgrav Channel * 208a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 209a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 210511b41d2SMark Murray { 211511b41d2SMark Murray int i, found; 212511b41d2SMark Murray Channel *c; 213511b41d2SMark Murray 214511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 215511b41d2SMark Murray if (channels_alloc == 0) { 216511b41d2SMark Murray channels_alloc = 10; 217af12a3e7SDag-Erling Smørgrav channels = xmalloc(channels_alloc * sizeof(Channel *)); 218511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 219af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 220af12a3e7SDag-Erling Smørgrav fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); 221511b41d2SMark Murray } 222511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 223511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 224af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 225511b41d2SMark Murray /* Found a free slot. */ 226511b41d2SMark Murray found = i; 227511b41d2SMark Murray break; 228511b41d2SMark Murray } 229511b41d2SMark Murray if (found == -1) { 230511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 231511b41d2SMark Murray found = channels_alloc; 232a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 233a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 234a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 2353533e7e5SJosef Karthauser channels = xrealloc(channels, 2363533e7e5SJosef Karthauser (channels_alloc + 10) * sizeof(Channel *)); 2373533e7e5SJosef Karthauser channels_alloc += 10; 2385b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 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; 260221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(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 { 308221552e4SDag-Erling Smørgrav debug3("channel %d: close_fds 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++; 328221552e4SDag-Erling Smørgrav debug("channel %d: free: %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(); 332221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, 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()) { 417e73e9afaSDag-Erling Smørgrav debug2("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()) { 423221552e4SDag-Erling Smørgrav debug2("channel %d: big output buffer %u > %u", 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) { 579221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 580af12a3e7SDag-Erling Smørgrav return; 581af12a3e7SDag-Erling Smørgrav } 582e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", 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 592e73e9afaSDag-Erling Smørgrav channel_request_start(int id, char *service, int wantconfirm) 593af12a3e7SDag-Erling Smørgrav { 594e73e9afaSDag-Erling Smørgrav Channel *c = channel_lookup(id); 595f388f5efSDag-Erling Smørgrav 596af12a3e7SDag-Erling Smørgrav if (c == NULL) { 597221552e4SDag-Erling Smørgrav logit("channel_request_start: %d: unknown channel id", id); 598af12a3e7SDag-Erling Smørgrav return; 599af12a3e7SDag-Erling Smørgrav } 600221552e4SDag-Erling Smørgrav debug2("channel %d: request %s", 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) { 612221552e4SDag-Erling Smørgrav logit("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) { 623221552e4SDag-Erling Smørgrav logit("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) { 634221552e4SDag-Erling Smørgrav logit("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) { 645221552e4SDag-Erling Smørgrav logit("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; 743221552e4SDag-Erling Smørgrav debug2("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 { 784221552e4SDag-Erling Smørgrav debug2("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) { 797221552e4SDag-Erling Smørgrav debug2("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) { 804221552e4SDag-Erling Smørgrav debug2("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 */ 837221552e4SDag-Erling Smørgrav logit("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) { 860221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 861221552e4SDag-Erling Smørgrav debug2("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; 871221552e4SDag-Erling Smørgrav debug2("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 929221552e4SDag-Erling Smørgrav debug2("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 945221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 946221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 947221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 948221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 949221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 950221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 951221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 952221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 953221552e4SDag-Erling Smørgrav 954221552e4SDag-Erling Smørgrav static int 955221552e4SDag-Erling Smørgrav channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) 956221552e4SDag-Erling Smørgrav { 957221552e4SDag-Erling Smørgrav struct { 958221552e4SDag-Erling Smørgrav u_int8_t version; 959221552e4SDag-Erling Smørgrav u_int8_t command; 960221552e4SDag-Erling Smørgrav u_int8_t reserved; 961221552e4SDag-Erling Smørgrav u_int8_t atyp; 962221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 963221552e4SDag-Erling Smørgrav u_int16_t dest_port; 964221552e4SDag-Erling Smørgrav u_char *p, dest_addr[255+1]; 965221552e4SDag-Erling Smørgrav int i, have, found, nmethods, addrlen, af; 966221552e4SDag-Erling Smørgrav 967221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 968221552e4SDag-Erling Smørgrav p = buffer_ptr(&c->input); 969221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 970221552e4SDag-Erling Smørgrav return -1; 971221552e4SDag-Erling Smørgrav have = buffer_len(&c->input); 972221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 973221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 974221552e4SDag-Erling Smørgrav if (have < 2) 975221552e4SDag-Erling Smørgrav return 0; 976221552e4SDag-Erling Smørgrav nmethods = p[1]; 977221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 978221552e4SDag-Erling Smørgrav return 0; 979221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 980221552e4SDag-Erling Smørgrav for (found = 0, i = 2 ; i < nmethods + 2; i++) { 981221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH ) { 982221552e4SDag-Erling Smørgrav found = 1; 983221552e4SDag-Erling Smørgrav break; 984221552e4SDag-Erling Smørgrav } 985221552e4SDag-Erling Smørgrav } 986221552e4SDag-Erling Smørgrav if (!found) { 987221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 988221552e4SDag-Erling Smørgrav c->self); 989221552e4SDag-Erling Smørgrav return -1; 990221552e4SDag-Erling Smørgrav } 991221552e4SDag-Erling Smørgrav buffer_consume(&c->input, nmethods + 2); 992221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, 0x05); /* version */ 993221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 994221552e4SDag-Erling Smørgrav FD_SET(c->sock, writeset); 995221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 996221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 997221552e4SDag-Erling Smørgrav return 0; /* need more */ 998221552e4SDag-Erling Smørgrav } 999221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1000221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1001221552e4SDag-Erling Smørgrav return 0; /* need more */ 1002221552e4SDag-Erling Smørgrav memcpy((char *)&s5_req, p, sizeof(s5_req)); 1003221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1004221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1005221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1006221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1007221552e4SDag-Erling Smørgrav return -1; 1008221552e4SDag-Erling Smørgrav } 1009221552e4SDag-Erling Smørgrav switch(s5_req.atyp){ 1010221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1011221552e4SDag-Erling Smørgrav addrlen = 4; 1012221552e4SDag-Erling Smørgrav af = AF_INET; 1013221552e4SDag-Erling Smørgrav break; 1014221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1015221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1016221552e4SDag-Erling Smørgrav af = -1; 1017221552e4SDag-Erling Smørgrav break; 1018221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1019221552e4SDag-Erling Smørgrav addrlen = 16; 1020221552e4SDag-Erling Smørgrav af = AF_INET6; 1021221552e4SDag-Erling Smørgrav break; 1022221552e4SDag-Erling Smørgrav default: 1023221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1024221552e4SDag-Erling Smørgrav return -1; 1025221552e4SDag-Erling Smørgrav } 1026221552e4SDag-Erling Smørgrav if (have < 4 + addrlen + 2) 1027221552e4SDag-Erling Smørgrav return 0; 1028221552e4SDag-Erling Smørgrav buffer_consume(&c->input, sizeof(s5_req)); 1029221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1030221552e4SDag-Erling Smørgrav buffer_consume(&c->input, 1); /* host string length */ 1031221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_addr, addrlen); 1032221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_port, 2); 1033221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1034221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1035221552e4SDag-Erling Smørgrav strlcpy(c->path, dest_addr, sizeof(c->path)); 1036221552e4SDag-Erling Smørgrav else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) 1037221552e4SDag-Erling Smørgrav return -1; 1038221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1039221552e4SDag-Erling Smørgrav 1040221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1041221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1042221552e4SDag-Erling Smørgrav 1043221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1044221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1045221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1046221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1047221552e4SDag-Erling Smørgrav ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; 1048221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1049221552e4SDag-Erling Smørgrav 1050221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); 1051221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); 1052221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); 1053221552e4SDag-Erling Smørgrav return 1; 1054221552e4SDag-Erling Smørgrav } 1055221552e4SDag-Erling Smørgrav 1056ca3176e7SBrian Feldman /* dynamic port forwarding */ 1057af12a3e7SDag-Erling Smørgrav static void 1058ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 1059ca3176e7SBrian Feldman { 1060ca3176e7SBrian Feldman u_char *p; 1061ca3176e7SBrian Feldman int have, ret; 1062ca3176e7SBrian Feldman 1063ca3176e7SBrian Feldman have = buffer_len(&c->input); 1064af12a3e7SDag-Erling Smørgrav c->delayed = 0; 1065ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 1066ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 1067ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1068221552e4SDag-Erling Smørgrav if (have < 3) { 1069ca3176e7SBrian Feldman /* need more */ 1070ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1071ca3176e7SBrian Feldman return; 1072ca3176e7SBrian Feldman } 1073ca3176e7SBrian Feldman /* try to guess the protocol */ 1074ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1075ca3176e7SBrian Feldman switch (p[0]) { 1076ca3176e7SBrian Feldman case 0x04: 1077ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 1078ca3176e7SBrian Feldman break; 1079221552e4SDag-Erling Smørgrav case 0x05: 1080221552e4SDag-Erling Smørgrav ret = channel_decode_socks5(c, readset, writeset); 1081221552e4SDag-Erling Smørgrav break; 1082ca3176e7SBrian Feldman default: 1083ca3176e7SBrian Feldman ret = -1; 1084ca3176e7SBrian Feldman break; 1085ca3176e7SBrian Feldman } 1086ca3176e7SBrian Feldman if (ret < 0) { 1087af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1088ca3176e7SBrian Feldman } else if (ret == 0) { 1089ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1090ca3176e7SBrian Feldman /* need more */ 1091ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1092ca3176e7SBrian Feldman } else { 1093ca3176e7SBrian Feldman /* switch to the next state */ 1094ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 1095ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 1096ca3176e7SBrian Feldman } 1097ca3176e7SBrian Feldman } 1098ca3176e7SBrian Feldman 1099a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1100af12a3e7SDag-Erling Smørgrav static void 1101a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 1102511b41d2SMark Murray { 1103af12a3e7SDag-Erling Smørgrav Channel *nc; 1104511b41d2SMark Murray struct sockaddr addr; 1105af12a3e7SDag-Erling Smørgrav int newsock; 1106511b41d2SMark Murray socklen_t addrlen; 1107ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1108a04a10f8SKris Kennaway int remote_port; 1109511b41d2SMark Murray 1110a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1111511b41d2SMark Murray debug("X11 connection requested."); 1112511b41d2SMark Murray addrlen = sizeof(addr); 1113a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1114af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1115221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 1116af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1117af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1118af12a3e7SDag-Erling Smørgrav } 1119511b41d2SMark Murray if (newsock < 0) { 1120511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1121a04a10f8SKris Kennaway return; 1122511b41d2SMark Murray } 1123af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1124ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1125a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1126511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1127ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1128a04a10f8SKris Kennaway 1129af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1130a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1131221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 1132a04a10f8SKris Kennaway if (compat20) { 1133a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1134a04a10f8SKris Kennaway packet_put_cstring("x11"); 1135af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1136af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1137af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1138ca3176e7SBrian Feldman /* originator ipaddr and port */ 1139ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1140a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1141221552e4SDag-Erling Smørgrav debug2("ssh2 x11 bug compat mode"); 1142a04a10f8SKris Kennaway } else { 1143a04a10f8SKris Kennaway packet_put_int(remote_port); 1144a04a10f8SKris Kennaway } 1145a04a10f8SKris Kennaway packet_send(); 1146a04a10f8SKris Kennaway } else { 1147511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1148af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1149af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1150af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1151af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1152511b41d2SMark Murray packet_send(); 1153511b41d2SMark Murray } 1154ca3176e7SBrian Feldman xfree(remote_ipaddr); 1155a04a10f8SKris Kennaway } 1156a04a10f8SKris Kennaway } 1157511b41d2SMark Murray 1158af12a3e7SDag-Erling Smørgrav static void 1159ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1160ca3176e7SBrian Feldman { 1161ca3176e7SBrian Feldman int direct; 1162ca3176e7SBrian Feldman char buf[1024]; 1163ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1164ca3176e7SBrian Feldman u_short remote_port = get_peer_port(c->sock); 1165ca3176e7SBrian Feldman 1166ca3176e7SBrian Feldman direct = (strcmp(rtype, "direct-tcpip") == 0); 1167ca3176e7SBrian Feldman 1168ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1169ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1170ca3176e7SBrian Feldman "connect from %.200s port %d", 1171ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1172ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1173ca3176e7SBrian Feldman 1174ca3176e7SBrian Feldman xfree(c->remote_name); 1175ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1176ca3176e7SBrian Feldman 1177ca3176e7SBrian Feldman if (compat20) { 1178ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1179ca3176e7SBrian Feldman packet_put_cstring(rtype); 1180ca3176e7SBrian Feldman packet_put_int(c->self); 1181ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1182ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1183ca3176e7SBrian Feldman if (direct) { 1184ca3176e7SBrian Feldman /* target host, port */ 1185ca3176e7SBrian Feldman packet_put_cstring(c->path); 1186ca3176e7SBrian Feldman packet_put_int(c->host_port); 1187ca3176e7SBrian Feldman } else { 1188ca3176e7SBrian Feldman /* listen address, port */ 1189ca3176e7SBrian Feldman packet_put_cstring(c->path); 1190ca3176e7SBrian Feldman packet_put_int(c->listening_port); 1191ca3176e7SBrian Feldman } 1192ca3176e7SBrian Feldman /* originator host and port */ 1193ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1194ca3176e7SBrian Feldman packet_put_int(remote_port); 1195ca3176e7SBrian Feldman packet_send(); 1196ca3176e7SBrian Feldman } else { 1197ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1198ca3176e7SBrian Feldman packet_put_int(c->self); 1199ca3176e7SBrian Feldman packet_put_cstring(c->path); 1200ca3176e7SBrian Feldman packet_put_int(c->host_port); 1201af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1202af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1203ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1204ca3176e7SBrian Feldman packet_send(); 1205ca3176e7SBrian Feldman } 1206ca3176e7SBrian Feldman xfree(remote_ipaddr); 1207ca3176e7SBrian Feldman } 1208ca3176e7SBrian Feldman 1209511b41d2SMark Murray /* 1210a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1211511b41d2SMark Murray */ 1212af12a3e7SDag-Erling Smørgrav static void 1213a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 1214a04a10f8SKris Kennaway { 1215ca3176e7SBrian Feldman Channel *nc; 1216a04a10f8SKris Kennaway struct sockaddr addr; 1217af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1218a04a10f8SKris Kennaway socklen_t addrlen; 1219ca3176e7SBrian Feldman char *rtype; 1220a04a10f8SKris Kennaway 1221a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1222a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1223a04a10f8SKris Kennaway "to %.100s port %d requested.", 1224a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1225ca3176e7SBrian Feldman 1226af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1227af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1228af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1229af12a3e7SDag-Erling Smørgrav } else { 1230af12a3e7SDag-Erling Smørgrav if (c->host_port == 0) { 1231af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1232af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1233af12a3e7SDag-Erling Smørgrav } else { 1234af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1235af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1236af12a3e7SDag-Erling Smørgrav } 1237af12a3e7SDag-Erling Smørgrav } 1238ca3176e7SBrian Feldman 1239511b41d2SMark Murray addrlen = sizeof(addr); 1240a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1241511b41d2SMark Murray if (newsock < 0) { 1242511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1243a04a10f8SKris Kennaway return; 1244511b41d2SMark Murray } 1245af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1246221552e4SDag-Erling Smørgrav nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1247221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1248ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1249ca3176e7SBrian Feldman nc->host_port = c->host_port; 1250ca3176e7SBrian Feldman strlcpy(nc->path, c->path, sizeof(nc->path)); 1251ca3176e7SBrian Feldman 1252af12a3e7SDag-Erling Smørgrav if (nextstate == SSH_CHANNEL_DYNAMIC) { 1253af12a3e7SDag-Erling Smørgrav /* 1254af12a3e7SDag-Erling Smørgrav * do not call the channel_post handler until 1255af12a3e7SDag-Erling Smørgrav * this flag has been reset by a pre-handler. 1256af12a3e7SDag-Erling Smørgrav * otherwise the FD_ISSET calls might overflow 1257af12a3e7SDag-Erling Smørgrav */ 1258af12a3e7SDag-Erling Smørgrav nc->delayed = 1; 1259af12a3e7SDag-Erling Smørgrav } else { 1260ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1261a04a10f8SKris Kennaway } 1262a04a10f8SKris Kennaway } 1263af12a3e7SDag-Erling Smørgrav } 1264511b41d2SMark Murray 1265511b41d2SMark Murray /* 1266a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1267a04a10f8SKris Kennaway * clients. 1268511b41d2SMark Murray */ 1269af12a3e7SDag-Erling Smørgrav static void 1270a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 1271a04a10f8SKris Kennaway { 1272af12a3e7SDag-Erling Smørgrav Channel *nc; 1273af12a3e7SDag-Erling Smørgrav int newsock; 1274a04a10f8SKris Kennaway struct sockaddr addr; 1275a04a10f8SKris Kennaway socklen_t addrlen; 1276a04a10f8SKris Kennaway 1277a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1278511b41d2SMark Murray addrlen = sizeof(addr); 1279a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1280511b41d2SMark Murray if (newsock < 0) { 1281511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 1282a04a10f8SKris Kennaway return; 1283511b41d2SMark Murray } 1284af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1285ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1286ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1287221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 1288ca3176e7SBrian Feldman if (compat20) { 1289ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1290ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1291af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1292ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1293ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1294ca3176e7SBrian Feldman } else { 1295511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1296af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1297ca3176e7SBrian Feldman } 1298511b41d2SMark Murray packet_send(); 1299511b41d2SMark Murray } 1300a04a10f8SKris Kennaway } 1301511b41d2SMark Murray 1302af12a3e7SDag-Erling Smørgrav static void 1303ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 1304ca3176e7SBrian Feldman { 1305ca3176e7SBrian Feldman int err = 0; 1306af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1307af12a3e7SDag-Erling Smørgrav 1308af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1309af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1310af12a3e7SDag-Erling Smørgrav err = errno; 1311af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1312af12a3e7SDag-Erling Smørgrav } 1313ca3176e7SBrian Feldman if (err == 0) { 1314af12a3e7SDag-Erling Smørgrav debug("channel %d: connected", c->self); 1315af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1316af12a3e7SDag-Erling Smørgrav if (compat20) { 1317af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1318af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1319af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1320af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1321af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1322af12a3e7SDag-Erling Smørgrav } else { 1323af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1324af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1325af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1326af12a3e7SDag-Erling Smørgrav } 1327ca3176e7SBrian Feldman } else { 1328ca3176e7SBrian Feldman debug("channel %d: not connected: %s", 1329ca3176e7SBrian Feldman c->self, strerror(err)); 1330af12a3e7SDag-Erling Smørgrav if (compat20) { 1331af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1332af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1333af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1334af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1335af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1336af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1337ca3176e7SBrian Feldman } 1338af12a3e7SDag-Erling Smørgrav } else { 1339af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1340af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1341ca3176e7SBrian Feldman } 1342af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1343af12a3e7SDag-Erling Smørgrav } 1344af12a3e7SDag-Erling Smørgrav packet_send(); 1345ca3176e7SBrian Feldman } 1346ca3176e7SBrian Feldman } 1347ca3176e7SBrian Feldman 1348af12a3e7SDag-Erling Smørgrav static int 1349a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 1350a04a10f8SKris Kennaway { 1351a04a10f8SKris Kennaway char buf[16*1024]; 1352a04a10f8SKris Kennaway int len; 1353511b41d2SMark Murray 1354a04a10f8SKris Kennaway if (c->rfd != -1 && 1355a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 1356a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1357a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1358a04a10f8SKris Kennaway return 1; 13590c82706bSBrian Feldman if (len <= 0) { 1360221552e4SDag-Erling Smørgrav debug2("channel %d: read<=0 rfd %d len %d", 1361a04a10f8SKris Kennaway c->self, c->rfd, len); 1362ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1363221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1364af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1365ca3176e7SBrian Feldman return -1; 1366ca3176e7SBrian Feldman } else if (compat13) { 1367af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1368a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1369221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1370a04a10f8SKris Kennaway } else { 1371a04a10f8SKris Kennaway chan_read_failed(c); 1372a04a10f8SKris Kennaway } 1373a04a10f8SKris Kennaway return -1; 1374a04a10f8SKris Kennaway } 1375b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1376b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1377221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1378b66f2d16SKris Kennaway chan_read_failed(c); 1379b66f2d16SKris Kennaway } 1380b66f2d16SKris Kennaway } else { 1381a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1382a04a10f8SKris Kennaway } 1383b66f2d16SKris Kennaway } 1384a04a10f8SKris Kennaway return 1; 1385a04a10f8SKris Kennaway } 1386af12a3e7SDag-Erling Smørgrav static int 1387a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 1388a04a10f8SKris Kennaway { 1389ca3176e7SBrian Feldman struct termios tio; 1390af12a3e7SDag-Erling Smørgrav u_char *data; 1391af12a3e7SDag-Erling Smørgrav u_int dlen; 1392a04a10f8SKris Kennaway int len; 1393a04a10f8SKris Kennaway 1394a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1395a04a10f8SKris Kennaway if (c->wfd != -1 && 1396a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1397a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1398af12a3e7SDag-Erling Smørgrav data = buffer_ptr(&c->output); 1399af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1400f388f5efSDag-Erling Smørgrav #ifdef _AIX 1401f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 1402f388f5efSDag-Erling Smørgrav if (compat20 && c->wfd_isatty && dlen > 8*1024) 1403f388f5efSDag-Erling Smørgrav dlen = 8*1024; 1404f388f5efSDag-Erling Smørgrav #endif 1405af12a3e7SDag-Erling Smørgrav len = write(c->wfd, data, dlen); 1406a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1407a04a10f8SKris Kennaway return 1; 1408511b41d2SMark Murray if (len <= 0) { 1409ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1410221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1411af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1412ca3176e7SBrian Feldman return -1; 1413ca3176e7SBrian Feldman } else if (compat13) { 1414af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1415221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1416a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1417511b41d2SMark Murray } else { 1418a04a10f8SKris Kennaway chan_write_failed(c); 1419511b41d2SMark Murray } 1420a04a10f8SKris Kennaway return -1; 1421511b41d2SMark Murray } 1422af12a3e7SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { 1423e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1424e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1425e0fbb1d2SBrian Feldman /* 1426e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1427ca3176e7SBrian Feldman * traffic analysis. We need to match the 1428ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1429ca3176e7SBrian Feldman * (4 byte channel id + data) 1430e0fbb1d2SBrian Feldman */ 1431ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1432e0fbb1d2SBrian Feldman packet_send(); 1433e0fbb1d2SBrian Feldman } 1434e0fbb1d2SBrian Feldman } 1435a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1436a04a10f8SKris Kennaway if (compat20 && len > 0) { 1437a04a10f8SKris Kennaway c->local_consumed += len; 1438511b41d2SMark Murray } 1439511b41d2SMark Murray } 1440a04a10f8SKris Kennaway return 1; 1441511b41d2SMark Murray } 1442af12a3e7SDag-Erling Smørgrav static int 1443a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 1444a04a10f8SKris Kennaway { 1445a04a10f8SKris Kennaway char buf[16*1024]; 1446a04a10f8SKris Kennaway int len; 1447511b41d2SMark Murray 1448a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1449a04a10f8SKris Kennaway if (c->efd != -1) { 1450a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1451a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1452a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1453a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1454a04a10f8SKris Kennaway buffer_len(&c->extended)); 14555b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1456a04a10f8SKris Kennaway c->self, len, c->efd); 1457ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1458ca3176e7SBrian Feldman return 1; 1459ca3176e7SBrian Feldman if (len <= 0) { 1460ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1461ca3176e7SBrian Feldman c->self, c->efd); 1462af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1463ca3176e7SBrian Feldman } else { 1464a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1465a04a10f8SKris Kennaway c->local_consumed += len; 1466a04a10f8SKris Kennaway } 1467a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 1468a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 1469a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 14705b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1471a04a10f8SKris Kennaway c->self, len, c->efd); 1472ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1473ca3176e7SBrian Feldman return 1; 1474ca3176e7SBrian Feldman if (len <= 0) { 1475ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1476a04a10f8SKris Kennaway c->self, c->efd); 1477af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1478ca3176e7SBrian Feldman } else { 1479a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1480a04a10f8SKris Kennaway } 1481a04a10f8SKris Kennaway } 1482ca3176e7SBrian Feldman } 1483a04a10f8SKris Kennaway return 1; 1484a04a10f8SKris Kennaway } 1485af12a3e7SDag-Erling Smørgrav static int 1486ca3176e7SBrian Feldman channel_check_window(Channel *c) 1487a04a10f8SKris Kennaway { 1488ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1489ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1490a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 1491a04a10f8SKris Kennaway c->local_consumed > 0) { 1492a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1493a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1494a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 1495a04a10f8SKris Kennaway packet_send(); 14965b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1497a04a10f8SKris Kennaway c->self, c->local_window, 1498a04a10f8SKris Kennaway c->local_consumed); 1499a04a10f8SKris Kennaway c->local_window += c->local_consumed; 1500a04a10f8SKris Kennaway c->local_consumed = 0; 1501a04a10f8SKris Kennaway } 1502a04a10f8SKris Kennaway return 1; 1503a04a10f8SKris Kennaway } 1504a04a10f8SKris Kennaway 1505af12a3e7SDag-Erling Smørgrav static void 1506af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 1507a04a10f8SKris Kennaway { 1508af12a3e7SDag-Erling Smørgrav if (c->delayed) 1509af12a3e7SDag-Erling Smørgrav return; 1510a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1511a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1512af12a3e7SDag-Erling Smørgrav if (!compat20) 1513af12a3e7SDag-Erling Smørgrav return; 1514a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1515ca3176e7SBrian Feldman channel_check_window(c); 1516a04a10f8SKris Kennaway } 1517a04a10f8SKris Kennaway 1518af12a3e7SDag-Erling Smørgrav static void 1519a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 1520a04a10f8SKris Kennaway { 1521a04a10f8SKris Kennaway int len; 1522f388f5efSDag-Erling Smørgrav 1523511b41d2SMark Murray /* Send buffered output data to the socket. */ 1524a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 1525a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 1526a04a10f8SKris Kennaway buffer_len(&c->output)); 1527511b41d2SMark Murray if (len <= 0) 1528af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1529511b41d2SMark Murray else 1530a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1531511b41d2SMark Murray } 1532a04a10f8SKris Kennaway } 1533511b41d2SMark Murray 1534af12a3e7SDag-Erling Smørgrav static void 1535a04a10f8SKris Kennaway channel_handler_init_20(void) 1536a04a10f8SKris Kennaway { 1537af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1538a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1539a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1540ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 1541a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1542ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1543ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1544ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1545a04a10f8SKris Kennaway 1546af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1547a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1548ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 1549a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1550ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1551ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1552af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1553a04a10f8SKris Kennaway } 1554a04a10f8SKris Kennaway 1555af12a3e7SDag-Erling Smørgrav static void 1556a04a10f8SKris Kennaway channel_handler_init_13(void) 1557a04a10f8SKris Kennaway { 1558a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 1559a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 1560a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1561a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1562a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1563a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 1564a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 1565ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1566ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1567a04a10f8SKris Kennaway 1568af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1569a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1570a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1571a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1572a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 1573ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1574af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1575a04a10f8SKris Kennaway } 1576a04a10f8SKris Kennaway 1577af12a3e7SDag-Erling Smørgrav static void 1578a04a10f8SKris Kennaway channel_handler_init_15(void) 1579a04a10f8SKris Kennaway { 1580af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1581a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1582a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1583a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1584a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1585ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1586ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1587a04a10f8SKris Kennaway 1588a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1589a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1590a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1591af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1592ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1593af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1594a04a10f8SKris Kennaway } 1595a04a10f8SKris Kennaway 1596af12a3e7SDag-Erling Smørgrav static void 1597a04a10f8SKris Kennaway channel_handler_init(void) 1598a04a10f8SKris Kennaway { 1599a04a10f8SKris Kennaway int i; 1600f388f5efSDag-Erling Smørgrav 1601a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 1602a04a10f8SKris Kennaway channel_pre[i] = NULL; 1603a04a10f8SKris Kennaway channel_post[i] = NULL; 1604a04a10f8SKris Kennaway } 1605a04a10f8SKris Kennaway if (compat20) 1606a04a10f8SKris Kennaway channel_handler_init_20(); 1607a04a10f8SKris Kennaway else if (compat13) 1608a04a10f8SKris Kennaway channel_handler_init_13(); 1609a04a10f8SKris Kennaway else 1610a04a10f8SKris Kennaway channel_handler_init_15(); 1611a04a10f8SKris Kennaway } 1612a04a10f8SKris Kennaway 1613af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 1614af12a3e7SDag-Erling Smørgrav static void 1615af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 1616af12a3e7SDag-Erling Smørgrav { 1617af12a3e7SDag-Erling Smørgrav if (c == NULL) 1618af12a3e7SDag-Erling Smørgrav return; 1619af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 1620af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 0)) 1621af12a3e7SDag-Erling Smørgrav return; 1622221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 1623af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 1624af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 1625af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 1626af12a3e7SDag-Erling Smørgrav return; 1627221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 1628af12a3e7SDag-Erling Smørgrav } 1629af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 1630af12a3e7SDag-Erling Smørgrav return; 1631221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 1632af12a3e7SDag-Erling Smørgrav channel_free(c); 1633af12a3e7SDag-Erling Smørgrav } 1634af12a3e7SDag-Erling Smørgrav 1635af12a3e7SDag-Erling Smørgrav static void 1636a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 1637a04a10f8SKris Kennaway { 1638a04a10f8SKris Kennaway static int did_init = 0; 1639a04a10f8SKris Kennaway int i; 1640a04a10f8SKris Kennaway Channel *c; 1641a04a10f8SKris Kennaway 1642a04a10f8SKris Kennaway if (!did_init) { 1643a04a10f8SKris Kennaway channel_handler_init(); 1644a04a10f8SKris Kennaway did_init = 1; 1645a04a10f8SKris Kennaway } 1646a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 1647af12a3e7SDag-Erling Smørgrav c = channels[i]; 1648af12a3e7SDag-Erling Smørgrav if (c == NULL) 1649511b41d2SMark Murray continue; 1650af12a3e7SDag-Erling Smørgrav if (ftab[c->type] != NULL) 1651a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 1652af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 1653511b41d2SMark Murray } 1654511b41d2SMark Murray } 1655a04a10f8SKris Kennaway 1656af12a3e7SDag-Erling Smørgrav /* 1657af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 1658af12a3e7SDag-Erling Smørgrav * select bitmasks. 1659af12a3e7SDag-Erling Smørgrav */ 1660a04a10f8SKris Kennaway void 1661ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 1662af12a3e7SDag-Erling Smørgrav int *nallocp, int rekeying) 1663a04a10f8SKris Kennaway { 1664ca3176e7SBrian Feldman int n; 1665ca3176e7SBrian Feldman u_int sz; 1666ca3176e7SBrian Feldman 1667ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 1668ca3176e7SBrian Feldman 1669ca3176e7SBrian Feldman sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 1670af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 1671af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 1672af12a3e7SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, sz); 1673af12a3e7SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, sz); 1674af12a3e7SDag-Erling Smørgrav *nallocp = sz; 1675ca3176e7SBrian Feldman } 1676af12a3e7SDag-Erling Smørgrav *maxfdp = n; 1677ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 1678ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 1679ca3176e7SBrian Feldman 1680ca3176e7SBrian Feldman if (!rekeying) 1681ca3176e7SBrian Feldman channel_handler(channel_pre, *readsetp, *writesetp); 1682a04a10f8SKris Kennaway } 1683a04a10f8SKris Kennaway 1684af12a3e7SDag-Erling Smørgrav /* 1685af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 1686af12a3e7SDag-Erling Smørgrav * events pending. 1687af12a3e7SDag-Erling Smørgrav */ 1688a04a10f8SKris Kennaway void 1689a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 1690a04a10f8SKris Kennaway { 1691a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 1692511b41d2SMark Murray } 1693511b41d2SMark Murray 1694af12a3e7SDag-Erling Smørgrav 1695ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 1696511b41d2SMark Murray 1697511b41d2SMark Murray void 1698af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 1699511b41d2SMark Murray { 1700a04a10f8SKris Kennaway Channel *c; 1701a82e551fSDag-Erling Smørgrav int i; 1702a82e551fSDag-Erling Smørgrav u_int len; 1703511b41d2SMark Murray 1704511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1705af12a3e7SDag-Erling Smørgrav c = channels[i]; 1706af12a3e7SDag-Erling Smørgrav if (c == NULL) 1707af12a3e7SDag-Erling Smørgrav continue; 1708511b41d2SMark Murray 1709af12a3e7SDag-Erling Smørgrav /* 1710af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 1711af12a3e7SDag-Erling Smørgrav * incoming data. 1712af12a3e7SDag-Erling Smørgrav */ 1713511b41d2SMark Murray if (compat13) { 1714a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1715a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 1716511b41d2SMark Murray continue; 1717511b41d2SMark Murray } else { 1718a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 1719511b41d2SMark Murray continue; 1720a04a10f8SKris Kennaway } 1721a04a10f8SKris Kennaway if (compat20 && 1722a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 1723ca3176e7SBrian Feldman /* XXX is this true? */ 1724af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 1725511b41d2SMark Murray continue; 1726511b41d2SMark Murray } 1727511b41d2SMark Murray 1728511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 1729ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 1730ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 1731ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 1732af12a3e7SDag-Erling Smørgrav /* 1733af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 1734af12a3e7SDag-Erling Smørgrav * connection. 1735af12a3e7SDag-Erling Smørgrav */ 1736a04a10f8SKris Kennaway if (compat20) { 1737a04a10f8SKris Kennaway if (len > c->remote_window) 1738a04a10f8SKris Kennaway len = c->remote_window; 1739a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1740a04a10f8SKris Kennaway len = c->remote_maxpacket; 1741a04a10f8SKris Kennaway } else { 1742511b41d2SMark Murray if (packet_is_interactive()) { 1743511b41d2SMark Murray if (len > 1024) 1744511b41d2SMark Murray len = 512; 1745511b41d2SMark Murray } else { 1746511b41d2SMark Murray /* Keep the packets at reasonable size. */ 1747511b41d2SMark Murray if (len > packet_get_maxsize()/2) 1748511b41d2SMark Murray len = packet_get_maxsize()/2; 1749511b41d2SMark Murray } 1750a04a10f8SKris Kennaway } 1751a04a10f8SKris Kennaway if (len > 0) { 1752a04a10f8SKris Kennaway packet_start(compat20 ? 1753a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 1754a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1755a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 1756511b41d2SMark Murray packet_send(); 1757a04a10f8SKris Kennaway buffer_consume(&c->input, len); 1758a04a10f8SKris Kennaway c->remote_window -= len; 1759a04a10f8SKris Kennaway } 1760a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1761511b41d2SMark Murray if (compat13) 1762511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1763511b41d2SMark Murray /* 1764511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 176580628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 176680628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 1767511b41d2SMark Murray */ 176880628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 176980628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 177080628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 177180628bacSDag-Erling Smørgrav else 1772a04a10f8SKris Kennaway chan_ibuf_empty(c); 1773a04a10f8SKris Kennaway } 1774a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1775a04a10f8SKris Kennaway if (compat20 && 177680628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 1777a04a10f8SKris Kennaway c->remote_window > 0 && 1778a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1779a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1780a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 1781ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 1782ca3176e7SBrian Feldman c->extended_usage); 1783a04a10f8SKris Kennaway if (len > c->remote_window) 1784a04a10f8SKris Kennaway len = c->remote_window; 1785a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1786a04a10f8SKris Kennaway len = c->remote_maxpacket; 1787a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1788a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1789a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1790a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1791a04a10f8SKris Kennaway packet_send(); 1792a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1793a04a10f8SKris Kennaway c->remote_window -= len; 1794ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 1795511b41d2SMark Murray } 1796511b41d2SMark Murray } 1797511b41d2SMark Murray } 1798511b41d2SMark Murray 1799af12a3e7SDag-Erling Smørgrav 1800af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 1801511b41d2SMark Murray 1802511b41d2SMark Murray void 1803af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 1804511b41d2SMark Murray { 1805511b41d2SMark Murray int id; 1806511b41d2SMark Murray char *data; 1807ca3176e7SBrian Feldman u_int data_len; 1808a04a10f8SKris Kennaway Channel *c; 1809511b41d2SMark Murray 1810511b41d2SMark Murray /* Get the channel number and verify it. */ 1811511b41d2SMark Murray id = packet_get_int(); 1812a04a10f8SKris Kennaway c = channel_lookup(id); 1813a04a10f8SKris Kennaway if (c == NULL) 1814511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1815511b41d2SMark Murray 1816511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1817a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1818a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1819511b41d2SMark Murray return; 1820511b41d2SMark Murray 1821511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1822a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1823511b41d2SMark Murray return; 1824511b41d2SMark Murray 1825511b41d2SMark Murray /* Get the data. */ 1826511b41d2SMark Murray data = packet_get_string(&data_len); 1827a04a10f8SKris Kennaway 1828a04a10f8SKris Kennaway if (compat20) { 1829a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1830221552e4SDag-Erling Smørgrav logit("channel %d: rcvd big packet %d, maxpack %d", 1831a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1832a04a10f8SKris Kennaway } 1833a04a10f8SKris Kennaway if (data_len > c->local_window) { 1834221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much data %d, win %d", 1835a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1836a04a10f8SKris Kennaway xfree(data); 1837a04a10f8SKris Kennaway return; 1838a04a10f8SKris Kennaway } 1839a04a10f8SKris Kennaway c->local_window -= data_len; 1840a04a10f8SKris Kennaway } 1841af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1842a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1843511b41d2SMark Murray xfree(data); 1844511b41d2SMark Murray } 1845af12a3e7SDag-Erling Smørgrav 1846a04a10f8SKris Kennaway void 1847af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 1848a04a10f8SKris Kennaway { 1849a04a10f8SKris Kennaway int id; 1850a04a10f8SKris Kennaway char *data; 1851a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 1852a04a10f8SKris Kennaway Channel *c; 1853a04a10f8SKris Kennaway 1854a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1855a04a10f8SKris Kennaway id = packet_get_int(); 1856a04a10f8SKris Kennaway c = channel_lookup(id); 1857a04a10f8SKris Kennaway 1858a04a10f8SKris Kennaway if (c == NULL) 1859a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1860a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1861221552e4SDag-Erling Smørgrav logit("channel %d: ext data for non open", id); 1862a04a10f8SKris Kennaway return; 1863a04a10f8SKris Kennaway } 186480628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 186580628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 186680628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 186780628bacSDag-Erling Smørgrav else 186880628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 186980628bacSDag-Erling Smørgrav "on channel %d.", id); 187080628bacSDag-Erling Smørgrav } 1871a04a10f8SKris Kennaway tcode = packet_get_int(); 1872a04a10f8SKris Kennaway if (c->efd == -1 || 1873a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1874a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1875221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 1876a04a10f8SKris Kennaway return; 1877a04a10f8SKris Kennaway } 1878a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1879af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1880a04a10f8SKris Kennaway if (data_len > c->local_window) { 1881221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %d, win %d", 1882a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1883a04a10f8SKris Kennaway xfree(data); 1884a04a10f8SKris Kennaway return; 1885a04a10f8SKris Kennaway } 18865b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1887a04a10f8SKris Kennaway c->local_window -= data_len; 1888a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1889a04a10f8SKris Kennaway xfree(data); 1890a04a10f8SKris Kennaway } 1891a04a10f8SKris Kennaway 1892a04a10f8SKris Kennaway void 1893af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 1894a04a10f8SKris Kennaway { 1895a04a10f8SKris Kennaway int id; 1896a04a10f8SKris Kennaway Channel *c; 1897a04a10f8SKris Kennaway 1898a04a10f8SKris Kennaway id = packet_get_int(); 1899af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1900a04a10f8SKris Kennaway c = channel_lookup(id); 1901a04a10f8SKris Kennaway if (c == NULL) 1902a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1903a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1904af12a3e7SDag-Erling Smørgrav 1905af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 1906af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 1907af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 1908af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 1909af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 1910af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1911af12a3e7SDag-Erling Smørgrav } 1912af12a3e7SDag-Erling Smørgrav 1913a04a10f8SKris Kennaway } 1914511b41d2SMark Murray 1915511b41d2SMark Murray void 1916af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 1917511b41d2SMark Murray { 1918a04a10f8SKris Kennaway int id; 1919a04a10f8SKris Kennaway Channel *c; 1920511b41d2SMark Murray 1921a04a10f8SKris Kennaway id = packet_get_int(); 1922af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1923a04a10f8SKris Kennaway c = channel_lookup(id); 1924a04a10f8SKris Kennaway if (c == NULL) 1925a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1926511b41d2SMark Murray 1927511b41d2SMark Murray /* 1928511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1929511b41d2SMark Murray * data is coming for it. 1930511b41d2SMark Murray */ 1931511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1932a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1933511b41d2SMark Murray packet_send(); 1934511b41d2SMark Murray 1935511b41d2SMark Murray /* 1936511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1937511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1938511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1939511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1940511b41d2SMark Murray * the confirmation arrives. 1941511b41d2SMark Murray */ 1942a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1943511b41d2SMark Murray /* 1944511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1945511b41d2SMark Murray * cause it to be freed later. 1946511b41d2SMark Murray */ 1947af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1948a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1949511b41d2SMark Murray } 1950511b41d2SMark Murray } 1951511b41d2SMark Murray 1952a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1953a04a10f8SKris Kennaway void 1954af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 1955a04a10f8SKris Kennaway { 1956a04a10f8SKris Kennaway int id = packet_get_int(); 1957a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1958af12a3e7SDag-Erling Smørgrav 1959af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1960a04a10f8SKris Kennaway if (c == NULL) 1961a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1962a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1963a04a10f8SKris Kennaway } 1964511b41d2SMark Murray 1965511b41d2SMark Murray void 1966af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 1967511b41d2SMark Murray { 1968a04a10f8SKris Kennaway int id = packet_get_int(); 1969a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1970a04a10f8SKris Kennaway 1971af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1972a04a10f8SKris Kennaway if (c == NULL) 1973a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1974a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1975a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1976a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1977a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1978af12a3e7SDag-Erling Smørgrav channel_free(c); 1979a04a10f8SKris Kennaway } 1980a04a10f8SKris Kennaway 1981a04a10f8SKris Kennaway void 1982af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 1983a04a10f8SKris Kennaway { 1984a04a10f8SKris Kennaway int id, remote_id; 1985a04a10f8SKris Kennaway Channel *c; 1986a04a10f8SKris Kennaway 1987a04a10f8SKris Kennaway id = packet_get_int(); 1988a04a10f8SKris Kennaway c = channel_lookup(id); 1989a04a10f8SKris Kennaway 1990a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1991a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1992a04a10f8SKris Kennaway "non-opening channel %d.", id); 1993a04a10f8SKris Kennaway remote_id = packet_get_int(); 1994a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1995a04a10f8SKris Kennaway c->remote_id = remote_id; 1996a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1997a04a10f8SKris Kennaway 1998a04a10f8SKris Kennaway if (compat20) { 1999a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 2000a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 2001af12a3e7SDag-Erling Smørgrav if (c->confirm) { 20025b9b2fafSBrian Feldman debug2("callback start"); 2003af12a3e7SDag-Erling Smørgrav c->confirm(c->self, NULL); 20045b9b2fafSBrian Feldman debug2("callback done"); 2005a04a10f8SKris Kennaway } 2006221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 2007a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 2008a04a10f8SKris Kennaway } 2009af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2010af12a3e7SDag-Erling Smørgrav } 2011af12a3e7SDag-Erling Smørgrav 2012af12a3e7SDag-Erling Smørgrav static char * 2013af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 2014af12a3e7SDag-Erling Smørgrav { 2015af12a3e7SDag-Erling Smørgrav switch (reason) { 2016af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 2017af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 2018af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 2019af12a3e7SDag-Erling Smørgrav return "connect failed"; 2020af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 2021af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 2022af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 2023af12a3e7SDag-Erling Smørgrav return "resource shortage"; 2024af12a3e7SDag-Erling Smørgrav } 2025af12a3e7SDag-Erling Smørgrav return "unknown reason"; 2026a04a10f8SKris Kennaway } 2027a04a10f8SKris Kennaway 2028a04a10f8SKris Kennaway void 2029af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 2030a04a10f8SKris Kennaway { 2031ca3176e7SBrian Feldman int id, reason; 2032ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 2033a04a10f8SKris Kennaway Channel *c; 2034a04a10f8SKris Kennaway 2035a04a10f8SKris Kennaway id = packet_get_int(); 2036a04a10f8SKris Kennaway c = channel_lookup(id); 2037a04a10f8SKris Kennaway 2038a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 2039a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 2040a04a10f8SKris Kennaway "non-opening channel %d.", id); 2041a04a10f8SKris Kennaway if (compat20) { 2042ca3176e7SBrian Feldman reason = packet_get_int(); 2043af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 2044ca3176e7SBrian Feldman msg = packet_get_string(NULL); 2045ca3176e7SBrian Feldman lang = packet_get_string(NULL); 2046ca3176e7SBrian Feldman } 2047221552e4SDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", id, 2048af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 2049ca3176e7SBrian Feldman if (msg != NULL) 2050a04a10f8SKris Kennaway xfree(msg); 2051ca3176e7SBrian Feldman if (lang != NULL) 2052a04a10f8SKris Kennaway xfree(lang); 2053a04a10f8SKris Kennaway } 2054af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2055a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 2056af12a3e7SDag-Erling Smørgrav channel_free(c); 2057a04a10f8SKris Kennaway } 2058a04a10f8SKris Kennaway 2059a04a10f8SKris Kennaway void 2060af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 2061a04a10f8SKris Kennaway { 2062a04a10f8SKris Kennaway Channel *c; 2063a82e551fSDag-Erling Smørgrav int id; 2064a82e551fSDag-Erling Smørgrav u_int adjust; 2065a04a10f8SKris Kennaway 2066a04a10f8SKris Kennaway if (!compat20) 2067a04a10f8SKris Kennaway return; 2068511b41d2SMark Murray 2069511b41d2SMark Murray /* Get the channel number and verify it. */ 2070a04a10f8SKris Kennaway id = packet_get_int(); 2071a04a10f8SKris Kennaway c = channel_lookup(id); 2072511b41d2SMark Murray 2073a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 2074221552e4SDag-Erling Smørgrav logit("Received window adjust for " 2075a04a10f8SKris Kennaway "non-open channel %d.", id); 2076511b41d2SMark Murray return; 2077511b41d2SMark Murray } 2078a04a10f8SKris Kennaway adjust = packet_get_int(); 2079af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2080a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 2081a04a10f8SKris Kennaway c->remote_window += adjust; 2082511b41d2SMark Murray } 2083511b41d2SMark Murray 2084af12a3e7SDag-Erling Smørgrav void 2085af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 2086af12a3e7SDag-Erling Smørgrav { 2087af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2088af12a3e7SDag-Erling Smørgrav u_short host_port; 2089af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 2090af12a3e7SDag-Erling Smørgrav int remote_id, sock = -1; 2091af12a3e7SDag-Erling Smørgrav 2092af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2093af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 2094af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 2095af12a3e7SDag-Erling Smørgrav 2096af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2097af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 2098af12a3e7SDag-Erling Smørgrav } else { 2099af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 2100af12a3e7SDag-Erling Smørgrav } 2101af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2102af12a3e7SDag-Erling Smørgrav sock = channel_connect_to(host, host_port); 2103af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2104af12a3e7SDag-Erling Smørgrav c = channel_new("connected socket", 2105af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 2106af12a3e7SDag-Erling Smørgrav originator_string, 1); 2107af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2108af12a3e7SDag-Erling Smørgrav } 2109e73e9afaSDag-Erling Smørgrav xfree(originator_string); 2110221552e4SDag-Erling Smørgrav if (c == NULL) { 2111af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2112af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2113af12a3e7SDag-Erling Smørgrav packet_send(); 2114af12a3e7SDag-Erling Smørgrav } 2115af12a3e7SDag-Erling Smørgrav xfree(host); 2116af12a3e7SDag-Erling Smørgrav } 2117af12a3e7SDag-Erling Smørgrav 2118af12a3e7SDag-Erling Smørgrav 2119af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 2120511b41d2SMark Murray 2121511b41d2SMark Murray void 2122af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 2123511b41d2SMark Murray { 2124af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 2125511b41d2SMark Murray } 2126511b41d2SMark Murray 2127af12a3e7SDag-Erling Smørgrav static int 2128af12a3e7SDag-Erling Smørgrav channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 2129af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2130511b41d2SMark Murray { 2131af12a3e7SDag-Erling Smørgrav Channel *c; 2132af12a3e7SDag-Erling Smørgrav int success, sock, on = 1; 2133511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2134ca3176e7SBrian Feldman const char *host; 2135af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2136511b41d2SMark Murray 2137ca3176e7SBrian Feldman success = 0; 2138af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2139af12a3e7SDag-Erling Smørgrav listen_addr : host_to_connect; 2140511b41d2SMark Murray 2141af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2142af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2143af12a3e7SDag-Erling Smørgrav return success; 2144ca3176e7SBrian Feldman } 2145af12a3e7SDag-Erling Smørgrav if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 2146ca3176e7SBrian Feldman error("Forward host name too long."); 2147ca3176e7SBrian Feldman return success; 2148ca3176e7SBrian Feldman } 2149ca3176e7SBrian Feldman 2150511b41d2SMark Murray /* 2151511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2152511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2153511b41d2SMark Murray */ 2154511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2155511b41d2SMark Murray hints.ai_family = IPv4or6; 2156511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 2157511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2158ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", listen_port); 2159511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 2160511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 2161511b41d2SMark Murray 2162511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2163511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2164511b41d2SMark Murray continue; 2165511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2166511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2167af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: getnameinfo failed"); 2168511b41d2SMark Murray continue; 2169511b41d2SMark Murray } 2170511b41d2SMark Murray /* Create a port to listen for the host. */ 2171221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2172511b41d2SMark Murray if (sock < 0) { 2173511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2174511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2175511b41d2SMark Murray continue; 2176511b41d2SMark Murray } 2177511b41d2SMark Murray /* 2178f388f5efSDag-Erling Smørgrav * Set socket options. 2179f388f5efSDag-Erling Smørgrav * Allow local port reuse in TIME_WAIT. 2180511b41d2SMark Murray */ 2181f388f5efSDag-Erling Smørgrav if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, 2182f388f5efSDag-Erling Smørgrav sizeof(on)) == -1) 2183f388f5efSDag-Erling Smørgrav error("setsockopt SO_REUSEADDR: %s", strerror(errno)); 2184f388f5efSDag-Erling Smørgrav 2185511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 2186511b41d2SMark Murray 2187511b41d2SMark Murray /* Bind the socket to the address. */ 2188511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2189511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2190989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2191989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2192989dd127SDag-Erling Smørgrav else 2193511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2194989dd127SDag-Erling Smørgrav 2195511b41d2SMark Murray close(sock); 2196511b41d2SMark Murray continue; 2197511b41d2SMark Murray } 2198511b41d2SMark Murray /* Start listening for connections on the socket. */ 2199511b41d2SMark Murray if (listen(sock, 5) < 0) { 2200511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2201511b41d2SMark Murray close(sock); 2202511b41d2SMark Murray continue; 2203511b41d2SMark Murray } 2204511b41d2SMark Murray /* Allocate a channel number for the socket. */ 2205af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2206a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2207221552e4SDag-Erling Smørgrav 0, "port listener", 1); 2208af12a3e7SDag-Erling Smørgrav strlcpy(c->path, host, sizeof(c->path)); 2209af12a3e7SDag-Erling Smørgrav c->host_port = port_to_connect; 2210af12a3e7SDag-Erling Smørgrav c->listening_port = listen_port; 2211511b41d2SMark Murray success = 1; 2212511b41d2SMark Murray } 2213511b41d2SMark Murray if (success == 0) 2214af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: cannot listen to port: %d", 2215ca3176e7SBrian Feldman listen_port); 2216511b41d2SMark Murray freeaddrinfo(aitop); 2217ca3176e7SBrian Feldman return success; 2218511b41d2SMark Murray } 2219511b41d2SMark Murray 2220af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 2221af12a3e7SDag-Erling Smørgrav int 2222af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(u_short listen_port, 2223af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2224af12a3e7SDag-Erling Smørgrav { 2225af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2226af12a3e7SDag-Erling Smørgrav NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); 2227af12a3e7SDag-Erling Smørgrav } 2228af12a3e7SDag-Erling Smørgrav 2229af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 2230af12a3e7SDag-Erling Smørgrav int 2231af12a3e7SDag-Erling Smørgrav channel_setup_remote_fwd_listener(const char *listen_address, 2232af12a3e7SDag-Erling Smørgrav u_short listen_port, int gateway_ports) 2233af12a3e7SDag-Erling Smørgrav { 2234af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2235af12a3e7SDag-Erling Smørgrav listen_address, listen_port, NULL, 0, gateway_ports); 2236af12a3e7SDag-Erling Smørgrav } 2237af12a3e7SDag-Erling Smørgrav 2238511b41d2SMark Murray /* 2239511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 2240511b41d2SMark Murray * the secure channel to host:port from local side. 2241511b41d2SMark Murray */ 2242511b41d2SMark Murray 2243511b41d2SMark Murray void 2244ca3176e7SBrian Feldman channel_request_remote_forwarding(u_short listen_port, 2245ca3176e7SBrian Feldman const char *host_to_connect, u_short port_to_connect) 2246511b41d2SMark Murray { 2247af12a3e7SDag-Erling Smørgrav int type, success = 0; 2248ca3176e7SBrian Feldman 2249511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 2250511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2251511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 2252511b41d2SMark Murray 2253511b41d2SMark Murray /* Send the forward request to the remote side. */ 2254a04a10f8SKris Kennaway if (compat20) { 2255a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 2256a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 2257a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 225880628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2259a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 2260a04a10f8SKris Kennaway packet_put_int(listen_port); 2261ca3176e7SBrian Feldman packet_send(); 2262ca3176e7SBrian Feldman packet_write_wait(); 2263ca3176e7SBrian Feldman /* Assume that server accepts the request */ 2264ca3176e7SBrian Feldman success = 1; 2265a04a10f8SKris Kennaway } else { 2266511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 2267a04a10f8SKris Kennaway packet_put_int(listen_port); 2268a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 2269a04a10f8SKris Kennaway packet_put_int(port_to_connect); 2270511b41d2SMark Murray packet_send(); 2271511b41d2SMark Murray packet_write_wait(); 2272ca3176e7SBrian Feldman 2273ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 2274af12a3e7SDag-Erling Smørgrav type = packet_read(); 2275ca3176e7SBrian Feldman switch (type) { 2276ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 2277ca3176e7SBrian Feldman success = 1; 2278ca3176e7SBrian Feldman break; 2279ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 2280221552e4SDag-Erling Smørgrav logit("Warning: Server denied remote port forwarding."); 2281ca3176e7SBrian Feldman break; 2282ca3176e7SBrian Feldman default: 2283ca3176e7SBrian Feldman /* Unknown packet */ 2284ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 2285ca3176e7SBrian Feldman "received packet type %d.", type); 2286ca3176e7SBrian Feldman } 2287ca3176e7SBrian Feldman } 2288ca3176e7SBrian Feldman if (success) { 2289ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 2290ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 2291ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].listen_port = listen_port; 2292ca3176e7SBrian Feldman num_permitted_opens++; 2293511b41d2SMark Murray } 2294a04a10f8SKris Kennaway } 2295511b41d2SMark Murray 2296511b41d2SMark Murray /* 2297511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 2298511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 2299511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 2300511b41d2SMark Murray */ 2301511b41d2SMark Murray 2302511b41d2SMark Murray void 2303a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 2304511b41d2SMark Murray { 2305511b41d2SMark Murray u_short port, host_port; 2306511b41d2SMark Murray char *hostname; 2307511b41d2SMark Murray 2308511b41d2SMark Murray /* Get arguments from the packet. */ 2309511b41d2SMark Murray port = packet_get_int(); 2310511b41d2SMark Murray hostname = packet_get_string(NULL); 2311511b41d2SMark Murray host_port = packet_get_int(); 2312511b41d2SMark Murray 2313989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 2314511b41d2SMark Murray /* 2315511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 2316511b41d2SMark Murray * privileged port. 2317511b41d2SMark Murray */ 2318511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 2319221552e4SDag-Erling Smørgrav packet_disconnect( 2320221552e4SDag-Erling Smørgrav "Requested forwarding of port %d but user is not root.", 2321511b41d2SMark Murray port); 2322221552e4SDag-Erling Smørgrav if (host_port == 0) 2323221552e4SDag-Erling Smørgrav packet_disconnect("Dynamic forwarding denied."); 2324989dd127SDag-Erling Smørgrav #endif 2325221552e4SDag-Erling Smørgrav 2326ca3176e7SBrian Feldman /* Initiate forwarding */ 2327af12a3e7SDag-Erling Smørgrav channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); 2328511b41d2SMark Murray 2329511b41d2SMark Murray /* Free the argument string. */ 2330511b41d2SMark Murray xfree(hostname); 2331511b41d2SMark Murray } 2332511b41d2SMark Murray 2333ca3176e7SBrian Feldman /* 2334ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 2335ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 2336ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 2337ca3176e7SBrian Feldman */ 2338ca3176e7SBrian Feldman void 2339af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 2340ca3176e7SBrian Feldman { 2341ca3176e7SBrian Feldman if (num_permitted_opens == 0) 2342ca3176e7SBrian Feldman all_opens_permitted = 1; 2343ca3176e7SBrian Feldman } 2344ca3176e7SBrian Feldman 2345ca3176e7SBrian Feldman void 2346ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 2347ca3176e7SBrian Feldman { 2348ca3176e7SBrian Feldman if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2349ca3176e7SBrian Feldman fatal("channel_request_remote_forwarding: too many forwards"); 2350ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 2351ca3176e7SBrian Feldman 2352ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 2353ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 2354ca3176e7SBrian Feldman num_permitted_opens++; 2355ca3176e7SBrian Feldman 2356ca3176e7SBrian Feldman all_opens_permitted = 0; 2357ca3176e7SBrian Feldman } 2358ca3176e7SBrian Feldman 2359ca3176e7SBrian Feldman void 2360ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 2361ca3176e7SBrian Feldman { 2362ca3176e7SBrian Feldman int i; 2363ca3176e7SBrian Feldman 2364ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2365ca3176e7SBrian Feldman xfree(permitted_opens[i].host_to_connect); 2366ca3176e7SBrian Feldman num_permitted_opens = 0; 2367ca3176e7SBrian Feldman 2368ca3176e7SBrian Feldman } 2369ca3176e7SBrian Feldman 2370ca3176e7SBrian Feldman 2371ca3176e7SBrian Feldman /* return socket to remote host, port */ 2372af12a3e7SDag-Erling Smørgrav static int 2373ca3176e7SBrian Feldman connect_to(const char *host, u_short port) 2374511b41d2SMark Murray { 2375511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2376511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2377511b41d2SMark Murray int gaierr; 2378a04a10f8SKris Kennaway int sock = -1; 2379511b41d2SMark Murray 2380511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2381511b41d2SMark Murray hints.ai_family = IPv4or6; 2382511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2383ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", port); 2384511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 2385ca3176e7SBrian Feldman error("connect_to %.100s: unknown host (%s)", host, 2386ca3176e7SBrian Feldman gai_strerror(gaierr)); 2387a04a10f8SKris Kennaway return -1; 2388511b41d2SMark Murray } 2389511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2390511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2391511b41d2SMark Murray continue; 2392511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2393511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2394ca3176e7SBrian Feldman error("connect_to: getnameinfo failed"); 2395511b41d2SMark Murray continue; 2396511b41d2SMark Murray } 2397221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2398511b41d2SMark Murray if (sock < 0) { 2399e73e9afaSDag-Erling Smørgrav if (ai->ai_next == NULL) 2400511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2401e73e9afaSDag-Erling Smørgrav else 2402e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 2403511b41d2SMark Murray continue; 2404511b41d2SMark Murray } 2405ca3176e7SBrian Feldman if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) 2406ca3176e7SBrian Feldman fatal("connect_to: F_SETFL: %s", strerror(errno)); 2407ca3176e7SBrian Feldman if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2408ca3176e7SBrian Feldman errno != EINPROGRESS) { 2409ca3176e7SBrian Feldman error("connect_to %.100s port %s: %.100s", ntop, strport, 2410511b41d2SMark Murray strerror(errno)); 2411511b41d2SMark Murray close(sock); 2412511b41d2SMark Murray continue; /* fail -- try next */ 2413511b41d2SMark Murray } 2414511b41d2SMark Murray break; /* success */ 2415511b41d2SMark Murray 2416511b41d2SMark Murray } 2417511b41d2SMark Murray freeaddrinfo(aitop); 2418511b41d2SMark Murray if (!ai) { 2419ca3176e7SBrian Feldman error("connect_to %.100s port %d: failed.", host, port); 2420a04a10f8SKris Kennaway return -1; 2421a04a10f8SKris Kennaway } 2422a04a10f8SKris Kennaway /* success */ 2423af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2424a04a10f8SKris Kennaway return sock; 2425a04a10f8SKris Kennaway } 2426ca3176e7SBrian Feldman 2427ca3176e7SBrian Feldman int 2428af12a3e7SDag-Erling Smørgrav channel_connect_by_listen_address(u_short listen_port) 2429ca3176e7SBrian Feldman { 2430ca3176e7SBrian Feldman int i; 2431ca3176e7SBrian Feldman 2432ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2433ca3176e7SBrian Feldman if (permitted_opens[i].listen_port == listen_port) 2434ca3176e7SBrian Feldman return connect_to( 2435ca3176e7SBrian Feldman permitted_opens[i].host_to_connect, 2436ca3176e7SBrian Feldman permitted_opens[i].port_to_connect); 2437ca3176e7SBrian Feldman error("WARNING: Server requests forwarding for unknown listen_port %d", 2438ca3176e7SBrian Feldman listen_port); 2439ca3176e7SBrian Feldman return -1; 2440ca3176e7SBrian Feldman } 2441ca3176e7SBrian Feldman 2442ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 2443ca3176e7SBrian Feldman int 2444ca3176e7SBrian Feldman channel_connect_to(const char *host, u_short port) 2445ca3176e7SBrian Feldman { 2446ca3176e7SBrian Feldman int i, permit; 2447ca3176e7SBrian Feldman 2448ca3176e7SBrian Feldman permit = all_opens_permitted; 2449ca3176e7SBrian Feldman if (!permit) { 2450ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 2451ca3176e7SBrian Feldman if (permitted_opens[i].port_to_connect == port && 2452ca3176e7SBrian Feldman strcmp(permitted_opens[i].host_to_connect, host) == 0) 2453ca3176e7SBrian Feldman permit = 1; 2454ca3176e7SBrian Feldman 2455ca3176e7SBrian Feldman } 2456ca3176e7SBrian Feldman if (!permit) { 2457221552e4SDag-Erling Smørgrav logit("Received request to connect to host %.100s port %d, " 2458ca3176e7SBrian Feldman "but the request was denied.", host, port); 2459ca3176e7SBrian Feldman return -1; 2460ca3176e7SBrian Feldman } 2461ca3176e7SBrian Feldman return connect_to(host, port); 2462ca3176e7SBrian Feldman } 2463ca3176e7SBrian Feldman 2464af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 2465511b41d2SMark Murray 2466511b41d2SMark Murray /* 2467511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 2468a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 2469a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 2470511b41d2SMark Murray */ 2471af12a3e7SDag-Erling Smørgrav int 2472af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2473a82e551fSDag-Erling Smørgrav int single_connection, u_int *display_numberp) 2474511b41d2SMark Murray { 2475af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 2476511b41d2SMark Murray int display_number, sock; 2477511b41d2SMark Murray u_short port; 2478511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2479511b41d2SMark Murray char strport[NI_MAXSERV]; 2480511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 2481511b41d2SMark Murray 2482511b41d2SMark Murray for (display_number = x11_display_offset; 2483511b41d2SMark Murray display_number < MAX_DISPLAYS; 2484511b41d2SMark Murray display_number++) { 2485511b41d2SMark Murray port = 6000 + display_number; 2486511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2487511b41d2SMark Murray hints.ai_family = IPv4or6; 2488af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 2489511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2490511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2491511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2492511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 2493af12a3e7SDag-Erling Smørgrav return -1; 2494511b41d2SMark Murray } 2495511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2496511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2497511b41d2SMark Murray continue; 2498221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 2499221552e4SDag-Erling Smørgrav ai->ai_protocol); 2500511b41d2SMark Murray if (sock < 0) { 2501989dd127SDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 2502511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2503af12a3e7SDag-Erling Smørgrav return -1; 2504989dd127SDag-Erling Smørgrav } else { 2505989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 2506989dd127SDag-Erling Smørgrav ai->ai_family); 2507989dd127SDag-Erling Smørgrav continue; 2508511b41d2SMark Murray } 2509989dd127SDag-Erling Smørgrav } 2510989dd127SDag-Erling Smørgrav #ifdef IPV6_V6ONLY 2511989dd127SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 2512989dd127SDag-Erling Smørgrav int on = 1; 2513989dd127SDag-Erling Smørgrav if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 2514989dd127SDag-Erling Smørgrav error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 2515989dd127SDag-Erling Smørgrav } 2516989dd127SDag-Erling Smørgrav #endif 2517511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2518221552e4SDag-Erling Smørgrav debug2("bind port %d: %.100s", port, strerror(errno)); 2519511b41d2SMark Murray close(sock); 2520989dd127SDag-Erling Smørgrav 2521989dd127SDag-Erling Smørgrav if (ai->ai_next) 2522989dd127SDag-Erling Smørgrav continue; 2523989dd127SDag-Erling Smørgrav 2524511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2525511b41d2SMark Murray close(socks[n]); 2526511b41d2SMark Murray } 2527511b41d2SMark Murray num_socks = 0; 2528511b41d2SMark Murray break; 2529511b41d2SMark Murray } 2530511b41d2SMark Murray socks[num_socks++] = sock; 2531989dd127SDag-Erling Smørgrav #ifndef DONT_TRY_OTHER_AF 2532511b41d2SMark Murray if (num_socks == NUM_SOCKS) 2533511b41d2SMark Murray break; 2534989dd127SDag-Erling Smørgrav #else 2535989dd127SDag-Erling Smørgrav if (x11_use_localhost) { 2536989dd127SDag-Erling Smørgrav if (num_socks == NUM_SOCKS) 2537989dd127SDag-Erling Smørgrav break; 2538989dd127SDag-Erling Smørgrav } else { 2539989dd127SDag-Erling Smørgrav break; 2540989dd127SDag-Erling Smørgrav } 2541989dd127SDag-Erling Smørgrav #endif 2542511b41d2SMark Murray } 2543ca3176e7SBrian Feldman freeaddrinfo(aitop); 2544511b41d2SMark Murray if (num_socks > 0) 2545511b41d2SMark Murray break; 2546511b41d2SMark Murray } 2547511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 2548511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 2549af12a3e7SDag-Erling Smørgrav return -1; 2550511b41d2SMark Murray } 2551511b41d2SMark Murray /* Start listening for connections on the socket. */ 2552511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2553511b41d2SMark Murray sock = socks[n]; 2554511b41d2SMark Murray if (listen(sock, 5) < 0) { 2555511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2556511b41d2SMark Murray close(sock); 2557af12a3e7SDag-Erling Smørgrav return -1; 2558511b41d2SMark Murray } 2559511b41d2SMark Murray } 2560511b41d2SMark Murray 2561511b41d2SMark Murray /* Allocate a channel for each socket. */ 2562511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2563511b41d2SMark Murray sock = socks[n]; 2564af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 2565a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 2566a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 2567221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 2568af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 2569511b41d2SMark Murray } 2570511b41d2SMark Murray 2571af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 2572a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 2573a82e551fSDag-Erling Smørgrav return (0); 2574511b41d2SMark Murray } 2575511b41d2SMark Murray 2576af12a3e7SDag-Erling Smørgrav static int 2577ca3176e7SBrian Feldman connect_local_xsocket(u_int dnr) 2578511b41d2SMark Murray { 2579511b41d2SMark Murray int sock; 2580511b41d2SMark Murray struct sockaddr_un addr; 2581511b41d2SMark Murray 2582511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2583511b41d2SMark Murray if (sock < 0) 2584511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2585511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 2586511b41d2SMark Murray addr.sun_family = AF_UNIX; 2587af12a3e7SDag-Erling Smørgrav snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 2588511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 2589511b41d2SMark Murray return sock; 2590511b41d2SMark Murray close(sock); 2591511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 2592511b41d2SMark Murray return -1; 2593511b41d2SMark Murray } 2594511b41d2SMark Murray 2595a04a10f8SKris Kennaway int 2596a04a10f8SKris Kennaway x11_connect_display(void) 2597511b41d2SMark Murray { 2598a04a10f8SKris Kennaway int display_number, sock = 0; 2599511b41d2SMark Murray const char *display; 2600a04a10f8SKris Kennaway char buf[1024], *cp; 2601511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2602511b41d2SMark Murray char strport[NI_MAXSERV]; 2603511b41d2SMark Murray int gaierr; 2604511b41d2SMark Murray 2605511b41d2SMark Murray /* Try to open a socket for the local X server. */ 2606511b41d2SMark Murray display = getenv("DISPLAY"); 2607511b41d2SMark Murray if (!display) { 2608511b41d2SMark Murray error("DISPLAY not set."); 2609a04a10f8SKris Kennaway return -1; 2610511b41d2SMark Murray } 2611511b41d2SMark Murray /* 2612511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 2613511b41d2SMark Murray * connection to the real X server. 2614511b41d2SMark Murray */ 2615511b41d2SMark Murray 2616511b41d2SMark Murray /* 2617511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 2618511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 2619511b41d2SMark Murray */ 2620511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 2621511b41d2SMark Murray display[0] == ':') { 2622511b41d2SMark Murray /* Connect to the unix domain socket. */ 2623511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 2624511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2625511b41d2SMark Murray display); 2626a04a10f8SKris Kennaway return -1; 2627511b41d2SMark Murray } 2628511b41d2SMark Murray /* Create a socket. */ 2629511b41d2SMark Murray sock = connect_local_xsocket(display_number); 2630511b41d2SMark Murray if (sock < 0) 2631a04a10f8SKris Kennaway return -1; 2632511b41d2SMark Murray 2633511b41d2SMark Murray /* OK, we now have a connection to the display. */ 2634a04a10f8SKris Kennaway return sock; 2635511b41d2SMark Murray } 2636511b41d2SMark Murray /* 2637511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 2638511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 2639511b41d2SMark Murray */ 2640af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 2641511b41d2SMark Murray cp = strchr(buf, ':'); 2642511b41d2SMark Murray if (!cp) { 2643511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 2644a04a10f8SKris Kennaway return -1; 2645511b41d2SMark Murray } 2646511b41d2SMark Murray *cp = 0; 2647511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 2648511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 2649511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2650511b41d2SMark Murray display); 2651a04a10f8SKris Kennaway return -1; 2652511b41d2SMark Murray } 2653511b41d2SMark Murray 2654511b41d2SMark Murray /* Look up the host address */ 2655511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2656511b41d2SMark Murray hints.ai_family = IPv4or6; 2657511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2658511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 2659511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 2660511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 2661a04a10f8SKris Kennaway return -1; 2662511b41d2SMark Murray } 2663511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2664511b41d2SMark Murray /* Create a socket. */ 2665221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2666511b41d2SMark Murray if (sock < 0) { 2667221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 2668511b41d2SMark Murray continue; 2669511b41d2SMark Murray } 2670511b41d2SMark Murray /* Connect it to the display. */ 2671511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2672221552e4SDag-Erling Smørgrav debug2("connect %.100s port %d: %.100s", buf, 2673a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 2674511b41d2SMark Murray close(sock); 2675511b41d2SMark Murray continue; 2676511b41d2SMark Murray } 2677511b41d2SMark Murray /* Success */ 2678511b41d2SMark Murray break; 2679a04a10f8SKris Kennaway } 2680511b41d2SMark Murray freeaddrinfo(aitop); 2681511b41d2SMark Murray if (!ai) { 2682511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 2683511b41d2SMark Murray strerror(errno)); 2684a04a10f8SKris Kennaway return -1; 2685511b41d2SMark Murray } 2686af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2687a04a10f8SKris Kennaway return sock; 2688a04a10f8SKris Kennaway } 2689511b41d2SMark Murray 2690a04a10f8SKris Kennaway /* 2691a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 2692a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 2693a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 2694a04a10f8SKris Kennaway */ 2695a04a10f8SKris Kennaway 2696a04a10f8SKris Kennaway void 2697af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 2698a04a10f8SKris Kennaway { 2699af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2700af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 2701a04a10f8SKris Kennaway char *remote_host; 2702a04a10f8SKris Kennaway 2703a04a10f8SKris Kennaway debug("Received X11 open request."); 2704af12a3e7SDag-Erling Smørgrav 2705af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2706af12a3e7SDag-Erling Smørgrav 2707af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2708af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 2709af12a3e7SDag-Erling Smørgrav } else { 2710af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 2711af12a3e7SDag-Erling Smørgrav } 2712af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2713a04a10f8SKris Kennaway 2714a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 2715a04a10f8SKris Kennaway sock = x11_connect_display(); 2716af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2717af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 2718af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 2719af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 2720af12a3e7SDag-Erling Smørgrav remote_host, 1); 2721af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2722af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2723af12a3e7SDag-Erling Smørgrav } 2724221552e4SDag-Erling Smørgrav xfree(remote_host); 2725af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2726a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 2727a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2728af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2729a04a10f8SKris Kennaway } else { 2730511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2731511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2732af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2733af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 2734a04a10f8SKris Kennaway } 2735af12a3e7SDag-Erling Smørgrav packet_send(); 2736511b41d2SMark Murray } 2737511b41d2SMark Murray 27385b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 27395b9b2fafSBrian Feldman void 2740af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 27415b9b2fafSBrian Feldman { 27425b9b2fafSBrian Feldman int rchan = packet_get_int(); 2743f388f5efSDag-Erling Smørgrav 27445b9b2fafSBrian Feldman switch (type) { 27455b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 27465b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 27475b9b2fafSBrian Feldman break; 27485b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 27495b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 27505b9b2fafSBrian Feldman break; 27515b9b2fafSBrian Feldman default: 2752af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 27535b9b2fafSBrian Feldman break; 27545b9b2fafSBrian Feldman } 27555b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 27565b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 27575b9b2fafSBrian Feldman packet_put_int(rchan); 27585b9b2fafSBrian Feldman packet_send(); 27595b9b2fafSBrian Feldman } 27605b9b2fafSBrian Feldman 2761511b41d2SMark Murray /* 2762511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2763511b41d2SMark Murray * data, and enables authentication spoofing. 2764af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 2765511b41d2SMark Murray */ 2766511b41d2SMark Murray void 2767a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2768a04a10f8SKris Kennaway const char *proto, const char *data) 2769511b41d2SMark Murray { 2770ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 2771ca3176e7SBrian Feldman u_int i, value, len; 2772511b41d2SMark Murray char *new_data; 2773511b41d2SMark Murray int screen_number; 2774511b41d2SMark Murray const char *cp; 2775511b41d2SMark Murray u_int32_t rand = 0; 2776511b41d2SMark Murray 2777511b41d2SMark Murray cp = getenv("DISPLAY"); 2778511b41d2SMark Murray if (cp) 2779511b41d2SMark Murray cp = strchr(cp, ':'); 2780511b41d2SMark Murray if (cp) 2781511b41d2SMark Murray cp = strchr(cp, '.'); 2782511b41d2SMark Murray if (cp) 2783511b41d2SMark Murray screen_number = atoi(cp + 1); 2784511b41d2SMark Murray else 2785511b41d2SMark Murray screen_number = 0; 2786511b41d2SMark Murray 2787511b41d2SMark Murray /* Save protocol name. */ 2788511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2789511b41d2SMark Murray 2790511b41d2SMark Murray /* 2791511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2792511b41d2SMark Murray * same length. 2793511b41d2SMark Murray */ 2794511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2795511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2796511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2797511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2798511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2799511b41d2SMark Murray if (i % 4 == 0) 2800511b41d2SMark Murray rand = arc4random(); 2801511b41d2SMark Murray x11_saved_data[i] = value; 2802511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2803511b41d2SMark Murray rand >>= 8; 2804511b41d2SMark Murray } 2805511b41d2SMark Murray x11_saved_data_len = data_len; 2806511b41d2SMark Murray x11_fake_data_len = data_len; 2807511b41d2SMark Murray 2808511b41d2SMark Murray /* Convert the fake data into hex. */ 2809ca3176e7SBrian Feldman len = 2 * data_len + 1; 2810ca3176e7SBrian Feldman new_data = xmalloc(len); 2811511b41d2SMark Murray for (i = 0; i < data_len; i++) 2812ca3176e7SBrian Feldman snprintf(new_data + 2 * i, len - 2 * i, 2813ca3176e7SBrian Feldman "%02x", (u_char) x11_fake_data[i]); 2814511b41d2SMark Murray 2815511b41d2SMark Murray /* Send the request packet. */ 2816a04a10f8SKris Kennaway if (compat20) { 2817a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2818a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2819a04a10f8SKris Kennaway } else { 2820511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2821a04a10f8SKris Kennaway } 2822a04a10f8SKris Kennaway packet_put_cstring(proto); 2823a04a10f8SKris Kennaway packet_put_cstring(new_data); 2824511b41d2SMark Murray packet_put_int(screen_number); 2825511b41d2SMark Murray packet_send(); 2826511b41d2SMark Murray packet_write_wait(); 2827511b41d2SMark Murray xfree(new_data); 2828511b41d2SMark Murray } 2829511b41d2SMark Murray 2830af12a3e7SDag-Erling Smørgrav 2831af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 2832af12a3e7SDag-Erling Smørgrav 2833511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2834511b41d2SMark Murray 2835511b41d2SMark Murray void 2836af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 2837511b41d2SMark Murray { 2838511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2839511b41d2SMark Murray packet_send(); 2840511b41d2SMark Murray packet_write_wait(); 2841511b41d2SMark Murray } 2842511b41d2SMark Murray 2843511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2844511b41d2SMark Murray 2845511b41d2SMark Murray void 2846af12a3e7SDag-Erling Smørgrav auth_input_open_request(int type, u_int32_t seq, void *ctxt) 2847511b41d2SMark Murray { 2848af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2849af12a3e7SDag-Erling Smørgrav int remote_id, sock; 2850a04a10f8SKris Kennaway 2851511b41d2SMark Murray /* Read the remote channel number from the message. */ 2852af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2853af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2854511b41d2SMark Murray 2855511b41d2SMark Murray /* 2856511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2857511b41d2SMark Murray * get forwarded). 2858511b41d2SMark Murray */ 2859511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2860511b41d2SMark Murray 2861511b41d2SMark Murray /* 2862511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2863511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2864511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2865511b41d2SMark Murray * agent. 2866511b41d2SMark Murray */ 2867af12a3e7SDag-Erling Smørgrav if (sock >= 0) { 2868af12a3e7SDag-Erling Smørgrav c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 2869221552e4SDag-Erling Smørgrav -1, 0, 0, 0, "authentication agent connection", 1); 2870af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2871af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 2872af12a3e7SDag-Erling Smørgrav } 2873af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2874511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2875af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2876af12a3e7SDag-Erling Smørgrav } else { 2877511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2878af12a3e7SDag-Erling Smørgrav debug("Forwarding authentication connection."); 2879511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2880af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2881a04a10f8SKris Kennaway packet_put_int(c->self); 2882a04a10f8SKris Kennaway } 2883a04a10f8SKris Kennaway packet_send(); 2884a04a10f8SKris Kennaway } 2885