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 * 15b66f2d16SKris Kennaway * 16a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 17b66f2d16SKris Kennaway * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. 18b66f2d16SKris Kennaway * Copyright (c) 1999 Dug Song. All rights reserved. 19b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 20b66f2d16SKris Kennaway * 21b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 22b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 23b66f2d16SKris Kennaway * are met: 24b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 25b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 26b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 27b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 28b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 29b66f2d16SKris Kennaway * 30b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 31b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 33b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 34b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 35b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 39b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40511b41d2SMark Murray */ 41511b41d2SMark Murray 42511b41d2SMark Murray #include "includes.h" 43e0fbb1d2SBrian Feldman RCSID("$FreeBSD$"); 445b9b2fafSBrian Feldman RCSID("$OpenBSD: channels.c,v 1.72 2000/10/27 07:48:22 markus Exp $"); 45511b41d2SMark Murray 46511b41d2SMark Murray #include "ssh.h" 47511b41d2SMark Murray #include "packet.h" 48511b41d2SMark Murray #include "xmalloc.h" 49511b41d2SMark Murray #include "buffer.h" 50511b41d2SMark Murray #include "uidswap.h" 51511b41d2SMark Murray #include "readconf.h" 52511b41d2SMark Murray #include "servconf.h" 53511b41d2SMark Murray 54511b41d2SMark Murray #include "channels.h" 55511b41d2SMark Murray #include "nchan.h" 56511b41d2SMark Murray #include "compat.h" 57511b41d2SMark Murray 58a04a10f8SKris Kennaway #include "ssh2.h" 59a04a10f8SKris Kennaway 60b66f2d16SKris Kennaway #include <openssl/rsa.h> 61b66f2d16SKris Kennaway #include <openssl/dsa.h> 62b66f2d16SKris Kennaway #include "key.h" 63b66f2d16SKris Kennaway #include "authfd.h" 64b66f2d16SKris Kennaway 65511b41d2SMark Murray /* Maximum number of fake X11 displays to try. */ 66511b41d2SMark Murray #define MAX_DISPLAYS 1000 67511b41d2SMark Murray 68511b41d2SMark Murray /* Max len of agent socket */ 69511b41d2SMark Murray #define MAX_SOCKET_NAME 100 70511b41d2SMark Murray 71511b41d2SMark Murray /* 72511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 73511b41d2SMark Murray * dynamically extended as needed. 74511b41d2SMark Murray */ 75511b41d2SMark Murray static Channel *channels = NULL; 76511b41d2SMark Murray 77511b41d2SMark Murray /* 78511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 79511b41d2SMark Murray * initialized (at least the type field); unused slots are marked with type 80511b41d2SMark Murray * SSH_CHANNEL_FREE. 81511b41d2SMark Murray */ 82511b41d2SMark Murray static int channels_alloc = 0; 83511b41d2SMark Murray 84511b41d2SMark Murray /* 85511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 86511b41d2SMark Murray * updated in channel_allocate. 87511b41d2SMark Murray */ 88511b41d2SMark Murray static int channel_max_fd_value = 0; 89511b41d2SMark Murray 90511b41d2SMark Murray /* Name and directory of socket for authentication agent forwarding. */ 91511b41d2SMark Murray static char *channel_forwarded_auth_socket_name = NULL; 92511b41d2SMark Murray static char *channel_forwarded_auth_socket_dir = NULL; 93511b41d2SMark Murray 94511b41d2SMark Murray /* Saved X11 authentication protocol name. */ 95511b41d2SMark Murray char *x11_saved_proto = NULL; 96511b41d2SMark Murray 97511b41d2SMark Murray /* Saved X11 authentication data. This is the real data. */ 98511b41d2SMark Murray char *x11_saved_data = NULL; 99511b41d2SMark Murray unsigned int x11_saved_data_len = 0; 100511b41d2SMark Murray 101511b41d2SMark Murray /* 102511b41d2SMark Murray * Fake X11 authentication data. This is what the server will be sending us; 103511b41d2SMark Murray * we should replace any occurrences of this by the real data. 104511b41d2SMark Murray */ 105511b41d2SMark Murray char *x11_fake_data = NULL; 106511b41d2SMark Murray unsigned int x11_fake_data_len; 107511b41d2SMark Murray 108511b41d2SMark Murray /* 109511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 110511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 111511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 112511b41d2SMark Murray * network (which might be behind a firewall). 113511b41d2SMark Murray */ 114511b41d2SMark Murray typedef struct { 115a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 116a04a10f8SKris Kennaway u_short port_to_connect; /* Connect to 'port'. */ 117a04a10f8SKris Kennaway u_short listen_port; /* Remote side should listen port number. */ 118511b41d2SMark Murray } ForwardPermission; 119511b41d2SMark Murray 120511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */ 121511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 122511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */ 123511b41d2SMark Murray static int num_permitted_opens = 0; 124511b41d2SMark Murray /* 125511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 126511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 127511b41d2SMark Murray * anything after logging in anyway. 128511b41d2SMark Murray */ 129511b41d2SMark Murray static int all_opens_permitted = 0; 130511b41d2SMark Murray 131511b41d2SMark Murray /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ 132511b41d2SMark Murray static int have_hostname_in_open = 0; 133511b41d2SMark Murray 134511b41d2SMark Murray /* Sets specific protocol options. */ 135511b41d2SMark Murray 136511b41d2SMark Murray void 137511b41d2SMark Murray channel_set_options(int hostname_in_open) 138511b41d2SMark Murray { 139511b41d2SMark Murray have_hostname_in_open = hostname_in_open; 140511b41d2SMark Murray } 141511b41d2SMark Murray 142511b41d2SMark Murray /* 143511b41d2SMark Murray * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually 144511b41d2SMark Murray * called by the server, because the user could connect to any port anyway, 145511b41d2SMark Murray * and the server has no way to know but to trust the client anyway. 146511b41d2SMark Murray */ 147511b41d2SMark Murray 148511b41d2SMark Murray void 149511b41d2SMark Murray channel_permit_all_opens() 150511b41d2SMark Murray { 151511b41d2SMark Murray all_opens_permitted = 1; 152511b41d2SMark Murray } 153511b41d2SMark Murray 154a04a10f8SKris Kennaway /* lookup channel by id */ 155a04a10f8SKris Kennaway 156a04a10f8SKris Kennaway Channel * 157a04a10f8SKris Kennaway channel_lookup(int id) 158a04a10f8SKris Kennaway { 159a04a10f8SKris Kennaway Channel *c; 160b66f2d16SKris Kennaway if (id < 0 || id > channels_alloc) { 161a04a10f8SKris Kennaway log("channel_lookup: %d: bad id", id); 162a04a10f8SKris Kennaway return NULL; 163a04a10f8SKris Kennaway } 164a04a10f8SKris Kennaway c = &channels[id]; 165a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_FREE) { 166a04a10f8SKris Kennaway log("channel_lookup: %d: bad id: channel free", id); 167a04a10f8SKris Kennaway return NULL; 168a04a10f8SKris Kennaway } 169a04a10f8SKris Kennaway return c; 170a04a10f8SKris Kennaway } 171a04a10f8SKris Kennaway 172a04a10f8SKris Kennaway /* 173a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 174a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 175a04a10f8SKris Kennaway */ 176a04a10f8SKris Kennaway 177a04a10f8SKris Kennaway void 1785b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1795b9b2fafSBrian Feldman int extusage, int nonblock) 180a04a10f8SKris Kennaway { 181a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 182a04a10f8SKris Kennaway if (rfd > channel_max_fd_value) 183a04a10f8SKris Kennaway channel_max_fd_value = rfd; 184a04a10f8SKris Kennaway if (wfd > channel_max_fd_value) 185a04a10f8SKris Kennaway channel_max_fd_value = wfd; 186a04a10f8SKris Kennaway if (efd > channel_max_fd_value) 187a04a10f8SKris Kennaway channel_max_fd_value = efd; 188a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 189a04a10f8SKris Kennaway 190a04a10f8SKris Kennaway c->rfd = rfd; 191a04a10f8SKris Kennaway c->wfd = wfd; 192a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 193a04a10f8SKris Kennaway c->efd = efd; 194a04a10f8SKris Kennaway c->extended_usage = extusage; 1955b9b2fafSBrian Feldman 196e0fbb1d2SBrian Feldman /* XXX ugly hack: nonblock is only set by the server */ 197e0fbb1d2SBrian Feldman if (nonblock && isatty(c->rfd)) { 198e0fbb1d2SBrian Feldman debug("channel: %d: rfd %d isatty", c->self, c->rfd); 199e0fbb1d2SBrian Feldman c->isatty = 1; 200e0fbb1d2SBrian Feldman if (!isatty(c->wfd)) { 201e0fbb1d2SBrian Feldman error("channel: %d: wfd %d is not a tty?", 202e0fbb1d2SBrian Feldman c->self, c->wfd); 203e0fbb1d2SBrian Feldman } 204e0fbb1d2SBrian Feldman } else { 205e0fbb1d2SBrian Feldman c->isatty = 0; 206e0fbb1d2SBrian Feldman } 207e0fbb1d2SBrian Feldman 2085b9b2fafSBrian Feldman /* enable nonblocking mode */ 2095b9b2fafSBrian Feldman if (nonblock) { 210a04a10f8SKris Kennaway if (rfd != -1) 211a04a10f8SKris Kennaway set_nonblock(rfd); 212a04a10f8SKris Kennaway if (wfd != -1) 213a04a10f8SKris Kennaway set_nonblock(wfd); 214a04a10f8SKris Kennaway if (efd != -1) 215a04a10f8SKris Kennaway set_nonblock(efd); 216a04a10f8SKris Kennaway } 2175b9b2fafSBrian Feldman } 218a04a10f8SKris Kennaway 219511b41d2SMark Murray /* 220511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 221511b41d2SMark Murray * remote_name to be freed. 222511b41d2SMark Murray */ 223511b41d2SMark Murray 224511b41d2SMark Murray int 225a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 2265b9b2fafSBrian Feldman int window, int maxpack, int extusage, char *remote_name, int nonblock) 227511b41d2SMark Murray { 228511b41d2SMark Murray int i, found; 229511b41d2SMark Murray Channel *c; 230511b41d2SMark Murray 231511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 232511b41d2SMark Murray if (channels_alloc == 0) { 233a04a10f8SKris Kennaway chan_init(); 234511b41d2SMark Murray channels_alloc = 10; 235511b41d2SMark Murray channels = xmalloc(channels_alloc * sizeof(Channel)); 236511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 237511b41d2SMark Murray channels[i].type = SSH_CHANNEL_FREE; 238511b41d2SMark Murray /* 239511b41d2SMark Murray * Kludge: arrange a call to channel_stop_listening if we 240511b41d2SMark Murray * terminate with fatal(). 241511b41d2SMark Murray */ 242511b41d2SMark Murray fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); 243511b41d2SMark Murray } 244511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 245511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 246511b41d2SMark Murray if (channels[i].type == SSH_CHANNEL_FREE) { 247511b41d2SMark Murray /* Found a free slot. */ 248511b41d2SMark Murray found = i; 249511b41d2SMark Murray break; 250511b41d2SMark Murray } 251511b41d2SMark Murray if (found == -1) { 252511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 253511b41d2SMark Murray found = channels_alloc; 254511b41d2SMark Murray channels_alloc += 10; 2555b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 256511b41d2SMark Murray channels = xrealloc(channels, channels_alloc * sizeof(Channel)); 257511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 258511b41d2SMark Murray channels[i].type = SSH_CHANNEL_FREE; 259511b41d2SMark Murray } 260511b41d2SMark Murray /* Initialize and return new channel number. */ 261511b41d2SMark Murray c = &channels[found]; 262511b41d2SMark Murray buffer_init(&c->input); 263511b41d2SMark Murray buffer_init(&c->output); 264a04a10f8SKris Kennaway buffer_init(&c->extended); 265511b41d2SMark Murray chan_init_iostates(c); 2665b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 267511b41d2SMark Murray c->self = found; 268511b41d2SMark Murray c->type = type; 269a04a10f8SKris Kennaway c->ctype = ctype; 270a04a10f8SKris Kennaway c->local_window = window; 271a04a10f8SKris Kennaway c->local_window_max = window; 272a04a10f8SKris Kennaway c->local_consumed = 0; 273a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 274511b41d2SMark Murray c->remote_id = -1; 275511b41d2SMark Murray c->remote_name = remote_name; 276a04a10f8SKris Kennaway c->remote_window = 0; 277a04a10f8SKris Kennaway c->remote_maxpacket = 0; 278a04a10f8SKris Kennaway c->cb_fn = NULL; 279a04a10f8SKris Kennaway c->cb_arg = NULL; 280a04a10f8SKris Kennaway c->cb_event = 0; 281a04a10f8SKris Kennaway c->dettach_user = NULL; 282b66f2d16SKris Kennaway c->input_filter = NULL; 283511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 284511b41d2SMark Murray return found; 285511b41d2SMark Murray } 286a04a10f8SKris Kennaway /* old interface XXX */ 287a04a10f8SKris Kennaway int 288a04a10f8SKris Kennaway channel_allocate(int type, int sock, char *remote_name) 289a04a10f8SKris Kennaway { 2905b9b2fafSBrian Feldman return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name, 1); 291a04a10f8SKris Kennaway } 292511b41d2SMark Murray 293a04a10f8SKris Kennaway 294a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 295511b41d2SMark Murray 296511b41d2SMark Murray void 297a04a10f8SKris Kennaway channel_close_fds(Channel *c) 298511b41d2SMark Murray { 299a04a10f8SKris Kennaway if (c->sock != -1) { 300a04a10f8SKris Kennaway close(c->sock); 301a04a10f8SKris Kennaway c->sock = -1; 302a04a10f8SKris Kennaway } 303a04a10f8SKris Kennaway if (c->rfd != -1) { 304a04a10f8SKris Kennaway close(c->rfd); 305a04a10f8SKris Kennaway c->rfd = -1; 306a04a10f8SKris Kennaway } 307a04a10f8SKris Kennaway if (c->wfd != -1) { 308a04a10f8SKris Kennaway close(c->wfd); 309a04a10f8SKris Kennaway c->wfd = -1; 310a04a10f8SKris Kennaway } 311a04a10f8SKris Kennaway if (c->efd != -1) { 312a04a10f8SKris Kennaway close(c->efd); 313a04a10f8SKris Kennaway c->efd = -1; 314a04a10f8SKris Kennaway } 315a04a10f8SKris Kennaway } 316511b41d2SMark Murray 317a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 318a04a10f8SKris Kennaway 319a04a10f8SKris Kennaway void 320a04a10f8SKris Kennaway channel_free(int id) 321a04a10f8SKris Kennaway { 322a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 323a04a10f8SKris Kennaway if (c == NULL) 324a04a10f8SKris Kennaway packet_disconnect("channel free: bad local channel %d", id); 325a04a10f8SKris Kennaway debug("channel_free: channel %d: status: %s", id, channel_open_message()); 326a04a10f8SKris Kennaway if (c->dettach_user != NULL) { 327a04a10f8SKris Kennaway debug("channel_free: channel %d: dettaching channel user", id); 328a04a10f8SKris Kennaway c->dettach_user(c->self, NULL); 329a04a10f8SKris Kennaway } 330a04a10f8SKris Kennaway if (c->sock != -1) 331a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 332a04a10f8SKris Kennaway channel_close_fds(c); 333a04a10f8SKris Kennaway buffer_free(&c->input); 334a04a10f8SKris Kennaway buffer_free(&c->output); 335a04a10f8SKris Kennaway buffer_free(&c->extended); 336a04a10f8SKris Kennaway c->type = SSH_CHANNEL_FREE; 337a04a10f8SKris Kennaway if (c->remote_name) { 338a04a10f8SKris Kennaway xfree(c->remote_name); 339a04a10f8SKris Kennaway c->remote_name = NULL; 340511b41d2SMark Murray } 341511b41d2SMark Murray } 342511b41d2SMark Murray 343511b41d2SMark Murray /* 344a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 345a04a10f8SKris Kennaway * channels in the select bitmasks. 346511b41d2SMark Murray */ 347a04a10f8SKris Kennaway /* 348a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 349a04a10f8SKris Kennaway * have events pending. 350a04a10f8SKris Kennaway */ 351a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 352a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 353a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 354511b41d2SMark Murray 355511b41d2SMark Murray void 356a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 357511b41d2SMark Murray { 358a04a10f8SKris Kennaway FD_SET(c->sock, readset); 359a04a10f8SKris Kennaway } 360a04a10f8SKris Kennaway 361a04a10f8SKris Kennaway void 362a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 363a04a10f8SKris Kennaway { 364a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 365a04a10f8SKris Kennaway FD_SET(c->sock, readset); 366a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 367a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 368a04a10f8SKris Kennaway } 369a04a10f8SKris Kennaway 370a04a10f8SKris Kennaway void 371a04a10f8SKris Kennaway channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) 372a04a10f8SKris Kennaway { 373a04a10f8SKris Kennaway /* test whether sockets are 'alive' for read/write */ 374a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN) 375a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 376a04a10f8SKris Kennaway FD_SET(c->sock, readset); 377a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 378a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 379a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 380a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 381a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 382a04a10f8SKris Kennaway chan_obuf_empty(c); 383a04a10f8SKris Kennaway } 384a04a10f8SKris Kennaway } 385a04a10f8SKris Kennaway } 386a04a10f8SKris Kennaway 387a04a10f8SKris Kennaway void 388a04a10f8SKris Kennaway channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset) 389a04a10f8SKris Kennaway { 390a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 391a04a10f8SKris Kennaway c->remote_window > 0 && 392a04a10f8SKris Kennaway buffer_len(&c->input) < c->remote_window) 393a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 394a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 395a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 396a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 397a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 398a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 399a04a10f8SKris Kennaway chan_obuf_empty(c); 400a04a10f8SKris Kennaway } 401a04a10f8SKris Kennaway } 402a04a10f8SKris Kennaway /** XXX check close conditions, too */ 403a04a10f8SKris Kennaway if (c->efd != -1) { 404a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 405a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 406a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 407a04a10f8SKris Kennaway else if (c->extended_usage == CHAN_EXTENDED_READ && 408a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 409a04a10f8SKris Kennaway FD_SET(c->efd, readset); 410a04a10f8SKris Kennaway } 411a04a10f8SKris Kennaway } 412a04a10f8SKris Kennaway 413a04a10f8SKris Kennaway void 414a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 415a04a10f8SKris Kennaway { 416a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 417a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 418a04a10f8SKris Kennaway packet_put_int(c->remote_id); 419a04a10f8SKris Kennaway packet_send(); 420a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 421a04a10f8SKris Kennaway debug("Closing channel %d after input drain.", c->self); 422a04a10f8SKris Kennaway } 423a04a10f8SKris Kennaway } 424a04a10f8SKris Kennaway 425a04a10f8SKris Kennaway void 426a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 427a04a10f8SKris Kennaway { 428a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 429a04a10f8SKris Kennaway channel_free(c->self); 430a04a10f8SKris Kennaway else 431a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 432a04a10f8SKris Kennaway } 433a04a10f8SKris Kennaway 434a04a10f8SKris Kennaway /* 435a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 436a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 437a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 438a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 439a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 440a04a10f8SKris Kennaway * XXX All this happens at the client side. 441a04a10f8SKris Kennaway */ 442a04a10f8SKris Kennaway int 443a04a10f8SKris Kennaway x11_open_helper(Channel *c) 444a04a10f8SKris Kennaway { 445511b41d2SMark Murray unsigned char *ucp; 446511b41d2SMark Murray unsigned int proto_len, data_len; 447511b41d2SMark Murray 448511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 449a04a10f8SKris Kennaway if (buffer_len(&c->output) < 12) 450a04a10f8SKris Kennaway return 0; 451511b41d2SMark Murray 452511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 453a04a10f8SKris Kennaway ucp = (unsigned char *) buffer_ptr(&c->output); 454511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 455511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 456511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 457511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 458511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 459511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 460511b41d2SMark Murray } else { 461511b41d2SMark Murray debug("Initial X11 packet contains bad byte order byte: 0x%x", 462511b41d2SMark Murray ucp[0]); 463a04a10f8SKris Kennaway return -1; 464511b41d2SMark Murray } 465511b41d2SMark Murray 466511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 467a04a10f8SKris Kennaway if (buffer_len(&c->output) < 468511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 469a04a10f8SKris Kennaway return 0; 470511b41d2SMark Murray 471511b41d2SMark Murray /* Check if authentication protocol matches. */ 472511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 473511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 474511b41d2SMark Murray debug("X11 connection uses different authentication protocol."); 475a04a10f8SKris Kennaway return -1; 476511b41d2SMark Murray } 477511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 478511b41d2SMark Murray if (data_len != x11_fake_data_len || 479511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 480511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 481511b41d2SMark Murray debug("X11 auth data does not match fake data."); 482a04a10f8SKris Kennaway return -1; 483511b41d2SMark Murray } 484511b41d2SMark Murray /* Check fake data length */ 485511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 486511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 487511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 488a04a10f8SKris Kennaway return -1; 489511b41d2SMark Murray } 490511b41d2SMark Murray /* 491511b41d2SMark Murray * Received authentication protocol and data match 492511b41d2SMark Murray * our fake data. Substitute the fake data with real 493511b41d2SMark Murray * data. 494511b41d2SMark Murray */ 495511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 496511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 497a04a10f8SKris Kennaway return 1; 498a04a10f8SKris Kennaway } 499511b41d2SMark Murray 500a04a10f8SKris Kennaway void 501a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 502a04a10f8SKris Kennaway { 503a04a10f8SKris Kennaway int ret = x11_open_helper(c); 504a04a10f8SKris Kennaway if (ret == 1) { 505511b41d2SMark Murray /* Start normal processing for the channel. */ 506a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 507a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 508a04a10f8SKris Kennaway } else if (ret == -1) { 509511b41d2SMark Murray /* 510511b41d2SMark Murray * We have received an X11 connection that has bad 511511b41d2SMark Murray * authentication information. 512511b41d2SMark Murray */ 513511b41d2SMark Murray log("X11 connection rejected because of wrong authentication.\r\n"); 514a04a10f8SKris Kennaway buffer_clear(&c->input); 515a04a10f8SKris Kennaway buffer_clear(&c->output); 516a04a10f8SKris Kennaway close(c->sock); 517a04a10f8SKris Kennaway c->sock = -1; 518a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 519511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 520a04a10f8SKris Kennaway packet_put_int(c->remote_id); 521511b41d2SMark Murray packet_send(); 522511b41d2SMark Murray } 523511b41d2SMark Murray } 524511b41d2SMark Murray 525511b41d2SMark Murray void 526a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 527a04a10f8SKris Kennaway { 528a04a10f8SKris Kennaway int ret = x11_open_helper(c); 529a04a10f8SKris Kennaway if (ret == 1) { 530a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 531a04a10f8SKris Kennaway if (compat20) 532a04a10f8SKris Kennaway channel_pre_open_20(c, readset, writeset); 533a04a10f8SKris Kennaway else 534a04a10f8SKris Kennaway channel_pre_open_15(c, readset, writeset); 535a04a10f8SKris Kennaway } else if (ret == -1) { 536a04a10f8SKris Kennaway debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 537a04a10f8SKris Kennaway chan_read_failed(c); /** force close? */ 538a04a10f8SKris Kennaway chan_write_failed(c); 539a04a10f8SKris Kennaway debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 540a04a10f8SKris Kennaway } 541a04a10f8SKris Kennaway } 542a04a10f8SKris Kennaway 543a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 544a04a10f8SKris Kennaway void 545a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 546511b41d2SMark Murray { 547511b41d2SMark Murray struct sockaddr addr; 548a04a10f8SKris Kennaway int newsock, newch; 549511b41d2SMark Murray socklen_t addrlen; 550511b41d2SMark Murray char buf[16384], *remote_hostname; 551a04a10f8SKris Kennaway int remote_port; 552511b41d2SMark Murray 553a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 554511b41d2SMark Murray debug("X11 connection requested."); 555511b41d2SMark Murray addrlen = sizeof(addr); 556a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 557511b41d2SMark Murray if (newsock < 0) { 558511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 559a04a10f8SKris Kennaway return; 560511b41d2SMark Murray } 561511b41d2SMark Murray remote_hostname = get_remote_hostname(newsock); 562a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 563511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 564a04a10f8SKris Kennaway remote_hostname, remote_port); 565a04a10f8SKris Kennaway 566a04a10f8SKris Kennaway newch = channel_new("x11", 567a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 568a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 5695b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 570a04a10f8SKris Kennaway if (compat20) { 571a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 572a04a10f8SKris Kennaway packet_put_cstring("x11"); 573a04a10f8SKris Kennaway packet_put_int(newch); 574a04a10f8SKris Kennaway packet_put_int(c->local_window_max); 575a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 576a04a10f8SKris Kennaway /* originator host and port */ 577a04a10f8SKris Kennaway packet_put_cstring(remote_hostname); 578a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 579a04a10f8SKris Kennaway debug("ssh2 x11 bug compat mode"); 580a04a10f8SKris Kennaway } else { 581a04a10f8SKris Kennaway packet_put_int(remote_port); 582a04a10f8SKris Kennaway } 583a04a10f8SKris Kennaway packet_send(); 584a04a10f8SKris Kennaway } else { 585511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 586511b41d2SMark Murray packet_put_int(newch); 587511b41d2SMark Murray if (have_hostname_in_open) 588511b41d2SMark Murray packet_put_string(buf, strlen(buf)); 589511b41d2SMark Murray packet_send(); 590511b41d2SMark Murray } 591a04a10f8SKris Kennaway xfree(remote_hostname); 592a04a10f8SKris Kennaway } 593a04a10f8SKris Kennaway } 594511b41d2SMark Murray 595511b41d2SMark Murray /* 596a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 597511b41d2SMark Murray */ 598a04a10f8SKris Kennaway void 599a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 600a04a10f8SKris Kennaway { 601a04a10f8SKris Kennaway struct sockaddr addr; 602a04a10f8SKris Kennaway int newsock, newch; 603a04a10f8SKris Kennaway socklen_t addrlen; 604a04a10f8SKris Kennaway char buf[1024], *remote_hostname; 605a04a10f8SKris Kennaway int remote_port; 606a04a10f8SKris Kennaway 607a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 608a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 609a04a10f8SKris Kennaway "to %.100s port %d requested.", 610a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 611511b41d2SMark Murray addrlen = sizeof(addr); 612a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 613511b41d2SMark Murray if (newsock < 0) { 614511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 615a04a10f8SKris Kennaway return; 616511b41d2SMark Murray } 617511b41d2SMark Murray remote_hostname = get_remote_hostname(newsock); 618a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 619a04a10f8SKris Kennaway snprintf(buf, sizeof buf, 620a04a10f8SKris Kennaway "listen port %d for %.100s port %d, " 621a04a10f8SKris Kennaway "connect from %.200s port %d", 622a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port, 623a04a10f8SKris Kennaway remote_hostname, remote_port); 624a04a10f8SKris Kennaway newch = channel_new("direct-tcpip", 625a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 626a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 6275b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 628a04a10f8SKris Kennaway if (compat20) { 629a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 630a04a10f8SKris Kennaway packet_put_cstring("direct-tcpip"); 631a04a10f8SKris Kennaway packet_put_int(newch); 632a04a10f8SKris Kennaway packet_put_int(c->local_window_max); 633a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 634a04a10f8SKris Kennaway /* target host and port */ 635a04a10f8SKris Kennaway packet_put_string(c->path, strlen(c->path)); 636a04a10f8SKris Kennaway packet_put_int(c->host_port); 637a04a10f8SKris Kennaway /* originator host and port */ 638a04a10f8SKris Kennaway packet_put_cstring(remote_hostname); 639a04a10f8SKris Kennaway packet_put_int(remote_port); 640a04a10f8SKris Kennaway packet_send(); 641a04a10f8SKris Kennaway } else { 642511b41d2SMark Murray packet_start(SSH_MSG_PORT_OPEN); 643511b41d2SMark Murray packet_put_int(newch); 644a04a10f8SKris Kennaway packet_put_string(c->path, strlen(c->path)); 645a04a10f8SKris Kennaway packet_put_int(c->host_port); 646a04a10f8SKris Kennaway if (have_hostname_in_open) { 647511b41d2SMark Murray packet_put_string(buf, strlen(buf)); 648a04a10f8SKris Kennaway } 649511b41d2SMark Murray packet_send(); 650511b41d2SMark Murray } 651a04a10f8SKris Kennaway xfree(remote_hostname); 652a04a10f8SKris Kennaway } 653a04a10f8SKris Kennaway } 654511b41d2SMark Murray 655511b41d2SMark Murray /* 656a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 657a04a10f8SKris Kennaway * clients. 658511b41d2SMark Murray */ 659a04a10f8SKris Kennaway void 660a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 661a04a10f8SKris Kennaway { 662a04a10f8SKris Kennaway struct sockaddr addr; 663a04a10f8SKris Kennaway int newsock, newch; 664a04a10f8SKris Kennaway socklen_t addrlen; 665a04a10f8SKris Kennaway 666a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 667511b41d2SMark Murray addrlen = sizeof(addr); 668a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 669511b41d2SMark Murray if (newsock < 0) { 670511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 671a04a10f8SKris Kennaway return; 672511b41d2SMark Murray } 673511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, 674511b41d2SMark Murray xstrdup("accepted auth socket")); 675511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 676511b41d2SMark Murray packet_put_int(newch); 677511b41d2SMark Murray packet_send(); 678511b41d2SMark Murray } 679a04a10f8SKris Kennaway } 680511b41d2SMark Murray 681a04a10f8SKris Kennaway int 682a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 683a04a10f8SKris Kennaway { 684a04a10f8SKris Kennaway char buf[16*1024]; 685a04a10f8SKris Kennaway int len; 686511b41d2SMark Murray 687a04a10f8SKris Kennaway if (c->rfd != -1 && 688a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 689a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 690a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 691a04a10f8SKris Kennaway return 1; 692a04a10f8SKris Kennaway if (len <= 0) { 693a04a10f8SKris Kennaway debug("channel %d: read<=0 rfd %d len %d", 694a04a10f8SKris Kennaway c->self, c->rfd, len); 695a04a10f8SKris Kennaway if (compat13) { 696a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 697a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 698a04a10f8SKris Kennaway debug("Channel %d status set to input draining.", c->self); 699a04a10f8SKris Kennaway } else { 700a04a10f8SKris Kennaway chan_read_failed(c); 701a04a10f8SKris Kennaway } 702a04a10f8SKris Kennaway return -1; 703a04a10f8SKris Kennaway } 704b66f2d16SKris Kennaway if(c->input_filter != NULL) { 705b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 706b66f2d16SKris Kennaway debug("filter stops channel %d", c->self); 707b66f2d16SKris Kennaway chan_read_failed(c); 708b66f2d16SKris Kennaway } 709b66f2d16SKris Kennaway } else { 710a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 711a04a10f8SKris Kennaway } 712b66f2d16SKris Kennaway } 713a04a10f8SKris Kennaway return 1; 714a04a10f8SKris Kennaway } 715a04a10f8SKris Kennaway int 716a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 717a04a10f8SKris Kennaway { 718a04a10f8SKris Kennaway int len; 719a04a10f8SKris Kennaway 720a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 721a04a10f8SKris Kennaway if (c->wfd != -1 && 722a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 723a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 724a04a10f8SKris Kennaway len = write(c->wfd, buffer_ptr(&c->output), 725a04a10f8SKris Kennaway buffer_len(&c->output)); 726a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 727a04a10f8SKris Kennaway return 1; 728511b41d2SMark Murray if (len <= 0) { 729511b41d2SMark Murray if (compat13) { 730a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 731a04a10f8SKris Kennaway debug("Channel %d status set to input draining.", c->self); 732a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 733511b41d2SMark Murray } else { 734a04a10f8SKris Kennaway chan_write_failed(c); 735511b41d2SMark Murray } 736a04a10f8SKris Kennaway return -1; 737511b41d2SMark Murray } 738e0fbb1d2SBrian Feldman if (compat20 && c->isatty) { 739e0fbb1d2SBrian Feldman struct termios tio; 740e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 741e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 742e0fbb1d2SBrian Feldman /* 743e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 744e0fbb1d2SBrian Feldman * traffic analysis. 745e0fbb1d2SBrian Feldman */ 746e0fbb1d2SBrian Feldman packet_start(SSH2_MSG_IGNORE); 747e0fbb1d2SBrian Feldman memset(buffer_ptr(&c->output), 0, len); 748e0fbb1d2SBrian Feldman packet_put_string(buffer_ptr(&c->output), len); 749e0fbb1d2SBrian Feldman packet_send(); 750e0fbb1d2SBrian Feldman } 751e0fbb1d2SBrian Feldman } 752a04a10f8SKris Kennaway buffer_consume(&c->output, len); 753a04a10f8SKris Kennaway if (compat20 && len > 0) { 754a04a10f8SKris Kennaway c->local_consumed += len; 755511b41d2SMark Murray } 756511b41d2SMark Murray } 757a04a10f8SKris Kennaway return 1; 758511b41d2SMark Murray } 759a04a10f8SKris Kennaway int 760a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 761a04a10f8SKris Kennaway { 762a04a10f8SKris Kennaway char buf[16*1024]; 763a04a10f8SKris Kennaway int len; 764511b41d2SMark Murray 765a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 766a04a10f8SKris Kennaway if (c->efd != -1) { 767a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 768a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 769a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 770a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 771a04a10f8SKris Kennaway buffer_len(&c->extended)); 7725b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 773a04a10f8SKris Kennaway c->self, len, c->efd); 774a04a10f8SKris Kennaway if (len > 0) { 775a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 776a04a10f8SKris Kennaway c->local_consumed += len; 777a04a10f8SKris Kennaway } 778a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 779a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 780a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 7815b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 782a04a10f8SKris Kennaway c->self, len, c->efd); 783a04a10f8SKris Kennaway if (len == 0) { 784a04a10f8SKris Kennaway debug("channel %d: closing efd %d", 785a04a10f8SKris Kennaway c->self, c->efd); 786a04a10f8SKris Kennaway close(c->efd); 787a04a10f8SKris Kennaway c->efd = -1; 788a04a10f8SKris Kennaway } else if (len > 0) 789a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 790a04a10f8SKris Kennaway } 791a04a10f8SKris Kennaway } 792a04a10f8SKris Kennaway return 1; 793a04a10f8SKris Kennaway } 794a04a10f8SKris Kennaway int 795a04a10f8SKris Kennaway channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) 796a04a10f8SKris Kennaway { 797a04a10f8SKris Kennaway if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 798a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 799a04a10f8SKris Kennaway c->local_consumed > 0) { 800a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 801a04a10f8SKris Kennaway packet_put_int(c->remote_id); 802a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 803a04a10f8SKris Kennaway packet_send(); 8045b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 805a04a10f8SKris Kennaway c->self, c->local_window, 806a04a10f8SKris Kennaway c->local_consumed); 807a04a10f8SKris Kennaway c->local_window += c->local_consumed; 808a04a10f8SKris Kennaway c->local_consumed = 0; 809a04a10f8SKris Kennaway } 810a04a10f8SKris Kennaway return 1; 811a04a10f8SKris Kennaway } 812a04a10f8SKris Kennaway 813a04a10f8SKris Kennaway void 814a04a10f8SKris Kennaway channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) 815a04a10f8SKris Kennaway { 816a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 817a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 818a04a10f8SKris Kennaway } 819a04a10f8SKris Kennaway 820a04a10f8SKris Kennaway void 821a04a10f8SKris Kennaway channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset) 822a04a10f8SKris Kennaway { 823a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 824a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 825a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 826a04a10f8SKris Kennaway channel_check_window(c, readset, writeset); 827a04a10f8SKris Kennaway } 828a04a10f8SKris Kennaway 829a04a10f8SKris Kennaway void 830a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 831a04a10f8SKris Kennaway { 832a04a10f8SKris Kennaway int len; 833511b41d2SMark Murray /* Send buffered output data to the socket. */ 834a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 835a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 836a04a10f8SKris Kennaway buffer_len(&c->output)); 837511b41d2SMark Murray if (len <= 0) 838a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 839511b41d2SMark Murray else 840a04a10f8SKris Kennaway buffer_consume(&c->output, len); 841511b41d2SMark Murray } 842a04a10f8SKris Kennaway } 843511b41d2SMark Murray 844a04a10f8SKris Kennaway void 845a04a10f8SKris Kennaway channel_handler_init_20(void) 846a04a10f8SKris Kennaway { 847a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; 848a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 849a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 850a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 851a04a10f8SKris Kennaway 852a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; 853a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 854a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 855a04a10f8SKris Kennaway } 856a04a10f8SKris Kennaway 857a04a10f8SKris Kennaway void 858a04a10f8SKris Kennaway channel_handler_init_13(void) 859a04a10f8SKris Kennaway { 860a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 861a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 862a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 863a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 864a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 865a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 866a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 867a04a10f8SKris Kennaway 868a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; 869a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 870a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 871a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 872a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 873a04a10f8SKris Kennaway } 874a04a10f8SKris Kennaway 875a04a10f8SKris Kennaway void 876a04a10f8SKris Kennaway channel_handler_init_15(void) 877a04a10f8SKris Kennaway { 878a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15; 879a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 880a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 881a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 882a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 883a04a10f8SKris Kennaway 884a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 885a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 886a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 887a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; 888a04a10f8SKris Kennaway } 889a04a10f8SKris Kennaway 890a04a10f8SKris Kennaway void 891a04a10f8SKris Kennaway channel_handler_init(void) 892a04a10f8SKris Kennaway { 893a04a10f8SKris Kennaway int i; 894a04a10f8SKris Kennaway for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 895a04a10f8SKris Kennaway channel_pre[i] = NULL; 896a04a10f8SKris Kennaway channel_post[i] = NULL; 897a04a10f8SKris Kennaway } 898a04a10f8SKris Kennaway if (compat20) 899a04a10f8SKris Kennaway channel_handler_init_20(); 900a04a10f8SKris Kennaway else if (compat13) 901a04a10f8SKris Kennaway channel_handler_init_13(); 902a04a10f8SKris Kennaway else 903a04a10f8SKris Kennaway channel_handler_init_15(); 904a04a10f8SKris Kennaway } 905a04a10f8SKris Kennaway 906a04a10f8SKris Kennaway void 907a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 908a04a10f8SKris Kennaway { 909a04a10f8SKris Kennaway static int did_init = 0; 910a04a10f8SKris Kennaway int i; 911a04a10f8SKris Kennaway Channel *c; 912a04a10f8SKris Kennaway 913a04a10f8SKris Kennaway if (!did_init) { 914a04a10f8SKris Kennaway channel_handler_init(); 915a04a10f8SKris Kennaway did_init = 1; 916a04a10f8SKris Kennaway } 917a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 918a04a10f8SKris Kennaway c = &channels[i]; 919a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_FREE) 920511b41d2SMark Murray continue; 921a04a10f8SKris Kennaway if (ftab[c->type] == NULL) 922a04a10f8SKris Kennaway continue; 923a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 924a04a10f8SKris Kennaway chan_delete_if_full_closed(c); 925511b41d2SMark Murray } 926511b41d2SMark Murray } 927a04a10f8SKris Kennaway 928a04a10f8SKris Kennaway void 929a04a10f8SKris Kennaway channel_prepare_select(fd_set * readset, fd_set * writeset) 930a04a10f8SKris Kennaway { 931a04a10f8SKris Kennaway channel_handler(channel_pre, readset, writeset); 932a04a10f8SKris Kennaway } 933a04a10f8SKris Kennaway 934a04a10f8SKris Kennaway void 935a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 936a04a10f8SKris Kennaway { 937a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 938511b41d2SMark Murray } 939511b41d2SMark Murray 940511b41d2SMark Murray /* If there is data to send to the connection, send some of it now. */ 941511b41d2SMark Murray 942511b41d2SMark Murray void 943511b41d2SMark Murray channel_output_poll() 944511b41d2SMark Murray { 945511b41d2SMark Murray int len, i; 946a04a10f8SKris Kennaway Channel *c; 947511b41d2SMark Murray 948511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 949a04a10f8SKris Kennaway c = &channels[i]; 950511b41d2SMark Murray 951511b41d2SMark Murray /* We are only interested in channels that can have buffered incoming data. */ 952511b41d2SMark Murray if (compat13) { 953a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 954a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 955511b41d2SMark Murray continue; 956511b41d2SMark Murray } else { 957a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 958511b41d2SMark Murray continue; 959a04a10f8SKris Kennaway if (c->istate != CHAN_INPUT_OPEN && 960a04a10f8SKris Kennaway c->istate != CHAN_INPUT_WAIT_DRAIN) 961a04a10f8SKris Kennaway continue; 962a04a10f8SKris Kennaway } 963a04a10f8SKris Kennaway if (compat20 && 964a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 965a04a10f8SKris Kennaway debug("channel: %d: no data after CLOSE", c->self); 966511b41d2SMark Murray continue; 967511b41d2SMark Murray } 968511b41d2SMark Murray 969511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 970a04a10f8SKris Kennaway len = buffer_len(&c->input); 971511b41d2SMark Murray if (len > 0) { 972511b41d2SMark Murray /* Send some data for the other side over the secure connection. */ 973a04a10f8SKris Kennaway if (compat20) { 974a04a10f8SKris Kennaway if (len > c->remote_window) 975a04a10f8SKris Kennaway len = c->remote_window; 976a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 977a04a10f8SKris Kennaway len = c->remote_maxpacket; 978a04a10f8SKris Kennaway } else { 979511b41d2SMark Murray if (packet_is_interactive()) { 980511b41d2SMark Murray if (len > 1024) 981511b41d2SMark Murray len = 512; 982511b41d2SMark Murray } else { 983511b41d2SMark Murray /* Keep the packets at reasonable size. */ 984511b41d2SMark Murray if (len > packet_get_maxsize()/2) 985511b41d2SMark Murray len = packet_get_maxsize()/2; 986511b41d2SMark Murray } 987a04a10f8SKris Kennaway } 988a04a10f8SKris Kennaway if (len > 0) { 989a04a10f8SKris Kennaway packet_start(compat20 ? 990a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 991a04a10f8SKris Kennaway packet_put_int(c->remote_id); 992a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 993511b41d2SMark Murray packet_send(); 994a04a10f8SKris Kennaway buffer_consume(&c->input, len); 995a04a10f8SKris Kennaway c->remote_window -= len; 996a04a10f8SKris Kennaway } 997a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 998511b41d2SMark Murray if (compat13) 999511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 1000511b41d2SMark Murray /* 1001511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 1002511b41d2SMark Murray * tell peer, that we will not send more data: send IEOF 1003511b41d2SMark Murray */ 1004a04a10f8SKris Kennaway chan_ibuf_empty(c); 1005a04a10f8SKris Kennaway } 1006a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 1007a04a10f8SKris Kennaway if (compat20 && 1008a04a10f8SKris Kennaway c->remote_window > 0 && 1009a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 1010a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 1011a04a10f8SKris Kennaway if (len > c->remote_window) 1012a04a10f8SKris Kennaway len = c->remote_window; 1013a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 1014a04a10f8SKris Kennaway len = c->remote_maxpacket; 1015a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 1016a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1017a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 1018a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 1019a04a10f8SKris Kennaway packet_send(); 1020a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1021a04a10f8SKris Kennaway c->remote_window -= len; 1022511b41d2SMark Murray } 1023511b41d2SMark Murray } 1024511b41d2SMark Murray } 1025511b41d2SMark Murray 1026511b41d2SMark Murray /* 1027511b41d2SMark Murray * This is called when a packet of type CHANNEL_DATA has just been received. 1028511b41d2SMark Murray * The message type has already been consumed, but channel number and data is 1029511b41d2SMark Murray * still there. 1030511b41d2SMark Murray */ 1031511b41d2SMark Murray 1032511b41d2SMark Murray void 10335b9b2fafSBrian Feldman channel_input_data(int type, int plen, void *ctxt) 1034511b41d2SMark Murray { 1035511b41d2SMark Murray int id; 1036511b41d2SMark Murray char *data; 1037511b41d2SMark Murray unsigned int data_len; 1038a04a10f8SKris Kennaway Channel *c; 1039511b41d2SMark Murray 1040511b41d2SMark Murray /* Get the channel number and verify it. */ 1041511b41d2SMark Murray id = packet_get_int(); 1042a04a10f8SKris Kennaway c = channel_lookup(id); 1043a04a10f8SKris Kennaway if (c == NULL) 1044511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1045511b41d2SMark Murray 1046511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1047a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1048a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1049511b41d2SMark Murray return; 1050511b41d2SMark Murray 1051511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1052a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1053511b41d2SMark Murray return; 1054511b41d2SMark Murray 1055511b41d2SMark Murray /* Get the data. */ 1056511b41d2SMark Murray data = packet_get_string(&data_len); 1057a04a10f8SKris Kennaway packet_done(); 1058a04a10f8SKris Kennaway 1059a04a10f8SKris Kennaway if (compat20){ 1060a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1061a04a10f8SKris Kennaway log("channel %d: rcvd big packet %d, maxpack %d", 1062a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1063a04a10f8SKris Kennaway } 1064a04a10f8SKris Kennaway if (data_len > c->local_window) { 1065a04a10f8SKris Kennaway log("channel %d: rcvd too much data %d, win %d", 1066a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1067a04a10f8SKris Kennaway xfree(data); 1068a04a10f8SKris Kennaway return; 1069a04a10f8SKris Kennaway } 1070a04a10f8SKris Kennaway c->local_window -= data_len; 1071a04a10f8SKris Kennaway }else{ 1072a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + 4 + data_len, type); 1073a04a10f8SKris Kennaway } 1074a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1075511b41d2SMark Murray xfree(data); 1076511b41d2SMark Murray } 1077a04a10f8SKris Kennaway void 10785b9b2fafSBrian Feldman channel_input_extended_data(int type, int plen, void *ctxt) 1079a04a10f8SKris Kennaway { 1080a04a10f8SKris Kennaway int id; 1081a04a10f8SKris Kennaway int tcode; 1082a04a10f8SKris Kennaway char *data; 1083a04a10f8SKris Kennaway unsigned int data_len; 1084a04a10f8SKris Kennaway Channel *c; 1085a04a10f8SKris Kennaway 1086a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1087a04a10f8SKris Kennaway id = packet_get_int(); 1088a04a10f8SKris Kennaway c = channel_lookup(id); 1089a04a10f8SKris Kennaway 1090a04a10f8SKris Kennaway if (c == NULL) 1091a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1092a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1093a04a10f8SKris Kennaway log("channel %d: ext data for non open", id); 1094a04a10f8SKris Kennaway return; 1095a04a10f8SKris Kennaway } 1096a04a10f8SKris Kennaway tcode = packet_get_int(); 1097a04a10f8SKris Kennaway if (c->efd == -1 || 1098a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1099a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1100a04a10f8SKris Kennaway log("channel %d: bad ext data", c->self); 1101a04a10f8SKris Kennaway return; 1102a04a10f8SKris Kennaway } 1103a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1104a04a10f8SKris Kennaway packet_done(); 1105a04a10f8SKris Kennaway if (data_len > c->local_window) { 1106a04a10f8SKris Kennaway log("channel %d: rcvd too much extended_data %d, win %d", 1107a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1108a04a10f8SKris Kennaway xfree(data); 1109a04a10f8SKris Kennaway return; 1110a04a10f8SKris Kennaway } 11115b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1112a04a10f8SKris Kennaway c->local_window -= data_len; 1113a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1114a04a10f8SKris Kennaway xfree(data); 1115a04a10f8SKris Kennaway } 1116a04a10f8SKris Kennaway 1117511b41d2SMark Murray 1118511b41d2SMark Murray /* 1119511b41d2SMark Murray * Returns true if no channel has too much buffered data, and false if one or 1120511b41d2SMark Murray * more channel is overfull. 1121511b41d2SMark Murray */ 1122511b41d2SMark Murray 1123511b41d2SMark Murray int 1124511b41d2SMark Murray channel_not_very_much_buffered_data() 1125511b41d2SMark Murray { 1126511b41d2SMark Murray unsigned int i; 1127a04a10f8SKris Kennaway Channel *c; 1128511b41d2SMark Murray 1129511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1130a04a10f8SKris Kennaway c = &channels[i]; 1131a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_OPEN) { 1132a04a10f8SKris Kennaway if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { 1133a04a10f8SKris Kennaway debug("channel %d: big input buffer %d", 1134a04a10f8SKris Kennaway c->self, buffer_len(&c->input)); 1135511b41d2SMark Murray return 0; 1136a04a10f8SKris Kennaway } 1137a04a10f8SKris Kennaway if (buffer_len(&c->output) > packet_get_maxsize()) { 1138a04a10f8SKris Kennaway debug("channel %d: big output buffer %d", 1139a04a10f8SKris Kennaway c->self, buffer_len(&c->output)); 1140511b41d2SMark Murray return 0; 1141511b41d2SMark Murray } 1142511b41d2SMark Murray } 1143a04a10f8SKris Kennaway } 1144511b41d2SMark Murray return 1; 1145511b41d2SMark Murray } 1146511b41d2SMark Murray 1147a04a10f8SKris Kennaway void 11485b9b2fafSBrian Feldman channel_input_ieof(int type, int plen, void *ctxt) 1149a04a10f8SKris Kennaway { 1150a04a10f8SKris Kennaway int id; 1151a04a10f8SKris Kennaway Channel *c; 1152a04a10f8SKris Kennaway 1153a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1154a04a10f8SKris Kennaway 1155a04a10f8SKris Kennaway id = packet_get_int(); 1156a04a10f8SKris Kennaway c = channel_lookup(id); 1157a04a10f8SKris Kennaway if (c == NULL) 1158a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1159a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1160a04a10f8SKris Kennaway } 1161511b41d2SMark Murray 1162511b41d2SMark Murray void 11635b9b2fafSBrian Feldman channel_input_close(int type, int plen, void *ctxt) 1164511b41d2SMark Murray { 1165a04a10f8SKris Kennaway int id; 1166a04a10f8SKris Kennaway Channel *c; 1167511b41d2SMark Murray 1168a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1169511b41d2SMark Murray 1170a04a10f8SKris Kennaway id = packet_get_int(); 1171a04a10f8SKris Kennaway c = channel_lookup(id); 1172a04a10f8SKris Kennaway if (c == NULL) 1173a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1174511b41d2SMark Murray 1175511b41d2SMark Murray /* 1176511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1177511b41d2SMark Murray * data is coming for it. 1178511b41d2SMark Murray */ 1179511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1180a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1181511b41d2SMark Murray packet_send(); 1182511b41d2SMark Murray 1183511b41d2SMark Murray /* 1184511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1185511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1186511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1187511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1188511b41d2SMark Murray * the confirmation arrives. 1189511b41d2SMark Murray */ 1190a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1191511b41d2SMark Murray /* 1192511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1193511b41d2SMark Murray * cause it to be freed later. 1194511b41d2SMark Murray */ 1195a04a10f8SKris Kennaway buffer_consume(&c->input, buffer_len(&c->input)); 1196a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1197511b41d2SMark Murray } 1198511b41d2SMark Murray } 1199511b41d2SMark Murray 1200a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1201a04a10f8SKris Kennaway void 12025b9b2fafSBrian Feldman channel_input_oclose(int type, int plen, void *ctxt) 1203a04a10f8SKris Kennaway { 1204a04a10f8SKris Kennaway int id = packet_get_int(); 1205a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1206a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1207a04a10f8SKris Kennaway if (c == NULL) 1208a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1209a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1210a04a10f8SKris Kennaway } 1211511b41d2SMark Murray 1212511b41d2SMark Murray void 12135b9b2fafSBrian Feldman channel_input_close_confirmation(int type, int plen, void *ctxt) 1214511b41d2SMark Murray { 1215a04a10f8SKris Kennaway int id = packet_get_int(); 1216a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1217a04a10f8SKris Kennaway 1218a04a10f8SKris Kennaway packet_done(); 1219a04a10f8SKris Kennaway if (c == NULL) 1220a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1221a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1222a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1223a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1224a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1225a04a10f8SKris Kennaway channel_free(c->self); 1226a04a10f8SKris Kennaway } 1227a04a10f8SKris Kennaway 1228a04a10f8SKris Kennaway void 12295b9b2fafSBrian Feldman channel_input_open_confirmation(int type, int plen, void *ctxt) 1230a04a10f8SKris Kennaway { 1231a04a10f8SKris Kennaway int id, remote_id; 1232a04a10f8SKris Kennaway Channel *c; 1233a04a10f8SKris Kennaway 1234a04a10f8SKris Kennaway if (!compat20) 1235a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + 4, type); 1236a04a10f8SKris Kennaway 1237a04a10f8SKris Kennaway id = packet_get_int(); 1238a04a10f8SKris Kennaway c = channel_lookup(id); 1239a04a10f8SKris Kennaway 1240a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1241a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1242a04a10f8SKris Kennaway "non-opening channel %d.", id); 1243a04a10f8SKris Kennaway remote_id = packet_get_int(); 1244a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1245a04a10f8SKris Kennaway c->remote_id = remote_id; 1246a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1247a04a10f8SKris Kennaway 1248a04a10f8SKris Kennaway if (compat20) { 1249a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 1250a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 1251a04a10f8SKris Kennaway packet_done(); 1252a04a10f8SKris Kennaway if (c->cb_fn != NULL && c->cb_event == type) { 12535b9b2fafSBrian Feldman debug2("callback start"); 1254a04a10f8SKris Kennaway c->cb_fn(c->self, c->cb_arg); 12555b9b2fafSBrian Feldman debug2("callback done"); 1256a04a10f8SKris Kennaway } 1257a04a10f8SKris Kennaway debug("channel %d: open confirm rwindow %d rmax %d", c->self, 1258a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 1259a04a10f8SKris Kennaway } 1260a04a10f8SKris Kennaway } 1261a04a10f8SKris Kennaway 1262a04a10f8SKris Kennaway void 12635b9b2fafSBrian Feldman channel_input_open_failure(int type, int plen, void *ctxt) 1264a04a10f8SKris Kennaway { 1265a04a10f8SKris Kennaway int id; 1266a04a10f8SKris Kennaway Channel *c; 1267a04a10f8SKris Kennaway 1268a04a10f8SKris Kennaway if (!compat20) 1269a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1270a04a10f8SKris Kennaway 1271a04a10f8SKris Kennaway id = packet_get_int(); 1272a04a10f8SKris Kennaway c = channel_lookup(id); 1273a04a10f8SKris Kennaway 1274a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1275a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 1276a04a10f8SKris Kennaway "non-opening channel %d.", id); 1277a04a10f8SKris Kennaway if (compat20) { 1278a04a10f8SKris Kennaway int reason = packet_get_int(); 1279a04a10f8SKris Kennaway char *msg = packet_get_string(NULL); 1280a04a10f8SKris Kennaway char *lang = packet_get_string(NULL); 1281a04a10f8SKris Kennaway log("channel_open_failure: %d: reason %d: %s", id, reason, msg); 1282a04a10f8SKris Kennaway packet_done(); 1283a04a10f8SKris Kennaway xfree(msg); 1284a04a10f8SKris Kennaway xfree(lang); 1285a04a10f8SKris Kennaway } 1286a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 1287a04a10f8SKris Kennaway channel_free(id); 1288a04a10f8SKris Kennaway } 1289a04a10f8SKris Kennaway 1290a04a10f8SKris Kennaway void 12915b9b2fafSBrian Feldman channel_input_channel_request(int type, int plen, void *ctxt) 1292a04a10f8SKris Kennaway { 1293a04a10f8SKris Kennaway int id; 1294a04a10f8SKris Kennaway Channel *c; 1295a04a10f8SKris Kennaway 1296a04a10f8SKris Kennaway id = packet_get_int(); 1297a04a10f8SKris Kennaway c = channel_lookup(id); 1298a04a10f8SKris Kennaway 1299a04a10f8SKris Kennaway if (c == NULL || 1300a04a10f8SKris Kennaway (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL)) 1301a04a10f8SKris Kennaway packet_disconnect("Received request for " 1302a04a10f8SKris Kennaway "non-open channel %d.", id); 1303a04a10f8SKris Kennaway if (c->cb_fn != NULL && c->cb_event == type) { 13045b9b2fafSBrian Feldman debug2("callback start"); 1305a04a10f8SKris Kennaway c->cb_fn(c->self, c->cb_arg); 13065b9b2fafSBrian Feldman debug2("callback done"); 1307a04a10f8SKris Kennaway } else { 1308a04a10f8SKris Kennaway char *service = packet_get_string(NULL); 1309a04a10f8SKris Kennaway debug("channel: %d rcvd request for %s", c->self, service); 1310a04a10f8SKris Kennaway debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event); 1311a04a10f8SKris Kennaway xfree(service); 1312a04a10f8SKris Kennaway } 1313a04a10f8SKris Kennaway } 1314a04a10f8SKris Kennaway 1315a04a10f8SKris Kennaway void 13165b9b2fafSBrian Feldman channel_input_window_adjust(int type, int plen, void *ctxt) 1317a04a10f8SKris Kennaway { 1318a04a10f8SKris Kennaway Channel *c; 1319a04a10f8SKris Kennaway int id, adjust; 1320a04a10f8SKris Kennaway 1321a04a10f8SKris Kennaway if (!compat20) 1322a04a10f8SKris Kennaway return; 1323511b41d2SMark Murray 1324511b41d2SMark Murray /* Get the channel number and verify it. */ 1325a04a10f8SKris Kennaway id = packet_get_int(); 1326a04a10f8SKris Kennaway c = channel_lookup(id); 1327511b41d2SMark Murray 1328a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 1329a04a10f8SKris Kennaway log("Received window adjust for " 1330a04a10f8SKris Kennaway "non-open channel %d.", id); 1331511b41d2SMark Murray return; 1332511b41d2SMark Murray } 1333a04a10f8SKris Kennaway adjust = packet_get_int(); 1334a04a10f8SKris Kennaway packet_done(); 13355b9b2fafSBrian Feldman debug2("channel %d: rcvd adjust %d", id, adjust); 1336a04a10f8SKris Kennaway c->remote_window += adjust; 1337511b41d2SMark Murray } 1338511b41d2SMark Murray 1339511b41d2SMark Murray /* 1340511b41d2SMark Murray * Stops listening for channels, and removes any unix domain sockets that we 1341511b41d2SMark Murray * might have. 1342511b41d2SMark Murray */ 1343511b41d2SMark Murray 1344511b41d2SMark Murray void 1345511b41d2SMark Murray channel_stop_listening() 1346511b41d2SMark Murray { 1347511b41d2SMark Murray int i; 1348511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1349511b41d2SMark Murray switch (channels[i].type) { 1350511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1351511b41d2SMark Murray close(channels[i].sock); 1352511b41d2SMark Murray remove(channels[i].path); 1353511b41d2SMark Murray channel_free(i); 1354511b41d2SMark Murray break; 1355511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1356511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1357511b41d2SMark Murray close(channels[i].sock); 1358511b41d2SMark Murray channel_free(i); 1359511b41d2SMark Murray break; 1360511b41d2SMark Murray default: 1361511b41d2SMark Murray break; 1362511b41d2SMark Murray } 1363511b41d2SMark Murray } 1364511b41d2SMark Murray } 1365511b41d2SMark Murray 1366511b41d2SMark Murray /* 1367a04a10f8SKris Kennaway * Closes the sockets/fds of all channels. This is used to close extra file 1368511b41d2SMark Murray * descriptors after a fork. 1369511b41d2SMark Murray */ 1370511b41d2SMark Murray 1371511b41d2SMark Murray void 1372511b41d2SMark Murray channel_close_all() 1373511b41d2SMark Murray { 1374511b41d2SMark Murray int i; 1375a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) 1376511b41d2SMark Murray if (channels[i].type != SSH_CHANNEL_FREE) 1377a04a10f8SKris Kennaway channel_close_fds(&channels[i]); 1378511b41d2SMark Murray } 1379511b41d2SMark Murray 1380511b41d2SMark Murray /* Returns the maximum file descriptor number used by the channels. */ 1381511b41d2SMark Murray 1382511b41d2SMark Murray int 1383511b41d2SMark Murray channel_max_fd() 1384511b41d2SMark Murray { 1385511b41d2SMark Murray return channel_max_fd_value; 1386511b41d2SMark Murray } 1387511b41d2SMark Murray 1388511b41d2SMark Murray /* Returns true if any channel is still open. */ 1389511b41d2SMark Murray 1390511b41d2SMark Murray int 1391511b41d2SMark Murray channel_still_open() 1392511b41d2SMark Murray { 1393511b41d2SMark Murray unsigned int i; 1394511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 1395511b41d2SMark Murray switch (channels[i].type) { 1396511b41d2SMark Murray case SSH_CHANNEL_FREE: 1397511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1398511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1399511b41d2SMark Murray case SSH_CHANNEL_CLOSED: 1400511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1401511b41d2SMark Murray continue; 1402a04a10f8SKris Kennaway case SSH_CHANNEL_LARVAL: 1403a04a10f8SKris Kennaway if (!compat20) 1404a04a10f8SKris Kennaway fatal("cannot happen: SSH_CHANNEL_LARVAL"); 1405a04a10f8SKris Kennaway continue; 1406511b41d2SMark Murray case SSH_CHANNEL_OPENING: 1407511b41d2SMark Murray case SSH_CHANNEL_OPEN: 1408511b41d2SMark Murray case SSH_CHANNEL_X11_OPEN: 1409511b41d2SMark Murray return 1; 1410511b41d2SMark Murray case SSH_CHANNEL_INPUT_DRAINING: 1411511b41d2SMark Murray case SSH_CHANNEL_OUTPUT_DRAINING: 1412511b41d2SMark Murray if (!compat13) 1413511b41d2SMark Murray fatal("cannot happen: OUT_DRAIN"); 1414511b41d2SMark Murray return 1; 1415511b41d2SMark Murray default: 1416511b41d2SMark Murray fatal("channel_still_open: bad channel type %d", channels[i].type); 1417511b41d2SMark Murray /* NOTREACHED */ 1418511b41d2SMark Murray } 1419511b41d2SMark Murray return 0; 1420511b41d2SMark Murray } 1421511b41d2SMark Murray 1422511b41d2SMark Murray /* 1423511b41d2SMark Murray * Returns a message describing the currently open forwarded connections, 1424511b41d2SMark Murray * suitable for sending to the client. The message contains crlf pairs for 1425511b41d2SMark Murray * newlines. 1426511b41d2SMark Murray */ 1427511b41d2SMark Murray 1428511b41d2SMark Murray char * 1429511b41d2SMark Murray channel_open_message() 1430511b41d2SMark Murray { 1431511b41d2SMark Murray Buffer buffer; 1432511b41d2SMark Murray int i; 1433511b41d2SMark Murray char buf[512], *cp; 1434511b41d2SMark Murray 1435511b41d2SMark Murray buffer_init(&buffer); 1436511b41d2SMark Murray snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 1437511b41d2SMark Murray buffer_append(&buffer, buf, strlen(buf)); 1438511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1439511b41d2SMark Murray Channel *c = &channels[i]; 1440511b41d2SMark Murray switch (c->type) { 1441511b41d2SMark Murray case SSH_CHANNEL_FREE: 1442511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1443511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1444511b41d2SMark Murray case SSH_CHANNEL_CLOSED: 1445511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1446511b41d2SMark Murray continue; 1447a04a10f8SKris Kennaway case SSH_CHANNEL_LARVAL: 1448511b41d2SMark Murray case SSH_CHANNEL_OPENING: 1449511b41d2SMark Murray case SSH_CHANNEL_OPEN: 1450511b41d2SMark Murray case SSH_CHANNEL_X11_OPEN: 1451511b41d2SMark Murray case SSH_CHANNEL_INPUT_DRAINING: 1452511b41d2SMark Murray case SSH_CHANNEL_OUTPUT_DRAINING: 1453a04a10f8SKris Kennaway snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 1454511b41d2SMark Murray c->self, c->remote_name, 1455511b41d2SMark Murray c->type, c->remote_id, 1456511b41d2SMark Murray c->istate, buffer_len(&c->input), 1457a04a10f8SKris Kennaway c->ostate, buffer_len(&c->output), 1458a04a10f8SKris Kennaway c->rfd, c->wfd); 1459511b41d2SMark Murray buffer_append(&buffer, buf, strlen(buf)); 1460511b41d2SMark Murray continue; 1461511b41d2SMark Murray default: 1462a04a10f8SKris Kennaway fatal("channel_open_message: bad channel type %d", c->type); 1463511b41d2SMark Murray /* NOTREACHED */ 1464511b41d2SMark Murray } 1465511b41d2SMark Murray } 1466511b41d2SMark Murray buffer_append(&buffer, "\0", 1); 1467511b41d2SMark Murray cp = xstrdup(buffer_ptr(&buffer)); 1468511b41d2SMark Murray buffer_free(&buffer); 1469511b41d2SMark Murray return cp; 1470511b41d2SMark Murray } 1471511b41d2SMark Murray 1472511b41d2SMark Murray /* 1473511b41d2SMark Murray * Initiate forwarding of connections to local port "port" through the secure 1474511b41d2SMark Murray * channel to host:port from remote side. 1475511b41d2SMark Murray */ 1476511b41d2SMark Murray 1477511b41d2SMark Murray void 1478511b41d2SMark Murray channel_request_local_forwarding(u_short port, const char *host, 1479511b41d2SMark Murray u_short host_port, int gateway_ports) 1480511b41d2SMark Murray { 1481511b41d2SMark Murray int success, ch, sock, on = 1; 1482511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1483511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 1484511b41d2SMark Murray struct linger linger; 1485511b41d2SMark Murray 1486511b41d2SMark Murray if (strlen(host) > sizeof(channels[0].path) - 1) 1487511b41d2SMark Murray packet_disconnect("Forward host name too long."); 1488511b41d2SMark Murray 1489511b41d2SMark Murray /* 1490511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 1491511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 1492511b41d2SMark Murray */ 1493511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1494511b41d2SMark Murray hints.ai_family = IPv4or6; 1495511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 1496511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1497511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 1498511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 1499511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 1500511b41d2SMark Murray 1501511b41d2SMark Murray success = 0; 1502511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1503511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1504511b41d2SMark Murray continue; 1505511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 1506511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 1507511b41d2SMark Murray error("channel_request_local_forwarding: getnameinfo failed"); 1508511b41d2SMark Murray continue; 1509511b41d2SMark Murray } 1510511b41d2SMark Murray /* Create a port to listen for the host. */ 1511511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1512511b41d2SMark Murray if (sock < 0) { 1513511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 1514511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 1515511b41d2SMark Murray continue; 1516511b41d2SMark Murray } 1517511b41d2SMark Murray /* 1518511b41d2SMark Murray * Set socket options. We would like the socket to disappear 1519511b41d2SMark Murray * as soon as it has been closed for whatever reason. 1520511b41d2SMark Murray */ 1521511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); 1522511b41d2SMark Murray linger.l_onoff = 1; 1523511b41d2SMark Murray linger.l_linger = 5; 1524511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); 1525511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 1526511b41d2SMark Murray 1527511b41d2SMark Murray /* Bind the socket to the address. */ 1528511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1529511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 1530511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 1531511b41d2SMark Murray close(sock); 1532511b41d2SMark Murray continue; 1533511b41d2SMark Murray } 1534511b41d2SMark Murray /* Start listening for connections on the socket. */ 1535511b41d2SMark Murray if (listen(sock, 5) < 0) { 1536511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 1537511b41d2SMark Murray close(sock); 1538511b41d2SMark Murray continue; 1539511b41d2SMark Murray } 1540511b41d2SMark Murray /* Allocate a channel number for the socket. */ 1541a04a10f8SKris Kennaway ch = channel_new( 1542a04a10f8SKris Kennaway "port listener", SSH_CHANNEL_PORT_LISTENER, 1543a04a10f8SKris Kennaway sock, sock, -1, 1544a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 15455b9b2fafSBrian Feldman 0, xstrdup("port listener"), 1); 1546511b41d2SMark Murray strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); 1547511b41d2SMark Murray channels[ch].host_port = host_port; 1548511b41d2SMark Murray channels[ch].listening_port = port; 1549511b41d2SMark Murray success = 1; 1550511b41d2SMark Murray } 1551511b41d2SMark Murray if (success == 0) 1552511b41d2SMark Murray packet_disconnect("cannot listen port: %d", port); 1553511b41d2SMark Murray freeaddrinfo(aitop); 1554511b41d2SMark Murray } 1555511b41d2SMark Murray 1556511b41d2SMark Murray /* 1557511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 1558511b41d2SMark Murray * the secure channel to host:port from local side. 1559511b41d2SMark Murray */ 1560511b41d2SMark Murray 1561511b41d2SMark Murray void 1562a04a10f8SKris Kennaway channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, 1563a04a10f8SKris Kennaway u_short port_to_connect) 1564511b41d2SMark Murray { 1565511b41d2SMark Murray int payload_len; 1566511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 1567511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 1568511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 1569511b41d2SMark Murray 1570a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 1571a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 1572a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].listen_port = listen_port; 1573511b41d2SMark Murray num_permitted_opens++; 1574511b41d2SMark Murray 1575511b41d2SMark Murray /* Send the forward request to the remote side. */ 1576a04a10f8SKris Kennaway if (compat20) { 1577a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 1578a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 1579a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 1580a04a10f8SKris Kennaway packet_put_char(0); /* boolean: want reply */ 1581a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 1582a04a10f8SKris Kennaway packet_put_int(listen_port); 1583a04a10f8SKris Kennaway } else { 1584511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 1585a04a10f8SKris Kennaway packet_put_int(listen_port); 1586a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 1587a04a10f8SKris Kennaway packet_put_int(port_to_connect); 1588511b41d2SMark Murray packet_send(); 1589511b41d2SMark Murray packet_write_wait(); 1590511b41d2SMark Murray /* 1591511b41d2SMark Murray * Wait for response from the remote side. It will send a disconnect 1592511b41d2SMark Murray * message on failure, and we will never see it here. 1593511b41d2SMark Murray */ 1594511b41d2SMark Murray packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); 1595511b41d2SMark Murray } 1596a04a10f8SKris Kennaway } 1597511b41d2SMark Murray 1598511b41d2SMark Murray /* 1599511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 1600511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 1601511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 1602511b41d2SMark Murray */ 1603511b41d2SMark Murray 1604511b41d2SMark Murray void 1605a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 1606511b41d2SMark Murray { 1607511b41d2SMark Murray u_short port, host_port; 1608511b41d2SMark Murray char *hostname; 1609511b41d2SMark Murray 1610511b41d2SMark Murray /* Get arguments from the packet. */ 1611511b41d2SMark Murray port = packet_get_int(); 1612511b41d2SMark Murray hostname = packet_get_string(NULL); 1613511b41d2SMark Murray host_port = packet_get_int(); 1614511b41d2SMark Murray 1615511b41d2SMark Murray /* 1616511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 1617511b41d2SMark Murray * privileged port. 1618511b41d2SMark Murray */ 1619511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 1620511b41d2SMark Murray packet_disconnect("Requested forwarding of port %d but user is not root.", 1621511b41d2SMark Murray port); 1622511b41d2SMark Murray /* 1623511b41d2SMark Murray * Initiate forwarding, 1624511b41d2SMark Murray */ 1625a04a10f8SKris Kennaway channel_request_local_forwarding(port, hostname, host_port, gateway_ports); 1626511b41d2SMark Murray 1627511b41d2SMark Murray /* Free the argument string. */ 1628511b41d2SMark Murray xfree(hostname); 1629511b41d2SMark Murray } 1630511b41d2SMark Murray 1631a04a10f8SKris Kennaway /* XXX move to aux.c */ 1632a04a10f8SKris Kennaway int 1633a04a10f8SKris Kennaway channel_connect_to(const char *host, u_short host_port) 1634511b41d2SMark Murray { 1635511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1636511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 1637511b41d2SMark Murray int gaierr; 1638a04a10f8SKris Kennaway int sock = -1; 1639511b41d2SMark Murray 1640511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1641511b41d2SMark Murray hints.ai_family = IPv4or6; 1642511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1643511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", host_port); 1644511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 1645511b41d2SMark Murray error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); 1646a04a10f8SKris Kennaway return -1; 1647511b41d2SMark Murray } 1648511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1649511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1650511b41d2SMark Murray continue; 1651511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 1652511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 1653a04a10f8SKris Kennaway error("channel_connect_to: getnameinfo failed"); 1654511b41d2SMark Murray continue; 1655511b41d2SMark Murray } 1656511b41d2SMark Murray /* Create the socket. */ 1657511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1658511b41d2SMark Murray if (sock < 0) { 1659511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1660511b41d2SMark Murray continue; 1661511b41d2SMark Murray } 1662511b41d2SMark Murray /* Connect to the host/port. */ 1663511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1664511b41d2SMark Murray error("connect %.100s port %s: %.100s", ntop, strport, 1665511b41d2SMark Murray strerror(errno)); 1666511b41d2SMark Murray close(sock); 1667511b41d2SMark Murray continue; /* fail -- try next */ 1668511b41d2SMark Murray } 1669511b41d2SMark Murray break; /* success */ 1670511b41d2SMark Murray 1671511b41d2SMark Murray } 1672511b41d2SMark Murray freeaddrinfo(aitop); 1673511b41d2SMark Murray if (!ai) { 1674511b41d2SMark Murray error("connect %.100s port %d: failed.", host, host_port); 1675a04a10f8SKris Kennaway return -1; 1676a04a10f8SKris Kennaway } 1677a04a10f8SKris Kennaway /* success */ 1678a04a10f8SKris Kennaway return sock; 1679a04a10f8SKris Kennaway } 1680a04a10f8SKris Kennaway /* 1681a04a10f8SKris Kennaway * This is called after receiving PORT_OPEN message. This attempts to 1682a04a10f8SKris Kennaway * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION 1683a04a10f8SKris Kennaway * or CHANNEL_OPEN_FAILURE. 1684a04a10f8SKris Kennaway */ 1685a04a10f8SKris Kennaway 1686a04a10f8SKris Kennaway void 16875b9b2fafSBrian Feldman channel_input_port_open(int type, int plen, void *ctxt) 1688a04a10f8SKris Kennaway { 1689a04a10f8SKris Kennaway u_short host_port; 1690a04a10f8SKris Kennaway char *host, *originator_string; 1691a04a10f8SKris Kennaway int remote_channel, sock = -1, newch, i, denied; 1692a04a10f8SKris Kennaway unsigned int host_len, originator_len; 1693a04a10f8SKris Kennaway 1694a04a10f8SKris Kennaway /* Get remote channel number. */ 1695a04a10f8SKris Kennaway remote_channel = packet_get_int(); 1696a04a10f8SKris Kennaway 1697a04a10f8SKris Kennaway /* Get host name to connect to. */ 1698a04a10f8SKris Kennaway host = packet_get_string(&host_len); 1699a04a10f8SKris Kennaway 1700a04a10f8SKris Kennaway /* Get port to connect to. */ 1701a04a10f8SKris Kennaway host_port = packet_get_int(); 1702a04a10f8SKris Kennaway 1703a04a10f8SKris Kennaway /* Get remote originator name. */ 1704a04a10f8SKris Kennaway if (have_hostname_in_open) { 1705a04a10f8SKris Kennaway originator_string = packet_get_string(&originator_len); 1706a04a10f8SKris Kennaway originator_len += 4; /* size of packet_int */ 1707a04a10f8SKris Kennaway } else { 1708a04a10f8SKris Kennaway originator_string = xstrdup("unknown (remote did not supply name)"); 1709a04a10f8SKris Kennaway originator_len = 0; /* no originator supplied */ 1710511b41d2SMark Murray } 1711511b41d2SMark Murray 1712a04a10f8SKris Kennaway packet_integrity_check(plen, 1713a04a10f8SKris Kennaway 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); 1714511b41d2SMark Murray 1715a04a10f8SKris Kennaway /* Check if opening that port is permitted. */ 1716a04a10f8SKris Kennaway denied = 0; 1717a04a10f8SKris Kennaway if (!all_opens_permitted) { 1718a04a10f8SKris Kennaway /* Go trough all permitted ports. */ 1719a04a10f8SKris Kennaway for (i = 0; i < num_permitted_opens; i++) 1720a04a10f8SKris Kennaway if (permitted_opens[i].port_to_connect == host_port && 1721a04a10f8SKris Kennaway strcmp(permitted_opens[i].host_to_connect, host) == 0) 1722a04a10f8SKris Kennaway break; 1723a04a10f8SKris Kennaway 1724a04a10f8SKris Kennaway /* Check if we found the requested port among those permitted. */ 1725a04a10f8SKris Kennaway if (i >= num_permitted_opens) { 1726a04a10f8SKris Kennaway /* The port is not permitted. */ 1727a04a10f8SKris Kennaway log("Received request to connect to %.100s:%d, but the request was denied.", 1728a04a10f8SKris Kennaway host, host_port); 1729a04a10f8SKris Kennaway denied = 1; 1730a04a10f8SKris Kennaway } 1731a04a10f8SKris Kennaway } 1732a04a10f8SKris Kennaway sock = denied ? -1 : channel_connect_to(host, host_port); 1733a04a10f8SKris Kennaway if (sock > 0) { 1734511b41d2SMark Murray /* Allocate a channel for this connection. */ 1735511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); 1736511b41d2SMark Murray channels[newch].remote_id = remote_channel; 1737511b41d2SMark Murray 1738511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1739511b41d2SMark Murray packet_put_int(remote_channel); 1740511b41d2SMark Murray packet_put_int(newch); 1741511b41d2SMark Murray packet_send(); 1742a04a10f8SKris Kennaway } else { 1743511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1744511b41d2SMark Murray packet_put_int(remote_channel); 1745511b41d2SMark Murray packet_send(); 1746511b41d2SMark Murray } 1747a04a10f8SKris Kennaway xfree(host); 1748a04a10f8SKris Kennaway } 1749511b41d2SMark Murray 1750511b41d2SMark Murray /* 1751511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 1752511b41d2SMark Murray * Returns a suitable value for the DISPLAY variable, or NULL if an error 1753511b41d2SMark Murray * occurs. 1754511b41d2SMark Murray */ 1755511b41d2SMark Murray 1756511b41d2SMark Murray #define NUM_SOCKS 10 1757511b41d2SMark Murray 1758511b41d2SMark Murray char * 1759511b41d2SMark Murray x11_create_display_inet(int screen_number, int x11_display_offset) 1760511b41d2SMark Murray { 1761511b41d2SMark Murray int display_number, sock; 1762511b41d2SMark Murray u_short port; 1763511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1764511b41d2SMark Murray char strport[NI_MAXSERV]; 1765511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 1766511b41d2SMark Murray char display[512]; 1767511b41d2SMark Murray char hostname[MAXHOSTNAMELEN]; 1768511b41d2SMark Murray 1769511b41d2SMark Murray for (display_number = x11_display_offset; 1770511b41d2SMark Murray display_number < MAX_DISPLAYS; 1771511b41d2SMark Murray display_number++) { 1772511b41d2SMark Murray port = 6000 + display_number; 1773511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1774511b41d2SMark Murray hints.ai_family = IPv4or6; 1775511b41d2SMark Murray hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */ 1776511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1777511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 1778511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 1779511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 1780511b41d2SMark Murray return NULL; 1781511b41d2SMark Murray } 1782511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1783511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1784511b41d2SMark Murray continue; 1785511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1786511b41d2SMark Murray if (sock < 0) { 1787511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1788511b41d2SMark Murray return NULL; 1789511b41d2SMark Murray } 1790511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1791511b41d2SMark Murray debug("bind port %d: %.100s", port, strerror(errno)); 1792511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 1793511b41d2SMark Murray close(sock); 1794511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1795511b41d2SMark Murray shutdown(socks[n], SHUT_RDWR); 1796511b41d2SMark Murray close(socks[n]); 1797511b41d2SMark Murray } 1798511b41d2SMark Murray num_socks = 0; 1799511b41d2SMark Murray break; 1800511b41d2SMark Murray } 1801511b41d2SMark Murray socks[num_socks++] = sock; 1802511b41d2SMark Murray if (num_socks == NUM_SOCKS) 1803511b41d2SMark Murray break; 1804511b41d2SMark Murray } 1805511b41d2SMark Murray if (num_socks > 0) 1806511b41d2SMark Murray break; 1807511b41d2SMark Murray } 1808511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 1809511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 1810511b41d2SMark Murray return NULL; 1811511b41d2SMark Murray } 1812511b41d2SMark Murray /* Start listening for connections on the socket. */ 1813511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1814511b41d2SMark Murray sock = socks[n]; 1815511b41d2SMark Murray if (listen(sock, 5) < 0) { 1816511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 1817511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 1818511b41d2SMark Murray close(sock); 1819511b41d2SMark Murray return NULL; 1820511b41d2SMark Murray } 1821511b41d2SMark Murray } 1822511b41d2SMark Murray 1823511b41d2SMark Murray /* Set up a suitable value for the DISPLAY variable. */ 1824511b41d2SMark Murray if (gethostname(hostname, sizeof(hostname)) < 0) 1825511b41d2SMark Murray fatal("gethostname: %.100s", strerror(errno)); 1826511b41d2SMark Murray snprintf(display, sizeof display, "%.400s:%d.%d", hostname, 1827511b41d2SMark Murray display_number, screen_number); 1828511b41d2SMark Murray 1829511b41d2SMark Murray /* Allocate a channel for each socket. */ 1830511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1831511b41d2SMark Murray sock = socks[n]; 1832a04a10f8SKris Kennaway (void) channel_new("x11 listener", 1833a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 1834a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 18355b9b2fafSBrian Feldman 0, xstrdup("X11 inet listener"), 1); 1836511b41d2SMark Murray } 1837511b41d2SMark Murray 1838511b41d2SMark Murray /* Return a suitable value for the DISPLAY environment variable. */ 1839511b41d2SMark Murray return xstrdup(display); 1840511b41d2SMark Murray } 1841511b41d2SMark Murray 1842511b41d2SMark Murray #ifndef X_UNIX_PATH 1843511b41d2SMark Murray #define X_UNIX_PATH "/tmp/.X11-unix/X" 1844511b41d2SMark Murray #endif 1845511b41d2SMark Murray 1846511b41d2SMark Murray static 1847511b41d2SMark Murray int 1848511b41d2SMark Murray connect_local_xsocket(unsigned int dnr) 1849511b41d2SMark Murray { 1850511b41d2SMark Murray static const char *const x_sockets[] = { 1851511b41d2SMark Murray X_UNIX_PATH "%u", 1852511b41d2SMark Murray "/var/X/.X11-unix/X" "%u", 1853511b41d2SMark Murray "/usr/spool/sockets/X11/" "%u", 1854511b41d2SMark Murray NULL 1855511b41d2SMark Murray }; 1856511b41d2SMark Murray int sock; 1857511b41d2SMark Murray struct sockaddr_un addr; 1858511b41d2SMark Murray const char *const * path; 1859511b41d2SMark Murray 1860511b41d2SMark Murray for (path = x_sockets; *path; ++path) { 1861511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 1862511b41d2SMark Murray if (sock < 0) 1863511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1864511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 1865511b41d2SMark Murray addr.sun_family = AF_UNIX; 1866511b41d2SMark Murray snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); 1867511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 1868511b41d2SMark Murray return sock; 1869511b41d2SMark Murray close(sock); 1870511b41d2SMark Murray } 1871511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 1872511b41d2SMark Murray return -1; 1873511b41d2SMark Murray } 1874511b41d2SMark Murray 1875a04a10f8SKris Kennaway int 1876a04a10f8SKris Kennaway x11_connect_display(void) 1877511b41d2SMark Murray { 1878a04a10f8SKris Kennaway int display_number, sock = 0; 1879511b41d2SMark Murray const char *display; 1880a04a10f8SKris Kennaway char buf[1024], *cp; 1881511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1882511b41d2SMark Murray char strport[NI_MAXSERV]; 1883511b41d2SMark Murray int gaierr; 1884511b41d2SMark Murray 1885511b41d2SMark Murray /* Try to open a socket for the local X server. */ 1886511b41d2SMark Murray display = getenv("DISPLAY"); 1887511b41d2SMark Murray if (!display) { 1888511b41d2SMark Murray error("DISPLAY not set."); 1889a04a10f8SKris Kennaway return -1; 1890511b41d2SMark Murray } 1891511b41d2SMark Murray /* 1892511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 1893511b41d2SMark Murray * connection to the real X server. 1894511b41d2SMark Murray */ 1895511b41d2SMark Murray 1896511b41d2SMark Murray /* 1897511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 1898511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 1899511b41d2SMark Murray */ 1900511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 1901511b41d2SMark Murray display[0] == ':') { 1902511b41d2SMark Murray /* Connect to the unix domain socket. */ 1903511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 1904511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 1905511b41d2SMark Murray display); 1906a04a10f8SKris Kennaway return -1; 1907511b41d2SMark Murray } 1908511b41d2SMark Murray /* Create a socket. */ 1909511b41d2SMark Murray sock = connect_local_xsocket(display_number); 1910511b41d2SMark Murray if (sock < 0) 1911a04a10f8SKris Kennaway return -1; 1912511b41d2SMark Murray 1913511b41d2SMark Murray /* OK, we now have a connection to the display. */ 1914a04a10f8SKris Kennaway return sock; 1915511b41d2SMark Murray } 1916511b41d2SMark Murray /* 1917511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 1918511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 1919511b41d2SMark Murray */ 1920511b41d2SMark Murray strncpy(buf, display, sizeof(buf)); 1921511b41d2SMark Murray buf[sizeof(buf) - 1] = 0; 1922511b41d2SMark Murray cp = strchr(buf, ':'); 1923511b41d2SMark Murray if (!cp) { 1924511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 1925a04a10f8SKris Kennaway return -1; 1926511b41d2SMark Murray } 1927511b41d2SMark Murray *cp = 0; 1928511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 1929511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 1930511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 1931511b41d2SMark Murray display); 1932a04a10f8SKris Kennaway return -1; 1933511b41d2SMark Murray } 1934511b41d2SMark Murray 1935511b41d2SMark Murray /* Look up the host address */ 1936511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1937511b41d2SMark Murray hints.ai_family = IPv4or6; 1938511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1939511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 1940511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 1941511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 1942a04a10f8SKris Kennaway return -1; 1943511b41d2SMark Murray } 1944511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1945511b41d2SMark Murray /* Create a socket. */ 1946511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1947511b41d2SMark Murray if (sock < 0) { 1948511b41d2SMark Murray debug("socket: %.100s", strerror(errno)); 1949511b41d2SMark Murray continue; 1950511b41d2SMark Murray } 1951511b41d2SMark Murray /* Connect it to the display. */ 1952511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1953a04a10f8SKris Kennaway debug("connect %.100s port %d: %.100s", buf, 1954a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 1955511b41d2SMark Murray close(sock); 1956511b41d2SMark Murray continue; 1957511b41d2SMark Murray } 1958511b41d2SMark Murray /* Success */ 1959511b41d2SMark Murray break; 1960a04a10f8SKris Kennaway } 1961511b41d2SMark Murray freeaddrinfo(aitop); 1962511b41d2SMark Murray if (!ai) { 1963511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 1964511b41d2SMark Murray strerror(errno)); 1965a04a10f8SKris Kennaway return -1; 1966511b41d2SMark Murray } 1967a04a10f8SKris Kennaway return sock; 1968a04a10f8SKris Kennaway } 1969511b41d2SMark Murray 1970a04a10f8SKris Kennaway /* 1971a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 1972a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 1973a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 1974a04a10f8SKris Kennaway */ 1975a04a10f8SKris Kennaway 1976a04a10f8SKris Kennaway void 19775b9b2fafSBrian Feldman x11_input_open(int type, int plen, void *ctxt) 1978a04a10f8SKris Kennaway { 1979a04a10f8SKris Kennaway int remote_channel, sock = 0, newch; 1980a04a10f8SKris Kennaway char *remote_host; 1981a04a10f8SKris Kennaway unsigned int remote_len; 1982a04a10f8SKris Kennaway 1983a04a10f8SKris Kennaway /* Get remote channel number. */ 1984a04a10f8SKris Kennaway remote_channel = packet_get_int(); 1985a04a10f8SKris Kennaway 1986a04a10f8SKris Kennaway /* Get remote originator name. */ 1987a04a10f8SKris Kennaway if (have_hostname_in_open) { 1988a04a10f8SKris Kennaway remote_host = packet_get_string(&remote_len); 1989a04a10f8SKris Kennaway remote_len += 4; 1990a04a10f8SKris Kennaway } else { 1991a04a10f8SKris Kennaway remote_host = xstrdup("unknown (remote did not supply name)"); 1992a04a10f8SKris Kennaway remote_len = 0; 1993a04a10f8SKris Kennaway } 1994a04a10f8SKris Kennaway 1995a04a10f8SKris Kennaway debug("Received X11 open request."); 1996a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN); 1997a04a10f8SKris Kennaway 1998a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 1999a04a10f8SKris Kennaway sock = x11_connect_display(); 2000a04a10f8SKris Kennaway if (sock == -1) { 2001a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 2002a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2003a04a10f8SKris Kennaway packet_put_int(remote_channel); 2004a04a10f8SKris Kennaway packet_send(); 2005a04a10f8SKris Kennaway } else { 2006511b41d2SMark Murray /* Allocate a channel for this connection. */ 2007a04a10f8SKris Kennaway newch = channel_allocate( 2008a04a10f8SKris Kennaway (x11_saved_proto == NULL) ? 2009a04a10f8SKris Kennaway SSH_CHANNEL_OPEN : SSH_CHANNEL_X11_OPEN, 2010a04a10f8SKris Kennaway sock, remote_host); 2011511b41d2SMark Murray channels[newch].remote_id = remote_channel; 2012511b41d2SMark Murray 2013511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2014511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2015511b41d2SMark Murray packet_put_int(remote_channel); 2016511b41d2SMark Murray packet_put_int(newch); 2017511b41d2SMark Murray packet_send(); 2018a04a10f8SKris Kennaway } 2019511b41d2SMark Murray } 2020511b41d2SMark Murray 20215b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 20225b9b2fafSBrian Feldman void 20235b9b2fafSBrian Feldman deny_input_open(int type, int plen, void *ctxt) 20245b9b2fafSBrian Feldman { 20255b9b2fafSBrian Feldman int rchan = packet_get_int(); 20265b9b2fafSBrian Feldman switch(type){ 20275b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 20285b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 20295b9b2fafSBrian Feldman break; 20305b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 20315b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 20325b9b2fafSBrian Feldman break; 20335b9b2fafSBrian Feldman default: 20345b9b2fafSBrian Feldman error("deny_input_open: type %d plen %d", type, plen); 20355b9b2fafSBrian Feldman break; 20365b9b2fafSBrian Feldman } 20375b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 20385b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 20395b9b2fafSBrian Feldman packet_put_int(rchan); 20405b9b2fafSBrian Feldman packet_send(); 20415b9b2fafSBrian Feldman } 20425b9b2fafSBrian Feldman 2043511b41d2SMark Murray /* 2044511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2045511b41d2SMark Murray * data, and enables authentication spoofing. 2046511b41d2SMark Murray */ 2047511b41d2SMark Murray 2048511b41d2SMark Murray void 2049a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2050a04a10f8SKris Kennaway const char *proto, const char *data) 2051511b41d2SMark Murray { 2052511b41d2SMark Murray unsigned int data_len = (unsigned int) strlen(data) / 2; 2053511b41d2SMark Murray unsigned int i, value; 2054511b41d2SMark Murray char *new_data; 2055511b41d2SMark Murray int screen_number; 2056511b41d2SMark Murray const char *cp; 2057511b41d2SMark Murray u_int32_t rand = 0; 2058511b41d2SMark Murray 2059511b41d2SMark Murray cp = getenv("DISPLAY"); 2060511b41d2SMark Murray if (cp) 2061511b41d2SMark Murray cp = strchr(cp, ':'); 2062511b41d2SMark Murray if (cp) 2063511b41d2SMark Murray cp = strchr(cp, '.'); 2064511b41d2SMark Murray if (cp) 2065511b41d2SMark Murray screen_number = atoi(cp + 1); 2066511b41d2SMark Murray else 2067511b41d2SMark Murray screen_number = 0; 2068511b41d2SMark Murray 2069511b41d2SMark Murray /* Save protocol name. */ 2070511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2071511b41d2SMark Murray 2072511b41d2SMark Murray /* 2073511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2074511b41d2SMark Murray * same length. 2075511b41d2SMark Murray */ 2076511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2077511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2078511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2079511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2080511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2081511b41d2SMark Murray if (i % 4 == 0) 2082511b41d2SMark Murray rand = arc4random(); 2083511b41d2SMark Murray x11_saved_data[i] = value; 2084511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2085511b41d2SMark Murray rand >>= 8; 2086511b41d2SMark Murray } 2087511b41d2SMark Murray x11_saved_data_len = data_len; 2088511b41d2SMark Murray x11_fake_data_len = data_len; 2089511b41d2SMark Murray 2090511b41d2SMark Murray /* Convert the fake data into hex. */ 2091511b41d2SMark Murray new_data = xmalloc(2 * data_len + 1); 2092511b41d2SMark Murray for (i = 0; i < data_len; i++) 2093511b41d2SMark Murray sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); 2094511b41d2SMark Murray 2095511b41d2SMark Murray /* Send the request packet. */ 2096a04a10f8SKris Kennaway if (compat20) { 2097a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2098a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2099a04a10f8SKris Kennaway } else { 2100511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2101a04a10f8SKris Kennaway } 2102a04a10f8SKris Kennaway packet_put_cstring(proto); 2103a04a10f8SKris Kennaway packet_put_cstring(new_data); 2104511b41d2SMark Murray packet_put_int(screen_number); 2105511b41d2SMark Murray packet_send(); 2106511b41d2SMark Murray packet_write_wait(); 2107511b41d2SMark Murray xfree(new_data); 2108511b41d2SMark Murray } 2109511b41d2SMark Murray 2110511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2111511b41d2SMark Murray 2112511b41d2SMark Murray void 2113511b41d2SMark Murray auth_request_forwarding() 2114511b41d2SMark Murray { 2115511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2116511b41d2SMark Murray packet_send(); 2117511b41d2SMark Murray packet_write_wait(); 2118511b41d2SMark Murray } 2119511b41d2SMark Murray 2120511b41d2SMark Murray /* 2121511b41d2SMark Murray * Returns the name of the forwarded authentication socket. Returns NULL if 2122511b41d2SMark Murray * there is no forwarded authentication socket. The returned value points to 2123511b41d2SMark Murray * a static buffer. 2124511b41d2SMark Murray */ 2125511b41d2SMark Murray 2126511b41d2SMark Murray char * 2127511b41d2SMark Murray auth_get_socket_name() 2128511b41d2SMark Murray { 2129511b41d2SMark Murray return channel_forwarded_auth_socket_name; 2130511b41d2SMark Murray } 2131511b41d2SMark Murray 2132511b41d2SMark Murray /* removes the agent forwarding socket */ 2133511b41d2SMark Murray 2134511b41d2SMark Murray void 2135511b41d2SMark Murray cleanup_socket(void) 2136511b41d2SMark Murray { 2137511b41d2SMark Murray remove(channel_forwarded_auth_socket_name); 2138511b41d2SMark Murray rmdir(channel_forwarded_auth_socket_dir); 2139511b41d2SMark Murray } 2140511b41d2SMark Murray 2141511b41d2SMark Murray /* 2142fcee55a2SKris Kennaway * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. 2143511b41d2SMark Murray * This starts forwarding authentication requests. 2144511b41d2SMark Murray */ 2145511b41d2SMark Murray 2146fcee55a2SKris Kennaway int 2147511b41d2SMark Murray auth_input_request_forwarding(struct passwd * pw) 2148511b41d2SMark Murray { 2149511b41d2SMark Murray int sock, newch; 2150511b41d2SMark Murray struct sockaddr_un sunaddr; 2151511b41d2SMark Murray 2152511b41d2SMark Murray if (auth_get_socket_name() != NULL) 2153511b41d2SMark Murray fatal("Protocol error: authentication forwarding requested twice."); 2154511b41d2SMark Murray 2155511b41d2SMark Murray /* Temporarily drop privileged uid for mkdir/bind. */ 2156511b41d2SMark Murray temporarily_use_uid(pw->pw_uid); 2157511b41d2SMark Murray 2158511b41d2SMark Murray /* Allocate a buffer for the socket name, and format the name. */ 2159511b41d2SMark Murray channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); 2160511b41d2SMark Murray channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); 2161511b41d2SMark Murray strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); 2162511b41d2SMark Murray 2163511b41d2SMark Murray /* Create private directory for socket */ 2164fcee55a2SKris Kennaway if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) { 2165fcee55a2SKris Kennaway packet_send_debug("Agent forwarding disabled: mkdtemp() failed: %.100s", 2166fcee55a2SKris Kennaway strerror(errno)); 2167fcee55a2SKris Kennaway restore_uid(); 2168fcee55a2SKris Kennaway xfree(channel_forwarded_auth_socket_name); 2169fcee55a2SKris Kennaway xfree(channel_forwarded_auth_socket_dir); 2170fcee55a2SKris Kennaway channel_forwarded_auth_socket_name = NULL; 2171fcee55a2SKris Kennaway channel_forwarded_auth_socket_dir = NULL; 2172fcee55a2SKris Kennaway return 0; 2173fcee55a2SKris Kennaway } 2174511b41d2SMark Murray snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", 2175511b41d2SMark Murray channel_forwarded_auth_socket_dir, (int) getpid()); 2176511b41d2SMark Murray 2177511b41d2SMark Murray if (atexit(cleanup_socket) < 0) { 2178511b41d2SMark Murray int saved = errno; 2179511b41d2SMark Murray cleanup_socket(); 2180511b41d2SMark Murray packet_disconnect("socket: %.100s", strerror(saved)); 2181511b41d2SMark Murray } 2182511b41d2SMark Murray /* Create the socket. */ 2183511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2184511b41d2SMark Murray if (sock < 0) 2185511b41d2SMark Murray packet_disconnect("socket: %.100s", strerror(errno)); 2186511b41d2SMark Murray 2187511b41d2SMark Murray /* Bind it to the name. */ 2188511b41d2SMark Murray memset(&sunaddr, 0, sizeof(sunaddr)); 2189511b41d2SMark Murray sunaddr.sun_family = AF_UNIX; 2190511b41d2SMark Murray strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, 2191511b41d2SMark Murray sizeof(sunaddr.sun_path)); 2192511b41d2SMark Murray 2193511b41d2SMark Murray if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) 2194511b41d2SMark Murray packet_disconnect("bind: %.100s", strerror(errno)); 2195511b41d2SMark Murray 2196511b41d2SMark Murray /* Restore the privileged uid. */ 2197511b41d2SMark Murray restore_uid(); 2198511b41d2SMark Murray 2199511b41d2SMark Murray /* Start listening on the socket. */ 2200511b41d2SMark Murray if (listen(sock, 5) < 0) 2201511b41d2SMark Murray packet_disconnect("listen: %.100s", strerror(errno)); 2202511b41d2SMark Murray 2203511b41d2SMark Murray /* Allocate a channel for the authentication agent socket. */ 2204511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, 2205511b41d2SMark Murray xstrdup("auth socket")); 2206511b41d2SMark Murray strlcpy(channels[newch].path, channel_forwarded_auth_socket_name, 2207511b41d2SMark Murray sizeof(channels[newch].path)); 2208fcee55a2SKris Kennaway return 1; 2209511b41d2SMark Murray } 2210511b41d2SMark Murray 2211511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2212511b41d2SMark Murray 2213511b41d2SMark Murray void 22145b9b2fafSBrian Feldman auth_input_open_request(int type, int plen, void *ctxt) 2215511b41d2SMark Murray { 2216511b41d2SMark Murray int remch, sock, newch; 2217511b41d2SMark Murray char *dummyname; 2218511b41d2SMark Murray 2219a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 2220a04a10f8SKris Kennaway 2221511b41d2SMark Murray /* Read the remote channel number from the message. */ 2222511b41d2SMark Murray remch = packet_get_int(); 2223511b41d2SMark Murray 2224511b41d2SMark Murray /* 2225511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2226511b41d2SMark Murray * get forwarded). 2227511b41d2SMark Murray */ 2228511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2229511b41d2SMark Murray 2230511b41d2SMark Murray /* 2231511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2232511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2233511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2234511b41d2SMark Murray * agent. 2235511b41d2SMark Murray */ 2236511b41d2SMark Murray if (sock < 0) { 2237511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2238511b41d2SMark Murray packet_put_int(remch); 2239511b41d2SMark Murray packet_send(); 2240511b41d2SMark Murray return; 2241511b41d2SMark Murray } 2242511b41d2SMark Murray debug("Forwarding authentication connection."); 2243511b41d2SMark Murray 2244511b41d2SMark Murray /* 2245511b41d2SMark Murray * Dummy host name. This will be freed when the channel is freed; it 2246511b41d2SMark Murray * will still be valid in the packet_put_string below since the 2247511b41d2SMark Murray * channel cannot yet be freed at that point. 2248511b41d2SMark Murray */ 2249511b41d2SMark Murray dummyname = xstrdup("authentication agent connection"); 2250511b41d2SMark Murray 2251511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); 2252511b41d2SMark Murray channels[newch].remote_id = remch; 2253511b41d2SMark Murray 2254511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2255511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2256511b41d2SMark Murray packet_put_int(remch); 2257511b41d2SMark Murray packet_put_int(newch); 2258511b41d2SMark Murray packet_send(); 2259511b41d2SMark Murray } 2260a04a10f8SKris Kennaway 2261a04a10f8SKris Kennaway void 2262a04a10f8SKris Kennaway channel_start_open(int id) 2263a04a10f8SKris Kennaway { 2264a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2265a04a10f8SKris Kennaway if (c == NULL) { 2266a04a10f8SKris Kennaway log("channel_open: %d: bad id", id); 2267a04a10f8SKris Kennaway return; 2268a04a10f8SKris Kennaway } 2269a04a10f8SKris Kennaway debug("send channel open %d", id); 2270a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 2271a04a10f8SKris Kennaway packet_put_cstring(c->ctype); 2272a04a10f8SKris Kennaway packet_put_int(c->self); 2273a04a10f8SKris Kennaway packet_put_int(c->local_window); 2274a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 2275a04a10f8SKris Kennaway } 2276a04a10f8SKris Kennaway void 2277a04a10f8SKris Kennaway channel_open(int id) 2278a04a10f8SKris Kennaway { 2279a04a10f8SKris Kennaway /* XXX REMOVE ME */ 2280a04a10f8SKris Kennaway channel_start_open(id); 2281a04a10f8SKris Kennaway packet_send(); 2282a04a10f8SKris Kennaway } 2283a04a10f8SKris Kennaway void 2284a04a10f8SKris Kennaway channel_request(int id, char *service, int wantconfirm) 2285a04a10f8SKris Kennaway { 2286a04a10f8SKris Kennaway channel_request_start(id, service, wantconfirm); 2287a04a10f8SKris Kennaway packet_send(); 2288a04a10f8SKris Kennaway debug("channel request %d: %s", id, service) ; 2289a04a10f8SKris Kennaway } 2290a04a10f8SKris Kennaway void 2291a04a10f8SKris Kennaway channel_request_start(int id, char *service, int wantconfirm) 2292a04a10f8SKris Kennaway { 2293a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2294a04a10f8SKris Kennaway if (c == NULL) { 2295a04a10f8SKris Kennaway log("channel_request: %d: bad id", id); 2296a04a10f8SKris Kennaway return; 2297a04a10f8SKris Kennaway } 2298a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_REQUEST); 2299a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2300a04a10f8SKris Kennaway packet_put_cstring(service); 2301a04a10f8SKris Kennaway packet_put_char(wantconfirm); 2302a04a10f8SKris Kennaway } 2303a04a10f8SKris Kennaway void 2304a04a10f8SKris Kennaway channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg) 2305a04a10f8SKris Kennaway { 2306a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2307a04a10f8SKris Kennaway if (c == NULL) { 2308a04a10f8SKris Kennaway log("channel_register_callback: %d: bad id", id); 2309a04a10f8SKris Kennaway return; 2310a04a10f8SKris Kennaway } 2311a04a10f8SKris Kennaway c->cb_event = mtype; 2312a04a10f8SKris Kennaway c->cb_fn = fn; 2313a04a10f8SKris Kennaway c->cb_arg = arg; 2314a04a10f8SKris Kennaway } 2315a04a10f8SKris Kennaway void 2316a04a10f8SKris Kennaway channel_register_cleanup(int id, channel_callback_fn *fn) 2317a04a10f8SKris Kennaway { 2318a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2319a04a10f8SKris Kennaway if (c == NULL) { 2320a04a10f8SKris Kennaway log("channel_register_cleanup: %d: bad id", id); 2321a04a10f8SKris Kennaway return; 2322a04a10f8SKris Kennaway } 2323a04a10f8SKris Kennaway c->dettach_user = fn; 2324a04a10f8SKris Kennaway } 2325a04a10f8SKris Kennaway void 2326a04a10f8SKris Kennaway channel_cancel_cleanup(int id) 2327a04a10f8SKris Kennaway { 2328a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2329a04a10f8SKris Kennaway if (c == NULL) { 2330a04a10f8SKris Kennaway log("channel_cancel_cleanup: %d: bad id", id); 2331a04a10f8SKris Kennaway return; 2332a04a10f8SKris Kennaway } 2333a04a10f8SKris Kennaway c->dettach_user = NULL; 2334a04a10f8SKris Kennaway } 2335b66f2d16SKris Kennaway void 2336b66f2d16SKris Kennaway channel_register_filter(int id, channel_filter_fn *fn) 2337b66f2d16SKris Kennaway { 2338b66f2d16SKris Kennaway Channel *c = channel_lookup(id); 2339b66f2d16SKris Kennaway if (c == NULL) { 2340b66f2d16SKris Kennaway log("channel_register_filter: %d: bad id", id); 2341b66f2d16SKris Kennaway return; 2342b66f2d16SKris Kennaway } 2343b66f2d16SKris Kennaway c->input_filter = fn; 2344b66f2d16SKris Kennaway } 2345a04a10f8SKris Kennaway 2346a04a10f8SKris Kennaway void 23475b9b2fafSBrian Feldman channel_set_fds(int id, int rfd, int wfd, int efd, 23485b9b2fafSBrian Feldman int extusage, int nonblock) 2349a04a10f8SKris Kennaway { 2350a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2351a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 2352a04a10f8SKris Kennaway fatal("channel_activate for non-larval channel %d.", id); 23535b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 2354a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 2355a04a10f8SKris Kennaway /* XXX window size? */ 2356b66f2d16SKris Kennaway c->local_window = c->local_window_max = c->local_maxpacket * 2; 2357a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 2358a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2359a04a10f8SKris Kennaway packet_put_int(c->local_window); 2360a04a10f8SKris Kennaway packet_send(); 2361a04a10f8SKris Kennaway } 2362