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" 42b74df5b2SDag-Erling Smørgrav RCSID("$OpenBSD: channels.c,v 1.232 2006/01/30 12:22:22 reyk 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 */ 7121e764dfSDag-Erling Smørgrav static u_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 112d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 113d4ecd108SDag-Erling Smørgrav static char *x11_saved_display = NULL; 114d4ecd108SDag-Erling Smørgrav 115af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 116af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 117af12a3e7SDag-Erling Smørgrav 118af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 119af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 120af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 121af12a3e7SDag-Erling Smørgrav 122af12a3e7SDag-Erling Smørgrav /* 123af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 124af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 125af12a3e7SDag-Erling Smørgrav */ 126af12a3e7SDag-Erling Smørgrav static char *x11_fake_data = NULL; 127af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 128af12a3e7SDag-Erling Smørgrav 129af12a3e7SDag-Erling Smørgrav 130af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 131af12a3e7SDag-Erling Smørgrav 132af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 133af12a3e7SDag-Erling Smørgrav 134ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 135989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 136ca3176e7SBrian Feldman 137af12a3e7SDag-Erling Smørgrav /* helper */ 138af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 139ca3176e7SBrian Feldman 140af12a3e7SDag-Erling Smørgrav /* -- channel core */ 141a04a10f8SKris Kennaway 142a04a10f8SKris Kennaway Channel * 143b74df5b2SDag-Erling Smørgrav channel_by_id(int id) 144a04a10f8SKris Kennaway { 145a04a10f8SKris Kennaway Channel *c; 146af12a3e7SDag-Erling Smørgrav 14721e764dfSDag-Erling Smørgrav if (id < 0 || (u_int)id >= channels_alloc) { 148b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id", id); 149a04a10f8SKris Kennaway return NULL; 150a04a10f8SKris Kennaway } 151af12a3e7SDag-Erling Smørgrav c = channels[id]; 152af12a3e7SDag-Erling Smørgrav if (c == NULL) { 153b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id: channel free", id); 154a04a10f8SKris Kennaway return NULL; 155a04a10f8SKris Kennaway } 156a04a10f8SKris Kennaway return c; 157a04a10f8SKris Kennaway } 158a04a10f8SKris Kennaway 159a04a10f8SKris Kennaway /* 160b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 161b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 162b74df5b2SDag-Erling Smørgrav */ 163b74df5b2SDag-Erling Smørgrav Channel * 164b74df5b2SDag-Erling Smørgrav channel_lookup(int id) 165b74df5b2SDag-Erling Smørgrav { 166b74df5b2SDag-Erling Smørgrav Channel *c; 167b74df5b2SDag-Erling Smørgrav 168b74df5b2SDag-Erling Smørgrav if ((c = channel_by_id(id)) == NULL) 169b74df5b2SDag-Erling Smørgrav return (NULL); 170b74df5b2SDag-Erling Smørgrav 171b74df5b2SDag-Erling Smørgrav switch(c->type) { 172b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 173b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 174b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 175b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 176b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 177b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 178b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 179b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 180b74df5b2SDag-Erling Smørgrav return (c); 181b74df5b2SDag-Erling Smørgrav break; 182b74df5b2SDag-Erling Smørgrav } 183b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 184b74df5b2SDag-Erling Smørgrav return (NULL); 185b74df5b2SDag-Erling Smørgrav } 186b74df5b2SDag-Erling Smørgrav 187b74df5b2SDag-Erling Smørgrav /* 188a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 189a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 190a04a10f8SKris Kennaway */ 191a04a10f8SKris Kennaway 192af12a3e7SDag-Erling Smørgrav static void 1935b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1945b9b2fafSBrian Feldman int extusage, int nonblock) 195a04a10f8SKris Kennaway { 196a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 197ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, rfd); 198ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, wfd); 199ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, efd); 200ca3176e7SBrian Feldman 201a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 202a04a10f8SKris Kennaway 203a04a10f8SKris Kennaway c->rfd = rfd; 204a04a10f8SKris Kennaway c->wfd = wfd; 205a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 20621e764dfSDag-Erling Smørgrav c->ctl_fd = -1; /* XXX: set elsewhere */ 207a04a10f8SKris Kennaway c->efd = efd; 208a04a10f8SKris Kennaway c->extended_usage = extusage; 2095b9b2fafSBrian Feldman 210e0fbb1d2SBrian Feldman /* XXX ugly hack: nonblock is only set by the server */ 211e0fbb1d2SBrian Feldman if (nonblock && isatty(c->rfd)) { 212221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 213e0fbb1d2SBrian Feldman c->isatty = 1; 214e0fbb1d2SBrian Feldman if (!isatty(c->wfd)) { 215ca3176e7SBrian Feldman error("channel %d: wfd %d is not a tty?", 216e0fbb1d2SBrian Feldman c->self, c->wfd); 217e0fbb1d2SBrian Feldman } 218e0fbb1d2SBrian Feldman } else { 219e0fbb1d2SBrian Feldman c->isatty = 0; 220e0fbb1d2SBrian Feldman } 221f388f5efSDag-Erling Smørgrav c->wfd_isatty = isatty(c->wfd); 222e0fbb1d2SBrian Feldman 2235b9b2fafSBrian Feldman /* enable nonblocking mode */ 2245b9b2fafSBrian Feldman if (nonblock) { 225a04a10f8SKris Kennaway if (rfd != -1) 226a04a10f8SKris Kennaway set_nonblock(rfd); 227a04a10f8SKris Kennaway if (wfd != -1) 228a04a10f8SKris Kennaway set_nonblock(wfd); 229a04a10f8SKris Kennaway if (efd != -1) 230a04a10f8SKris Kennaway set_nonblock(efd); 231a04a10f8SKris Kennaway } 2325b9b2fafSBrian Feldman } 233a04a10f8SKris Kennaway 234511b41d2SMark Murray /* 235511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 236511b41d2SMark Murray * remote_name to be freed. 237511b41d2SMark Murray */ 238511b41d2SMark Murray 239af12a3e7SDag-Erling Smørgrav Channel * 240a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 241a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 242511b41d2SMark Murray { 24321e764dfSDag-Erling Smørgrav int found; 24421e764dfSDag-Erling Smørgrav u_int i; 245511b41d2SMark Murray Channel *c; 246511b41d2SMark Murray 247511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 248511b41d2SMark Murray if (channels_alloc == 0) { 249511b41d2SMark Murray channels_alloc = 10; 250af12a3e7SDag-Erling Smørgrav channels = xmalloc(channels_alloc * sizeof(Channel *)); 251511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 252af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 253511b41d2SMark Murray } 254511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 255511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 256af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 257511b41d2SMark Murray /* Found a free slot. */ 25821e764dfSDag-Erling Smørgrav found = (int)i; 259511b41d2SMark Murray break; 260511b41d2SMark Murray } 26121e764dfSDag-Erling Smørgrav if (found < 0) { 262511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 263511b41d2SMark Murray found = channels_alloc; 264a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 265a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 266a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 2673533e7e5SJosef Karthauser channels = xrealloc(channels, 2683533e7e5SJosef Karthauser (channels_alloc + 10) * sizeof(Channel *)); 2693533e7e5SJosef Karthauser channels_alloc += 10; 2705b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 271511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 272af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 273511b41d2SMark Murray } 274af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 275af12a3e7SDag-Erling Smørgrav c = channels[found] = xmalloc(sizeof(Channel)); 276af12a3e7SDag-Erling Smørgrav memset(c, 0, sizeof(Channel)); 277511b41d2SMark Murray buffer_init(&c->input); 278511b41d2SMark Murray buffer_init(&c->output); 279a04a10f8SKris Kennaway buffer_init(&c->extended); 280af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 281af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 282af12a3e7SDag-Erling Smørgrav c->flags = 0; 2835b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 284511b41d2SMark Murray c->self = found; 285511b41d2SMark Murray c->type = type; 286a04a10f8SKris Kennaway c->ctype = ctype; 287a04a10f8SKris Kennaway c->local_window = window; 288a04a10f8SKris Kennaway c->local_window_max = window; 289a04a10f8SKris Kennaway c->local_consumed = 0; 290a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 291511b41d2SMark Murray c->remote_id = -1; 292221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 293a04a10f8SKris Kennaway c->remote_window = 0; 294a04a10f8SKris Kennaway c->remote_maxpacket = 0; 295af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 296af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 297af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 298b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 299af12a3e7SDag-Erling Smørgrav c->confirm = NULL; 30021e764dfSDag-Erling Smørgrav c->confirm_ctx = NULL; 301b66f2d16SKris Kennaway c->input_filter = NULL; 302b74df5b2SDag-Erling Smørgrav c->output_filter = NULL; 303511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 304af12a3e7SDag-Erling Smørgrav return c; 305a04a10f8SKris Kennaway } 306511b41d2SMark Murray 307af12a3e7SDag-Erling Smørgrav static int 308af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 309af12a3e7SDag-Erling Smørgrav { 31021e764dfSDag-Erling Smørgrav u_int i; 31121e764dfSDag-Erling Smørgrav int max = 0; 312af12a3e7SDag-Erling Smørgrav Channel *c; 313af12a3e7SDag-Erling Smørgrav 314af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 315af12a3e7SDag-Erling Smørgrav c = channels[i]; 316af12a3e7SDag-Erling Smørgrav if (c != NULL) { 317af12a3e7SDag-Erling Smørgrav max = MAX(max, c->rfd); 318af12a3e7SDag-Erling Smørgrav max = MAX(max, c->wfd); 319af12a3e7SDag-Erling Smørgrav max = MAX(max, c->efd); 320af12a3e7SDag-Erling Smørgrav } 321af12a3e7SDag-Erling Smørgrav } 322af12a3e7SDag-Erling Smørgrav return max; 323af12a3e7SDag-Erling Smørgrav } 324af12a3e7SDag-Erling Smørgrav 325af12a3e7SDag-Erling Smørgrav int 326af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 327af12a3e7SDag-Erling Smørgrav { 328af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 329af12a3e7SDag-Erling Smørgrav 330af12a3e7SDag-Erling Smørgrav if (fd != -1) { 331af12a3e7SDag-Erling Smørgrav ret = close(fd); 332af12a3e7SDag-Erling Smørgrav *fdp = -1; 333af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 334af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 335af12a3e7SDag-Erling Smørgrav } 336af12a3e7SDag-Erling Smørgrav return ret; 337af12a3e7SDag-Erling Smørgrav } 338a04a10f8SKris Kennaway 339a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 340511b41d2SMark Murray 341af12a3e7SDag-Erling Smørgrav static void 342a04a10f8SKris Kennaway channel_close_fds(Channel *c) 343511b41d2SMark Murray { 34421e764dfSDag-Erling Smørgrav debug3("channel %d: close_fds r %d w %d e %d c %d", 34521e764dfSDag-Erling Smørgrav c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); 346af12a3e7SDag-Erling Smørgrav 347af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 34821e764dfSDag-Erling Smørgrav channel_close_fd(&c->ctl_fd); 349af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 350af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 351af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 352a04a10f8SKris Kennaway } 353511b41d2SMark Murray 354a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 355a04a10f8SKris Kennaway 356a04a10f8SKris Kennaway void 357af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 358a04a10f8SKris Kennaway { 359af12a3e7SDag-Erling Smørgrav char *s; 36021e764dfSDag-Erling Smørgrav u_int i, n; 361ca3176e7SBrian Feldman 362af12a3e7SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) 363af12a3e7SDag-Erling Smørgrav if (channels[i]) 364af12a3e7SDag-Erling Smørgrav n++; 36521e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 366af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 367af12a3e7SDag-Erling Smørgrav 368af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 369221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 370ca3176e7SBrian Feldman xfree(s); 371ca3176e7SBrian Feldman 372a04a10f8SKris Kennaway if (c->sock != -1) 373a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 37421e764dfSDag-Erling Smørgrav if (c->ctl_fd != -1) 37521e764dfSDag-Erling Smørgrav shutdown(c->ctl_fd, SHUT_RDWR); 376a04a10f8SKris Kennaway channel_close_fds(c); 377a04a10f8SKris Kennaway buffer_free(&c->input); 378a04a10f8SKris Kennaway buffer_free(&c->output); 379a04a10f8SKris Kennaway buffer_free(&c->extended); 380a04a10f8SKris Kennaway if (c->remote_name) { 381a04a10f8SKris Kennaway xfree(c->remote_name); 382a04a10f8SKris Kennaway c->remote_name = NULL; 383511b41d2SMark Murray } 384af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 385af12a3e7SDag-Erling Smørgrav xfree(c); 386af12a3e7SDag-Erling Smørgrav } 387af12a3e7SDag-Erling Smørgrav 388af12a3e7SDag-Erling Smørgrav void 389af12a3e7SDag-Erling Smørgrav channel_free_all(void) 390af12a3e7SDag-Erling Smørgrav { 39121e764dfSDag-Erling Smørgrav u_int i; 392af12a3e7SDag-Erling Smørgrav 393af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 394af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 395af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 396af12a3e7SDag-Erling Smørgrav } 397af12a3e7SDag-Erling Smørgrav 398af12a3e7SDag-Erling Smørgrav /* 399af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 400af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 401af12a3e7SDag-Erling Smørgrav */ 402af12a3e7SDag-Erling Smørgrav 403af12a3e7SDag-Erling Smørgrav void 404af12a3e7SDag-Erling Smørgrav channel_close_all(void) 405af12a3e7SDag-Erling Smørgrav { 40621e764dfSDag-Erling Smørgrav u_int i; 407af12a3e7SDag-Erling Smørgrav 408af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 409af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 410af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 411af12a3e7SDag-Erling Smørgrav } 412af12a3e7SDag-Erling Smørgrav 413af12a3e7SDag-Erling Smørgrav /* 414af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 415af12a3e7SDag-Erling Smørgrav */ 416af12a3e7SDag-Erling Smørgrav 417af12a3e7SDag-Erling Smørgrav void 418af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 419af12a3e7SDag-Erling Smørgrav { 42021e764dfSDag-Erling Smørgrav u_int i; 421af12a3e7SDag-Erling Smørgrav Channel *c; 422af12a3e7SDag-Erling Smørgrav 423af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 424af12a3e7SDag-Erling Smørgrav c = channels[i]; 425af12a3e7SDag-Erling Smørgrav if (c != NULL) { 426af12a3e7SDag-Erling Smørgrav switch (c->type) { 427af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 428af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 429af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 430af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 431af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 432af12a3e7SDag-Erling Smørgrav channel_free(c); 433af12a3e7SDag-Erling Smørgrav break; 434af12a3e7SDag-Erling Smørgrav } 435af12a3e7SDag-Erling Smørgrav } 436af12a3e7SDag-Erling Smørgrav } 437af12a3e7SDag-Erling Smørgrav } 438af12a3e7SDag-Erling Smørgrav 439af12a3e7SDag-Erling Smørgrav /* 440af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 441af12a3e7SDag-Erling Smørgrav * more channel is overfull. 442af12a3e7SDag-Erling Smørgrav */ 443af12a3e7SDag-Erling Smørgrav 444af12a3e7SDag-Erling Smørgrav int 445af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 446af12a3e7SDag-Erling Smørgrav { 447af12a3e7SDag-Erling Smørgrav u_int i; 448af12a3e7SDag-Erling Smørgrav Channel *c; 449af12a3e7SDag-Erling Smørgrav 450af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 451af12a3e7SDag-Erling Smørgrav c = channels[i]; 452af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 453af12a3e7SDag-Erling Smørgrav #if 0 454af12a3e7SDag-Erling Smørgrav if (!compat20 && 455af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 456e73e9afaSDag-Erling Smørgrav debug2("channel %d: big input buffer %d", 457af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 458af12a3e7SDag-Erling Smørgrav return 0; 459af12a3e7SDag-Erling Smørgrav } 460af12a3e7SDag-Erling Smørgrav #endif 461af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 462221552e4SDag-Erling Smørgrav debug2("channel %d: big output buffer %u > %u", 463af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 464af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 465af12a3e7SDag-Erling Smørgrav return 0; 466af12a3e7SDag-Erling Smørgrav } 467af12a3e7SDag-Erling Smørgrav } 468af12a3e7SDag-Erling Smørgrav } 469af12a3e7SDag-Erling Smørgrav return 1; 470af12a3e7SDag-Erling Smørgrav } 471af12a3e7SDag-Erling Smørgrav 472af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 473af12a3e7SDag-Erling Smørgrav 474af12a3e7SDag-Erling Smørgrav int 475af12a3e7SDag-Erling Smørgrav channel_still_open(void) 476af12a3e7SDag-Erling Smørgrav { 47721e764dfSDag-Erling Smørgrav u_int i; 478af12a3e7SDag-Erling Smørgrav Channel *c; 479af12a3e7SDag-Erling Smørgrav 480af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 481af12a3e7SDag-Erling Smørgrav c = channels[i]; 482af12a3e7SDag-Erling Smørgrav if (c == NULL) 483af12a3e7SDag-Erling Smørgrav continue; 484af12a3e7SDag-Erling Smørgrav switch (c->type) { 485af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 486af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 487af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 488af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 489af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 490af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 491af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 492af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 493af12a3e7SDag-Erling Smørgrav continue; 494af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 495af12a3e7SDag-Erling Smørgrav if (!compat20) 496af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 497af12a3e7SDag-Erling Smørgrav continue; 498af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 499af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 500af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 501af12a3e7SDag-Erling Smørgrav return 1; 502af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 503af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 504af12a3e7SDag-Erling Smørgrav if (!compat13) 505af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 506af12a3e7SDag-Erling Smørgrav return 1; 507af12a3e7SDag-Erling Smørgrav default: 508af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 509af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 510af12a3e7SDag-Erling Smørgrav } 511af12a3e7SDag-Erling Smørgrav } 512af12a3e7SDag-Erling Smørgrav return 0; 513af12a3e7SDag-Erling Smørgrav } 514af12a3e7SDag-Erling Smørgrav 515af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 516af12a3e7SDag-Erling Smørgrav 517af12a3e7SDag-Erling Smørgrav int 518af12a3e7SDag-Erling Smørgrav channel_find_open(void) 519af12a3e7SDag-Erling Smørgrav { 52021e764dfSDag-Erling Smørgrav u_int i; 521af12a3e7SDag-Erling Smørgrav Channel *c; 522af12a3e7SDag-Erling Smørgrav 523af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 524af12a3e7SDag-Erling Smørgrav c = channels[i]; 52521e764dfSDag-Erling Smørgrav if (c == NULL || c->remote_id < 0) 526af12a3e7SDag-Erling Smørgrav continue; 527af12a3e7SDag-Erling Smørgrav switch (c->type) { 528af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 529af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 530af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 531af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 532af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 533af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 534af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 535af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 536af12a3e7SDag-Erling Smørgrav continue; 537af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 538af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 539af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 540af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 541af12a3e7SDag-Erling Smørgrav return i; 542af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 543af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 544af12a3e7SDag-Erling Smørgrav if (!compat13) 545af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 546af12a3e7SDag-Erling Smørgrav return i; 547af12a3e7SDag-Erling Smørgrav default: 548af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 549af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 550af12a3e7SDag-Erling Smørgrav } 551af12a3e7SDag-Erling Smørgrav } 552af12a3e7SDag-Erling Smørgrav return -1; 553af12a3e7SDag-Erling Smørgrav } 554af12a3e7SDag-Erling Smørgrav 555af12a3e7SDag-Erling Smørgrav 556af12a3e7SDag-Erling Smørgrav /* 557af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 558af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 559af12a3e7SDag-Erling Smørgrav * newlines. 560af12a3e7SDag-Erling Smørgrav */ 561af12a3e7SDag-Erling Smørgrav 562af12a3e7SDag-Erling Smørgrav char * 563af12a3e7SDag-Erling Smørgrav channel_open_message(void) 564af12a3e7SDag-Erling Smørgrav { 565af12a3e7SDag-Erling Smørgrav Buffer buffer; 566af12a3e7SDag-Erling Smørgrav Channel *c; 567af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 56821e764dfSDag-Erling Smørgrav u_int i; 569af12a3e7SDag-Erling Smørgrav 570af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 571af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 572af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 573af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 574af12a3e7SDag-Erling Smørgrav c = channels[i]; 575af12a3e7SDag-Erling Smørgrav if (c == NULL) 576af12a3e7SDag-Erling Smørgrav continue; 577af12a3e7SDag-Erling Smørgrav switch (c->type) { 578af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 579af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 580af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 581af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 582af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 583af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 584af12a3e7SDag-Erling Smørgrav continue; 585af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 586af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 587af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 588af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 589af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 590af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 591af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 592af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 59321e764dfSDag-Erling Smørgrav snprintf(buf, sizeof buf, 59421e764dfSDag-Erling Smørgrav " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", 595af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 596af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 597af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 598af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 59921e764dfSDag-Erling Smørgrav c->rfd, c->wfd, c->ctl_fd); 600af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 601af12a3e7SDag-Erling Smørgrav continue; 602af12a3e7SDag-Erling Smørgrav default: 603af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 604af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 605af12a3e7SDag-Erling Smørgrav } 606af12a3e7SDag-Erling Smørgrav } 607af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 608af12a3e7SDag-Erling Smørgrav cp = xstrdup(buffer_ptr(&buffer)); 609af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 610af12a3e7SDag-Erling Smørgrav return cp; 611af12a3e7SDag-Erling Smørgrav } 612af12a3e7SDag-Erling Smørgrav 613af12a3e7SDag-Erling Smørgrav void 614af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 615af12a3e7SDag-Erling Smørgrav { 616af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 617f388f5efSDag-Erling Smørgrav 618af12a3e7SDag-Erling Smørgrav if (c == NULL) { 619221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 620af12a3e7SDag-Erling Smørgrav return; 621af12a3e7SDag-Erling Smørgrav } 622e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 623af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 624af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 625af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 626af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 627af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 628af12a3e7SDag-Erling Smørgrav packet_send(); 629af12a3e7SDag-Erling Smørgrav } 630af12a3e7SDag-Erling Smørgrav 631af12a3e7SDag-Erling Smørgrav void 632e73e9afaSDag-Erling Smørgrav channel_request_start(int id, char *service, int wantconfirm) 633af12a3e7SDag-Erling Smørgrav { 634e73e9afaSDag-Erling Smørgrav Channel *c = channel_lookup(id); 635f388f5efSDag-Erling Smørgrav 636af12a3e7SDag-Erling Smørgrav if (c == NULL) { 637221552e4SDag-Erling Smørgrav logit("channel_request_start: %d: unknown channel id", id); 638af12a3e7SDag-Erling Smørgrav return; 639af12a3e7SDag-Erling Smørgrav } 64021e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 641af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 642af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 643af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 644af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 645af12a3e7SDag-Erling Smørgrav } 646af12a3e7SDag-Erling Smørgrav void 64721e764dfSDag-Erling Smørgrav channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) 648af12a3e7SDag-Erling Smørgrav { 649af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 650f388f5efSDag-Erling Smørgrav 651af12a3e7SDag-Erling Smørgrav if (c == NULL) { 652221552e4SDag-Erling Smørgrav logit("channel_register_comfirm: %d: bad id", id); 653af12a3e7SDag-Erling Smørgrav return; 654af12a3e7SDag-Erling Smørgrav } 655af12a3e7SDag-Erling Smørgrav c->confirm = fn; 65621e764dfSDag-Erling Smørgrav c->confirm_ctx = ctx; 657af12a3e7SDag-Erling Smørgrav } 658af12a3e7SDag-Erling Smørgrav void 659b74df5b2SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 660af12a3e7SDag-Erling Smørgrav { 661b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 662f388f5efSDag-Erling Smørgrav 663af12a3e7SDag-Erling Smørgrav if (c == NULL) { 664221552e4SDag-Erling Smørgrav logit("channel_register_cleanup: %d: bad id", id); 665af12a3e7SDag-Erling Smørgrav return; 666af12a3e7SDag-Erling Smørgrav } 667af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 668b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 669af12a3e7SDag-Erling Smørgrav } 670af12a3e7SDag-Erling Smørgrav void 671af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 672af12a3e7SDag-Erling Smørgrav { 673b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 674f388f5efSDag-Erling Smørgrav 675af12a3e7SDag-Erling Smørgrav if (c == NULL) { 676221552e4SDag-Erling Smørgrav logit("channel_cancel_cleanup: %d: bad id", id); 677af12a3e7SDag-Erling Smørgrav return; 678af12a3e7SDag-Erling Smørgrav } 679af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 680b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 681af12a3e7SDag-Erling Smørgrav } 682af12a3e7SDag-Erling Smørgrav void 683b74df5b2SDag-Erling Smørgrav channel_register_filter(int id, channel_infilter_fn *ifn, 684b74df5b2SDag-Erling Smørgrav channel_outfilter_fn *ofn) 685af12a3e7SDag-Erling Smørgrav { 686af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 687f388f5efSDag-Erling Smørgrav 688af12a3e7SDag-Erling Smørgrav if (c == NULL) { 689221552e4SDag-Erling Smørgrav logit("channel_register_filter: %d: bad id", id); 690af12a3e7SDag-Erling Smørgrav return; 691af12a3e7SDag-Erling Smørgrav } 692b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 693b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 694af12a3e7SDag-Erling Smørgrav } 695af12a3e7SDag-Erling Smørgrav 696af12a3e7SDag-Erling Smørgrav void 697af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 698af12a3e7SDag-Erling Smørgrav int extusage, int nonblock, u_int window_max) 699af12a3e7SDag-Erling Smørgrav { 700af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 701f388f5efSDag-Erling Smørgrav 702af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 703af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 704af12a3e7SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 705af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 706af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 707af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 708af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 709af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 710af12a3e7SDag-Erling Smørgrav packet_send(); 711511b41d2SMark Murray } 712511b41d2SMark Murray 713511b41d2SMark Murray /* 714a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 715a04a10f8SKris Kennaway * channels in the select bitmasks. 716511b41d2SMark Murray */ 717a04a10f8SKris Kennaway /* 718a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 719a04a10f8SKris Kennaway * have events pending. 720a04a10f8SKris Kennaway */ 721a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 722a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 723a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 724511b41d2SMark Murray 725af12a3e7SDag-Erling Smørgrav static void 726a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 727511b41d2SMark Murray { 728a04a10f8SKris Kennaway FD_SET(c->sock, readset); 729a04a10f8SKris Kennaway } 730a04a10f8SKris Kennaway 731af12a3e7SDag-Erling Smørgrav static void 732ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) 733ca3176e7SBrian Feldman { 734ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 735ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 736ca3176e7SBrian Feldman } 737ca3176e7SBrian Feldman 738af12a3e7SDag-Erling Smørgrav static void 739a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 740a04a10f8SKris Kennaway { 741a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 742a04a10f8SKris Kennaway FD_SET(c->sock, readset); 743a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 744a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 745a04a10f8SKris Kennaway } 746a04a10f8SKris Kennaway 747af12a3e7SDag-Erling Smørgrav static void 748af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) 749a04a10f8SKris Kennaway { 750af12a3e7SDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 751a04a10f8SKris Kennaway 752aa49c926SDag-Erling Smørgrav /* check buffer limits */ 753aa49c926SDag-Erling Smørgrav limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF)); 754aa49c926SDag-Erling Smørgrav 755a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 756af12a3e7SDag-Erling Smørgrav limit > 0 && 757af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) < limit) 758a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 759a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 760a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 761a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 762a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 763a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 76480628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 76580628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 76680628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 76780628bacSDag-Erling Smørgrav else 768a04a10f8SKris Kennaway chan_obuf_empty(c); 769a04a10f8SKris Kennaway } 770a04a10f8SKris Kennaway } 771a04a10f8SKris Kennaway /** XXX check close conditions, too */ 772af12a3e7SDag-Erling Smørgrav if (compat20 && c->efd != -1) { 773a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 774a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 775a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 77680628bacSDag-Erling Smørgrav else if (!(c->flags & CHAN_EOF_SENT) && 77780628bacSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ && 778a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 779a04a10f8SKris Kennaway FD_SET(c->efd, readset); 780a04a10f8SKris Kennaway } 78121e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 78221e764dfSDag-Erling Smørgrav if (compat20 && c->ctl_fd != -1 && 78321e764dfSDag-Erling Smørgrav c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) 78421e764dfSDag-Erling Smørgrav FD_SET(c->ctl_fd, readset); 785a04a10f8SKris Kennaway } 786a04a10f8SKris Kennaway 787af12a3e7SDag-Erling Smørgrav static void 788a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 789a04a10f8SKris Kennaway { 790a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 791a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 792a04a10f8SKris Kennaway packet_put_int(c->remote_id); 793a04a10f8SKris Kennaway packet_send(); 794a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 795221552e4SDag-Erling Smørgrav debug2("channel %d: closing after input drain.", c->self); 796a04a10f8SKris Kennaway } 797a04a10f8SKris Kennaway } 798a04a10f8SKris Kennaway 799af12a3e7SDag-Erling Smørgrav static void 800a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 801a04a10f8SKris Kennaway { 802a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 803af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 804a04a10f8SKris Kennaway else 805a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 806a04a10f8SKris Kennaway } 807a04a10f8SKris Kennaway 808a04a10f8SKris Kennaway /* 809a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 810a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 811a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 812a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 813a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 814a04a10f8SKris Kennaway * XXX All this happens at the client side. 815af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 816a04a10f8SKris Kennaway */ 817af12a3e7SDag-Erling Smørgrav static int 818af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 819a04a10f8SKris Kennaway { 820ca3176e7SBrian Feldman u_char *ucp; 821ca3176e7SBrian Feldman u_int proto_len, data_len; 822511b41d2SMark Murray 823511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 824af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 825a04a10f8SKris Kennaway return 0; 826511b41d2SMark Murray 827511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 828af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 829511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 830511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 831511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 832511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 833511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 834511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 835511b41d2SMark Murray } else { 836221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 837511b41d2SMark Murray ucp[0]); 838a04a10f8SKris Kennaway return -1; 839511b41d2SMark Murray } 840511b41d2SMark Murray 841511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 842af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 843511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 844a04a10f8SKris Kennaway return 0; 845511b41d2SMark Murray 846511b41d2SMark Murray /* Check if authentication protocol matches. */ 847511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 848511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 849221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 850a04a10f8SKris Kennaway return -1; 851511b41d2SMark Murray } 852511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 853511b41d2SMark Murray if (data_len != x11_fake_data_len || 854511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 855511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 856221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 857a04a10f8SKris Kennaway return -1; 858511b41d2SMark Murray } 859511b41d2SMark Murray /* Check fake data length */ 860511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 861511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 862511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 863a04a10f8SKris Kennaway return -1; 864511b41d2SMark Murray } 865511b41d2SMark Murray /* 866511b41d2SMark Murray * Received authentication protocol and data match 867511b41d2SMark Murray * our fake data. Substitute the fake data with real 868511b41d2SMark Murray * data. 869511b41d2SMark Murray */ 870511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 871511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 872a04a10f8SKris Kennaway return 1; 873a04a10f8SKris Kennaway } 874511b41d2SMark Murray 875af12a3e7SDag-Erling Smørgrav static void 876a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 877a04a10f8SKris Kennaway { 878af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 879f388f5efSDag-Erling Smørgrav 880a04a10f8SKris Kennaway if (ret == 1) { 881511b41d2SMark Murray /* Start normal processing for the channel. */ 882a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 883a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 884a04a10f8SKris Kennaway } else if (ret == -1) { 885511b41d2SMark Murray /* 886511b41d2SMark Murray * We have received an X11 connection that has bad 887511b41d2SMark Murray * authentication information. 888511b41d2SMark Murray */ 889221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 890a04a10f8SKris Kennaway buffer_clear(&c->input); 891a04a10f8SKris Kennaway buffer_clear(&c->output); 892af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 893a04a10f8SKris Kennaway c->sock = -1; 894a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 895511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 896a04a10f8SKris Kennaway packet_put_int(c->remote_id); 897511b41d2SMark Murray packet_send(); 898511b41d2SMark Murray } 899511b41d2SMark Murray } 900511b41d2SMark Murray 901af12a3e7SDag-Erling Smørgrav static void 902a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 903a04a10f8SKris Kennaway { 904af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 905af12a3e7SDag-Erling Smørgrav 906af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 907af12a3e7SDag-Erling Smørgrav 908a04a10f8SKris Kennaway if (ret == 1) { 909a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 910af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 911a04a10f8SKris Kennaway } else if (ret == -1) { 912221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 913221552e4SDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 914af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 915af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 916af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 917af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 918af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 919af12a3e7SDag-Erling Smørgrav if (compat20) 920a04a10f8SKris Kennaway chan_write_failed(c); 921af12a3e7SDag-Erling Smørgrav else 922af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 923221552e4SDag-Erling Smørgrav debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 924a04a10f8SKris Kennaway } 925a04a10f8SKris Kennaway } 926a04a10f8SKris Kennaway 927ca3176e7SBrian Feldman /* try to decode a socks4 header */ 928af12a3e7SDag-Erling Smørgrav static int 929ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) 930ca3176e7SBrian Feldman { 931f388f5efSDag-Erling Smørgrav char *p, *host; 932d4ecd108SDag-Erling Smørgrav u_int len, have, i, found; 933ca3176e7SBrian Feldman char username[256]; 934ca3176e7SBrian Feldman struct { 935ca3176e7SBrian Feldman u_int8_t version; 936ca3176e7SBrian Feldman u_int8_t command; 937ca3176e7SBrian Feldman u_int16_t dest_port; 938ca3176e7SBrian Feldman struct in_addr dest_addr; 939ca3176e7SBrian Feldman } s4_req, s4_rsp; 940ca3176e7SBrian Feldman 941ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 942ca3176e7SBrian Feldman 943ca3176e7SBrian Feldman have = buffer_len(&c->input); 944ca3176e7SBrian Feldman len = sizeof(s4_req); 945ca3176e7SBrian Feldman if (have < len) 946ca3176e7SBrian Feldman return 0; 947ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 948ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 949ca3176e7SBrian Feldman if (p[i] == '\0') { 950ca3176e7SBrian Feldman found = 1; 951ca3176e7SBrian Feldman break; 952ca3176e7SBrian Feldman } 953ca3176e7SBrian Feldman if (i > 1024) { 954ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 955ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 956ca3176e7SBrian Feldman c->self); 957ca3176e7SBrian Feldman return -1; 958ca3176e7SBrian Feldman } 959ca3176e7SBrian Feldman } 960ca3176e7SBrian Feldman if (!found) 961ca3176e7SBrian Feldman return 0; 962ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 963ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 964ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 965ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 966ca3176e7SBrian Feldman have = buffer_len(&c->input); 967ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 968ca3176e7SBrian Feldman len = strlen(p); 969ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 970ca3176e7SBrian Feldman if (len > have) 971ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 972ca3176e7SBrian Feldman c->self, len, have); 973ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 974ca3176e7SBrian Feldman buffer_consume(&c->input, len); 975ca3176e7SBrian Feldman buffer_consume(&c->input, 1); /* trailing '\0' */ 976ca3176e7SBrian Feldman 977ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 978ca3176e7SBrian Feldman strlcpy(c->path, host, sizeof(c->path)); 979ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 980ca3176e7SBrian Feldman 981221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 982ca3176e7SBrian Feldman c->self, host, c->host_port, s4_req.command); 983ca3176e7SBrian Feldman 984ca3176e7SBrian Feldman if (s4_req.command != 1) { 985ca3176e7SBrian Feldman debug("channel %d: cannot handle: socks4 cn %d", 986ca3176e7SBrian Feldman c->self, s4_req.command); 987ca3176e7SBrian Feldman return -1; 988ca3176e7SBrian Feldman } 989ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 990ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 991ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 992ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 993ca3176e7SBrian Feldman buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); 994ca3176e7SBrian Feldman return 1; 995ca3176e7SBrian Feldman } 996ca3176e7SBrian Feldman 997221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 998221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 999221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1000221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1001221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1002221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1003221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1004221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1005221552e4SDag-Erling Smørgrav 1006221552e4SDag-Erling Smørgrav static int 1007221552e4SDag-Erling Smørgrav channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) 1008221552e4SDag-Erling Smørgrav { 1009221552e4SDag-Erling Smørgrav struct { 1010221552e4SDag-Erling Smørgrav u_int8_t version; 1011221552e4SDag-Erling Smørgrav u_int8_t command; 1012221552e4SDag-Erling Smørgrav u_int8_t reserved; 1013221552e4SDag-Erling Smørgrav u_int8_t atyp; 1014221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1015221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1016221552e4SDag-Erling Smørgrav u_char *p, dest_addr[255+1]; 1017d4ecd108SDag-Erling Smørgrav u_int have, i, found, nmethods, addrlen, af; 1018221552e4SDag-Erling Smørgrav 1019221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 1020221552e4SDag-Erling Smørgrav p = buffer_ptr(&c->input); 1021221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1022221552e4SDag-Erling Smørgrav return -1; 1023221552e4SDag-Erling Smørgrav have = buffer_len(&c->input); 1024221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1025221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1026221552e4SDag-Erling Smørgrav if (have < 2) 1027221552e4SDag-Erling Smørgrav return 0; 1028221552e4SDag-Erling Smørgrav nmethods = p[1]; 1029221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1030221552e4SDag-Erling Smørgrav return 0; 1031221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1032221552e4SDag-Erling Smørgrav for (found = 0, i = 2 ; i < nmethods + 2; i++) { 1033221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH ) { 1034221552e4SDag-Erling Smørgrav found = 1; 1035221552e4SDag-Erling Smørgrav break; 1036221552e4SDag-Erling Smørgrav } 1037221552e4SDag-Erling Smørgrav } 1038221552e4SDag-Erling Smørgrav if (!found) { 1039221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1040221552e4SDag-Erling Smørgrav c->self); 1041221552e4SDag-Erling Smørgrav return -1; 1042221552e4SDag-Erling Smørgrav } 1043221552e4SDag-Erling Smørgrav buffer_consume(&c->input, nmethods + 2); 1044221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, 0x05); /* version */ 1045221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1046221552e4SDag-Erling Smørgrav FD_SET(c->sock, writeset); 1047221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1048221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1049221552e4SDag-Erling Smørgrav return 0; /* need more */ 1050221552e4SDag-Erling Smørgrav } 1051221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1052221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1053221552e4SDag-Erling Smørgrav return 0; /* need more */ 1054221552e4SDag-Erling Smørgrav memcpy((char *)&s5_req, p, sizeof(s5_req)); 1055221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1056221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1057221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1058221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1059221552e4SDag-Erling Smørgrav return -1; 1060221552e4SDag-Erling Smørgrav } 1061221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1062221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1063221552e4SDag-Erling Smørgrav addrlen = 4; 1064221552e4SDag-Erling Smørgrav af = AF_INET; 1065221552e4SDag-Erling Smørgrav break; 1066221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1067221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1068221552e4SDag-Erling Smørgrav af = -1; 1069221552e4SDag-Erling Smørgrav break; 1070221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1071221552e4SDag-Erling Smørgrav addrlen = 16; 1072221552e4SDag-Erling Smørgrav af = AF_INET6; 1073221552e4SDag-Erling Smørgrav break; 1074221552e4SDag-Erling Smørgrav default: 1075221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1076221552e4SDag-Erling Smørgrav return -1; 1077221552e4SDag-Erling Smørgrav } 1078221552e4SDag-Erling Smørgrav if (have < 4 + addrlen + 2) 1079221552e4SDag-Erling Smørgrav return 0; 1080221552e4SDag-Erling Smørgrav buffer_consume(&c->input, sizeof(s5_req)); 1081221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1082221552e4SDag-Erling Smørgrav buffer_consume(&c->input, 1); /* host string length */ 1083221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_addr, addrlen); 1084221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_port, 2); 1085221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1086221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 108721e764dfSDag-Erling Smørgrav strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); 1088221552e4SDag-Erling Smørgrav else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) 1089221552e4SDag-Erling Smørgrav return -1; 1090221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1091221552e4SDag-Erling Smørgrav 1092221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1093221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1094221552e4SDag-Erling Smørgrav 1095221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1096221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1097221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1098221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1099221552e4SDag-Erling Smørgrav ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; 1100221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1101221552e4SDag-Erling Smørgrav 1102221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); 1103221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); 1104221552e4SDag-Erling Smørgrav buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); 1105221552e4SDag-Erling Smørgrav return 1; 1106221552e4SDag-Erling Smørgrav } 1107221552e4SDag-Erling Smørgrav 1108ca3176e7SBrian Feldman /* dynamic port forwarding */ 1109af12a3e7SDag-Erling Smørgrav static void 1110ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) 1111ca3176e7SBrian Feldman { 1112ca3176e7SBrian Feldman u_char *p; 1113d4ecd108SDag-Erling Smørgrav u_int have; 1114d4ecd108SDag-Erling Smørgrav int ret; 1115ca3176e7SBrian Feldman 1116ca3176e7SBrian Feldman have = buffer_len(&c->input); 1117af12a3e7SDag-Erling Smørgrav c->delayed = 0; 1118ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 1119ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 1120ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1121221552e4SDag-Erling Smørgrav if (have < 3) { 1122ca3176e7SBrian Feldman /* need more */ 1123ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1124ca3176e7SBrian Feldman return; 1125ca3176e7SBrian Feldman } 1126ca3176e7SBrian Feldman /* try to guess the protocol */ 1127ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1128ca3176e7SBrian Feldman switch (p[0]) { 1129ca3176e7SBrian Feldman case 0x04: 1130ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 1131ca3176e7SBrian Feldman break; 1132221552e4SDag-Erling Smørgrav case 0x05: 1133221552e4SDag-Erling Smørgrav ret = channel_decode_socks5(c, readset, writeset); 1134221552e4SDag-Erling Smørgrav break; 1135ca3176e7SBrian Feldman default: 1136ca3176e7SBrian Feldman ret = -1; 1137ca3176e7SBrian Feldman break; 1138ca3176e7SBrian Feldman } 1139ca3176e7SBrian Feldman if (ret < 0) { 1140af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1141ca3176e7SBrian Feldman } else if (ret == 0) { 1142ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1143ca3176e7SBrian Feldman /* need more */ 1144ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1145ca3176e7SBrian Feldman } else { 1146ca3176e7SBrian Feldman /* switch to the next state */ 1147ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 1148ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 1149ca3176e7SBrian Feldman } 1150ca3176e7SBrian Feldman } 1151ca3176e7SBrian Feldman 1152a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1153af12a3e7SDag-Erling Smørgrav static void 1154a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 1155511b41d2SMark Murray { 1156af12a3e7SDag-Erling Smørgrav Channel *nc; 1157511b41d2SMark Murray struct sockaddr addr; 1158af12a3e7SDag-Erling Smørgrav int newsock; 1159511b41d2SMark Murray socklen_t addrlen; 1160ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1161a04a10f8SKris Kennaway int remote_port; 1162511b41d2SMark Murray 1163a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1164511b41d2SMark Murray debug("X11 connection requested."); 1165511b41d2SMark Murray addrlen = sizeof(addr); 1166a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1167af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1168221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 1169af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1170af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1171af12a3e7SDag-Erling Smørgrav } 1172511b41d2SMark Murray if (newsock < 0) { 1173511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1174a04a10f8SKris Kennaway return; 1175511b41d2SMark Murray } 1176af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1177ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1178a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1179511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1180ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1181a04a10f8SKris Kennaway 1182af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1183a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1184221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 1185a04a10f8SKris Kennaway if (compat20) { 1186a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1187a04a10f8SKris Kennaway packet_put_cstring("x11"); 1188af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1189af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1190af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1191ca3176e7SBrian Feldman /* originator ipaddr and port */ 1192ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1193a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1194221552e4SDag-Erling Smørgrav debug2("ssh2 x11 bug compat mode"); 1195a04a10f8SKris Kennaway } else { 1196a04a10f8SKris Kennaway packet_put_int(remote_port); 1197a04a10f8SKris Kennaway } 1198a04a10f8SKris Kennaway packet_send(); 1199a04a10f8SKris Kennaway } else { 1200511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1201af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1202af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1203af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1204af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1205511b41d2SMark Murray packet_send(); 1206511b41d2SMark Murray } 1207ca3176e7SBrian Feldman xfree(remote_ipaddr); 1208a04a10f8SKris Kennaway } 1209a04a10f8SKris Kennaway } 1210511b41d2SMark Murray 1211af12a3e7SDag-Erling Smørgrav static void 1212ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1213ca3176e7SBrian Feldman { 1214ca3176e7SBrian Feldman int direct; 1215ca3176e7SBrian Feldman char buf[1024]; 1216ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1217d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 1218ca3176e7SBrian Feldman 1219ca3176e7SBrian Feldman direct = (strcmp(rtype, "direct-tcpip") == 0); 1220ca3176e7SBrian Feldman 1221ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1222ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1223ca3176e7SBrian Feldman "connect from %.200s port %d", 1224ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1225ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1226ca3176e7SBrian Feldman 1227ca3176e7SBrian Feldman xfree(c->remote_name); 1228ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1229ca3176e7SBrian Feldman 1230ca3176e7SBrian Feldman if (compat20) { 1231ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1232ca3176e7SBrian Feldman packet_put_cstring(rtype); 1233ca3176e7SBrian Feldman packet_put_int(c->self); 1234ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1235ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1236ca3176e7SBrian Feldman if (direct) { 1237ca3176e7SBrian Feldman /* target host, port */ 1238ca3176e7SBrian Feldman packet_put_cstring(c->path); 1239ca3176e7SBrian Feldman packet_put_int(c->host_port); 1240ca3176e7SBrian Feldman } else { 1241ca3176e7SBrian Feldman /* listen address, port */ 1242ca3176e7SBrian Feldman packet_put_cstring(c->path); 1243ca3176e7SBrian Feldman packet_put_int(c->listening_port); 1244ca3176e7SBrian Feldman } 1245ca3176e7SBrian Feldman /* originator host and port */ 1246ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1247d4ecd108SDag-Erling Smørgrav packet_put_int((u_int)remote_port); 1248ca3176e7SBrian Feldman packet_send(); 1249ca3176e7SBrian Feldman } else { 1250ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1251ca3176e7SBrian Feldman packet_put_int(c->self); 1252ca3176e7SBrian Feldman packet_put_cstring(c->path); 1253ca3176e7SBrian Feldman packet_put_int(c->host_port); 1254af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1255af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1256ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1257ca3176e7SBrian Feldman packet_send(); 1258ca3176e7SBrian Feldman } 1259ca3176e7SBrian Feldman xfree(remote_ipaddr); 1260ca3176e7SBrian Feldman } 1261ca3176e7SBrian Feldman 1262b74df5b2SDag-Erling Smørgrav static void 1263b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(int fd) 1264b74df5b2SDag-Erling Smørgrav { 1265b74df5b2SDag-Erling Smørgrav int on = 1; 1266b74df5b2SDag-Erling Smørgrav 1267b74df5b2SDag-Erling Smørgrav /* 1268b74df5b2SDag-Erling Smørgrav * Set socket options. 1269b74df5b2SDag-Erling Smørgrav * Allow local port reuse in TIME_WAIT. 1270b74df5b2SDag-Erling Smørgrav */ 1271b74df5b2SDag-Erling Smørgrav if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1272b74df5b2SDag-Erling Smørgrav error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1273b74df5b2SDag-Erling Smørgrav } 1274b74df5b2SDag-Erling Smørgrav 1275511b41d2SMark Murray /* 1276a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1277511b41d2SMark Murray */ 1278af12a3e7SDag-Erling Smørgrav static void 1279a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 1280a04a10f8SKris Kennaway { 1281ca3176e7SBrian Feldman Channel *nc; 1282a04a10f8SKris Kennaway struct sockaddr addr; 1283af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1284a04a10f8SKris Kennaway socklen_t addrlen; 1285ca3176e7SBrian Feldman char *rtype; 1286a04a10f8SKris Kennaway 1287a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1288a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1289a04a10f8SKris Kennaway "to %.100s port %d requested.", 1290a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1291ca3176e7SBrian Feldman 1292af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1293af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1294af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1295af12a3e7SDag-Erling Smørgrav } else { 1296af12a3e7SDag-Erling Smørgrav if (c->host_port == 0) { 1297af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1298af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1299af12a3e7SDag-Erling Smørgrav } else { 1300af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1301af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1302af12a3e7SDag-Erling Smørgrav } 1303af12a3e7SDag-Erling Smørgrav } 1304ca3176e7SBrian Feldman 1305511b41d2SMark Murray addrlen = sizeof(addr); 1306a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1307511b41d2SMark Murray if (newsock < 0) { 1308511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1309a04a10f8SKris Kennaway return; 1310511b41d2SMark Murray } 1311af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1312221552e4SDag-Erling Smørgrav nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1313221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1314ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1315ca3176e7SBrian Feldman nc->host_port = c->host_port; 1316ca3176e7SBrian Feldman strlcpy(nc->path, c->path, sizeof(nc->path)); 1317ca3176e7SBrian Feldman 1318af12a3e7SDag-Erling Smørgrav if (nextstate == SSH_CHANNEL_DYNAMIC) { 1319af12a3e7SDag-Erling Smørgrav /* 1320af12a3e7SDag-Erling Smørgrav * do not call the channel_post handler until 1321af12a3e7SDag-Erling Smørgrav * this flag has been reset by a pre-handler. 1322af12a3e7SDag-Erling Smørgrav * otherwise the FD_ISSET calls might overflow 1323af12a3e7SDag-Erling Smørgrav */ 1324af12a3e7SDag-Erling Smørgrav nc->delayed = 1; 1325af12a3e7SDag-Erling Smørgrav } else { 1326ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1327a04a10f8SKris Kennaway } 1328a04a10f8SKris Kennaway } 1329af12a3e7SDag-Erling Smørgrav } 1330511b41d2SMark Murray 1331511b41d2SMark Murray /* 1332a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1333a04a10f8SKris Kennaway * clients. 1334511b41d2SMark Murray */ 1335af12a3e7SDag-Erling Smørgrav static void 1336a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 1337a04a10f8SKris Kennaway { 1338af12a3e7SDag-Erling Smørgrav Channel *nc; 1339af12a3e7SDag-Erling Smørgrav int newsock; 1340a04a10f8SKris Kennaway struct sockaddr addr; 1341a04a10f8SKris Kennaway socklen_t addrlen; 1342a04a10f8SKris Kennaway 1343a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1344511b41d2SMark Murray addrlen = sizeof(addr); 1345a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 1346511b41d2SMark Murray if (newsock < 0) { 1347511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 1348a04a10f8SKris Kennaway return; 1349511b41d2SMark Murray } 1350af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1351ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1352ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1353221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 1354ca3176e7SBrian Feldman if (compat20) { 1355ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1356ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1357af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1358ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1359ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1360ca3176e7SBrian Feldman } else { 1361511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1362af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1363ca3176e7SBrian Feldman } 1364511b41d2SMark Murray packet_send(); 1365511b41d2SMark Murray } 1366a04a10f8SKris Kennaway } 1367511b41d2SMark Murray 1368af12a3e7SDag-Erling Smørgrav static void 1369ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) 1370ca3176e7SBrian Feldman { 1371ca3176e7SBrian Feldman int err = 0; 1372af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1373af12a3e7SDag-Erling Smørgrav 1374af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1375af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1376af12a3e7SDag-Erling Smørgrav err = errno; 1377af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1378af12a3e7SDag-Erling Smørgrav } 1379ca3176e7SBrian Feldman if (err == 0) { 1380af12a3e7SDag-Erling Smørgrav debug("channel %d: connected", c->self); 1381af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1382af12a3e7SDag-Erling Smørgrav if (compat20) { 1383af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1384af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1385af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1386af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1387af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1388af12a3e7SDag-Erling Smørgrav } else { 1389af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1390af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1391af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1392af12a3e7SDag-Erling Smørgrav } 1393ca3176e7SBrian Feldman } else { 1394ca3176e7SBrian Feldman debug("channel %d: not connected: %s", 1395ca3176e7SBrian Feldman c->self, strerror(err)); 1396af12a3e7SDag-Erling Smørgrav if (compat20) { 1397af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1398af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1399af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1400af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1401af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1402af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1403ca3176e7SBrian Feldman } 1404af12a3e7SDag-Erling Smørgrav } else { 1405af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1406af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1407ca3176e7SBrian Feldman } 1408af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1409af12a3e7SDag-Erling Smørgrav } 1410af12a3e7SDag-Erling Smørgrav packet_send(); 1411ca3176e7SBrian Feldman } 1412ca3176e7SBrian Feldman } 1413ca3176e7SBrian Feldman 1414af12a3e7SDag-Erling Smørgrav static int 1415a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 1416a04a10f8SKris Kennaway { 1417aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1418a04a10f8SKris Kennaway int len; 1419511b41d2SMark Murray 1420a04a10f8SKris Kennaway if (c->rfd != -1 && 1421a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 1422a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1423a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1424a04a10f8SKris Kennaway return 1; 14250c82706bSBrian Feldman if (len <= 0) { 1426221552e4SDag-Erling Smørgrav debug2("channel %d: read<=0 rfd %d len %d", 1427a04a10f8SKris Kennaway c->self, c->rfd, len); 1428ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1429221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1430af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1431ca3176e7SBrian Feldman return -1; 1432ca3176e7SBrian Feldman } else if (compat13) { 1433af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1434a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1435221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1436a04a10f8SKris Kennaway } else { 1437a04a10f8SKris Kennaway chan_read_failed(c); 1438a04a10f8SKris Kennaway } 1439a04a10f8SKris Kennaway return -1; 1440a04a10f8SKris Kennaway } 1441b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1442b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1443221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1444b66f2d16SKris Kennaway chan_read_failed(c); 1445b66f2d16SKris Kennaway } 1446b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1447b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->input, buf, len); 1448b66f2d16SKris Kennaway } else { 1449a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1450a04a10f8SKris Kennaway } 1451b66f2d16SKris Kennaway } 1452a04a10f8SKris Kennaway return 1; 1453a04a10f8SKris Kennaway } 1454af12a3e7SDag-Erling Smørgrav static int 1455a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 1456a04a10f8SKris Kennaway { 1457ca3176e7SBrian Feldman struct termios tio; 1458b74df5b2SDag-Erling Smørgrav u_char *data = NULL, *buf; 1459af12a3e7SDag-Erling Smørgrav u_int dlen; 1460a04a10f8SKris Kennaway int len; 1461a04a10f8SKris Kennaway 1462a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1463a04a10f8SKris Kennaway if (c->wfd != -1 && 1464a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1465a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1466b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 1467b74df5b2SDag-Erling Smørgrav if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1468b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1469b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1470b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1471b74df5b2SDag-Erling Smørgrav else 1472b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1473b74df5b2SDag-Erling Smørgrav return -1; 1474b74df5b2SDag-Erling Smørgrav } 1475b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1476b74df5b2SDag-Erling Smørgrav buf = data = buffer_get_string(&c->output, &dlen); 1477b74df5b2SDag-Erling Smørgrav } else { 1478b74df5b2SDag-Erling Smørgrav buf = data = buffer_ptr(&c->output); 1479af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1480b74df5b2SDag-Erling Smørgrav } 1481b74df5b2SDag-Erling Smørgrav 1482b74df5b2SDag-Erling Smørgrav if (c->datagram) { 1483b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 1484b74df5b2SDag-Erling Smørgrav c->local_consumed += dlen + 4; 1485b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1486b74df5b2SDag-Erling Smørgrav xfree(data); 1487b74df5b2SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1488b74df5b2SDag-Erling Smørgrav return 1; 1489b74df5b2SDag-Erling Smørgrav if (len <= 0) { 1490b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1491b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1492b74df5b2SDag-Erling Smørgrav else 1493b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1494b74df5b2SDag-Erling Smørgrav return -1; 1495b74df5b2SDag-Erling Smørgrav } 1496b74df5b2SDag-Erling Smørgrav return 1; 1497b74df5b2SDag-Erling Smørgrav } 1498f388f5efSDag-Erling Smørgrav #ifdef _AIX 1499f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 1500476cd3b2SDag-Erling Smørgrav if (compat20 && c->wfd_isatty) 1501476cd3b2SDag-Erling Smørgrav dlen = MIN(dlen, 8*1024); 1502f388f5efSDag-Erling Smørgrav #endif 1503b74df5b2SDag-Erling Smørgrav 1504b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1505a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1506a04a10f8SKris Kennaway return 1; 1507511b41d2SMark Murray if (len <= 0) { 1508ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1509221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1510af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1511ca3176e7SBrian Feldman return -1; 1512ca3176e7SBrian Feldman } else if (compat13) { 1513af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1514221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1515a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1516511b41d2SMark Murray } else { 1517a04a10f8SKris Kennaway chan_write_failed(c); 1518511b41d2SMark Murray } 1519a04a10f8SKris Kennaway return -1; 1520511b41d2SMark Murray } 1521b74df5b2SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 1522e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1523e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1524e0fbb1d2SBrian Feldman /* 1525e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1526ca3176e7SBrian Feldman * traffic analysis. We need to match the 1527ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1528b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 1529e0fbb1d2SBrian Feldman */ 1530ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1531e0fbb1d2SBrian Feldman packet_send(); 1532e0fbb1d2SBrian Feldman } 1533e0fbb1d2SBrian Feldman } 1534a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1535a04a10f8SKris Kennaway if (compat20 && len > 0) { 1536a04a10f8SKris Kennaway c->local_consumed += len; 1537511b41d2SMark Murray } 1538511b41d2SMark Murray } 1539a04a10f8SKris Kennaway return 1; 1540511b41d2SMark Murray } 1541af12a3e7SDag-Erling Smørgrav static int 1542a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 1543a04a10f8SKris Kennaway { 1544aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1545a04a10f8SKris Kennaway int len; 1546511b41d2SMark Murray 1547a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1548a04a10f8SKris Kennaway if (c->efd != -1) { 1549a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1550a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1551a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1552a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1553a04a10f8SKris Kennaway buffer_len(&c->extended)); 15545b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1555a04a10f8SKris Kennaway c->self, len, c->efd); 1556ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1557ca3176e7SBrian Feldman return 1; 1558ca3176e7SBrian Feldman if (len <= 0) { 1559ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1560ca3176e7SBrian Feldman c->self, c->efd); 1561af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1562ca3176e7SBrian Feldman } else { 1563a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1564a04a10f8SKris Kennaway c->local_consumed += len; 1565a04a10f8SKris Kennaway } 1566a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 1567a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 1568a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 15695b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1570a04a10f8SKris Kennaway c->self, len, c->efd); 1571ca3176e7SBrian Feldman if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1572ca3176e7SBrian Feldman return 1; 1573ca3176e7SBrian Feldman if (len <= 0) { 1574ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1575a04a10f8SKris Kennaway c->self, c->efd); 1576af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1577ca3176e7SBrian Feldman } else { 1578a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1579a04a10f8SKris Kennaway } 1580a04a10f8SKris Kennaway } 1581ca3176e7SBrian Feldman } 1582a04a10f8SKris Kennaway return 1; 1583a04a10f8SKris Kennaway } 1584af12a3e7SDag-Erling Smørgrav static int 158521e764dfSDag-Erling Smørgrav channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset) 158621e764dfSDag-Erling Smørgrav { 158721e764dfSDag-Erling Smørgrav char buf[16]; 158821e764dfSDag-Erling Smørgrav int len; 158921e764dfSDag-Erling Smørgrav 159021e764dfSDag-Erling Smørgrav /* Monitor control fd to detect if the slave client exits */ 159121e764dfSDag-Erling Smørgrav if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { 159221e764dfSDag-Erling Smørgrav len = read(c->ctl_fd, buf, sizeof(buf)); 159321e764dfSDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 159421e764dfSDag-Erling Smørgrav return 1; 159521e764dfSDag-Erling Smørgrav if (len <= 0) { 159621e764dfSDag-Erling Smørgrav debug2("channel %d: ctl read<=0", c->self); 159721e764dfSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) { 159821e764dfSDag-Erling Smørgrav debug2("channel %d: not open", c->self); 159921e764dfSDag-Erling Smørgrav chan_mark_dead(c); 160021e764dfSDag-Erling Smørgrav return -1; 160121e764dfSDag-Erling Smørgrav } else { 160221e764dfSDag-Erling Smørgrav chan_read_failed(c); 160321e764dfSDag-Erling Smørgrav chan_write_failed(c); 160421e764dfSDag-Erling Smørgrav } 160521e764dfSDag-Erling Smørgrav return -1; 160621e764dfSDag-Erling Smørgrav } else 160721e764dfSDag-Erling Smørgrav fatal("%s: unexpected data on ctl fd", __func__); 160821e764dfSDag-Erling Smørgrav } 160921e764dfSDag-Erling Smørgrav return 1; 161021e764dfSDag-Erling Smørgrav } 161121e764dfSDag-Erling Smørgrav static int 1612ca3176e7SBrian Feldman channel_check_window(Channel *c) 1613a04a10f8SKris Kennaway { 1614ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1615ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1616a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 1617a04a10f8SKris Kennaway c->local_consumed > 0) { 1618a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1619a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1620a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 1621a04a10f8SKris Kennaway packet_send(); 16225b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1623a04a10f8SKris Kennaway c->self, c->local_window, 1624a04a10f8SKris Kennaway c->local_consumed); 1625a04a10f8SKris Kennaway c->local_window += c->local_consumed; 1626a04a10f8SKris Kennaway c->local_consumed = 0; 1627a04a10f8SKris Kennaway } 1628a04a10f8SKris Kennaway return 1; 1629a04a10f8SKris Kennaway } 1630a04a10f8SKris Kennaway 1631af12a3e7SDag-Erling Smørgrav static void 1632af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) 1633a04a10f8SKris Kennaway { 1634af12a3e7SDag-Erling Smørgrav if (c->delayed) 1635af12a3e7SDag-Erling Smørgrav return; 1636a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1637a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1638af12a3e7SDag-Erling Smørgrav if (!compat20) 1639af12a3e7SDag-Erling Smørgrav return; 1640a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 164121e764dfSDag-Erling Smørgrav channel_handle_ctl(c, readset, writeset); 1642ca3176e7SBrian Feldman channel_check_window(c); 1643a04a10f8SKris Kennaway } 1644a04a10f8SKris Kennaway 1645af12a3e7SDag-Erling Smørgrav static void 1646a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 1647a04a10f8SKris Kennaway { 1648a04a10f8SKris Kennaway int len; 1649f388f5efSDag-Erling Smørgrav 1650511b41d2SMark Murray /* Send buffered output data to the socket. */ 1651a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 1652a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 1653a04a10f8SKris Kennaway buffer_len(&c->output)); 1654511b41d2SMark Murray if (len <= 0) 1655af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1656511b41d2SMark Murray else 1657a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1658511b41d2SMark Murray } 1659a04a10f8SKris Kennaway } 1660511b41d2SMark Murray 1661af12a3e7SDag-Erling Smørgrav static void 1662a04a10f8SKris Kennaway channel_handler_init_20(void) 1663a04a10f8SKris Kennaway { 1664af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1665a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1666a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1667ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 1668a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1669ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1670ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1671ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1672a04a10f8SKris Kennaway 1673af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1674a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1675ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 1676a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1677ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1678ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1679af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1680a04a10f8SKris Kennaway } 1681a04a10f8SKris Kennaway 1682af12a3e7SDag-Erling Smørgrav static void 1683a04a10f8SKris Kennaway channel_handler_init_13(void) 1684a04a10f8SKris Kennaway { 1685a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 1686a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 1687a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1688a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1689a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1690a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 1691a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 1692ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1693ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1694a04a10f8SKris Kennaway 1695af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1696a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1697a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1698a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1699a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 1700ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1701af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1702a04a10f8SKris Kennaway } 1703a04a10f8SKris Kennaway 1704af12a3e7SDag-Erling Smørgrav static void 1705a04a10f8SKris Kennaway channel_handler_init_15(void) 1706a04a10f8SKris Kennaway { 1707af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 1708a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 1709a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 1710a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 1711a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 1712ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 1713ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 1714a04a10f8SKris Kennaway 1715a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 1716a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 1717a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 1718af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 1719ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 1720af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 1721a04a10f8SKris Kennaway } 1722a04a10f8SKris Kennaway 1723af12a3e7SDag-Erling Smørgrav static void 1724a04a10f8SKris Kennaway channel_handler_init(void) 1725a04a10f8SKris Kennaway { 1726a04a10f8SKris Kennaway int i; 1727f388f5efSDag-Erling Smørgrav 1728a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 1729a04a10f8SKris Kennaway channel_pre[i] = NULL; 1730a04a10f8SKris Kennaway channel_post[i] = NULL; 1731a04a10f8SKris Kennaway } 1732a04a10f8SKris Kennaway if (compat20) 1733a04a10f8SKris Kennaway channel_handler_init_20(); 1734a04a10f8SKris Kennaway else if (compat13) 1735a04a10f8SKris Kennaway channel_handler_init_13(); 1736a04a10f8SKris Kennaway else 1737a04a10f8SKris Kennaway channel_handler_init_15(); 1738a04a10f8SKris Kennaway } 1739a04a10f8SKris Kennaway 1740af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 1741af12a3e7SDag-Erling Smørgrav static void 1742af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 1743af12a3e7SDag-Erling Smørgrav { 1744af12a3e7SDag-Erling Smørgrav if (c == NULL) 1745af12a3e7SDag-Erling Smørgrav return; 1746af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 1747b74df5b2SDag-Erling Smørgrav if (!chan_is_dead(c, c->detach_close)) 1748af12a3e7SDag-Erling Smørgrav return; 1749221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 1750af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 1751af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 1752af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 1753af12a3e7SDag-Erling Smørgrav return; 1754221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 1755af12a3e7SDag-Erling Smørgrav } 1756af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 1757af12a3e7SDag-Erling Smørgrav return; 1758221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 1759af12a3e7SDag-Erling Smørgrav channel_free(c); 1760af12a3e7SDag-Erling Smørgrav } 1761af12a3e7SDag-Erling Smørgrav 1762af12a3e7SDag-Erling Smørgrav static void 1763a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 1764a04a10f8SKris Kennaway { 1765a04a10f8SKris Kennaway static int did_init = 0; 176621e764dfSDag-Erling Smørgrav u_int i; 1767a04a10f8SKris Kennaway Channel *c; 1768a04a10f8SKris Kennaway 1769a04a10f8SKris Kennaway if (!did_init) { 1770a04a10f8SKris Kennaway channel_handler_init(); 1771a04a10f8SKris Kennaway did_init = 1; 1772a04a10f8SKris Kennaway } 1773a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 1774af12a3e7SDag-Erling Smørgrav c = channels[i]; 1775af12a3e7SDag-Erling Smørgrav if (c == NULL) 1776511b41d2SMark Murray continue; 1777af12a3e7SDag-Erling Smørgrav if (ftab[c->type] != NULL) 1778a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 1779af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 1780511b41d2SMark Murray } 1781511b41d2SMark Murray } 1782a04a10f8SKris Kennaway 1783af12a3e7SDag-Erling Smørgrav /* 1784af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 1785af12a3e7SDag-Erling Smørgrav * select bitmasks. 1786af12a3e7SDag-Erling Smørgrav */ 1787a04a10f8SKris Kennaway void 1788ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 178921e764dfSDag-Erling Smørgrav u_int *nallocp, int rekeying) 1790a04a10f8SKris Kennaway { 179121e764dfSDag-Erling Smørgrav u_int n, sz; 1792ca3176e7SBrian Feldman 1793ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 1794ca3176e7SBrian Feldman 1795ca3176e7SBrian Feldman sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 1796af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 1797af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 1798af12a3e7SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, sz); 1799af12a3e7SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, sz); 1800af12a3e7SDag-Erling Smørgrav *nallocp = sz; 1801ca3176e7SBrian Feldman } 1802af12a3e7SDag-Erling Smørgrav *maxfdp = n; 1803ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 1804ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 1805ca3176e7SBrian Feldman 1806ca3176e7SBrian Feldman if (!rekeying) 1807ca3176e7SBrian Feldman channel_handler(channel_pre, *readsetp, *writesetp); 1808a04a10f8SKris Kennaway } 1809a04a10f8SKris Kennaway 1810af12a3e7SDag-Erling Smørgrav /* 1811af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 1812af12a3e7SDag-Erling Smørgrav * events pending. 1813af12a3e7SDag-Erling Smørgrav */ 1814a04a10f8SKris Kennaway void 1815a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 1816a04a10f8SKris Kennaway { 1817a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 1818511b41d2SMark Murray } 1819511b41d2SMark Murray 1820af12a3e7SDag-Erling Smørgrav 1821ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 1822511b41d2SMark Murray 1823511b41d2SMark Murray void 1824af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 1825511b41d2SMark Murray { 1826a04a10f8SKris Kennaway Channel *c; 182721e764dfSDag-Erling Smørgrav u_int i, len; 1828511b41d2SMark Murray 1829511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1830af12a3e7SDag-Erling Smørgrav c = channels[i]; 1831af12a3e7SDag-Erling Smørgrav if (c == NULL) 1832af12a3e7SDag-Erling Smørgrav continue; 1833511b41d2SMark Murray 1834af12a3e7SDag-Erling Smørgrav /* 1835af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 1836af12a3e7SDag-Erling Smørgrav * incoming data. 1837af12a3e7SDag-Erling Smørgrav */ 1838511b41d2SMark Murray if (compat13) { 1839a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1840a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 1841511b41d2SMark Murray continue; 1842511b41d2SMark Murray } else { 1843a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 1844511b41d2SMark Murray continue; 1845a04a10f8SKris Kennaway } 1846a04a10f8SKris Kennaway if (compat20 && 1847a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 1848ca3176e7SBrian Feldman /* XXX is this true? */ 1849af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 1850511b41d2SMark Murray continue; 1851511b41d2SMark Murray } 1852511b41d2SMark Murray 1853511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 1854ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 1855ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 1856ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 1857b74df5b2SDag-Erling Smørgrav if (c->datagram) { 1858b74df5b2SDag-Erling Smørgrav if (len > 0) { 1859b74df5b2SDag-Erling Smørgrav u_char *data; 1860b74df5b2SDag-Erling Smørgrav u_int dlen; 1861b74df5b2SDag-Erling Smørgrav 1862b74df5b2SDag-Erling Smørgrav data = buffer_get_string(&c->input, 1863b74df5b2SDag-Erling Smørgrav &dlen); 1864b74df5b2SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_DATA); 1865b74df5b2SDag-Erling Smørgrav packet_put_int(c->remote_id); 1866b74df5b2SDag-Erling Smørgrav packet_put_string(data, dlen); 1867b74df5b2SDag-Erling Smørgrav packet_send(); 1868b74df5b2SDag-Erling Smørgrav c->remote_window -= dlen + 4; 1869b74df5b2SDag-Erling Smørgrav xfree(data); 1870b74df5b2SDag-Erling Smørgrav } 1871b74df5b2SDag-Erling Smørgrav continue; 1872b74df5b2SDag-Erling Smørgrav } 1873af12a3e7SDag-Erling Smørgrav /* 1874af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 1875af12a3e7SDag-Erling Smørgrav * connection. 1876af12a3e7SDag-Erling Smørgrav */ 1877a04a10f8SKris Kennaway if (compat20) { 1878a04a10f8SKris Kennaway if (len > c->remote_window) 1879a04a10f8SKris Kennaway len = c->remote_window; 1880a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1881a04a10f8SKris Kennaway len = c->remote_maxpacket; 1882a04a10f8SKris Kennaway } else { 1883511b41d2SMark Murray if (packet_is_interactive()) { 1884511b41d2SMark Murray if (len > 1024) 1885511b41d2SMark Murray len = 512; 1886511b41d2SMark Murray } else { 1887511b41d2SMark Murray /* Keep the packets at reasonable size. */ 1888511b41d2SMark Murray if (len > packet_get_maxsize()/2) 1889511b41d2SMark Murray len = packet_get_maxsize()/2; 1890511b41d2SMark Murray } 1891a04a10f8SKris Kennaway } 1892a04a10f8SKris Kennaway if (len > 0) { 1893a04a10f8SKris Kennaway packet_start(compat20 ? 1894a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 1895a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1896a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 1897511b41d2SMark Murray packet_send(); 1898a04a10f8SKris Kennaway buffer_consume(&c->input, len); 1899a04a10f8SKris Kennaway c->remote_window -= len; 1900a04a10f8SKris Kennaway } 1901a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1902511b41d2SMark Murray if (compat13) 1903511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1904511b41d2SMark Murray /* 1905511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 190680628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 190780628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 1908511b41d2SMark Murray */ 190980628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 191080628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 191180628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 191280628bacSDag-Erling Smørgrav else 1913a04a10f8SKris Kennaway chan_ibuf_empty(c); 1914a04a10f8SKris Kennaway } 1915a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1916a04a10f8SKris Kennaway if (compat20 && 191780628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 1918a04a10f8SKris Kennaway c->remote_window > 0 && 1919a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1920a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1921a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 1922ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 1923ca3176e7SBrian Feldman c->extended_usage); 1924a04a10f8SKris Kennaway if (len > c->remote_window) 1925a04a10f8SKris Kennaway len = c->remote_window; 1926a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1927a04a10f8SKris Kennaway len = c->remote_maxpacket; 1928a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1929a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1930a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1931a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1932a04a10f8SKris Kennaway packet_send(); 1933a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1934a04a10f8SKris Kennaway c->remote_window -= len; 1935ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 1936511b41d2SMark Murray } 1937511b41d2SMark Murray } 1938511b41d2SMark Murray } 1939511b41d2SMark Murray 1940af12a3e7SDag-Erling Smørgrav 1941af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 1942511b41d2SMark Murray 1943511b41d2SMark Murray void 1944af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 1945511b41d2SMark Murray { 1946511b41d2SMark Murray int id; 1947511b41d2SMark Murray char *data; 1948ca3176e7SBrian Feldman u_int data_len; 1949a04a10f8SKris Kennaway Channel *c; 1950511b41d2SMark Murray 1951511b41d2SMark Murray /* Get the channel number and verify it. */ 1952511b41d2SMark Murray id = packet_get_int(); 1953a04a10f8SKris Kennaway c = channel_lookup(id); 1954a04a10f8SKris Kennaway if (c == NULL) 1955511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1956511b41d2SMark Murray 1957511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1958a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1959a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1960511b41d2SMark Murray return; 1961511b41d2SMark Murray 1962511b41d2SMark Murray /* Get the data. */ 1963511b41d2SMark Murray data = packet_get_string(&data_len); 1964a04a10f8SKris Kennaway 1965476cd3b2SDag-Erling Smørgrav /* 1966476cd3b2SDag-Erling Smørgrav * Ignore data for protocol > 1.3 if output end is no longer open. 1967476cd3b2SDag-Erling Smørgrav * For protocol 2 the sending side is reducing its window as it sends 1968476cd3b2SDag-Erling Smørgrav * data, so we must 'fake' consumption of the data in order to ensure 1969476cd3b2SDag-Erling Smørgrav * that window updates are sent back. Otherwise the connection might 1970476cd3b2SDag-Erling Smørgrav * deadlock. 1971476cd3b2SDag-Erling Smørgrav */ 1972476cd3b2SDag-Erling Smørgrav if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 1973476cd3b2SDag-Erling Smørgrav if (compat20) { 1974476cd3b2SDag-Erling Smørgrav c->local_window -= data_len; 1975476cd3b2SDag-Erling Smørgrav c->local_consumed += data_len; 1976476cd3b2SDag-Erling Smørgrav } 1977476cd3b2SDag-Erling Smørgrav xfree(data); 1978476cd3b2SDag-Erling Smørgrav return; 1979476cd3b2SDag-Erling Smørgrav } 1980476cd3b2SDag-Erling Smørgrav 1981a04a10f8SKris Kennaway if (compat20) { 1982a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1983221552e4SDag-Erling Smørgrav logit("channel %d: rcvd big packet %d, maxpack %d", 1984a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1985a04a10f8SKris Kennaway } 1986a04a10f8SKris Kennaway if (data_len > c->local_window) { 1987221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much data %d, win %d", 1988a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1989a04a10f8SKris Kennaway xfree(data); 1990a04a10f8SKris Kennaway return; 1991a04a10f8SKris Kennaway } 1992a04a10f8SKris Kennaway c->local_window -= data_len; 1993a04a10f8SKris Kennaway } 1994af12a3e7SDag-Erling Smørgrav packet_check_eom(); 1995b74df5b2SDag-Erling Smørgrav if (c->datagram) 1996b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->output, data, data_len); 1997b74df5b2SDag-Erling Smørgrav else 1998a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1999511b41d2SMark Murray xfree(data); 2000511b41d2SMark Murray } 2001af12a3e7SDag-Erling Smørgrav 2002a04a10f8SKris Kennaway void 2003af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 2004a04a10f8SKris Kennaway { 2005a04a10f8SKris Kennaway int id; 2006a04a10f8SKris Kennaway char *data; 2007a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 2008a04a10f8SKris Kennaway Channel *c; 2009a04a10f8SKris Kennaway 2010a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 2011a04a10f8SKris Kennaway id = packet_get_int(); 2012a04a10f8SKris Kennaway c = channel_lookup(id); 2013a04a10f8SKris Kennaway 2014a04a10f8SKris Kennaway if (c == NULL) 2015a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 2016a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 2017221552e4SDag-Erling Smørgrav logit("channel %d: ext data for non open", id); 2018a04a10f8SKris Kennaway return; 2019a04a10f8SKris Kennaway } 202080628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 202180628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 202280628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 202380628bacSDag-Erling Smørgrav else 202480628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 202580628bacSDag-Erling Smørgrav "on channel %d.", id); 202680628bacSDag-Erling Smørgrav } 2027a04a10f8SKris Kennaway tcode = packet_get_int(); 2028a04a10f8SKris Kennaway if (c->efd == -1 || 2029a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 2030a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 2031221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 2032a04a10f8SKris Kennaway return; 2033a04a10f8SKris Kennaway } 2034a04a10f8SKris Kennaway data = packet_get_string(&data_len); 2035af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2036a04a10f8SKris Kennaway if (data_len > c->local_window) { 2037221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %d, win %d", 2038a04a10f8SKris Kennaway c->self, data_len, c->local_window); 2039a04a10f8SKris Kennaway xfree(data); 2040a04a10f8SKris Kennaway return; 2041a04a10f8SKris Kennaway } 20425b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 2043a04a10f8SKris Kennaway c->local_window -= data_len; 2044a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 2045a04a10f8SKris Kennaway xfree(data); 2046a04a10f8SKris Kennaway } 2047a04a10f8SKris Kennaway 2048a04a10f8SKris Kennaway void 2049af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 2050a04a10f8SKris Kennaway { 2051a04a10f8SKris Kennaway int id; 2052a04a10f8SKris Kennaway Channel *c; 2053a04a10f8SKris Kennaway 2054a04a10f8SKris Kennaway id = packet_get_int(); 2055af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2056a04a10f8SKris Kennaway c = channel_lookup(id); 2057a04a10f8SKris Kennaway if (c == NULL) 2058a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 2059a04a10f8SKris Kennaway chan_rcvd_ieof(c); 2060af12a3e7SDag-Erling Smørgrav 2061af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 2062af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 2063af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 2064af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 2065af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 2066af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 2067af12a3e7SDag-Erling Smørgrav } 2068af12a3e7SDag-Erling Smørgrav 2069a04a10f8SKris Kennaway } 2070511b41d2SMark Murray 2071511b41d2SMark Murray void 2072af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 2073511b41d2SMark Murray { 2074a04a10f8SKris Kennaway int id; 2075a04a10f8SKris Kennaway Channel *c; 2076511b41d2SMark Murray 2077a04a10f8SKris Kennaway id = packet_get_int(); 2078af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2079a04a10f8SKris Kennaway c = channel_lookup(id); 2080a04a10f8SKris Kennaway if (c == NULL) 2081a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 2082511b41d2SMark Murray 2083511b41d2SMark Murray /* 2084511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 2085511b41d2SMark Murray * data is coming for it. 2086511b41d2SMark Murray */ 2087511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 2088a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2089511b41d2SMark Murray packet_send(); 2090511b41d2SMark Murray 2091511b41d2SMark Murray /* 2092511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 2093511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 2094511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 2095511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 2096511b41d2SMark Murray * the confirmation arrives. 2097511b41d2SMark Murray */ 2098a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 2099511b41d2SMark Murray /* 2100511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 2101511b41d2SMark Murray * cause it to be freed later. 2102511b41d2SMark Murray */ 2103af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 2104a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 2105511b41d2SMark Murray } 2106511b41d2SMark Murray } 2107511b41d2SMark Murray 2108a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2109a04a10f8SKris Kennaway void 2110af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 2111a04a10f8SKris Kennaway { 2112a04a10f8SKris Kennaway int id = packet_get_int(); 2113a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2114af12a3e7SDag-Erling Smørgrav 2115af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2116a04a10f8SKris Kennaway if (c == NULL) 2117a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 2118a04a10f8SKris Kennaway chan_rcvd_oclose(c); 2119a04a10f8SKris Kennaway } 2120511b41d2SMark Murray 2121511b41d2SMark Murray void 2122af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 2123511b41d2SMark Murray { 2124a04a10f8SKris Kennaway int id = packet_get_int(); 2125a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2126a04a10f8SKris Kennaway 2127af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2128a04a10f8SKris Kennaway if (c == NULL) 2129a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2130a04a10f8SKris Kennaway "out-of-range channel %d.", id); 2131a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 2132a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2133a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 2134af12a3e7SDag-Erling Smørgrav channel_free(c); 2135a04a10f8SKris Kennaway } 2136a04a10f8SKris Kennaway 2137a04a10f8SKris Kennaway void 2138af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 2139a04a10f8SKris Kennaway { 2140a04a10f8SKris Kennaway int id, remote_id; 2141a04a10f8SKris Kennaway Channel *c; 2142a04a10f8SKris Kennaway 2143a04a10f8SKris Kennaway id = packet_get_int(); 2144a04a10f8SKris Kennaway c = channel_lookup(id); 2145a04a10f8SKris Kennaway 2146a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 2147a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 2148a04a10f8SKris Kennaway "non-opening channel %d.", id); 2149a04a10f8SKris Kennaway remote_id = packet_get_int(); 2150a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 2151a04a10f8SKris Kennaway c->remote_id = remote_id; 2152a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 2153a04a10f8SKris Kennaway 2154a04a10f8SKris Kennaway if (compat20) { 2155a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 2156a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 2157af12a3e7SDag-Erling Smørgrav if (c->confirm) { 21585b9b2fafSBrian Feldman debug2("callback start"); 215921e764dfSDag-Erling Smørgrav c->confirm(c->self, c->confirm_ctx); 21605b9b2fafSBrian Feldman debug2("callback done"); 2161a04a10f8SKris Kennaway } 2162221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 2163a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 2164a04a10f8SKris Kennaway } 2165af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2166af12a3e7SDag-Erling Smørgrav } 2167af12a3e7SDag-Erling Smørgrav 2168af12a3e7SDag-Erling Smørgrav static char * 2169af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 2170af12a3e7SDag-Erling Smørgrav { 2171af12a3e7SDag-Erling Smørgrav switch (reason) { 2172af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 2173af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 2174af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 2175af12a3e7SDag-Erling Smørgrav return "connect failed"; 2176af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 2177af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 2178af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 2179af12a3e7SDag-Erling Smørgrav return "resource shortage"; 2180af12a3e7SDag-Erling Smørgrav } 2181af12a3e7SDag-Erling Smørgrav return "unknown reason"; 2182a04a10f8SKris Kennaway } 2183a04a10f8SKris Kennaway 2184a04a10f8SKris Kennaway void 2185af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 2186a04a10f8SKris Kennaway { 2187ca3176e7SBrian Feldman int id, reason; 2188ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 2189a04a10f8SKris Kennaway Channel *c; 2190a04a10f8SKris Kennaway 2191a04a10f8SKris Kennaway id = packet_get_int(); 2192a04a10f8SKris Kennaway c = channel_lookup(id); 2193a04a10f8SKris Kennaway 2194a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 2195a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 2196a04a10f8SKris Kennaway "non-opening channel %d.", id); 2197a04a10f8SKris Kennaway if (compat20) { 2198ca3176e7SBrian Feldman reason = packet_get_int(); 2199af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 2200ca3176e7SBrian Feldman msg = packet_get_string(NULL); 2201ca3176e7SBrian Feldman lang = packet_get_string(NULL); 2202ca3176e7SBrian Feldman } 2203221552e4SDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", id, 2204af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 2205ca3176e7SBrian Feldman if (msg != NULL) 2206a04a10f8SKris Kennaway xfree(msg); 2207ca3176e7SBrian Feldman if (lang != NULL) 2208a04a10f8SKris Kennaway xfree(lang); 2209a04a10f8SKris Kennaway } 2210af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2211a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 2212af12a3e7SDag-Erling Smørgrav channel_free(c); 2213a04a10f8SKris Kennaway } 2214a04a10f8SKris Kennaway 2215a04a10f8SKris Kennaway void 2216af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 2217a04a10f8SKris Kennaway { 2218a04a10f8SKris Kennaway Channel *c; 2219a82e551fSDag-Erling Smørgrav int id; 2220a82e551fSDag-Erling Smørgrav u_int adjust; 2221a04a10f8SKris Kennaway 2222a04a10f8SKris Kennaway if (!compat20) 2223a04a10f8SKris Kennaway return; 2224511b41d2SMark Murray 2225511b41d2SMark Murray /* Get the channel number and verify it. */ 2226a04a10f8SKris Kennaway id = packet_get_int(); 2227a04a10f8SKris Kennaway c = channel_lookup(id); 2228511b41d2SMark Murray 2229b74df5b2SDag-Erling Smørgrav if (c == NULL) { 2230b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 2231511b41d2SMark Murray return; 2232511b41d2SMark Murray } 2233a04a10f8SKris Kennaway adjust = packet_get_int(); 2234af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2235a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 2236a04a10f8SKris Kennaway c->remote_window += adjust; 2237511b41d2SMark Murray } 2238511b41d2SMark Murray 2239af12a3e7SDag-Erling Smørgrav void 2240af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 2241af12a3e7SDag-Erling Smørgrav { 2242af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2243af12a3e7SDag-Erling Smørgrav u_short host_port; 2244af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 2245af12a3e7SDag-Erling Smørgrav int remote_id, sock = -1; 2246af12a3e7SDag-Erling Smørgrav 2247af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2248af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 2249af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 2250af12a3e7SDag-Erling Smørgrav 2251af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2252af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 2253af12a3e7SDag-Erling Smørgrav } else { 2254af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 2255af12a3e7SDag-Erling Smørgrav } 2256af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2257af12a3e7SDag-Erling Smørgrav sock = channel_connect_to(host, host_port); 2258af12a3e7SDag-Erling Smørgrav if (sock != -1) { 2259af12a3e7SDag-Erling Smørgrav c = channel_new("connected socket", 2260af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, 2261af12a3e7SDag-Erling Smørgrav originator_string, 1); 2262af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 2263af12a3e7SDag-Erling Smørgrav } 2264e73e9afaSDag-Erling Smørgrav xfree(originator_string); 2265221552e4SDag-Erling Smørgrav if (c == NULL) { 2266af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2267af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2268af12a3e7SDag-Erling Smørgrav packet_send(); 2269af12a3e7SDag-Erling Smørgrav } 2270af12a3e7SDag-Erling Smørgrav xfree(host); 2271af12a3e7SDag-Erling Smørgrav } 2272af12a3e7SDag-Erling Smørgrav 2273af12a3e7SDag-Erling Smørgrav 2274af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 2275511b41d2SMark Murray 2276511b41d2SMark Murray void 2277af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 2278511b41d2SMark Murray { 2279af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 2280511b41d2SMark Murray } 2281511b41d2SMark Murray 2282af12a3e7SDag-Erling Smørgrav static int 2283af12a3e7SDag-Erling Smørgrav channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, 2284af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2285511b41d2SMark Murray { 2286af12a3e7SDag-Erling Smørgrav Channel *c; 2287b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 2288511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2289aa49c926SDag-Erling Smørgrav const char *host, *addr; 2290af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2291511b41d2SMark Murray 2292af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2293af12a3e7SDag-Erling Smørgrav listen_addr : host_to_connect; 2294aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 2295511b41d2SMark Murray 2296af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2297af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2298d4ecd108SDag-Erling Smørgrav return 0; 2299ca3176e7SBrian Feldman } 2300af12a3e7SDag-Erling Smørgrav if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { 2301ca3176e7SBrian Feldman error("Forward host name too long."); 2302d4ecd108SDag-Erling Smørgrav return 0; 2303ca3176e7SBrian Feldman } 2304ca3176e7SBrian Feldman 2305511b41d2SMark Murray /* 2306aa49c926SDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, 2307aa49c926SDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 2308aa49c926SDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 2309aa49c926SDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's 2310aa49c926SDag-Erling Smørgrav * specification and force a wildcard bind, whereas a value of 2 2311aa49c926SDag-Erling Smørgrav * (``clientspecified'') will bind to whatever address the client 2312aa49c926SDag-Erling Smørgrav * asked for. 2313aa49c926SDag-Erling Smørgrav * 2314aa49c926SDag-Erling Smørgrav * Special-case listen_addrs are: 2315aa49c926SDag-Erling Smørgrav * 2316aa49c926SDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2317aa49c926SDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 2318aa49c926SDag-Erling Smørgrav * "localhost" -> loopback v4/v6 2319aa49c926SDag-Erling Smørgrav */ 2320aa49c926SDag-Erling Smørgrav addr = NULL; 2321aa49c926SDag-Erling Smørgrav if (listen_addr == NULL) { 2322aa49c926SDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 2323aa49c926SDag-Erling Smørgrav if (gateway_ports) 2324aa49c926SDag-Erling Smørgrav wildcard = 1; 2325aa49c926SDag-Erling Smørgrav } else if (gateway_ports || is_client) { 2326aa49c926SDag-Erling Smørgrav if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2327aa49c926SDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0) || 2328aa49c926SDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2329aa49c926SDag-Erling Smørgrav (!is_client && gateway_ports == 1)) 2330aa49c926SDag-Erling Smørgrav wildcard = 1; 2331aa49c926SDag-Erling Smørgrav else if (strcmp(listen_addr, "localhost") != 0) 2332aa49c926SDag-Erling Smørgrav addr = listen_addr; 2333aa49c926SDag-Erling Smørgrav } 2334aa49c926SDag-Erling Smørgrav 2335aa49c926SDag-Erling Smørgrav debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", 2336aa49c926SDag-Erling Smørgrav type, wildcard, (addr == NULL) ? "NULL" : addr); 2337aa49c926SDag-Erling Smørgrav 2338aa49c926SDag-Erling Smørgrav /* 2339511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2340511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2341511b41d2SMark Murray */ 2342511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2343511b41d2SMark Murray hints.ai_family = IPv4or6; 2344aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 2345511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2346ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", listen_port); 2347aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2348aa49c926SDag-Erling Smørgrav if (addr == NULL) { 2349aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 2350aa49c926SDag-Erling Smørgrav packet_disconnect("getaddrinfo: fatal error: %s", 2351aa49c926SDag-Erling Smørgrav gai_strerror(r)); 2352aa49c926SDag-Erling Smørgrav } else { 2353d4ecd108SDag-Erling Smørgrav error("channel_setup_fwd_listener: " 2354aa49c926SDag-Erling Smørgrav "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); 2355aa49c926SDag-Erling Smørgrav } 2356d4ecd108SDag-Erling Smørgrav return 0; 2357aa49c926SDag-Erling Smørgrav } 2358511b41d2SMark Murray 2359511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2360511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2361511b41d2SMark Murray continue; 2362511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2363511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2364af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: getnameinfo failed"); 2365511b41d2SMark Murray continue; 2366511b41d2SMark Murray } 2367511b41d2SMark Murray /* Create a port to listen for the host. */ 2368221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2369511b41d2SMark Murray if (sock < 0) { 2370511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2371511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2372511b41d2SMark Murray continue; 2373511b41d2SMark Murray } 2374b74df5b2SDag-Erling Smørgrav 2375b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 2376f388f5efSDag-Erling Smørgrav 2377511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 2378511b41d2SMark Murray 2379511b41d2SMark Murray /* Bind the socket to the address. */ 2380511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2381511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2382989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2383989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2384989dd127SDag-Erling Smørgrav else 2385511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2386989dd127SDag-Erling Smørgrav 2387511b41d2SMark Murray close(sock); 2388511b41d2SMark Murray continue; 2389511b41d2SMark Murray } 2390511b41d2SMark Murray /* Start listening for connections on the socket. */ 2391476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 2392511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2393511b41d2SMark Murray close(sock); 2394511b41d2SMark Murray continue; 2395511b41d2SMark Murray } 2396511b41d2SMark Murray /* Allocate a channel number for the socket. */ 2397af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2398a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2399221552e4SDag-Erling Smørgrav 0, "port listener", 1); 2400af12a3e7SDag-Erling Smørgrav strlcpy(c->path, host, sizeof(c->path)); 2401af12a3e7SDag-Erling Smørgrav c->host_port = port_to_connect; 2402af12a3e7SDag-Erling Smørgrav c->listening_port = listen_port; 2403511b41d2SMark Murray success = 1; 2404511b41d2SMark Murray } 2405511b41d2SMark Murray if (success == 0) 2406af12a3e7SDag-Erling Smørgrav error("channel_setup_fwd_listener: cannot listen to port: %d", 2407ca3176e7SBrian Feldman listen_port); 2408511b41d2SMark Murray freeaddrinfo(aitop); 2409ca3176e7SBrian Feldman return success; 2410511b41d2SMark Murray } 2411511b41d2SMark Murray 241221e764dfSDag-Erling Smørgrav int 241321e764dfSDag-Erling Smørgrav channel_cancel_rport_listener(const char *host, u_short port) 241421e764dfSDag-Erling Smørgrav { 241521e764dfSDag-Erling Smørgrav u_int i; 241621e764dfSDag-Erling Smørgrav int found = 0; 241721e764dfSDag-Erling Smørgrav 241821e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 241921e764dfSDag-Erling Smørgrav Channel *c = channels[i]; 242021e764dfSDag-Erling Smørgrav 242121e764dfSDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && 242221e764dfSDag-Erling Smørgrav strncmp(c->path, host, sizeof(c->path)) == 0 && 242321e764dfSDag-Erling Smørgrav c->listening_port == port) { 2424aa49c926SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 242521e764dfSDag-Erling Smørgrav channel_free(c); 242621e764dfSDag-Erling Smørgrav found = 1; 242721e764dfSDag-Erling Smørgrav } 242821e764dfSDag-Erling Smørgrav } 242921e764dfSDag-Erling Smørgrav 243021e764dfSDag-Erling Smørgrav return (found); 243121e764dfSDag-Erling Smørgrav } 243221e764dfSDag-Erling Smørgrav 2433af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 2434af12a3e7SDag-Erling Smørgrav int 2435aa49c926SDag-Erling Smørgrav channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, 2436af12a3e7SDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int gateway_ports) 2437af12a3e7SDag-Erling Smørgrav { 2438af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, 2439aa49c926SDag-Erling Smørgrav listen_host, listen_port, host_to_connect, port_to_connect, 2440aa49c926SDag-Erling Smørgrav gateway_ports); 2441af12a3e7SDag-Erling Smørgrav } 2442af12a3e7SDag-Erling Smørgrav 2443af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 2444af12a3e7SDag-Erling Smørgrav int 2445af12a3e7SDag-Erling Smørgrav channel_setup_remote_fwd_listener(const char *listen_address, 2446af12a3e7SDag-Erling Smørgrav u_short listen_port, int gateway_ports) 2447af12a3e7SDag-Erling Smørgrav { 2448af12a3e7SDag-Erling Smørgrav return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, 2449af12a3e7SDag-Erling Smørgrav listen_address, listen_port, NULL, 0, gateway_ports); 2450af12a3e7SDag-Erling Smørgrav } 2451af12a3e7SDag-Erling Smørgrav 2452511b41d2SMark Murray /* 2453511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 2454511b41d2SMark Murray * the secure channel to host:port from local side. 2455511b41d2SMark Murray */ 2456511b41d2SMark Murray 2457511b41d2SMark Murray void 2458aa49c926SDag-Erling Smørgrav channel_request_remote_forwarding(const char *listen_host, u_short listen_port, 2459ca3176e7SBrian Feldman const char *host_to_connect, u_short port_to_connect) 2460511b41d2SMark Murray { 2461af12a3e7SDag-Erling Smørgrav int type, success = 0; 2462ca3176e7SBrian Feldman 2463511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 2464511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2465511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 2466511b41d2SMark Murray 2467511b41d2SMark Murray /* Send the forward request to the remote side. */ 2468a04a10f8SKris Kennaway if (compat20) { 2469aa49c926SDag-Erling Smørgrav const char *address_to_bind; 2470aa49c926SDag-Erling Smørgrav if (listen_host == NULL) 2471aa49c926SDag-Erling Smørgrav address_to_bind = "localhost"; 2472aa49c926SDag-Erling Smørgrav else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) 2473aa49c926SDag-Erling Smørgrav address_to_bind = ""; 2474aa49c926SDag-Erling Smørgrav else 2475aa49c926SDag-Erling Smørgrav address_to_bind = listen_host; 2476aa49c926SDag-Erling Smørgrav 2477a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 2478a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 247980628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2480a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 2481a04a10f8SKris Kennaway packet_put_int(listen_port); 2482ca3176e7SBrian Feldman packet_send(); 2483ca3176e7SBrian Feldman packet_write_wait(); 2484ca3176e7SBrian Feldman /* Assume that server accepts the request */ 2485ca3176e7SBrian Feldman success = 1; 2486a04a10f8SKris Kennaway } else { 2487511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 2488a04a10f8SKris Kennaway packet_put_int(listen_port); 2489a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 2490a04a10f8SKris Kennaway packet_put_int(port_to_connect); 2491511b41d2SMark Murray packet_send(); 2492511b41d2SMark Murray packet_write_wait(); 2493ca3176e7SBrian Feldman 2494ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 2495af12a3e7SDag-Erling Smørgrav type = packet_read(); 2496ca3176e7SBrian Feldman switch (type) { 2497ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 2498ca3176e7SBrian Feldman success = 1; 2499ca3176e7SBrian Feldman break; 2500ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 2501221552e4SDag-Erling Smørgrav logit("Warning: Server denied remote port forwarding."); 2502ca3176e7SBrian Feldman break; 2503ca3176e7SBrian Feldman default: 2504ca3176e7SBrian Feldman /* Unknown packet */ 2505ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 2506ca3176e7SBrian Feldman "received packet type %d.", type); 2507ca3176e7SBrian Feldman } 2508ca3176e7SBrian Feldman } 2509ca3176e7SBrian Feldman if (success) { 2510ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 2511ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 2512ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].listen_port = listen_port; 2513ca3176e7SBrian Feldman num_permitted_opens++; 2514511b41d2SMark Murray } 2515a04a10f8SKris Kennaway } 2516511b41d2SMark Murray 2517511b41d2SMark Murray /* 251821e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 251921e764dfSDag-Erling Smørgrav * local side. 252021e764dfSDag-Erling Smørgrav */ 252121e764dfSDag-Erling Smørgrav void 2522aa49c926SDag-Erling Smørgrav channel_request_rforward_cancel(const char *host, u_short port) 252321e764dfSDag-Erling Smørgrav { 252421e764dfSDag-Erling Smørgrav int i; 252521e764dfSDag-Erling Smørgrav 252621e764dfSDag-Erling Smørgrav if (!compat20) 252721e764dfSDag-Erling Smørgrav return; 252821e764dfSDag-Erling Smørgrav 252921e764dfSDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 253021e764dfSDag-Erling Smørgrav if (permitted_opens[i].host_to_connect != NULL && 253121e764dfSDag-Erling Smørgrav permitted_opens[i].listen_port == port) 253221e764dfSDag-Erling Smørgrav break; 253321e764dfSDag-Erling Smørgrav } 253421e764dfSDag-Erling Smørgrav if (i >= num_permitted_opens) { 253521e764dfSDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 253621e764dfSDag-Erling Smørgrav return; 253721e764dfSDag-Erling Smørgrav } 253821e764dfSDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 253921e764dfSDag-Erling Smørgrav packet_put_cstring("cancel-tcpip-forward"); 254021e764dfSDag-Erling Smørgrav packet_put_char(0); 2541aa49c926SDag-Erling Smørgrav packet_put_cstring(host == NULL ? "" : host); 254221e764dfSDag-Erling Smørgrav packet_put_int(port); 254321e764dfSDag-Erling Smørgrav packet_send(); 254421e764dfSDag-Erling Smørgrav 254521e764dfSDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 254621e764dfSDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 2547b74df5b2SDag-Erling Smørgrav xfree(permitted_opens[i].host_to_connect); 254821e764dfSDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 254921e764dfSDag-Erling Smørgrav } 255021e764dfSDag-Erling Smørgrav 255121e764dfSDag-Erling Smørgrav /* 2552511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 2553511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 2554511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 2555511b41d2SMark Murray */ 2556511b41d2SMark Murray 2557511b41d2SMark Murray void 2558a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 2559511b41d2SMark Murray { 2560511b41d2SMark Murray u_short port, host_port; 2561511b41d2SMark Murray char *hostname; 2562511b41d2SMark Murray 2563511b41d2SMark Murray /* Get arguments from the packet. */ 2564511b41d2SMark Murray port = packet_get_int(); 2565511b41d2SMark Murray hostname = packet_get_string(NULL); 2566511b41d2SMark Murray host_port = packet_get_int(); 2567511b41d2SMark Murray 2568989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 2569511b41d2SMark Murray /* 2570511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 2571511b41d2SMark Murray * privileged port. 2572511b41d2SMark Murray */ 2573511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 2574221552e4SDag-Erling Smørgrav packet_disconnect( 2575221552e4SDag-Erling Smørgrav "Requested forwarding of port %d but user is not root.", 2576511b41d2SMark Murray port); 2577221552e4SDag-Erling Smørgrav if (host_port == 0) 2578221552e4SDag-Erling Smørgrav packet_disconnect("Dynamic forwarding denied."); 2579989dd127SDag-Erling Smørgrav #endif 2580221552e4SDag-Erling Smørgrav 2581ca3176e7SBrian Feldman /* Initiate forwarding */ 2582aa49c926SDag-Erling Smørgrav channel_setup_local_fwd_listener(NULL, port, hostname, 2583aa49c926SDag-Erling Smørgrav host_port, gateway_ports); 2584511b41d2SMark Murray 2585511b41d2SMark Murray /* Free the argument string. */ 2586511b41d2SMark Murray xfree(hostname); 2587511b41d2SMark Murray } 2588511b41d2SMark Murray 2589ca3176e7SBrian Feldman /* 2590ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 2591ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 2592ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 2593ca3176e7SBrian Feldman */ 2594ca3176e7SBrian Feldman void 2595af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 2596ca3176e7SBrian Feldman { 2597ca3176e7SBrian Feldman if (num_permitted_opens == 0) 2598ca3176e7SBrian Feldman all_opens_permitted = 1; 2599ca3176e7SBrian Feldman } 2600ca3176e7SBrian Feldman 2601ca3176e7SBrian Feldman void 2602ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 2603ca3176e7SBrian Feldman { 2604ca3176e7SBrian Feldman if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 2605ca3176e7SBrian Feldman fatal("channel_request_remote_forwarding: too many forwards"); 2606ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 2607ca3176e7SBrian Feldman 2608ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 2609ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 2610ca3176e7SBrian Feldman num_permitted_opens++; 2611ca3176e7SBrian Feldman 2612ca3176e7SBrian Feldman all_opens_permitted = 0; 2613ca3176e7SBrian Feldman } 2614ca3176e7SBrian Feldman 2615ca3176e7SBrian Feldman void 2616ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 2617ca3176e7SBrian Feldman { 2618ca3176e7SBrian Feldman int i; 2619ca3176e7SBrian Feldman 2620ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 262121e764dfSDag-Erling Smørgrav if (permitted_opens[i].host_to_connect != NULL) 2622ca3176e7SBrian Feldman xfree(permitted_opens[i].host_to_connect); 2623ca3176e7SBrian Feldman num_permitted_opens = 0; 2624ca3176e7SBrian Feldman 2625ca3176e7SBrian Feldman } 2626ca3176e7SBrian Feldman 2627ca3176e7SBrian Feldman 2628ca3176e7SBrian Feldman /* return socket to remote host, port */ 2629af12a3e7SDag-Erling Smørgrav static int 2630ca3176e7SBrian Feldman connect_to(const char *host, u_short port) 2631511b41d2SMark Murray { 2632511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2633511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2634511b41d2SMark Murray int gaierr; 2635a04a10f8SKris Kennaway int sock = -1; 2636511b41d2SMark Murray 2637511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2638511b41d2SMark Murray hints.ai_family = IPv4or6; 2639511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2640ca3176e7SBrian Feldman snprintf(strport, sizeof strport, "%d", port); 2641511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 2642ca3176e7SBrian Feldman error("connect_to %.100s: unknown host (%s)", host, 2643ca3176e7SBrian Feldman gai_strerror(gaierr)); 2644a04a10f8SKris Kennaway return -1; 2645511b41d2SMark Murray } 2646511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2647511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2648511b41d2SMark Murray continue; 2649511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2650511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2651ca3176e7SBrian Feldman error("connect_to: getnameinfo failed"); 2652511b41d2SMark Murray continue; 2653511b41d2SMark Murray } 2654221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2655511b41d2SMark Murray if (sock < 0) { 2656e73e9afaSDag-Erling Smørgrav if (ai->ai_next == NULL) 2657511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2658e73e9afaSDag-Erling Smørgrav else 2659e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 2660511b41d2SMark Murray continue; 2661511b41d2SMark Murray } 266221e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 266321e764dfSDag-Erling Smørgrav fatal("%s: set_nonblock(%d)", __func__, sock); 2664ca3176e7SBrian Feldman if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && 2665ca3176e7SBrian Feldman errno != EINPROGRESS) { 2666ca3176e7SBrian Feldman error("connect_to %.100s port %s: %.100s", ntop, strport, 2667511b41d2SMark Murray strerror(errno)); 2668511b41d2SMark Murray close(sock); 2669511b41d2SMark Murray continue; /* fail -- try next */ 2670511b41d2SMark Murray } 2671511b41d2SMark Murray break; /* success */ 2672511b41d2SMark Murray 2673511b41d2SMark Murray } 2674511b41d2SMark Murray freeaddrinfo(aitop); 2675511b41d2SMark Murray if (!ai) { 2676ca3176e7SBrian Feldman error("connect_to %.100s port %d: failed.", host, port); 2677a04a10f8SKris Kennaway return -1; 2678a04a10f8SKris Kennaway } 2679a04a10f8SKris Kennaway /* success */ 2680af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2681a04a10f8SKris Kennaway return sock; 2682a04a10f8SKris Kennaway } 2683ca3176e7SBrian Feldman 2684ca3176e7SBrian Feldman int 2685af12a3e7SDag-Erling Smørgrav channel_connect_by_listen_address(u_short listen_port) 2686ca3176e7SBrian Feldman { 2687ca3176e7SBrian Feldman int i; 2688ca3176e7SBrian Feldman 2689ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 269021e764dfSDag-Erling Smørgrav if (permitted_opens[i].host_to_connect != NULL && 269121e764dfSDag-Erling Smørgrav permitted_opens[i].listen_port == listen_port) 2692ca3176e7SBrian Feldman return connect_to( 2693ca3176e7SBrian Feldman permitted_opens[i].host_to_connect, 2694ca3176e7SBrian Feldman permitted_opens[i].port_to_connect); 2695ca3176e7SBrian Feldman error("WARNING: Server requests forwarding for unknown listen_port %d", 2696ca3176e7SBrian Feldman listen_port); 2697ca3176e7SBrian Feldman return -1; 2698ca3176e7SBrian Feldman } 2699ca3176e7SBrian Feldman 2700ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 2701ca3176e7SBrian Feldman int 2702ca3176e7SBrian Feldman channel_connect_to(const char *host, u_short port) 2703ca3176e7SBrian Feldman { 2704ca3176e7SBrian Feldman int i, permit; 2705ca3176e7SBrian Feldman 2706ca3176e7SBrian Feldman permit = all_opens_permitted; 2707ca3176e7SBrian Feldman if (!permit) { 2708ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 270921e764dfSDag-Erling Smørgrav if (permitted_opens[i].host_to_connect != NULL && 271021e764dfSDag-Erling Smørgrav permitted_opens[i].port_to_connect == port && 2711ca3176e7SBrian Feldman strcmp(permitted_opens[i].host_to_connect, host) == 0) 2712ca3176e7SBrian Feldman permit = 1; 2713ca3176e7SBrian Feldman 2714ca3176e7SBrian Feldman } 2715ca3176e7SBrian Feldman if (!permit) { 2716221552e4SDag-Erling Smørgrav logit("Received request to connect to host %.100s port %d, " 2717ca3176e7SBrian Feldman "but the request was denied.", host, port); 2718ca3176e7SBrian Feldman return -1; 2719ca3176e7SBrian Feldman } 2720ca3176e7SBrian Feldman return connect_to(host, port); 2721ca3176e7SBrian Feldman } 2722ca3176e7SBrian Feldman 272321e764dfSDag-Erling Smørgrav void 272421e764dfSDag-Erling Smørgrav channel_send_window_changes(void) 272521e764dfSDag-Erling Smørgrav { 272621e764dfSDag-Erling Smørgrav u_int i; 272721e764dfSDag-Erling Smørgrav struct winsize ws; 272821e764dfSDag-Erling Smørgrav 272921e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 2730aa49c926SDag-Erling Smørgrav if (channels[i] == NULL || !channels[i]->client_tty || 273121e764dfSDag-Erling Smørgrav channels[i]->type != SSH_CHANNEL_OPEN) 273221e764dfSDag-Erling Smørgrav continue; 273321e764dfSDag-Erling Smørgrav if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 273421e764dfSDag-Erling Smørgrav continue; 273521e764dfSDag-Erling Smørgrav channel_request_start(i, "window-change", 0); 273621e764dfSDag-Erling Smørgrav packet_put_int(ws.ws_col); 273721e764dfSDag-Erling Smørgrav packet_put_int(ws.ws_row); 273821e764dfSDag-Erling Smørgrav packet_put_int(ws.ws_xpixel); 273921e764dfSDag-Erling Smørgrav packet_put_int(ws.ws_ypixel); 274021e764dfSDag-Erling Smørgrav packet_send(); 274121e764dfSDag-Erling Smørgrav } 274221e764dfSDag-Erling Smørgrav } 274321e764dfSDag-Erling Smørgrav 2744af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 2745511b41d2SMark Murray 2746511b41d2SMark Murray /* 2747511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 2748a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 2749a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 2750511b41d2SMark Murray */ 2751af12a3e7SDag-Erling Smørgrav int 2752af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 2753d4ecd108SDag-Erling Smørgrav int single_connection, u_int *display_numberp, int **chanids) 2754511b41d2SMark Murray { 2755af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 2756511b41d2SMark Murray int display_number, sock; 2757511b41d2SMark Murray u_short port; 2758511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2759511b41d2SMark Murray char strport[NI_MAXSERV]; 2760511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 2761511b41d2SMark Murray 2762b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 2763b74df5b2SDag-Erling Smørgrav return -1; 2764b74df5b2SDag-Erling Smørgrav 2765511b41d2SMark Murray for (display_number = x11_display_offset; 2766511b41d2SMark Murray display_number < MAX_DISPLAYS; 2767511b41d2SMark Murray display_number++) { 2768511b41d2SMark Murray port = 6000 + display_number; 2769511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2770511b41d2SMark Murray hints.ai_family = IPv4or6; 2771af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 2772511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2773511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2774511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 2775511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 2776af12a3e7SDag-Erling Smørgrav return -1; 2777511b41d2SMark Murray } 2778511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2779511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 2780511b41d2SMark Murray continue; 2781221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 2782221552e4SDag-Erling Smørgrav ai->ai_protocol); 2783511b41d2SMark Murray if (sock < 0) { 2784989dd127SDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { 2785511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 278621e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 2787af12a3e7SDag-Erling Smørgrav return -1; 2788989dd127SDag-Erling Smørgrav } else { 2789989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 2790989dd127SDag-Erling Smørgrav ai->ai_family); 2791989dd127SDag-Erling Smørgrav continue; 2792511b41d2SMark Murray } 2793989dd127SDag-Erling Smørgrav } 2794989dd127SDag-Erling Smørgrav #ifdef IPV6_V6ONLY 2795989dd127SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 2796989dd127SDag-Erling Smørgrav int on = 1; 2797989dd127SDag-Erling Smørgrav if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) 2798989dd127SDag-Erling Smørgrav error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); 2799989dd127SDag-Erling Smørgrav } 2800989dd127SDag-Erling Smørgrav #endif 2801b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 2802511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2803221552e4SDag-Erling Smørgrav debug2("bind port %d: %.100s", port, strerror(errno)); 2804511b41d2SMark Murray close(sock); 2805989dd127SDag-Erling Smørgrav 2806989dd127SDag-Erling Smørgrav if (ai->ai_next) 2807989dd127SDag-Erling Smørgrav continue; 2808989dd127SDag-Erling Smørgrav 2809511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2810511b41d2SMark Murray close(socks[n]); 2811511b41d2SMark Murray } 2812511b41d2SMark Murray num_socks = 0; 2813511b41d2SMark Murray break; 2814511b41d2SMark Murray } 2815511b41d2SMark Murray socks[num_socks++] = sock; 2816989dd127SDag-Erling Smørgrav #ifndef DONT_TRY_OTHER_AF 2817511b41d2SMark Murray if (num_socks == NUM_SOCKS) 2818511b41d2SMark Murray break; 2819989dd127SDag-Erling Smørgrav #else 2820989dd127SDag-Erling Smørgrav if (x11_use_localhost) { 2821989dd127SDag-Erling Smørgrav if (num_socks == NUM_SOCKS) 2822989dd127SDag-Erling Smørgrav break; 2823989dd127SDag-Erling Smørgrav } else { 2824989dd127SDag-Erling Smørgrav break; 2825989dd127SDag-Erling Smørgrav } 2826989dd127SDag-Erling Smørgrav #endif 2827511b41d2SMark Murray } 2828ca3176e7SBrian Feldman freeaddrinfo(aitop); 2829511b41d2SMark Murray if (num_socks > 0) 2830511b41d2SMark Murray break; 2831511b41d2SMark Murray } 2832511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 2833511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 2834af12a3e7SDag-Erling Smørgrav return -1; 2835511b41d2SMark Murray } 2836511b41d2SMark Murray /* Start listening for connections on the socket. */ 2837511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2838511b41d2SMark Murray sock = socks[n]; 2839476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 2840511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2841511b41d2SMark Murray close(sock); 2842af12a3e7SDag-Erling Smørgrav return -1; 2843511b41d2SMark Murray } 2844511b41d2SMark Murray } 2845511b41d2SMark Murray 2846511b41d2SMark Murray /* Allocate a channel for each socket. */ 2847d4ecd108SDag-Erling Smørgrav *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); 2848511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 2849511b41d2SMark Murray sock = socks[n]; 2850af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 2851a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 2852a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 2853221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 2854af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 2855d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 2856511b41d2SMark Murray } 2857d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 2858511b41d2SMark Murray 2859af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 2860a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 2861a82e551fSDag-Erling Smørgrav return (0); 2862511b41d2SMark Murray } 2863511b41d2SMark Murray 2864af12a3e7SDag-Erling Smørgrav static int 2865ca3176e7SBrian Feldman connect_local_xsocket(u_int dnr) 2866511b41d2SMark Murray { 2867511b41d2SMark Murray int sock; 2868511b41d2SMark Murray struct sockaddr_un addr; 2869511b41d2SMark Murray 2870511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2871511b41d2SMark Murray if (sock < 0) 2872511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 2873511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 2874511b41d2SMark Murray addr.sun_family = AF_UNIX; 2875af12a3e7SDag-Erling Smørgrav snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); 2876511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 2877511b41d2SMark Murray return sock; 2878511b41d2SMark Murray close(sock); 2879511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 2880511b41d2SMark Murray return -1; 2881511b41d2SMark Murray } 2882511b41d2SMark Murray 2883a04a10f8SKris Kennaway int 2884a04a10f8SKris Kennaway x11_connect_display(void) 2885511b41d2SMark Murray { 2886a04a10f8SKris Kennaway int display_number, sock = 0; 2887511b41d2SMark Murray const char *display; 2888a04a10f8SKris Kennaway char buf[1024], *cp; 2889511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2890511b41d2SMark Murray char strport[NI_MAXSERV]; 2891511b41d2SMark Murray int gaierr; 2892511b41d2SMark Murray 2893511b41d2SMark Murray /* Try to open a socket for the local X server. */ 2894511b41d2SMark Murray display = getenv("DISPLAY"); 2895511b41d2SMark Murray if (!display) { 2896511b41d2SMark Murray error("DISPLAY not set."); 2897a04a10f8SKris Kennaway return -1; 2898511b41d2SMark Murray } 2899511b41d2SMark Murray /* 2900511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 2901511b41d2SMark Murray * connection to the real X server. 2902511b41d2SMark Murray */ 2903511b41d2SMark Murray 2904511b41d2SMark Murray /* 2905511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 2906511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 2907511b41d2SMark Murray */ 2908511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 2909511b41d2SMark Murray display[0] == ':') { 2910511b41d2SMark Murray /* Connect to the unix domain socket. */ 2911511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 2912511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2913511b41d2SMark Murray display); 2914a04a10f8SKris Kennaway return -1; 2915511b41d2SMark Murray } 2916511b41d2SMark Murray /* Create a socket. */ 2917511b41d2SMark Murray sock = connect_local_xsocket(display_number); 2918511b41d2SMark Murray if (sock < 0) 2919a04a10f8SKris Kennaway return -1; 2920511b41d2SMark Murray 2921511b41d2SMark Murray /* OK, we now have a connection to the display. */ 2922a04a10f8SKris Kennaway return sock; 2923511b41d2SMark Murray } 2924511b41d2SMark Murray /* 2925511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 2926511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 2927511b41d2SMark Murray */ 2928af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 2929511b41d2SMark Murray cp = strchr(buf, ':'); 2930511b41d2SMark Murray if (!cp) { 2931511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 2932a04a10f8SKris Kennaway return -1; 2933511b41d2SMark Murray } 2934511b41d2SMark Murray *cp = 0; 2935511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 2936511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 2937511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 2938511b41d2SMark Murray display); 2939a04a10f8SKris Kennaway return -1; 2940511b41d2SMark Murray } 2941511b41d2SMark Murray 2942511b41d2SMark Murray /* Look up the host address */ 2943511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2944511b41d2SMark Murray hints.ai_family = IPv4or6; 2945511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2946511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 2947511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 2948511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 2949a04a10f8SKris Kennaway return -1; 2950511b41d2SMark Murray } 2951511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2952511b41d2SMark Murray /* Create a socket. */ 2953221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2954511b41d2SMark Murray if (sock < 0) { 2955221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 2956511b41d2SMark Murray continue; 2957511b41d2SMark Murray } 2958511b41d2SMark Murray /* Connect it to the display. */ 2959511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2960221552e4SDag-Erling Smørgrav debug2("connect %.100s port %d: %.100s", buf, 2961a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 2962511b41d2SMark Murray close(sock); 2963511b41d2SMark Murray continue; 2964511b41d2SMark Murray } 2965511b41d2SMark Murray /* Success */ 2966511b41d2SMark Murray break; 2967a04a10f8SKris Kennaway } 2968511b41d2SMark Murray freeaddrinfo(aitop); 2969511b41d2SMark Murray if (!ai) { 2970511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 2971511b41d2SMark Murray strerror(errno)); 2972a04a10f8SKris Kennaway return -1; 2973511b41d2SMark Murray } 2974af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 2975a04a10f8SKris Kennaway return sock; 2976a04a10f8SKris Kennaway } 2977511b41d2SMark Murray 2978a04a10f8SKris Kennaway /* 2979a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 2980a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 2981a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 2982a04a10f8SKris Kennaway */ 2983a04a10f8SKris Kennaway 2984a04a10f8SKris Kennaway void 2985af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 2986a04a10f8SKris Kennaway { 2987af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2988af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 2989a04a10f8SKris Kennaway char *remote_host; 2990a04a10f8SKris Kennaway 2991a04a10f8SKris Kennaway debug("Received X11 open request."); 2992af12a3e7SDag-Erling Smørgrav 2993af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2994af12a3e7SDag-Erling Smørgrav 2995af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2996af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 2997af12a3e7SDag-Erling Smørgrav } else { 2998af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 2999af12a3e7SDag-Erling Smørgrav } 3000af12a3e7SDag-Erling Smørgrav packet_check_eom(); 3001a04a10f8SKris Kennaway 3002a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 3003a04a10f8SKris Kennaway sock = x11_connect_display(); 3004af12a3e7SDag-Erling Smørgrav if (sock != -1) { 3005af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 3006af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 3007af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 3008af12a3e7SDag-Erling Smørgrav remote_host, 1); 3009af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 3010af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 3011af12a3e7SDag-Erling Smørgrav } 3012221552e4SDag-Erling Smørgrav xfree(remote_host); 3013af12a3e7SDag-Erling Smørgrav if (c == NULL) { 3014a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 3015a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 3016af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 3017a04a10f8SKris Kennaway } else { 3018511b41d2SMark Murray /* Send a confirmation to the remote host. */ 3019511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 3020af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 3021af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 3022a04a10f8SKris Kennaway } 3023af12a3e7SDag-Erling Smørgrav packet_send(); 3024511b41d2SMark Murray } 3025511b41d2SMark Murray 30265b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 30275b9b2fafSBrian Feldman void 3028af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 30295b9b2fafSBrian Feldman { 30305b9b2fafSBrian Feldman int rchan = packet_get_int(); 3031f388f5efSDag-Erling Smørgrav 30325b9b2fafSBrian Feldman switch (type) { 30335b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 30345b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 30355b9b2fafSBrian Feldman break; 30365b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 30375b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 30385b9b2fafSBrian Feldman break; 30395b9b2fafSBrian Feldman default: 3040af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 30415b9b2fafSBrian Feldman break; 30425b9b2fafSBrian Feldman } 3043b74df5b2SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a malicious server."); 30445b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 30455b9b2fafSBrian Feldman packet_put_int(rchan); 30465b9b2fafSBrian Feldman packet_send(); 30475b9b2fafSBrian Feldman } 30485b9b2fafSBrian Feldman 3049511b41d2SMark Murray /* 3050511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 3051511b41d2SMark Murray * data, and enables authentication spoofing. 3052af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 3053511b41d2SMark Murray */ 3054511b41d2SMark Murray void 3055d4ecd108SDag-Erling Smørgrav x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 3056a04a10f8SKris Kennaway const char *proto, const char *data) 3057511b41d2SMark Murray { 3058ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 3059d4ecd108SDag-Erling Smørgrav u_int i, value; 3060511b41d2SMark Murray char *new_data; 3061511b41d2SMark Murray int screen_number; 3062511b41d2SMark Murray const char *cp; 306321e764dfSDag-Erling Smørgrav u_int32_t rnd = 0; 3064511b41d2SMark Murray 3065d4ecd108SDag-Erling Smørgrav if (x11_saved_display == NULL) 3066d4ecd108SDag-Erling Smørgrav x11_saved_display = xstrdup(disp); 3067d4ecd108SDag-Erling Smørgrav else if (strcmp(disp, x11_saved_display) != 0) { 3068d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 3069d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 3070d4ecd108SDag-Erling Smørgrav return; 3071d4ecd108SDag-Erling Smørgrav } 3072d4ecd108SDag-Erling Smørgrav 3073d4ecd108SDag-Erling Smørgrav cp = disp; 3074d4ecd108SDag-Erling Smørgrav if (disp) 3075d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 3076511b41d2SMark Murray if (cp) 3077511b41d2SMark Murray cp = strchr(cp, '.'); 3078511b41d2SMark Murray if (cp) 3079511b41d2SMark Murray screen_number = atoi(cp + 1); 3080511b41d2SMark Murray else 3081511b41d2SMark Murray screen_number = 0; 3082511b41d2SMark Murray 3083d4ecd108SDag-Erling Smørgrav if (x11_saved_proto == NULL) { 3084511b41d2SMark Murray /* Save protocol name. */ 3085511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 3086511b41d2SMark Murray /* 3087d4ecd108SDag-Erling Smørgrav * Extract real authentication data and generate fake data 3088d4ecd108SDag-Erling Smørgrav * of the same length. 3089511b41d2SMark Murray */ 3090511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 3091511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 3092511b41d2SMark Murray for (i = 0; i < data_len; i++) { 3093511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 3094d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 3095d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 3096511b41d2SMark Murray if (i % 4 == 0) 309721e764dfSDag-Erling Smørgrav rnd = arc4random(); 3098511b41d2SMark Murray x11_saved_data[i] = value; 309921e764dfSDag-Erling Smørgrav x11_fake_data[i] = rnd & 0xff; 310021e764dfSDag-Erling Smørgrav rnd >>= 8; 3101511b41d2SMark Murray } 3102511b41d2SMark Murray x11_saved_data_len = data_len; 3103511b41d2SMark Murray x11_fake_data_len = data_len; 3104d4ecd108SDag-Erling Smørgrav } 3105511b41d2SMark Murray 3106511b41d2SMark Murray /* Convert the fake data into hex. */ 3107d4ecd108SDag-Erling Smørgrav new_data = tohex(x11_fake_data, data_len); 3108511b41d2SMark Murray 3109511b41d2SMark Murray /* Send the request packet. */ 3110a04a10f8SKris Kennaway if (compat20) { 3111a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 3112a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 3113a04a10f8SKris Kennaway } else { 3114511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 3115a04a10f8SKris Kennaway } 3116a04a10f8SKris Kennaway packet_put_cstring(proto); 3117a04a10f8SKris Kennaway packet_put_cstring(new_data); 3118511b41d2SMark Murray packet_put_int(screen_number); 3119511b41d2SMark Murray packet_send(); 3120511b41d2SMark Murray packet_write_wait(); 3121511b41d2SMark Murray xfree(new_data); 3122511b41d2SMark Murray } 3123511b41d2SMark Murray 3124af12a3e7SDag-Erling Smørgrav 3125af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 3126af12a3e7SDag-Erling Smørgrav 3127511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 3128511b41d2SMark Murray 3129511b41d2SMark Murray void 3130af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 3131511b41d2SMark Murray { 3132511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 3133511b41d2SMark Murray packet_send(); 3134511b41d2SMark Murray packet_write_wait(); 3135511b41d2SMark Murray } 3136