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" 435b9b2fafSBrian Feldman RCSID("$OpenBSD: channels.c,v 1.72 2000/10/27 07:48:22 markus Exp $"); 44511b41d2SMark Murray 45511b41d2SMark Murray #include "ssh.h" 46511b41d2SMark Murray #include "packet.h" 47511b41d2SMark Murray #include "xmalloc.h" 48511b41d2SMark Murray #include "buffer.h" 49511b41d2SMark Murray #include "uidswap.h" 50511b41d2SMark Murray #include "readconf.h" 51511b41d2SMark Murray #include "servconf.h" 52511b41d2SMark Murray 53511b41d2SMark Murray #include "channels.h" 54511b41d2SMark Murray #include "nchan.h" 55511b41d2SMark Murray #include "compat.h" 56511b41d2SMark Murray 57a04a10f8SKris Kennaway #include "ssh2.h" 58a04a10f8SKris Kennaway 59b66f2d16SKris Kennaway #include <openssl/rsa.h> 60b66f2d16SKris Kennaway #include <openssl/dsa.h> 61b66f2d16SKris Kennaway #include "key.h" 62b66f2d16SKris Kennaway #include "authfd.h" 63b66f2d16SKris Kennaway 64511b41d2SMark Murray /* Maximum number of fake X11 displays to try. */ 65511b41d2SMark Murray #define MAX_DISPLAYS 1000 66511b41d2SMark Murray 67511b41d2SMark Murray /* Max len of agent socket */ 68511b41d2SMark Murray #define MAX_SOCKET_NAME 100 69511b41d2SMark Murray 70511b41d2SMark Murray /* 71511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 72511b41d2SMark Murray * dynamically extended as needed. 73511b41d2SMark Murray */ 74511b41d2SMark Murray static Channel *channels = NULL; 75511b41d2SMark Murray 76511b41d2SMark Murray /* 77511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 78511b41d2SMark Murray * initialized (at least the type field); unused slots are marked with type 79511b41d2SMark Murray * SSH_CHANNEL_FREE. 80511b41d2SMark Murray */ 81511b41d2SMark Murray static int channels_alloc = 0; 82511b41d2SMark Murray 83511b41d2SMark Murray /* 84511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 85511b41d2SMark Murray * updated in channel_allocate. 86511b41d2SMark Murray */ 87511b41d2SMark Murray static int channel_max_fd_value = 0; 88511b41d2SMark Murray 89511b41d2SMark Murray /* Name and directory of socket for authentication agent forwarding. */ 90511b41d2SMark Murray static char *channel_forwarded_auth_socket_name = NULL; 91511b41d2SMark Murray static char *channel_forwarded_auth_socket_dir = NULL; 92511b41d2SMark Murray 93511b41d2SMark Murray /* Saved X11 authentication protocol name. */ 94511b41d2SMark Murray char *x11_saved_proto = NULL; 95511b41d2SMark Murray 96511b41d2SMark Murray /* Saved X11 authentication data. This is the real data. */ 97511b41d2SMark Murray char *x11_saved_data = NULL; 98511b41d2SMark Murray unsigned int x11_saved_data_len = 0; 99511b41d2SMark Murray 100511b41d2SMark Murray /* 101511b41d2SMark Murray * Fake X11 authentication data. This is what the server will be sending us; 102511b41d2SMark Murray * we should replace any occurrences of this by the real data. 103511b41d2SMark Murray */ 104511b41d2SMark Murray char *x11_fake_data = NULL; 105511b41d2SMark Murray unsigned int x11_fake_data_len; 106511b41d2SMark Murray 107511b41d2SMark Murray /* 108511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 109511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 110511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 111511b41d2SMark Murray * network (which might be behind a firewall). 112511b41d2SMark Murray */ 113511b41d2SMark Murray typedef struct { 114a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 115a04a10f8SKris Kennaway u_short port_to_connect; /* Connect to 'port'. */ 116a04a10f8SKris Kennaway u_short listen_port; /* Remote side should listen port number. */ 117511b41d2SMark Murray } ForwardPermission; 118511b41d2SMark Murray 119511b41d2SMark Murray /* List of all permitted host/port pairs to connect. */ 120511b41d2SMark Murray static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; 121511b41d2SMark Murray /* Number of permitted host/port pairs in the array. */ 122511b41d2SMark Murray static int num_permitted_opens = 0; 123511b41d2SMark Murray /* 124511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 125511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 126511b41d2SMark Murray * anything after logging in anyway. 127511b41d2SMark Murray */ 128511b41d2SMark Murray static int all_opens_permitted = 0; 129511b41d2SMark Murray 130511b41d2SMark Murray /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ 131511b41d2SMark Murray static int have_hostname_in_open = 0; 132511b41d2SMark Murray 133511b41d2SMark Murray /* Sets specific protocol options. */ 134511b41d2SMark Murray 135511b41d2SMark Murray void 136511b41d2SMark Murray channel_set_options(int hostname_in_open) 137511b41d2SMark Murray { 138511b41d2SMark Murray have_hostname_in_open = hostname_in_open; 139511b41d2SMark Murray } 140511b41d2SMark Murray 141511b41d2SMark Murray /* 142511b41d2SMark Murray * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually 143511b41d2SMark Murray * called by the server, because the user could connect to any port anyway, 144511b41d2SMark Murray * and the server has no way to know but to trust the client anyway. 145511b41d2SMark Murray */ 146511b41d2SMark Murray 147511b41d2SMark Murray void 148511b41d2SMark Murray channel_permit_all_opens() 149511b41d2SMark Murray { 150511b41d2SMark Murray all_opens_permitted = 1; 151511b41d2SMark Murray } 152511b41d2SMark Murray 153a04a10f8SKris Kennaway /* lookup channel by id */ 154a04a10f8SKris Kennaway 155a04a10f8SKris Kennaway Channel * 156a04a10f8SKris Kennaway channel_lookup(int id) 157a04a10f8SKris Kennaway { 158a04a10f8SKris Kennaway Channel *c; 159b66f2d16SKris Kennaway if (id < 0 || id > channels_alloc) { 160a04a10f8SKris Kennaway log("channel_lookup: %d: bad id", id); 161a04a10f8SKris Kennaway return NULL; 162a04a10f8SKris Kennaway } 163a04a10f8SKris Kennaway c = &channels[id]; 164a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_FREE) { 165a04a10f8SKris Kennaway log("channel_lookup: %d: bad id: channel free", id); 166a04a10f8SKris Kennaway return NULL; 167a04a10f8SKris Kennaway } 168a04a10f8SKris Kennaway return c; 169a04a10f8SKris Kennaway } 170a04a10f8SKris Kennaway 171a04a10f8SKris Kennaway /* 172a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 173a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 174a04a10f8SKris Kennaway */ 175a04a10f8SKris Kennaway 176a04a10f8SKris Kennaway void 1775b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 1785b9b2fafSBrian Feldman int extusage, int nonblock) 179a04a10f8SKris Kennaway { 180a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 181a04a10f8SKris Kennaway if (rfd > channel_max_fd_value) 182a04a10f8SKris Kennaway channel_max_fd_value = rfd; 183a04a10f8SKris Kennaway if (wfd > channel_max_fd_value) 184a04a10f8SKris Kennaway channel_max_fd_value = wfd; 185a04a10f8SKris Kennaway if (efd > channel_max_fd_value) 186a04a10f8SKris Kennaway channel_max_fd_value = efd; 187a04a10f8SKris Kennaway /* XXX set close-on-exec -markus */ 188a04a10f8SKris Kennaway 189a04a10f8SKris Kennaway c->rfd = rfd; 190a04a10f8SKris Kennaway c->wfd = wfd; 191a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 192a04a10f8SKris Kennaway c->efd = efd; 193a04a10f8SKris Kennaway c->extended_usage = extusage; 1945b9b2fafSBrian Feldman 1955b9b2fafSBrian Feldman /* enable nonblocking mode */ 1965b9b2fafSBrian Feldman if (nonblock) { 197a04a10f8SKris Kennaway if (rfd != -1) 198a04a10f8SKris Kennaway set_nonblock(rfd); 199a04a10f8SKris Kennaway if (wfd != -1) 200a04a10f8SKris Kennaway set_nonblock(wfd); 201a04a10f8SKris Kennaway if (efd != -1) 202a04a10f8SKris Kennaway set_nonblock(efd); 203a04a10f8SKris Kennaway } 2045b9b2fafSBrian Feldman } 205a04a10f8SKris Kennaway 206511b41d2SMark Murray /* 207511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 208511b41d2SMark Murray * remote_name to be freed. 209511b41d2SMark Murray */ 210511b41d2SMark Murray 211511b41d2SMark Murray int 212a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 2135b9b2fafSBrian Feldman int window, int maxpack, int extusage, char *remote_name, int nonblock) 214511b41d2SMark Murray { 215511b41d2SMark Murray int i, found; 216511b41d2SMark Murray Channel *c; 217511b41d2SMark Murray 218511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 219511b41d2SMark Murray if (channels_alloc == 0) { 220a04a10f8SKris Kennaway chan_init(); 221511b41d2SMark Murray channels_alloc = 10; 222511b41d2SMark Murray channels = xmalloc(channels_alloc * sizeof(Channel)); 223511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 224511b41d2SMark Murray channels[i].type = SSH_CHANNEL_FREE; 225511b41d2SMark Murray /* 226511b41d2SMark Murray * Kludge: arrange a call to channel_stop_listening if we 227511b41d2SMark Murray * terminate with fatal(). 228511b41d2SMark Murray */ 229511b41d2SMark Murray fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); 230511b41d2SMark Murray } 231511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 232511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 233511b41d2SMark Murray if (channels[i].type == SSH_CHANNEL_FREE) { 234511b41d2SMark Murray /* Found a free slot. */ 235511b41d2SMark Murray found = i; 236511b41d2SMark Murray break; 237511b41d2SMark Murray } 238511b41d2SMark Murray if (found == -1) { 239511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 240511b41d2SMark Murray found = channels_alloc; 241511b41d2SMark Murray channels_alloc += 10; 2425b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 243511b41d2SMark Murray channels = xrealloc(channels, channels_alloc * sizeof(Channel)); 244511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 245511b41d2SMark Murray channels[i].type = SSH_CHANNEL_FREE; 246511b41d2SMark Murray } 247511b41d2SMark Murray /* Initialize and return new channel number. */ 248511b41d2SMark Murray c = &channels[found]; 249511b41d2SMark Murray buffer_init(&c->input); 250511b41d2SMark Murray buffer_init(&c->output); 251a04a10f8SKris Kennaway buffer_init(&c->extended); 252511b41d2SMark Murray chan_init_iostates(c); 2535b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 254511b41d2SMark Murray c->self = found; 255511b41d2SMark Murray c->type = type; 256a04a10f8SKris Kennaway c->ctype = ctype; 257a04a10f8SKris Kennaway c->local_window = window; 258a04a10f8SKris Kennaway c->local_window_max = window; 259a04a10f8SKris Kennaway c->local_consumed = 0; 260a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 261511b41d2SMark Murray c->remote_id = -1; 262511b41d2SMark Murray c->remote_name = remote_name; 263a04a10f8SKris Kennaway c->remote_window = 0; 264a04a10f8SKris Kennaway c->remote_maxpacket = 0; 265a04a10f8SKris Kennaway c->cb_fn = NULL; 266a04a10f8SKris Kennaway c->cb_arg = NULL; 267a04a10f8SKris Kennaway c->cb_event = 0; 268a04a10f8SKris Kennaway c->dettach_user = NULL; 269b66f2d16SKris Kennaway c->input_filter = NULL; 270511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 271511b41d2SMark Murray return found; 272511b41d2SMark Murray } 273a04a10f8SKris Kennaway /* old interface XXX */ 274a04a10f8SKris Kennaway int 275a04a10f8SKris Kennaway channel_allocate(int type, int sock, char *remote_name) 276a04a10f8SKris Kennaway { 2775b9b2fafSBrian Feldman return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name, 1); 278a04a10f8SKris Kennaway } 279511b41d2SMark Murray 280a04a10f8SKris Kennaway 281a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 282511b41d2SMark Murray 283511b41d2SMark Murray void 284a04a10f8SKris Kennaway channel_close_fds(Channel *c) 285511b41d2SMark Murray { 286a04a10f8SKris Kennaway if (c->sock != -1) { 287a04a10f8SKris Kennaway close(c->sock); 288a04a10f8SKris Kennaway c->sock = -1; 289a04a10f8SKris Kennaway } 290a04a10f8SKris Kennaway if (c->rfd != -1) { 291a04a10f8SKris Kennaway close(c->rfd); 292a04a10f8SKris Kennaway c->rfd = -1; 293a04a10f8SKris Kennaway } 294a04a10f8SKris Kennaway if (c->wfd != -1) { 295a04a10f8SKris Kennaway close(c->wfd); 296a04a10f8SKris Kennaway c->wfd = -1; 297a04a10f8SKris Kennaway } 298a04a10f8SKris Kennaway if (c->efd != -1) { 299a04a10f8SKris Kennaway close(c->efd); 300a04a10f8SKris Kennaway c->efd = -1; 301a04a10f8SKris Kennaway } 302a04a10f8SKris Kennaway } 303511b41d2SMark Murray 304a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 305a04a10f8SKris Kennaway 306a04a10f8SKris Kennaway void 307a04a10f8SKris Kennaway channel_free(int id) 308a04a10f8SKris Kennaway { 309a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 310a04a10f8SKris Kennaway if (c == NULL) 311a04a10f8SKris Kennaway packet_disconnect("channel free: bad local channel %d", id); 312a04a10f8SKris Kennaway debug("channel_free: channel %d: status: %s", id, channel_open_message()); 313a04a10f8SKris Kennaway if (c->dettach_user != NULL) { 314a04a10f8SKris Kennaway debug("channel_free: channel %d: dettaching channel user", id); 315a04a10f8SKris Kennaway c->dettach_user(c->self, NULL); 316a04a10f8SKris Kennaway } 317a04a10f8SKris Kennaway if (c->sock != -1) 318a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 319a04a10f8SKris Kennaway channel_close_fds(c); 320a04a10f8SKris Kennaway buffer_free(&c->input); 321a04a10f8SKris Kennaway buffer_free(&c->output); 322a04a10f8SKris Kennaway buffer_free(&c->extended); 323a04a10f8SKris Kennaway c->type = SSH_CHANNEL_FREE; 324a04a10f8SKris Kennaway if (c->remote_name) { 325a04a10f8SKris Kennaway xfree(c->remote_name); 326a04a10f8SKris Kennaway c->remote_name = NULL; 327511b41d2SMark Murray } 328511b41d2SMark Murray } 329511b41d2SMark Murray 330511b41d2SMark Murray /* 331a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 332a04a10f8SKris Kennaway * channels in the select bitmasks. 333511b41d2SMark Murray */ 334a04a10f8SKris Kennaway /* 335a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 336a04a10f8SKris Kennaway * have events pending. 337a04a10f8SKris Kennaway */ 338a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); 339a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 340a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 341511b41d2SMark Murray 342511b41d2SMark Murray void 343a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) 344511b41d2SMark Murray { 345a04a10f8SKris Kennaway FD_SET(c->sock, readset); 346a04a10f8SKris Kennaway } 347a04a10f8SKris Kennaway 348a04a10f8SKris Kennaway void 349a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) 350a04a10f8SKris Kennaway { 351a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 352a04a10f8SKris Kennaway FD_SET(c->sock, readset); 353a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 354a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 355a04a10f8SKris Kennaway } 356a04a10f8SKris Kennaway 357a04a10f8SKris Kennaway void 358a04a10f8SKris Kennaway channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) 359a04a10f8SKris Kennaway { 360a04a10f8SKris Kennaway /* test whether sockets are 'alive' for read/write */ 361a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN) 362a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 363a04a10f8SKris Kennaway FD_SET(c->sock, readset); 364a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 365a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 366a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 367a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 368a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 369a04a10f8SKris Kennaway chan_obuf_empty(c); 370a04a10f8SKris Kennaway } 371a04a10f8SKris Kennaway } 372a04a10f8SKris Kennaway } 373a04a10f8SKris Kennaway 374a04a10f8SKris Kennaway void 375a04a10f8SKris Kennaway channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset) 376a04a10f8SKris Kennaway { 377a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 378a04a10f8SKris Kennaway c->remote_window > 0 && 379a04a10f8SKris Kennaway buffer_len(&c->input) < c->remote_window) 380a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 381a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 382a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 383a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 384a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 385a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 386a04a10f8SKris Kennaway chan_obuf_empty(c); 387a04a10f8SKris Kennaway } 388a04a10f8SKris Kennaway } 389a04a10f8SKris Kennaway /** XXX check close conditions, too */ 390a04a10f8SKris Kennaway if (c->efd != -1) { 391a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 392a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 393a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 394a04a10f8SKris Kennaway else if (c->extended_usage == CHAN_EXTENDED_READ && 395a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 396a04a10f8SKris Kennaway FD_SET(c->efd, readset); 397a04a10f8SKris Kennaway } 398a04a10f8SKris Kennaway } 399a04a10f8SKris Kennaway 400a04a10f8SKris Kennaway void 401a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) 402a04a10f8SKris Kennaway { 403a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 404a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 405a04a10f8SKris Kennaway packet_put_int(c->remote_id); 406a04a10f8SKris Kennaway packet_send(); 407a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 408a04a10f8SKris Kennaway debug("Closing channel %d after input drain.", c->self); 409a04a10f8SKris Kennaway } 410a04a10f8SKris Kennaway } 411a04a10f8SKris Kennaway 412a04a10f8SKris Kennaway void 413a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) 414a04a10f8SKris Kennaway { 415a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 416a04a10f8SKris Kennaway channel_free(c->self); 417a04a10f8SKris Kennaway else 418a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 419a04a10f8SKris Kennaway } 420a04a10f8SKris Kennaway 421a04a10f8SKris Kennaway /* 422a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 423a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 424a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 425a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 426a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 427a04a10f8SKris Kennaway * XXX All this happens at the client side. 428a04a10f8SKris Kennaway */ 429a04a10f8SKris Kennaway int 430a04a10f8SKris Kennaway x11_open_helper(Channel *c) 431a04a10f8SKris Kennaway { 432511b41d2SMark Murray unsigned char *ucp; 433511b41d2SMark Murray unsigned int proto_len, data_len; 434511b41d2SMark Murray 435511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 436a04a10f8SKris Kennaway if (buffer_len(&c->output) < 12) 437a04a10f8SKris Kennaway return 0; 438511b41d2SMark Murray 439511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 440a04a10f8SKris Kennaway ucp = (unsigned char *) buffer_ptr(&c->output); 441511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 442511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 443511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 444511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 445511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 446511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 447511b41d2SMark Murray } else { 448511b41d2SMark Murray debug("Initial X11 packet contains bad byte order byte: 0x%x", 449511b41d2SMark Murray ucp[0]); 450a04a10f8SKris Kennaway return -1; 451511b41d2SMark Murray } 452511b41d2SMark Murray 453511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 454a04a10f8SKris Kennaway if (buffer_len(&c->output) < 455511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 456a04a10f8SKris Kennaway return 0; 457511b41d2SMark Murray 458511b41d2SMark Murray /* Check if authentication protocol matches. */ 459511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 460511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 461511b41d2SMark Murray debug("X11 connection uses different authentication protocol."); 462a04a10f8SKris Kennaway return -1; 463511b41d2SMark Murray } 464511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 465511b41d2SMark Murray if (data_len != x11_fake_data_len || 466511b41d2SMark Murray memcmp(ucp + 12 + ((proto_len + 3) & ~3), 467511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 468511b41d2SMark Murray debug("X11 auth data does not match fake data."); 469a04a10f8SKris Kennaway return -1; 470511b41d2SMark Murray } 471511b41d2SMark Murray /* Check fake data length */ 472511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 473511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 474511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 475a04a10f8SKris Kennaway return -1; 476511b41d2SMark Murray } 477511b41d2SMark Murray /* 478511b41d2SMark Murray * Received authentication protocol and data match 479511b41d2SMark Murray * our fake data. Substitute the fake data with real 480511b41d2SMark Murray * data. 481511b41d2SMark Murray */ 482511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 483511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 484a04a10f8SKris Kennaway return 1; 485a04a10f8SKris Kennaway } 486511b41d2SMark Murray 487a04a10f8SKris Kennaway void 488a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) 489a04a10f8SKris Kennaway { 490a04a10f8SKris Kennaway int ret = x11_open_helper(c); 491a04a10f8SKris Kennaway if (ret == 1) { 492511b41d2SMark Murray /* Start normal processing for the channel. */ 493a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 494a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 495a04a10f8SKris Kennaway } else if (ret == -1) { 496511b41d2SMark Murray /* 497511b41d2SMark Murray * We have received an X11 connection that has bad 498511b41d2SMark Murray * authentication information. 499511b41d2SMark Murray */ 500511b41d2SMark Murray log("X11 connection rejected because of wrong authentication.\r\n"); 501a04a10f8SKris Kennaway buffer_clear(&c->input); 502a04a10f8SKris Kennaway buffer_clear(&c->output); 503a04a10f8SKris Kennaway close(c->sock); 504a04a10f8SKris Kennaway c->sock = -1; 505a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 506511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 507a04a10f8SKris Kennaway packet_put_int(c->remote_id); 508511b41d2SMark Murray packet_send(); 509511b41d2SMark Murray } 510511b41d2SMark Murray } 511511b41d2SMark Murray 512511b41d2SMark Murray void 513a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) 514a04a10f8SKris Kennaway { 515a04a10f8SKris Kennaway int ret = x11_open_helper(c); 516a04a10f8SKris Kennaway if (ret == 1) { 517a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 518a04a10f8SKris Kennaway if (compat20) 519a04a10f8SKris Kennaway channel_pre_open_20(c, readset, writeset); 520a04a10f8SKris Kennaway else 521a04a10f8SKris Kennaway channel_pre_open_15(c, readset, writeset); 522a04a10f8SKris Kennaway } else if (ret == -1) { 523a04a10f8SKris Kennaway debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 524a04a10f8SKris Kennaway chan_read_failed(c); /** force close? */ 525a04a10f8SKris Kennaway chan_write_failed(c); 526a04a10f8SKris Kennaway debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 527a04a10f8SKris Kennaway } 528a04a10f8SKris Kennaway } 529a04a10f8SKris Kennaway 530a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 531a04a10f8SKris Kennaway void 532a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 533511b41d2SMark Murray { 534511b41d2SMark Murray struct sockaddr addr; 535a04a10f8SKris Kennaway int newsock, newch; 536511b41d2SMark Murray socklen_t addrlen; 537511b41d2SMark Murray char buf[16384], *remote_hostname; 538a04a10f8SKris Kennaway int remote_port; 539511b41d2SMark Murray 540a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 541511b41d2SMark Murray debug("X11 connection requested."); 542511b41d2SMark Murray addrlen = sizeof(addr); 543a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 544511b41d2SMark Murray if (newsock < 0) { 545511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 546a04a10f8SKris Kennaway return; 547511b41d2SMark Murray } 548511b41d2SMark Murray remote_hostname = get_remote_hostname(newsock); 549a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 550511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 551a04a10f8SKris Kennaway remote_hostname, remote_port); 552a04a10f8SKris Kennaway 553a04a10f8SKris Kennaway newch = channel_new("x11", 554a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 555a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 5565b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 557a04a10f8SKris Kennaway if (compat20) { 558a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 559a04a10f8SKris Kennaway packet_put_cstring("x11"); 560a04a10f8SKris Kennaway packet_put_int(newch); 561a04a10f8SKris Kennaway packet_put_int(c->local_window_max); 562a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 563a04a10f8SKris Kennaway /* originator host and port */ 564a04a10f8SKris Kennaway packet_put_cstring(remote_hostname); 565a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 566a04a10f8SKris Kennaway debug("ssh2 x11 bug compat mode"); 567a04a10f8SKris Kennaway } else { 568a04a10f8SKris Kennaway packet_put_int(remote_port); 569a04a10f8SKris Kennaway } 570a04a10f8SKris Kennaway packet_send(); 571a04a10f8SKris Kennaway } else { 572511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 573511b41d2SMark Murray packet_put_int(newch); 574511b41d2SMark Murray if (have_hostname_in_open) 575511b41d2SMark Murray packet_put_string(buf, strlen(buf)); 576511b41d2SMark Murray packet_send(); 577511b41d2SMark Murray } 578a04a10f8SKris Kennaway xfree(remote_hostname); 579a04a10f8SKris Kennaway } 580a04a10f8SKris Kennaway } 581511b41d2SMark Murray 582511b41d2SMark Murray /* 583a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 584511b41d2SMark Murray */ 585a04a10f8SKris Kennaway void 586a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) 587a04a10f8SKris Kennaway { 588a04a10f8SKris Kennaway struct sockaddr addr; 589a04a10f8SKris Kennaway int newsock, newch; 590a04a10f8SKris Kennaway socklen_t addrlen; 591a04a10f8SKris Kennaway char buf[1024], *remote_hostname; 592a04a10f8SKris Kennaway int remote_port; 593a04a10f8SKris Kennaway 594a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 595a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 596a04a10f8SKris Kennaway "to %.100s port %d requested.", 597a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 598511b41d2SMark Murray addrlen = sizeof(addr); 599a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 600511b41d2SMark Murray if (newsock < 0) { 601511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 602a04a10f8SKris Kennaway return; 603511b41d2SMark Murray } 604511b41d2SMark Murray remote_hostname = get_remote_hostname(newsock); 605a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 606a04a10f8SKris Kennaway snprintf(buf, sizeof buf, 607a04a10f8SKris Kennaway "listen port %d for %.100s port %d, " 608a04a10f8SKris Kennaway "connect from %.200s port %d", 609a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port, 610a04a10f8SKris Kennaway remote_hostname, remote_port); 611a04a10f8SKris Kennaway newch = channel_new("direct-tcpip", 612a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 613a04a10f8SKris Kennaway c->local_window_max, c->local_maxpacket, 6145b9b2fafSBrian Feldman 0, xstrdup(buf), 1); 615a04a10f8SKris Kennaway if (compat20) { 616a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 617a04a10f8SKris Kennaway packet_put_cstring("direct-tcpip"); 618a04a10f8SKris Kennaway packet_put_int(newch); 619a04a10f8SKris Kennaway packet_put_int(c->local_window_max); 620a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 621a04a10f8SKris Kennaway /* target host and port */ 622a04a10f8SKris Kennaway packet_put_string(c->path, strlen(c->path)); 623a04a10f8SKris Kennaway packet_put_int(c->host_port); 624a04a10f8SKris Kennaway /* originator host and port */ 625a04a10f8SKris Kennaway packet_put_cstring(remote_hostname); 626a04a10f8SKris Kennaway packet_put_int(remote_port); 627a04a10f8SKris Kennaway packet_send(); 628a04a10f8SKris Kennaway } else { 629511b41d2SMark Murray packet_start(SSH_MSG_PORT_OPEN); 630511b41d2SMark Murray packet_put_int(newch); 631a04a10f8SKris Kennaway packet_put_string(c->path, strlen(c->path)); 632a04a10f8SKris Kennaway packet_put_int(c->host_port); 633a04a10f8SKris Kennaway if (have_hostname_in_open) { 634511b41d2SMark Murray packet_put_string(buf, strlen(buf)); 635a04a10f8SKris Kennaway } 636511b41d2SMark Murray packet_send(); 637511b41d2SMark Murray } 638a04a10f8SKris Kennaway xfree(remote_hostname); 639a04a10f8SKris Kennaway } 640a04a10f8SKris Kennaway } 641511b41d2SMark Murray 642511b41d2SMark Murray /* 643a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 644a04a10f8SKris Kennaway * clients. 645511b41d2SMark Murray */ 646a04a10f8SKris Kennaway void 647a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 648a04a10f8SKris Kennaway { 649a04a10f8SKris Kennaway struct sockaddr addr; 650a04a10f8SKris Kennaway int newsock, newch; 651a04a10f8SKris Kennaway socklen_t addrlen; 652a04a10f8SKris Kennaway 653a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 654511b41d2SMark Murray addrlen = sizeof(addr); 655a04a10f8SKris Kennaway newsock = accept(c->sock, &addr, &addrlen); 656511b41d2SMark Murray if (newsock < 0) { 657511b41d2SMark Murray error("accept from auth socket: %.100s", strerror(errno)); 658a04a10f8SKris Kennaway return; 659511b41d2SMark Murray } 660511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, 661511b41d2SMark Murray xstrdup("accepted auth socket")); 662511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 663511b41d2SMark Murray packet_put_int(newch); 664511b41d2SMark Murray packet_send(); 665511b41d2SMark Murray } 666a04a10f8SKris Kennaway } 667511b41d2SMark Murray 668a04a10f8SKris Kennaway int 669a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) 670a04a10f8SKris Kennaway { 671a04a10f8SKris Kennaway char buf[16*1024]; 672a04a10f8SKris Kennaway int len; 673511b41d2SMark Murray 674a04a10f8SKris Kennaway if (c->rfd != -1 && 675a04a10f8SKris Kennaway FD_ISSET(c->rfd, readset)) { 676a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 677a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 678a04a10f8SKris Kennaway return 1; 679a04a10f8SKris Kennaway if (len <= 0) { 680a04a10f8SKris Kennaway debug("channel %d: read<=0 rfd %d len %d", 681a04a10f8SKris Kennaway c->self, c->rfd, len); 682a04a10f8SKris Kennaway if (compat13) { 683a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 684a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 685a04a10f8SKris Kennaway debug("Channel %d status set to input draining.", c->self); 686a04a10f8SKris Kennaway } else { 687a04a10f8SKris Kennaway chan_read_failed(c); 688a04a10f8SKris Kennaway } 689a04a10f8SKris Kennaway return -1; 690a04a10f8SKris Kennaway } 691b66f2d16SKris Kennaway if(c->input_filter != NULL) { 692b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 693b66f2d16SKris Kennaway debug("filter stops channel %d", c->self); 694b66f2d16SKris Kennaway chan_read_failed(c); 695b66f2d16SKris Kennaway } 696b66f2d16SKris Kennaway } else { 697a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 698a04a10f8SKris Kennaway } 699b66f2d16SKris Kennaway } 700a04a10f8SKris Kennaway return 1; 701a04a10f8SKris Kennaway } 702a04a10f8SKris Kennaway int 703a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) 704a04a10f8SKris Kennaway { 705a04a10f8SKris Kennaway int len; 706a04a10f8SKris Kennaway 707a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 708a04a10f8SKris Kennaway if (c->wfd != -1 && 709a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 710a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 711a04a10f8SKris Kennaway len = write(c->wfd, buffer_ptr(&c->output), 712a04a10f8SKris Kennaway buffer_len(&c->output)); 713a04a10f8SKris Kennaway if (len < 0 && (errno == EINTR || errno == EAGAIN)) 714a04a10f8SKris Kennaway return 1; 715511b41d2SMark Murray if (len <= 0) { 716511b41d2SMark Murray if (compat13) { 717a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 718a04a10f8SKris Kennaway debug("Channel %d status set to input draining.", c->self); 719a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 720511b41d2SMark Murray } else { 721a04a10f8SKris Kennaway chan_write_failed(c); 722511b41d2SMark Murray } 723a04a10f8SKris Kennaway return -1; 724511b41d2SMark Murray } 725a04a10f8SKris Kennaway buffer_consume(&c->output, len); 726a04a10f8SKris Kennaway if (compat20 && len > 0) { 727a04a10f8SKris Kennaway c->local_consumed += len; 728511b41d2SMark Murray } 729511b41d2SMark Murray } 730a04a10f8SKris Kennaway return 1; 731511b41d2SMark Murray } 732a04a10f8SKris Kennaway int 733a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) 734a04a10f8SKris Kennaway { 735a04a10f8SKris Kennaway char buf[16*1024]; 736a04a10f8SKris Kennaway int len; 737511b41d2SMark Murray 738a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 739a04a10f8SKris Kennaway if (c->efd != -1) { 740a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 741a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 742a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 743a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 744a04a10f8SKris Kennaway buffer_len(&c->extended)); 7455b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 746a04a10f8SKris Kennaway c->self, len, c->efd); 747a04a10f8SKris Kennaway if (len > 0) { 748a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 749a04a10f8SKris Kennaway c->local_consumed += len; 750a04a10f8SKris Kennaway } 751a04a10f8SKris Kennaway } else if (c->extended_usage == CHAN_EXTENDED_READ && 752a04a10f8SKris Kennaway FD_ISSET(c->efd, readset)) { 753a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 7545b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 755a04a10f8SKris Kennaway c->self, len, c->efd); 756a04a10f8SKris Kennaway if (len == 0) { 757a04a10f8SKris Kennaway debug("channel %d: closing efd %d", 758a04a10f8SKris Kennaway c->self, c->efd); 759a04a10f8SKris Kennaway close(c->efd); 760a04a10f8SKris Kennaway c->efd = -1; 761a04a10f8SKris Kennaway } else if (len > 0) 762a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 763a04a10f8SKris Kennaway } 764a04a10f8SKris Kennaway } 765a04a10f8SKris Kennaway return 1; 766a04a10f8SKris Kennaway } 767a04a10f8SKris Kennaway int 768a04a10f8SKris Kennaway channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) 769a04a10f8SKris Kennaway { 770a04a10f8SKris Kennaway if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 771a04a10f8SKris Kennaway c->local_window < c->local_window_max/2 && 772a04a10f8SKris Kennaway c->local_consumed > 0) { 773a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 774a04a10f8SKris Kennaway packet_put_int(c->remote_id); 775a04a10f8SKris Kennaway packet_put_int(c->local_consumed); 776a04a10f8SKris Kennaway packet_send(); 7775b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 778a04a10f8SKris Kennaway c->self, c->local_window, 779a04a10f8SKris Kennaway c->local_consumed); 780a04a10f8SKris Kennaway c->local_window += c->local_consumed; 781a04a10f8SKris Kennaway c->local_consumed = 0; 782a04a10f8SKris Kennaway } 783a04a10f8SKris Kennaway return 1; 784a04a10f8SKris Kennaway } 785a04a10f8SKris Kennaway 786a04a10f8SKris Kennaway void 787a04a10f8SKris Kennaway channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) 788a04a10f8SKris Kennaway { 789a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 790a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 791a04a10f8SKris Kennaway } 792a04a10f8SKris Kennaway 793a04a10f8SKris Kennaway void 794a04a10f8SKris Kennaway channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset) 795a04a10f8SKris Kennaway { 796a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 797a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 798a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 799a04a10f8SKris Kennaway channel_check_window(c, readset, writeset); 800a04a10f8SKris Kennaway } 801a04a10f8SKris Kennaway 802a04a10f8SKris Kennaway void 803a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) 804a04a10f8SKris Kennaway { 805a04a10f8SKris Kennaway int len; 806511b41d2SMark Murray /* Send buffered output data to the socket. */ 807a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 808a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 809a04a10f8SKris Kennaway buffer_len(&c->output)); 810511b41d2SMark Murray if (len <= 0) 811a04a10f8SKris Kennaway buffer_consume(&c->output, buffer_len(&c->output)); 812511b41d2SMark Murray else 813a04a10f8SKris Kennaway buffer_consume(&c->output, len); 814511b41d2SMark Murray } 815a04a10f8SKris Kennaway } 816511b41d2SMark Murray 817a04a10f8SKris Kennaway void 818a04a10f8SKris Kennaway channel_handler_init_20(void) 819a04a10f8SKris Kennaway { 820a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; 821a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 822a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 823a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 824a04a10f8SKris Kennaway 825a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; 826a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 827a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 828a04a10f8SKris Kennaway } 829a04a10f8SKris Kennaway 830a04a10f8SKris Kennaway void 831a04a10f8SKris Kennaway channel_handler_init_13(void) 832a04a10f8SKris Kennaway { 833a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 834a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 835a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 836a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 837a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 838a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 839a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 840a04a10f8SKris Kennaway 841a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; 842a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 843a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 844a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 845a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 846a04a10f8SKris Kennaway } 847a04a10f8SKris Kennaway 848a04a10f8SKris Kennaway void 849a04a10f8SKris Kennaway channel_handler_init_15(void) 850a04a10f8SKris Kennaway { 851a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15; 852a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 853a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 854a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 855a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 856a04a10f8SKris Kennaway 857a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 858a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 859a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 860a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; 861a04a10f8SKris Kennaway } 862a04a10f8SKris Kennaway 863a04a10f8SKris Kennaway void 864a04a10f8SKris Kennaway channel_handler_init(void) 865a04a10f8SKris Kennaway { 866a04a10f8SKris Kennaway int i; 867a04a10f8SKris Kennaway for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 868a04a10f8SKris Kennaway channel_pre[i] = NULL; 869a04a10f8SKris Kennaway channel_post[i] = NULL; 870a04a10f8SKris Kennaway } 871a04a10f8SKris Kennaway if (compat20) 872a04a10f8SKris Kennaway channel_handler_init_20(); 873a04a10f8SKris Kennaway else if (compat13) 874a04a10f8SKris Kennaway channel_handler_init_13(); 875a04a10f8SKris Kennaway else 876a04a10f8SKris Kennaway channel_handler_init_15(); 877a04a10f8SKris Kennaway } 878a04a10f8SKris Kennaway 879a04a10f8SKris Kennaway void 880a04a10f8SKris Kennaway channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) 881a04a10f8SKris Kennaway { 882a04a10f8SKris Kennaway static int did_init = 0; 883a04a10f8SKris Kennaway int i; 884a04a10f8SKris Kennaway Channel *c; 885a04a10f8SKris Kennaway 886a04a10f8SKris Kennaway if (!did_init) { 887a04a10f8SKris Kennaway channel_handler_init(); 888a04a10f8SKris Kennaway did_init = 1; 889a04a10f8SKris Kennaway } 890a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) { 891a04a10f8SKris Kennaway c = &channels[i]; 892a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_FREE) 893511b41d2SMark Murray continue; 894a04a10f8SKris Kennaway if (ftab[c->type] == NULL) 895a04a10f8SKris Kennaway continue; 896a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 897a04a10f8SKris Kennaway chan_delete_if_full_closed(c); 898511b41d2SMark Murray } 899511b41d2SMark Murray } 900a04a10f8SKris Kennaway 901a04a10f8SKris Kennaway void 902a04a10f8SKris Kennaway channel_prepare_select(fd_set * readset, fd_set * writeset) 903a04a10f8SKris Kennaway { 904a04a10f8SKris Kennaway channel_handler(channel_pre, readset, writeset); 905a04a10f8SKris Kennaway } 906a04a10f8SKris Kennaway 907a04a10f8SKris Kennaway void 908a04a10f8SKris Kennaway channel_after_select(fd_set * readset, fd_set * writeset) 909a04a10f8SKris Kennaway { 910a04a10f8SKris Kennaway channel_handler(channel_post, readset, writeset); 911511b41d2SMark Murray } 912511b41d2SMark Murray 913511b41d2SMark Murray /* If there is data to send to the connection, send some of it now. */ 914511b41d2SMark Murray 915511b41d2SMark Murray void 916511b41d2SMark Murray channel_output_poll() 917511b41d2SMark Murray { 918511b41d2SMark Murray int len, i; 919a04a10f8SKris Kennaway Channel *c; 920511b41d2SMark Murray 921511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 922a04a10f8SKris Kennaway c = &channels[i]; 923511b41d2SMark Murray 924511b41d2SMark Murray /* We are only interested in channels that can have buffered incoming data. */ 925511b41d2SMark Murray if (compat13) { 926a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 927a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 928511b41d2SMark Murray continue; 929511b41d2SMark Murray } else { 930a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 931511b41d2SMark Murray continue; 932a04a10f8SKris Kennaway if (c->istate != CHAN_INPUT_OPEN && 933a04a10f8SKris Kennaway c->istate != CHAN_INPUT_WAIT_DRAIN) 934a04a10f8SKris Kennaway continue; 935a04a10f8SKris Kennaway } 936a04a10f8SKris Kennaway if (compat20 && 937a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 938a04a10f8SKris Kennaway debug("channel: %d: no data after CLOSE", c->self); 939511b41d2SMark Murray continue; 940511b41d2SMark Murray } 941511b41d2SMark Murray 942511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 943a04a10f8SKris Kennaway len = buffer_len(&c->input); 944511b41d2SMark Murray if (len > 0) { 945511b41d2SMark Murray /* Send some data for the other side over the secure connection. */ 946a04a10f8SKris Kennaway if (compat20) { 947a04a10f8SKris Kennaway if (len > c->remote_window) 948a04a10f8SKris Kennaway len = c->remote_window; 949a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 950a04a10f8SKris Kennaway len = c->remote_maxpacket; 951a04a10f8SKris Kennaway } else { 952511b41d2SMark Murray if (packet_is_interactive()) { 953511b41d2SMark Murray if (len > 1024) 954511b41d2SMark Murray len = 512; 955511b41d2SMark Murray } else { 956511b41d2SMark Murray /* Keep the packets at reasonable size. */ 957511b41d2SMark Murray if (len > packet_get_maxsize()/2) 958511b41d2SMark Murray len = packet_get_maxsize()/2; 959511b41d2SMark Murray } 960a04a10f8SKris Kennaway } 961a04a10f8SKris Kennaway if (len > 0) { 962a04a10f8SKris Kennaway packet_start(compat20 ? 963a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 964a04a10f8SKris Kennaway packet_put_int(c->remote_id); 965a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 966511b41d2SMark Murray packet_send(); 967a04a10f8SKris Kennaway buffer_consume(&c->input, len); 968a04a10f8SKris Kennaway c->remote_window -= len; 969a04a10f8SKris Kennaway } 970a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 971511b41d2SMark Murray if (compat13) 972511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 973511b41d2SMark Murray /* 974511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 975511b41d2SMark Murray * tell peer, that we will not send more data: send IEOF 976511b41d2SMark Murray */ 977a04a10f8SKris Kennaway chan_ibuf_empty(c); 978a04a10f8SKris Kennaway } 979a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 980a04a10f8SKris Kennaway if (compat20 && 981a04a10f8SKris Kennaway c->remote_window > 0 && 982a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 983a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 984a04a10f8SKris Kennaway if (len > c->remote_window) 985a04a10f8SKris Kennaway len = c->remote_window; 986a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 987a04a10f8SKris Kennaway len = c->remote_maxpacket; 988a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 989a04a10f8SKris Kennaway packet_put_int(c->remote_id); 990a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 991a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 992a04a10f8SKris Kennaway packet_send(); 993a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 994a04a10f8SKris Kennaway c->remote_window -= len; 995511b41d2SMark Murray } 996511b41d2SMark Murray } 997511b41d2SMark Murray } 998511b41d2SMark Murray 999511b41d2SMark Murray /* 1000511b41d2SMark Murray * This is called when a packet of type CHANNEL_DATA has just been received. 1001511b41d2SMark Murray * The message type has already been consumed, but channel number and data is 1002511b41d2SMark Murray * still there. 1003511b41d2SMark Murray */ 1004511b41d2SMark Murray 1005511b41d2SMark Murray void 10065b9b2fafSBrian Feldman channel_input_data(int type, int plen, void *ctxt) 1007511b41d2SMark Murray { 1008511b41d2SMark Murray int id; 1009511b41d2SMark Murray char *data; 1010511b41d2SMark Murray unsigned int data_len; 1011a04a10f8SKris Kennaway Channel *c; 1012511b41d2SMark Murray 1013511b41d2SMark Murray /* Get the channel number and verify it. */ 1014511b41d2SMark Murray id = packet_get_int(); 1015a04a10f8SKris Kennaway c = channel_lookup(id); 1016a04a10f8SKris Kennaway if (c == NULL) 1017511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 1018511b41d2SMark Murray 1019511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 1020a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 1021a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 1022511b41d2SMark Murray return; 1023511b41d2SMark Murray 1024511b41d2SMark Murray /* same for protocol 1.5 if output end is no longer open */ 1025a04a10f8SKris Kennaway if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) 1026511b41d2SMark Murray return; 1027511b41d2SMark Murray 1028511b41d2SMark Murray /* Get the data. */ 1029511b41d2SMark Murray data = packet_get_string(&data_len); 1030a04a10f8SKris Kennaway packet_done(); 1031a04a10f8SKris Kennaway 1032a04a10f8SKris Kennaway if (compat20){ 1033a04a10f8SKris Kennaway if (data_len > c->local_maxpacket) { 1034a04a10f8SKris Kennaway log("channel %d: rcvd big packet %d, maxpack %d", 1035a04a10f8SKris Kennaway c->self, data_len, c->local_maxpacket); 1036a04a10f8SKris Kennaway } 1037a04a10f8SKris Kennaway if (data_len > c->local_window) { 1038a04a10f8SKris Kennaway log("channel %d: rcvd too much data %d, win %d", 1039a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1040a04a10f8SKris Kennaway xfree(data); 1041a04a10f8SKris Kennaway return; 1042a04a10f8SKris Kennaway } 1043a04a10f8SKris Kennaway c->local_window -= data_len; 1044a04a10f8SKris Kennaway }else{ 1045a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + 4 + data_len, type); 1046a04a10f8SKris Kennaway } 1047a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 1048511b41d2SMark Murray xfree(data); 1049511b41d2SMark Murray } 1050a04a10f8SKris Kennaway void 10515b9b2fafSBrian Feldman channel_input_extended_data(int type, int plen, void *ctxt) 1052a04a10f8SKris Kennaway { 1053a04a10f8SKris Kennaway int id; 1054a04a10f8SKris Kennaway int tcode; 1055a04a10f8SKris Kennaway char *data; 1056a04a10f8SKris Kennaway unsigned int data_len; 1057a04a10f8SKris Kennaway Channel *c; 1058a04a10f8SKris Kennaway 1059a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 1060a04a10f8SKris Kennaway id = packet_get_int(); 1061a04a10f8SKris Kennaway c = channel_lookup(id); 1062a04a10f8SKris Kennaway 1063a04a10f8SKris Kennaway if (c == NULL) 1064a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 1065a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 1066a04a10f8SKris Kennaway log("channel %d: ext data for non open", id); 1067a04a10f8SKris Kennaway return; 1068a04a10f8SKris Kennaway } 1069a04a10f8SKris Kennaway tcode = packet_get_int(); 1070a04a10f8SKris Kennaway if (c->efd == -1 || 1071a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 1072a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 1073a04a10f8SKris Kennaway log("channel %d: bad ext data", c->self); 1074a04a10f8SKris Kennaway return; 1075a04a10f8SKris Kennaway } 1076a04a10f8SKris Kennaway data = packet_get_string(&data_len); 1077a04a10f8SKris Kennaway packet_done(); 1078a04a10f8SKris Kennaway if (data_len > c->local_window) { 1079a04a10f8SKris Kennaway log("channel %d: rcvd too much extended_data %d, win %d", 1080a04a10f8SKris Kennaway c->self, data_len, c->local_window); 1081a04a10f8SKris Kennaway xfree(data); 1082a04a10f8SKris Kennaway return; 1083a04a10f8SKris Kennaway } 10845b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 1085a04a10f8SKris Kennaway c->local_window -= data_len; 1086a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 1087a04a10f8SKris Kennaway xfree(data); 1088a04a10f8SKris Kennaway } 1089a04a10f8SKris Kennaway 1090511b41d2SMark Murray 1091511b41d2SMark Murray /* 1092511b41d2SMark Murray * Returns true if no channel has too much buffered data, and false if one or 1093511b41d2SMark Murray * more channel is overfull. 1094511b41d2SMark Murray */ 1095511b41d2SMark Murray 1096511b41d2SMark Murray int 1097511b41d2SMark Murray channel_not_very_much_buffered_data() 1098511b41d2SMark Murray { 1099511b41d2SMark Murray unsigned int i; 1100a04a10f8SKris Kennaway Channel *c; 1101511b41d2SMark Murray 1102511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1103a04a10f8SKris Kennaway c = &channels[i]; 1104a04a10f8SKris Kennaway if (c->type == SSH_CHANNEL_OPEN) { 1105a04a10f8SKris Kennaway if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { 1106a04a10f8SKris Kennaway debug("channel %d: big input buffer %d", 1107a04a10f8SKris Kennaway c->self, buffer_len(&c->input)); 1108511b41d2SMark Murray return 0; 1109a04a10f8SKris Kennaway } 1110a04a10f8SKris Kennaway if (buffer_len(&c->output) > packet_get_maxsize()) { 1111a04a10f8SKris Kennaway debug("channel %d: big output buffer %d", 1112a04a10f8SKris Kennaway c->self, buffer_len(&c->output)); 1113511b41d2SMark Murray return 0; 1114511b41d2SMark Murray } 1115511b41d2SMark Murray } 1116a04a10f8SKris Kennaway } 1117511b41d2SMark Murray return 1; 1118511b41d2SMark Murray } 1119511b41d2SMark Murray 1120a04a10f8SKris Kennaway void 11215b9b2fafSBrian Feldman channel_input_ieof(int type, int plen, void *ctxt) 1122a04a10f8SKris Kennaway { 1123a04a10f8SKris Kennaway int id; 1124a04a10f8SKris Kennaway Channel *c; 1125a04a10f8SKris Kennaway 1126a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1127a04a10f8SKris Kennaway 1128a04a10f8SKris Kennaway id = packet_get_int(); 1129a04a10f8SKris Kennaway c = channel_lookup(id); 1130a04a10f8SKris Kennaway if (c == NULL) 1131a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 1132a04a10f8SKris Kennaway chan_rcvd_ieof(c); 1133a04a10f8SKris Kennaway } 1134511b41d2SMark Murray 1135511b41d2SMark Murray void 11365b9b2fafSBrian Feldman channel_input_close(int type, int plen, void *ctxt) 1137511b41d2SMark Murray { 1138a04a10f8SKris Kennaway int id; 1139a04a10f8SKris Kennaway Channel *c; 1140511b41d2SMark Murray 1141a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1142511b41d2SMark Murray 1143a04a10f8SKris Kennaway id = packet_get_int(); 1144a04a10f8SKris Kennaway c = channel_lookup(id); 1145a04a10f8SKris Kennaway if (c == NULL) 1146a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 1147511b41d2SMark Murray 1148511b41d2SMark Murray /* 1149511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 1150511b41d2SMark Murray * data is coming for it. 1151511b41d2SMark Murray */ 1152511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 1153a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1154511b41d2SMark Murray packet_send(); 1155511b41d2SMark Murray 1156511b41d2SMark Murray /* 1157511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 1158511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 1159511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 1160511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 1161511b41d2SMark Murray * the confirmation arrives. 1162511b41d2SMark Murray */ 1163a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 1164511b41d2SMark Murray /* 1165511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 1166511b41d2SMark Murray * cause it to be freed later. 1167511b41d2SMark Murray */ 1168a04a10f8SKris Kennaway buffer_consume(&c->input, buffer_len(&c->input)); 1169a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 1170511b41d2SMark Murray } 1171511b41d2SMark Murray } 1172511b41d2SMark Murray 1173a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 1174a04a10f8SKris Kennaway void 11755b9b2fafSBrian Feldman channel_input_oclose(int type, int plen, void *ctxt) 1176a04a10f8SKris Kennaway { 1177a04a10f8SKris Kennaway int id = packet_get_int(); 1178a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1179a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1180a04a10f8SKris Kennaway if (c == NULL) 1181a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 1182a04a10f8SKris Kennaway chan_rcvd_oclose(c); 1183a04a10f8SKris Kennaway } 1184511b41d2SMark Murray 1185511b41d2SMark Murray void 11865b9b2fafSBrian Feldman channel_input_close_confirmation(int type, int plen, void *ctxt) 1187511b41d2SMark Murray { 1188a04a10f8SKris Kennaway int id = packet_get_int(); 1189a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 1190a04a10f8SKris Kennaway 1191a04a10f8SKris Kennaway packet_done(); 1192a04a10f8SKris Kennaway if (c == NULL) 1193a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1194a04a10f8SKris Kennaway "out-of-range channel %d.", id); 1195a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) 1196a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 1197a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 1198a04a10f8SKris Kennaway channel_free(c->self); 1199a04a10f8SKris Kennaway } 1200a04a10f8SKris Kennaway 1201a04a10f8SKris Kennaway void 12025b9b2fafSBrian Feldman channel_input_open_confirmation(int type, int plen, void *ctxt) 1203a04a10f8SKris Kennaway { 1204a04a10f8SKris Kennaway int id, remote_id; 1205a04a10f8SKris Kennaway Channel *c; 1206a04a10f8SKris Kennaway 1207a04a10f8SKris Kennaway if (!compat20) 1208a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + 4, type); 1209a04a10f8SKris Kennaway 1210a04a10f8SKris Kennaway id = packet_get_int(); 1211a04a10f8SKris Kennaway c = channel_lookup(id); 1212a04a10f8SKris Kennaway 1213a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1214a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 1215a04a10f8SKris Kennaway "non-opening channel %d.", id); 1216a04a10f8SKris Kennaway remote_id = packet_get_int(); 1217a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 1218a04a10f8SKris Kennaway c->remote_id = remote_id; 1219a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1220a04a10f8SKris Kennaway 1221a04a10f8SKris Kennaway if (compat20) { 1222a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 1223a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 1224a04a10f8SKris Kennaway packet_done(); 1225a04a10f8SKris Kennaway if (c->cb_fn != NULL && c->cb_event == type) { 12265b9b2fafSBrian Feldman debug2("callback start"); 1227a04a10f8SKris Kennaway c->cb_fn(c->self, c->cb_arg); 12285b9b2fafSBrian Feldman debug2("callback done"); 1229a04a10f8SKris Kennaway } 1230a04a10f8SKris Kennaway debug("channel %d: open confirm rwindow %d rmax %d", c->self, 1231a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 1232a04a10f8SKris Kennaway } 1233a04a10f8SKris Kennaway } 1234a04a10f8SKris Kennaway 1235a04a10f8SKris Kennaway void 12365b9b2fafSBrian Feldman channel_input_open_failure(int type, int plen, void *ctxt) 1237a04a10f8SKris Kennaway { 1238a04a10f8SKris Kennaway int id; 1239a04a10f8SKris Kennaway Channel *c; 1240a04a10f8SKris Kennaway 1241a04a10f8SKris Kennaway if (!compat20) 1242a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 1243a04a10f8SKris Kennaway 1244a04a10f8SKris Kennaway id = packet_get_int(); 1245a04a10f8SKris Kennaway c = channel_lookup(id); 1246a04a10f8SKris Kennaway 1247a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 1248a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 1249a04a10f8SKris Kennaway "non-opening channel %d.", id); 1250a04a10f8SKris Kennaway if (compat20) { 1251a04a10f8SKris Kennaway int reason = packet_get_int(); 1252a04a10f8SKris Kennaway char *msg = packet_get_string(NULL); 1253a04a10f8SKris Kennaway char *lang = packet_get_string(NULL); 1254a04a10f8SKris Kennaway log("channel_open_failure: %d: reason %d: %s", id, reason, msg); 1255a04a10f8SKris Kennaway packet_done(); 1256a04a10f8SKris Kennaway xfree(msg); 1257a04a10f8SKris Kennaway xfree(lang); 1258a04a10f8SKris Kennaway } 1259a04a10f8SKris Kennaway /* Free the channel. This will also close the socket. */ 1260a04a10f8SKris Kennaway channel_free(id); 1261a04a10f8SKris Kennaway } 1262a04a10f8SKris Kennaway 1263a04a10f8SKris Kennaway void 12645b9b2fafSBrian Feldman channel_input_channel_request(int type, int plen, void *ctxt) 1265a04a10f8SKris Kennaway { 1266a04a10f8SKris Kennaway int id; 1267a04a10f8SKris Kennaway Channel *c; 1268a04a10f8SKris Kennaway 1269a04a10f8SKris Kennaway id = packet_get_int(); 1270a04a10f8SKris Kennaway c = channel_lookup(id); 1271a04a10f8SKris Kennaway 1272a04a10f8SKris Kennaway if (c == NULL || 1273a04a10f8SKris Kennaway (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL)) 1274a04a10f8SKris Kennaway packet_disconnect("Received request for " 1275a04a10f8SKris Kennaway "non-open channel %d.", id); 1276a04a10f8SKris Kennaway if (c->cb_fn != NULL && c->cb_event == type) { 12775b9b2fafSBrian Feldman debug2("callback start"); 1278a04a10f8SKris Kennaway c->cb_fn(c->self, c->cb_arg); 12795b9b2fafSBrian Feldman debug2("callback done"); 1280a04a10f8SKris Kennaway } else { 1281a04a10f8SKris Kennaway char *service = packet_get_string(NULL); 1282a04a10f8SKris Kennaway debug("channel: %d rcvd request for %s", c->self, service); 1283a04a10f8SKris Kennaway debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event); 1284a04a10f8SKris Kennaway xfree(service); 1285a04a10f8SKris Kennaway } 1286a04a10f8SKris Kennaway } 1287a04a10f8SKris Kennaway 1288a04a10f8SKris Kennaway void 12895b9b2fafSBrian Feldman channel_input_window_adjust(int type, int plen, void *ctxt) 1290a04a10f8SKris Kennaway { 1291a04a10f8SKris Kennaway Channel *c; 1292a04a10f8SKris Kennaway int id, adjust; 1293a04a10f8SKris Kennaway 1294a04a10f8SKris Kennaway if (!compat20) 1295a04a10f8SKris Kennaway return; 1296511b41d2SMark Murray 1297511b41d2SMark Murray /* Get the channel number and verify it. */ 1298a04a10f8SKris Kennaway id = packet_get_int(); 1299a04a10f8SKris Kennaway c = channel_lookup(id); 1300511b41d2SMark Murray 1301a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_OPEN) { 1302a04a10f8SKris Kennaway log("Received window adjust for " 1303a04a10f8SKris Kennaway "non-open channel %d.", id); 1304511b41d2SMark Murray return; 1305511b41d2SMark Murray } 1306a04a10f8SKris Kennaway adjust = packet_get_int(); 1307a04a10f8SKris Kennaway packet_done(); 13085b9b2fafSBrian Feldman debug2("channel %d: rcvd adjust %d", id, adjust); 1309a04a10f8SKris Kennaway c->remote_window += adjust; 1310511b41d2SMark Murray } 1311511b41d2SMark Murray 1312511b41d2SMark Murray /* 1313511b41d2SMark Murray * Stops listening for channels, and removes any unix domain sockets that we 1314511b41d2SMark Murray * might have. 1315511b41d2SMark Murray */ 1316511b41d2SMark Murray 1317511b41d2SMark Murray void 1318511b41d2SMark Murray channel_stop_listening() 1319511b41d2SMark Murray { 1320511b41d2SMark Murray int i; 1321511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1322511b41d2SMark Murray switch (channels[i].type) { 1323511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1324511b41d2SMark Murray close(channels[i].sock); 1325511b41d2SMark Murray remove(channels[i].path); 1326511b41d2SMark Murray channel_free(i); 1327511b41d2SMark Murray break; 1328511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1329511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1330511b41d2SMark Murray close(channels[i].sock); 1331511b41d2SMark Murray channel_free(i); 1332511b41d2SMark Murray break; 1333511b41d2SMark Murray default: 1334511b41d2SMark Murray break; 1335511b41d2SMark Murray } 1336511b41d2SMark Murray } 1337511b41d2SMark Murray } 1338511b41d2SMark Murray 1339511b41d2SMark Murray /* 1340a04a10f8SKris Kennaway * Closes the sockets/fds of all channels. This is used to close extra file 1341511b41d2SMark Murray * descriptors after a fork. 1342511b41d2SMark Murray */ 1343511b41d2SMark Murray 1344511b41d2SMark Murray void 1345511b41d2SMark Murray channel_close_all() 1346511b41d2SMark Murray { 1347511b41d2SMark Murray int i; 1348a04a10f8SKris Kennaway for (i = 0; i < channels_alloc; i++) 1349511b41d2SMark Murray if (channels[i].type != SSH_CHANNEL_FREE) 1350a04a10f8SKris Kennaway channel_close_fds(&channels[i]); 1351511b41d2SMark Murray } 1352511b41d2SMark Murray 1353511b41d2SMark Murray /* Returns the maximum file descriptor number used by the channels. */ 1354511b41d2SMark Murray 1355511b41d2SMark Murray int 1356511b41d2SMark Murray channel_max_fd() 1357511b41d2SMark Murray { 1358511b41d2SMark Murray return channel_max_fd_value; 1359511b41d2SMark Murray } 1360511b41d2SMark Murray 1361511b41d2SMark Murray /* Returns true if any channel is still open. */ 1362511b41d2SMark Murray 1363511b41d2SMark Murray int 1364511b41d2SMark Murray channel_still_open() 1365511b41d2SMark Murray { 1366511b41d2SMark Murray unsigned int i; 1367511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 1368511b41d2SMark Murray switch (channels[i].type) { 1369511b41d2SMark Murray case SSH_CHANNEL_FREE: 1370511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1371511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1372511b41d2SMark Murray case SSH_CHANNEL_CLOSED: 1373511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1374511b41d2SMark Murray continue; 1375a04a10f8SKris Kennaway case SSH_CHANNEL_LARVAL: 1376a04a10f8SKris Kennaway if (!compat20) 1377a04a10f8SKris Kennaway fatal("cannot happen: SSH_CHANNEL_LARVAL"); 1378a04a10f8SKris Kennaway continue; 1379511b41d2SMark Murray case SSH_CHANNEL_OPENING: 1380511b41d2SMark Murray case SSH_CHANNEL_OPEN: 1381511b41d2SMark Murray case SSH_CHANNEL_X11_OPEN: 1382511b41d2SMark Murray return 1; 1383511b41d2SMark Murray case SSH_CHANNEL_INPUT_DRAINING: 1384511b41d2SMark Murray case SSH_CHANNEL_OUTPUT_DRAINING: 1385511b41d2SMark Murray if (!compat13) 1386511b41d2SMark Murray fatal("cannot happen: OUT_DRAIN"); 1387511b41d2SMark Murray return 1; 1388511b41d2SMark Murray default: 1389511b41d2SMark Murray fatal("channel_still_open: bad channel type %d", channels[i].type); 1390511b41d2SMark Murray /* NOTREACHED */ 1391511b41d2SMark Murray } 1392511b41d2SMark Murray return 0; 1393511b41d2SMark Murray } 1394511b41d2SMark Murray 1395511b41d2SMark Murray /* 1396511b41d2SMark Murray * Returns a message describing the currently open forwarded connections, 1397511b41d2SMark Murray * suitable for sending to the client. The message contains crlf pairs for 1398511b41d2SMark Murray * newlines. 1399511b41d2SMark Murray */ 1400511b41d2SMark Murray 1401511b41d2SMark Murray char * 1402511b41d2SMark Murray channel_open_message() 1403511b41d2SMark Murray { 1404511b41d2SMark Murray Buffer buffer; 1405511b41d2SMark Murray int i; 1406511b41d2SMark Murray char buf[512], *cp; 1407511b41d2SMark Murray 1408511b41d2SMark Murray buffer_init(&buffer); 1409511b41d2SMark Murray snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 1410511b41d2SMark Murray buffer_append(&buffer, buf, strlen(buf)); 1411511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 1412511b41d2SMark Murray Channel *c = &channels[i]; 1413511b41d2SMark Murray switch (c->type) { 1414511b41d2SMark Murray case SSH_CHANNEL_FREE: 1415511b41d2SMark Murray case SSH_CHANNEL_X11_LISTENER: 1416511b41d2SMark Murray case SSH_CHANNEL_PORT_LISTENER: 1417511b41d2SMark Murray case SSH_CHANNEL_CLOSED: 1418511b41d2SMark Murray case SSH_CHANNEL_AUTH_SOCKET: 1419511b41d2SMark Murray continue; 1420a04a10f8SKris Kennaway case SSH_CHANNEL_LARVAL: 1421511b41d2SMark Murray case SSH_CHANNEL_OPENING: 1422511b41d2SMark Murray case SSH_CHANNEL_OPEN: 1423511b41d2SMark Murray case SSH_CHANNEL_X11_OPEN: 1424511b41d2SMark Murray case SSH_CHANNEL_INPUT_DRAINING: 1425511b41d2SMark Murray case SSH_CHANNEL_OUTPUT_DRAINING: 1426a04a10f8SKris Kennaway snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 1427511b41d2SMark Murray c->self, c->remote_name, 1428511b41d2SMark Murray c->type, c->remote_id, 1429511b41d2SMark Murray c->istate, buffer_len(&c->input), 1430a04a10f8SKris Kennaway c->ostate, buffer_len(&c->output), 1431a04a10f8SKris Kennaway c->rfd, c->wfd); 1432511b41d2SMark Murray buffer_append(&buffer, buf, strlen(buf)); 1433511b41d2SMark Murray continue; 1434511b41d2SMark Murray default: 1435a04a10f8SKris Kennaway fatal("channel_open_message: bad channel type %d", c->type); 1436511b41d2SMark Murray /* NOTREACHED */ 1437511b41d2SMark Murray } 1438511b41d2SMark Murray } 1439511b41d2SMark Murray buffer_append(&buffer, "\0", 1); 1440511b41d2SMark Murray cp = xstrdup(buffer_ptr(&buffer)); 1441511b41d2SMark Murray buffer_free(&buffer); 1442511b41d2SMark Murray return cp; 1443511b41d2SMark Murray } 1444511b41d2SMark Murray 1445511b41d2SMark Murray /* 1446511b41d2SMark Murray * Initiate forwarding of connections to local port "port" through the secure 1447511b41d2SMark Murray * channel to host:port from remote side. 1448511b41d2SMark Murray */ 1449511b41d2SMark Murray 1450511b41d2SMark Murray void 1451511b41d2SMark Murray channel_request_local_forwarding(u_short port, const char *host, 1452511b41d2SMark Murray u_short host_port, int gateway_ports) 1453511b41d2SMark Murray { 1454511b41d2SMark Murray int success, ch, sock, on = 1; 1455511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1456511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 1457511b41d2SMark Murray struct linger linger; 1458511b41d2SMark Murray 1459511b41d2SMark Murray if (strlen(host) > sizeof(channels[0].path) - 1) 1460511b41d2SMark Murray packet_disconnect("Forward host name too long."); 1461511b41d2SMark Murray 1462511b41d2SMark Murray /* 1463511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 1464511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 1465511b41d2SMark Murray */ 1466511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1467511b41d2SMark Murray hints.ai_family = IPv4or6; 1468511b41d2SMark Murray hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; 1469511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1470511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 1471511b41d2SMark Murray if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) 1472511b41d2SMark Murray packet_disconnect("getaddrinfo: fatal error"); 1473511b41d2SMark Murray 1474511b41d2SMark Murray success = 0; 1475511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1476511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1477511b41d2SMark Murray continue; 1478511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 1479511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 1480511b41d2SMark Murray error("channel_request_local_forwarding: getnameinfo failed"); 1481511b41d2SMark Murray continue; 1482511b41d2SMark Murray } 1483511b41d2SMark Murray /* Create a port to listen for the host. */ 1484511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1485511b41d2SMark Murray if (sock < 0) { 1486511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 1487511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 1488511b41d2SMark Murray continue; 1489511b41d2SMark Murray } 1490511b41d2SMark Murray /* 1491511b41d2SMark Murray * Set socket options. We would like the socket to disappear 1492511b41d2SMark Murray * as soon as it has been closed for whatever reason. 1493511b41d2SMark Murray */ 1494511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); 1495511b41d2SMark Murray linger.l_onoff = 1; 1496511b41d2SMark Murray linger.l_linger = 5; 1497511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); 1498511b41d2SMark Murray debug("Local forwarding listening on %s port %s.", ntop, strport); 1499511b41d2SMark Murray 1500511b41d2SMark Murray /* Bind the socket to the address. */ 1501511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1502511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 1503511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 1504511b41d2SMark Murray close(sock); 1505511b41d2SMark Murray continue; 1506511b41d2SMark Murray } 1507511b41d2SMark Murray /* Start listening for connections on the socket. */ 1508511b41d2SMark Murray if (listen(sock, 5) < 0) { 1509511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 1510511b41d2SMark Murray close(sock); 1511511b41d2SMark Murray continue; 1512511b41d2SMark Murray } 1513511b41d2SMark Murray /* Allocate a channel number for the socket. */ 1514a04a10f8SKris Kennaway ch = channel_new( 1515a04a10f8SKris Kennaway "port listener", SSH_CHANNEL_PORT_LISTENER, 1516a04a10f8SKris Kennaway sock, sock, -1, 1517a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 15185b9b2fafSBrian Feldman 0, xstrdup("port listener"), 1); 1519511b41d2SMark Murray strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); 1520511b41d2SMark Murray channels[ch].host_port = host_port; 1521511b41d2SMark Murray channels[ch].listening_port = port; 1522511b41d2SMark Murray success = 1; 1523511b41d2SMark Murray } 1524511b41d2SMark Murray if (success == 0) 1525511b41d2SMark Murray packet_disconnect("cannot listen port: %d", port); 1526511b41d2SMark Murray freeaddrinfo(aitop); 1527511b41d2SMark Murray } 1528511b41d2SMark Murray 1529511b41d2SMark Murray /* 1530511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 1531511b41d2SMark Murray * the secure channel to host:port from local side. 1532511b41d2SMark Murray */ 1533511b41d2SMark Murray 1534511b41d2SMark Murray void 1535a04a10f8SKris Kennaway channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, 1536a04a10f8SKris Kennaway u_short port_to_connect) 1537511b41d2SMark Murray { 1538511b41d2SMark Murray int payload_len; 1539511b41d2SMark Murray /* Record locally that connection to this host/port is permitted. */ 1540511b41d2SMark Murray if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) 1541511b41d2SMark Murray fatal("channel_request_remote_forwarding: too many forwards"); 1542511b41d2SMark Murray 1543a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); 1544a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; 1545a04a10f8SKris Kennaway permitted_opens[num_permitted_opens].listen_port = listen_port; 1546511b41d2SMark Murray num_permitted_opens++; 1547511b41d2SMark Murray 1548511b41d2SMark Murray /* Send the forward request to the remote side. */ 1549a04a10f8SKris Kennaway if (compat20) { 1550a04a10f8SKris Kennaway const char *address_to_bind = "0.0.0.0"; 1551a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 1552a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 1553a04a10f8SKris Kennaway packet_put_char(0); /* boolean: want reply */ 1554a04a10f8SKris Kennaway packet_put_cstring(address_to_bind); 1555a04a10f8SKris Kennaway packet_put_int(listen_port); 1556a04a10f8SKris Kennaway } else { 1557511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 1558a04a10f8SKris Kennaway packet_put_int(listen_port); 1559a04a10f8SKris Kennaway packet_put_cstring(host_to_connect); 1560a04a10f8SKris Kennaway packet_put_int(port_to_connect); 1561511b41d2SMark Murray packet_send(); 1562511b41d2SMark Murray packet_write_wait(); 1563511b41d2SMark Murray /* 1564511b41d2SMark Murray * Wait for response from the remote side. It will send a disconnect 1565511b41d2SMark Murray * message on failure, and we will never see it here. 1566511b41d2SMark Murray */ 1567511b41d2SMark Murray packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); 1568511b41d2SMark Murray } 1569a04a10f8SKris Kennaway } 1570511b41d2SMark Murray 1571511b41d2SMark Murray /* 1572511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 1573511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 1574511b41d2SMark Murray * message if there was an error). This never returns if there was an error. 1575511b41d2SMark Murray */ 1576511b41d2SMark Murray 1577511b41d2SMark Murray void 1578a04a10f8SKris Kennaway channel_input_port_forward_request(int is_root, int gateway_ports) 1579511b41d2SMark Murray { 1580511b41d2SMark Murray u_short port, host_port; 1581511b41d2SMark Murray char *hostname; 1582511b41d2SMark Murray 1583511b41d2SMark Murray /* Get arguments from the packet. */ 1584511b41d2SMark Murray port = packet_get_int(); 1585511b41d2SMark Murray hostname = packet_get_string(NULL); 1586511b41d2SMark Murray host_port = packet_get_int(); 1587511b41d2SMark Murray 1588511b41d2SMark Murray /* 1589511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 1590511b41d2SMark Murray * privileged port. 1591511b41d2SMark Murray */ 1592511b41d2SMark Murray if (port < IPPORT_RESERVED && !is_root) 1593511b41d2SMark Murray packet_disconnect("Requested forwarding of port %d but user is not root.", 1594511b41d2SMark Murray port); 1595511b41d2SMark Murray /* 1596511b41d2SMark Murray * Initiate forwarding, 1597511b41d2SMark Murray */ 1598a04a10f8SKris Kennaway channel_request_local_forwarding(port, hostname, host_port, gateway_ports); 1599511b41d2SMark Murray 1600511b41d2SMark Murray /* Free the argument string. */ 1601511b41d2SMark Murray xfree(hostname); 1602511b41d2SMark Murray } 1603511b41d2SMark Murray 1604a04a10f8SKris Kennaway /* XXX move to aux.c */ 1605a04a10f8SKris Kennaway int 1606a04a10f8SKris Kennaway channel_connect_to(const char *host, u_short host_port) 1607511b41d2SMark Murray { 1608511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1609511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 1610511b41d2SMark Murray int gaierr; 1611a04a10f8SKris Kennaway int sock = -1; 1612511b41d2SMark Murray 1613511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1614511b41d2SMark Murray hints.ai_family = IPv4or6; 1615511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1616511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", host_port); 1617511b41d2SMark Murray if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 1618511b41d2SMark Murray error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); 1619a04a10f8SKris Kennaway return -1; 1620511b41d2SMark Murray } 1621511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1622511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1623511b41d2SMark Murray continue; 1624511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 1625511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 1626a04a10f8SKris Kennaway error("channel_connect_to: getnameinfo failed"); 1627511b41d2SMark Murray continue; 1628511b41d2SMark Murray } 1629511b41d2SMark Murray /* Create the socket. */ 1630511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1631511b41d2SMark Murray if (sock < 0) { 1632511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1633511b41d2SMark Murray continue; 1634511b41d2SMark Murray } 1635511b41d2SMark Murray /* Connect to the host/port. */ 1636511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1637511b41d2SMark Murray error("connect %.100s port %s: %.100s", ntop, strport, 1638511b41d2SMark Murray strerror(errno)); 1639511b41d2SMark Murray close(sock); 1640511b41d2SMark Murray continue; /* fail -- try next */ 1641511b41d2SMark Murray } 1642511b41d2SMark Murray break; /* success */ 1643511b41d2SMark Murray 1644511b41d2SMark Murray } 1645511b41d2SMark Murray freeaddrinfo(aitop); 1646511b41d2SMark Murray if (!ai) { 1647511b41d2SMark Murray error("connect %.100s port %d: failed.", host, host_port); 1648a04a10f8SKris Kennaway return -1; 1649a04a10f8SKris Kennaway } 1650a04a10f8SKris Kennaway /* success */ 1651a04a10f8SKris Kennaway return sock; 1652a04a10f8SKris Kennaway } 1653a04a10f8SKris Kennaway /* 1654a04a10f8SKris Kennaway * This is called after receiving PORT_OPEN message. This attempts to 1655a04a10f8SKris Kennaway * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION 1656a04a10f8SKris Kennaway * or CHANNEL_OPEN_FAILURE. 1657a04a10f8SKris Kennaway */ 1658a04a10f8SKris Kennaway 1659a04a10f8SKris Kennaway void 16605b9b2fafSBrian Feldman channel_input_port_open(int type, int plen, void *ctxt) 1661a04a10f8SKris Kennaway { 1662a04a10f8SKris Kennaway u_short host_port; 1663a04a10f8SKris Kennaway char *host, *originator_string; 1664a04a10f8SKris Kennaway int remote_channel, sock = -1, newch, i, denied; 1665a04a10f8SKris Kennaway unsigned int host_len, originator_len; 1666a04a10f8SKris Kennaway 1667a04a10f8SKris Kennaway /* Get remote channel number. */ 1668a04a10f8SKris Kennaway remote_channel = packet_get_int(); 1669a04a10f8SKris Kennaway 1670a04a10f8SKris Kennaway /* Get host name to connect to. */ 1671a04a10f8SKris Kennaway host = packet_get_string(&host_len); 1672a04a10f8SKris Kennaway 1673a04a10f8SKris Kennaway /* Get port to connect to. */ 1674a04a10f8SKris Kennaway host_port = packet_get_int(); 1675a04a10f8SKris Kennaway 1676a04a10f8SKris Kennaway /* Get remote originator name. */ 1677a04a10f8SKris Kennaway if (have_hostname_in_open) { 1678a04a10f8SKris Kennaway originator_string = packet_get_string(&originator_len); 1679a04a10f8SKris Kennaway originator_len += 4; /* size of packet_int */ 1680a04a10f8SKris Kennaway } else { 1681a04a10f8SKris Kennaway originator_string = xstrdup("unknown (remote did not supply name)"); 1682a04a10f8SKris Kennaway originator_len = 0; /* no originator supplied */ 1683511b41d2SMark Murray } 1684511b41d2SMark Murray 1685a04a10f8SKris Kennaway packet_integrity_check(plen, 1686a04a10f8SKris Kennaway 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); 1687511b41d2SMark Murray 1688a04a10f8SKris Kennaway /* Check if opening that port is permitted. */ 1689a04a10f8SKris Kennaway denied = 0; 1690a04a10f8SKris Kennaway if (!all_opens_permitted) { 1691a04a10f8SKris Kennaway /* Go trough all permitted ports. */ 1692a04a10f8SKris Kennaway for (i = 0; i < num_permitted_opens; i++) 1693a04a10f8SKris Kennaway if (permitted_opens[i].port_to_connect == host_port && 1694a04a10f8SKris Kennaway strcmp(permitted_opens[i].host_to_connect, host) == 0) 1695a04a10f8SKris Kennaway break; 1696a04a10f8SKris Kennaway 1697a04a10f8SKris Kennaway /* Check if we found the requested port among those permitted. */ 1698a04a10f8SKris Kennaway if (i >= num_permitted_opens) { 1699a04a10f8SKris Kennaway /* The port is not permitted. */ 1700a04a10f8SKris Kennaway log("Received request to connect to %.100s:%d, but the request was denied.", 1701a04a10f8SKris Kennaway host, host_port); 1702a04a10f8SKris Kennaway denied = 1; 1703a04a10f8SKris Kennaway } 1704a04a10f8SKris Kennaway } 1705a04a10f8SKris Kennaway sock = denied ? -1 : channel_connect_to(host, host_port); 1706a04a10f8SKris Kennaway if (sock > 0) { 1707511b41d2SMark Murray /* Allocate a channel for this connection. */ 1708511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); 1709511b41d2SMark Murray channels[newch].remote_id = remote_channel; 1710511b41d2SMark Murray 1711511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1712511b41d2SMark Murray packet_put_int(remote_channel); 1713511b41d2SMark Murray packet_put_int(newch); 1714511b41d2SMark Murray packet_send(); 1715a04a10f8SKris Kennaway } else { 1716511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1717511b41d2SMark Murray packet_put_int(remote_channel); 1718511b41d2SMark Murray packet_send(); 1719511b41d2SMark Murray } 1720a04a10f8SKris Kennaway xfree(host); 1721a04a10f8SKris Kennaway } 1722511b41d2SMark Murray 1723511b41d2SMark Murray /* 1724511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 1725511b41d2SMark Murray * Returns a suitable value for the DISPLAY variable, or NULL if an error 1726511b41d2SMark Murray * occurs. 1727511b41d2SMark Murray */ 1728511b41d2SMark Murray 1729511b41d2SMark Murray #define NUM_SOCKS 10 1730511b41d2SMark Murray 1731511b41d2SMark Murray char * 1732511b41d2SMark Murray x11_create_display_inet(int screen_number, int x11_display_offset) 1733511b41d2SMark Murray { 1734511b41d2SMark Murray int display_number, sock; 1735511b41d2SMark Murray u_short port; 1736511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1737511b41d2SMark Murray char strport[NI_MAXSERV]; 1738511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 1739511b41d2SMark Murray char display[512]; 1740511b41d2SMark Murray char hostname[MAXHOSTNAMELEN]; 1741511b41d2SMark Murray 1742511b41d2SMark Murray for (display_number = x11_display_offset; 1743511b41d2SMark Murray display_number < MAX_DISPLAYS; 1744511b41d2SMark Murray display_number++) { 1745511b41d2SMark Murray port = 6000 + display_number; 1746511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1747511b41d2SMark Murray hints.ai_family = IPv4or6; 1748511b41d2SMark Murray hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */ 1749511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1750511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 1751511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 1752511b41d2SMark Murray error("getaddrinfo: %.100s", gai_strerror(gaierr)); 1753511b41d2SMark Murray return NULL; 1754511b41d2SMark Murray } 1755511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1756511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 1757511b41d2SMark Murray continue; 1758511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1759511b41d2SMark Murray if (sock < 0) { 1760511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1761511b41d2SMark Murray return NULL; 1762511b41d2SMark Murray } 1763511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1764511b41d2SMark Murray debug("bind port %d: %.100s", port, strerror(errno)); 1765511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 1766511b41d2SMark Murray close(sock); 1767511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1768511b41d2SMark Murray shutdown(socks[n], SHUT_RDWR); 1769511b41d2SMark Murray close(socks[n]); 1770511b41d2SMark Murray } 1771511b41d2SMark Murray num_socks = 0; 1772511b41d2SMark Murray break; 1773511b41d2SMark Murray } 1774511b41d2SMark Murray socks[num_socks++] = sock; 1775511b41d2SMark Murray if (num_socks == NUM_SOCKS) 1776511b41d2SMark Murray break; 1777511b41d2SMark Murray } 1778511b41d2SMark Murray if (num_socks > 0) 1779511b41d2SMark Murray break; 1780511b41d2SMark Murray } 1781511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 1782511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 1783511b41d2SMark Murray return NULL; 1784511b41d2SMark Murray } 1785511b41d2SMark Murray /* Start listening for connections on the socket. */ 1786511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1787511b41d2SMark Murray sock = socks[n]; 1788511b41d2SMark Murray if (listen(sock, 5) < 0) { 1789511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 1790511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 1791511b41d2SMark Murray close(sock); 1792511b41d2SMark Murray return NULL; 1793511b41d2SMark Murray } 1794511b41d2SMark Murray } 1795511b41d2SMark Murray 1796511b41d2SMark Murray /* Set up a suitable value for the DISPLAY variable. */ 1797511b41d2SMark Murray if (gethostname(hostname, sizeof(hostname)) < 0) 1798511b41d2SMark Murray fatal("gethostname: %.100s", strerror(errno)); 1799511b41d2SMark Murray snprintf(display, sizeof display, "%.400s:%d.%d", hostname, 1800511b41d2SMark Murray display_number, screen_number); 1801511b41d2SMark Murray 1802511b41d2SMark Murray /* Allocate a channel for each socket. */ 1803511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 1804511b41d2SMark Murray sock = socks[n]; 1805a04a10f8SKris Kennaway (void) channel_new("x11 listener", 1806a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 1807a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 18085b9b2fafSBrian Feldman 0, xstrdup("X11 inet listener"), 1); 1809511b41d2SMark Murray } 1810511b41d2SMark Murray 1811511b41d2SMark Murray /* Return a suitable value for the DISPLAY environment variable. */ 1812511b41d2SMark Murray return xstrdup(display); 1813511b41d2SMark Murray } 1814511b41d2SMark Murray 1815511b41d2SMark Murray #ifndef X_UNIX_PATH 1816511b41d2SMark Murray #define X_UNIX_PATH "/tmp/.X11-unix/X" 1817511b41d2SMark Murray #endif 1818511b41d2SMark Murray 1819511b41d2SMark Murray static 1820511b41d2SMark Murray int 1821511b41d2SMark Murray connect_local_xsocket(unsigned int dnr) 1822511b41d2SMark Murray { 1823511b41d2SMark Murray static const char *const x_sockets[] = { 1824511b41d2SMark Murray X_UNIX_PATH "%u", 1825511b41d2SMark Murray "/var/X/.X11-unix/X" "%u", 1826511b41d2SMark Murray "/usr/spool/sockets/X11/" "%u", 1827511b41d2SMark Murray NULL 1828511b41d2SMark Murray }; 1829511b41d2SMark Murray int sock; 1830511b41d2SMark Murray struct sockaddr_un addr; 1831511b41d2SMark Murray const char *const * path; 1832511b41d2SMark Murray 1833511b41d2SMark Murray for (path = x_sockets; *path; ++path) { 1834511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 1835511b41d2SMark Murray if (sock < 0) 1836511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 1837511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 1838511b41d2SMark Murray addr.sun_family = AF_UNIX; 1839511b41d2SMark Murray snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); 1840511b41d2SMark Murray if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) 1841511b41d2SMark Murray return sock; 1842511b41d2SMark Murray close(sock); 1843511b41d2SMark Murray } 1844511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 1845511b41d2SMark Murray return -1; 1846511b41d2SMark Murray } 1847511b41d2SMark Murray 1848a04a10f8SKris Kennaway int 1849a04a10f8SKris Kennaway x11_connect_display(void) 1850511b41d2SMark Murray { 1851a04a10f8SKris Kennaway int display_number, sock = 0; 1852511b41d2SMark Murray const char *display; 1853a04a10f8SKris Kennaway char buf[1024], *cp; 1854511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 1855511b41d2SMark Murray char strport[NI_MAXSERV]; 1856511b41d2SMark Murray int gaierr; 1857511b41d2SMark Murray 1858511b41d2SMark Murray /* Try to open a socket for the local X server. */ 1859511b41d2SMark Murray display = getenv("DISPLAY"); 1860511b41d2SMark Murray if (!display) { 1861511b41d2SMark Murray error("DISPLAY not set."); 1862a04a10f8SKris Kennaway return -1; 1863511b41d2SMark Murray } 1864511b41d2SMark Murray /* 1865511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 1866511b41d2SMark Murray * connection to the real X server. 1867511b41d2SMark Murray */ 1868511b41d2SMark Murray 1869511b41d2SMark Murray /* 1870511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 1871511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 1872511b41d2SMark Murray */ 1873511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 1874511b41d2SMark Murray display[0] == ':') { 1875511b41d2SMark Murray /* Connect to the unix domain socket. */ 1876511b41d2SMark Murray if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { 1877511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 1878511b41d2SMark Murray display); 1879a04a10f8SKris Kennaway return -1; 1880511b41d2SMark Murray } 1881511b41d2SMark Murray /* Create a socket. */ 1882511b41d2SMark Murray sock = connect_local_xsocket(display_number); 1883511b41d2SMark Murray if (sock < 0) 1884a04a10f8SKris Kennaway return -1; 1885511b41d2SMark Murray 1886511b41d2SMark Murray /* OK, we now have a connection to the display. */ 1887a04a10f8SKris Kennaway return sock; 1888511b41d2SMark Murray } 1889511b41d2SMark Murray /* 1890511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 1891511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 1892511b41d2SMark Murray */ 1893511b41d2SMark Murray strncpy(buf, display, sizeof(buf)); 1894511b41d2SMark Murray buf[sizeof(buf) - 1] = 0; 1895511b41d2SMark Murray cp = strchr(buf, ':'); 1896511b41d2SMark Murray if (!cp) { 1897511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 1898a04a10f8SKris Kennaway return -1; 1899511b41d2SMark Murray } 1900511b41d2SMark Murray *cp = 0; 1901511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 1902511b41d2SMark Murray if (sscanf(cp + 1, "%d", &display_number) != 1) { 1903511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 1904511b41d2SMark Murray display); 1905a04a10f8SKris Kennaway return -1; 1906511b41d2SMark Murray } 1907511b41d2SMark Murray 1908511b41d2SMark Murray /* Look up the host address */ 1909511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 1910511b41d2SMark Murray hints.ai_family = IPv4or6; 1911511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 1912511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", 6000 + display_number); 1913511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 1914511b41d2SMark Murray error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); 1915a04a10f8SKris Kennaway return -1; 1916511b41d2SMark Murray } 1917511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 1918511b41d2SMark Murray /* Create a socket. */ 1919511b41d2SMark Murray sock = socket(ai->ai_family, SOCK_STREAM, 0); 1920511b41d2SMark Murray if (sock < 0) { 1921511b41d2SMark Murray debug("socket: %.100s", strerror(errno)); 1922511b41d2SMark Murray continue; 1923511b41d2SMark Murray } 1924511b41d2SMark Murray /* Connect it to the display. */ 1925511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 1926a04a10f8SKris Kennaway debug("connect %.100s port %d: %.100s", buf, 1927a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 1928511b41d2SMark Murray close(sock); 1929511b41d2SMark Murray continue; 1930511b41d2SMark Murray } 1931511b41d2SMark Murray /* Success */ 1932511b41d2SMark Murray break; 1933a04a10f8SKris Kennaway } 1934511b41d2SMark Murray freeaddrinfo(aitop); 1935511b41d2SMark Murray if (!ai) { 1936511b41d2SMark Murray error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 1937511b41d2SMark Murray strerror(errno)); 1938a04a10f8SKris Kennaway return -1; 1939511b41d2SMark Murray } 1940a04a10f8SKris Kennaway return sock; 1941a04a10f8SKris Kennaway } 1942511b41d2SMark Murray 1943a04a10f8SKris Kennaway /* 1944a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 1945a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 1946a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 1947a04a10f8SKris Kennaway */ 1948a04a10f8SKris Kennaway 1949a04a10f8SKris Kennaway void 19505b9b2fafSBrian Feldman x11_input_open(int type, int plen, void *ctxt) 1951a04a10f8SKris Kennaway { 1952a04a10f8SKris Kennaway int remote_channel, sock = 0, newch; 1953a04a10f8SKris Kennaway char *remote_host; 1954a04a10f8SKris Kennaway unsigned int remote_len; 1955a04a10f8SKris Kennaway 1956a04a10f8SKris Kennaway /* Get remote channel number. */ 1957a04a10f8SKris Kennaway remote_channel = packet_get_int(); 1958a04a10f8SKris Kennaway 1959a04a10f8SKris Kennaway /* Get remote originator name. */ 1960a04a10f8SKris Kennaway if (have_hostname_in_open) { 1961a04a10f8SKris Kennaway remote_host = packet_get_string(&remote_len); 1962a04a10f8SKris Kennaway remote_len += 4; 1963a04a10f8SKris Kennaway } else { 1964a04a10f8SKris Kennaway remote_host = xstrdup("unknown (remote did not supply name)"); 1965a04a10f8SKris Kennaway remote_len = 0; 1966a04a10f8SKris Kennaway } 1967a04a10f8SKris Kennaway 1968a04a10f8SKris Kennaway debug("Received X11 open request."); 1969a04a10f8SKris Kennaway packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN); 1970a04a10f8SKris Kennaway 1971a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 1972a04a10f8SKris Kennaway sock = x11_connect_display(); 1973a04a10f8SKris Kennaway if (sock == -1) { 1974a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 1975a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1976a04a10f8SKris Kennaway packet_put_int(remote_channel); 1977a04a10f8SKris Kennaway packet_send(); 1978a04a10f8SKris Kennaway } else { 1979511b41d2SMark Murray /* Allocate a channel for this connection. */ 1980a04a10f8SKris Kennaway newch = channel_allocate( 1981a04a10f8SKris Kennaway (x11_saved_proto == NULL) ? 1982a04a10f8SKris Kennaway SSH_CHANNEL_OPEN : SSH_CHANNEL_X11_OPEN, 1983a04a10f8SKris Kennaway sock, remote_host); 1984511b41d2SMark Murray channels[newch].remote_id = remote_channel; 1985511b41d2SMark Murray 1986511b41d2SMark Murray /* Send a confirmation to the remote host. */ 1987511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1988511b41d2SMark Murray packet_put_int(remote_channel); 1989511b41d2SMark Murray packet_put_int(newch); 1990511b41d2SMark Murray packet_send(); 1991a04a10f8SKris Kennaway } 1992511b41d2SMark Murray } 1993511b41d2SMark Murray 19945b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 19955b9b2fafSBrian Feldman void 19965b9b2fafSBrian Feldman deny_input_open(int type, int plen, void *ctxt) 19975b9b2fafSBrian Feldman { 19985b9b2fafSBrian Feldman int rchan = packet_get_int(); 19995b9b2fafSBrian Feldman switch(type){ 20005b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 20015b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 20025b9b2fafSBrian Feldman break; 20035b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 20045b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 20055b9b2fafSBrian Feldman break; 20065b9b2fafSBrian Feldman default: 20075b9b2fafSBrian Feldman error("deny_input_open: type %d plen %d", type, plen); 20085b9b2fafSBrian Feldman break; 20095b9b2fafSBrian Feldman } 20105b9b2fafSBrian Feldman error("Warning: this is probably a break in attempt by a malicious server."); 20115b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 20125b9b2fafSBrian Feldman packet_put_int(rchan); 20135b9b2fafSBrian Feldman packet_send(); 20145b9b2fafSBrian Feldman } 20155b9b2fafSBrian Feldman 2016511b41d2SMark Murray /* 2017511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 2018511b41d2SMark Murray * data, and enables authentication spoofing. 2019511b41d2SMark Murray */ 2020511b41d2SMark Murray 2021511b41d2SMark Murray void 2022a04a10f8SKris Kennaway x11_request_forwarding_with_spoofing(int client_session_id, 2023a04a10f8SKris Kennaway const char *proto, const char *data) 2024511b41d2SMark Murray { 2025511b41d2SMark Murray unsigned int data_len = (unsigned int) strlen(data) / 2; 2026511b41d2SMark Murray unsigned int i, value; 2027511b41d2SMark Murray char *new_data; 2028511b41d2SMark Murray int screen_number; 2029511b41d2SMark Murray const char *cp; 2030511b41d2SMark Murray u_int32_t rand = 0; 2031511b41d2SMark Murray 2032511b41d2SMark Murray cp = getenv("DISPLAY"); 2033511b41d2SMark Murray if (cp) 2034511b41d2SMark Murray cp = strchr(cp, ':'); 2035511b41d2SMark Murray if (cp) 2036511b41d2SMark Murray cp = strchr(cp, '.'); 2037511b41d2SMark Murray if (cp) 2038511b41d2SMark Murray screen_number = atoi(cp + 1); 2039511b41d2SMark Murray else 2040511b41d2SMark Murray screen_number = 0; 2041511b41d2SMark Murray 2042511b41d2SMark Murray /* Save protocol name. */ 2043511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 2044511b41d2SMark Murray 2045511b41d2SMark Murray /* 2046511b41d2SMark Murray * Extract real authentication data and generate fake data of the 2047511b41d2SMark Murray * same length. 2048511b41d2SMark Murray */ 2049511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 2050511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 2051511b41d2SMark Murray for (i = 0; i < data_len; i++) { 2052511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 2053511b41d2SMark Murray fatal("x11_request_forwarding: bad authentication data: %.100s", data); 2054511b41d2SMark Murray if (i % 4 == 0) 2055511b41d2SMark Murray rand = arc4random(); 2056511b41d2SMark Murray x11_saved_data[i] = value; 2057511b41d2SMark Murray x11_fake_data[i] = rand & 0xff; 2058511b41d2SMark Murray rand >>= 8; 2059511b41d2SMark Murray } 2060511b41d2SMark Murray x11_saved_data_len = data_len; 2061511b41d2SMark Murray x11_fake_data_len = data_len; 2062511b41d2SMark Murray 2063511b41d2SMark Murray /* Convert the fake data into hex. */ 2064511b41d2SMark Murray new_data = xmalloc(2 * data_len + 1); 2065511b41d2SMark Murray for (i = 0; i < data_len; i++) 2066511b41d2SMark Murray sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); 2067511b41d2SMark Murray 2068511b41d2SMark Murray /* Send the request packet. */ 2069a04a10f8SKris Kennaway if (compat20) { 2070a04a10f8SKris Kennaway channel_request_start(client_session_id, "x11-req", 0); 2071a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 2072a04a10f8SKris Kennaway } else { 2073511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 2074a04a10f8SKris Kennaway } 2075a04a10f8SKris Kennaway packet_put_cstring(proto); 2076a04a10f8SKris Kennaway packet_put_cstring(new_data); 2077511b41d2SMark Murray packet_put_int(screen_number); 2078511b41d2SMark Murray packet_send(); 2079511b41d2SMark Murray packet_write_wait(); 2080511b41d2SMark Murray xfree(new_data); 2081511b41d2SMark Murray } 2082511b41d2SMark Murray 2083511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 2084511b41d2SMark Murray 2085511b41d2SMark Murray void 2086511b41d2SMark Murray auth_request_forwarding() 2087511b41d2SMark Murray { 2088511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 2089511b41d2SMark Murray packet_send(); 2090511b41d2SMark Murray packet_write_wait(); 2091511b41d2SMark Murray } 2092511b41d2SMark Murray 2093511b41d2SMark Murray /* 2094511b41d2SMark Murray * Returns the name of the forwarded authentication socket. Returns NULL if 2095511b41d2SMark Murray * there is no forwarded authentication socket. The returned value points to 2096511b41d2SMark Murray * a static buffer. 2097511b41d2SMark Murray */ 2098511b41d2SMark Murray 2099511b41d2SMark Murray char * 2100511b41d2SMark Murray auth_get_socket_name() 2101511b41d2SMark Murray { 2102511b41d2SMark Murray return channel_forwarded_auth_socket_name; 2103511b41d2SMark Murray } 2104511b41d2SMark Murray 2105511b41d2SMark Murray /* removes the agent forwarding socket */ 2106511b41d2SMark Murray 2107511b41d2SMark Murray void 2108511b41d2SMark Murray cleanup_socket(void) 2109511b41d2SMark Murray { 2110511b41d2SMark Murray remove(channel_forwarded_auth_socket_name); 2111511b41d2SMark Murray rmdir(channel_forwarded_auth_socket_dir); 2112511b41d2SMark Murray } 2113511b41d2SMark Murray 2114511b41d2SMark Murray /* 2115fcee55a2SKris Kennaway * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. 2116511b41d2SMark Murray * This starts forwarding authentication requests. 2117511b41d2SMark Murray */ 2118511b41d2SMark Murray 2119fcee55a2SKris Kennaway int 2120511b41d2SMark Murray auth_input_request_forwarding(struct passwd * pw) 2121511b41d2SMark Murray { 2122511b41d2SMark Murray int sock, newch; 2123511b41d2SMark Murray struct sockaddr_un sunaddr; 2124511b41d2SMark Murray 2125511b41d2SMark Murray if (auth_get_socket_name() != NULL) 2126511b41d2SMark Murray fatal("Protocol error: authentication forwarding requested twice."); 2127511b41d2SMark Murray 2128511b41d2SMark Murray /* Temporarily drop privileged uid for mkdir/bind. */ 2129511b41d2SMark Murray temporarily_use_uid(pw->pw_uid); 2130511b41d2SMark Murray 2131511b41d2SMark Murray /* Allocate a buffer for the socket name, and format the name. */ 2132511b41d2SMark Murray channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); 2133511b41d2SMark Murray channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); 2134511b41d2SMark Murray strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); 2135511b41d2SMark Murray 2136511b41d2SMark Murray /* Create private directory for socket */ 2137fcee55a2SKris Kennaway if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) { 2138fcee55a2SKris Kennaway packet_send_debug("Agent forwarding disabled: mkdtemp() failed: %.100s", 2139fcee55a2SKris Kennaway strerror(errno)); 2140fcee55a2SKris Kennaway restore_uid(); 2141fcee55a2SKris Kennaway xfree(channel_forwarded_auth_socket_name); 2142fcee55a2SKris Kennaway xfree(channel_forwarded_auth_socket_dir); 2143fcee55a2SKris Kennaway channel_forwarded_auth_socket_name = NULL; 2144fcee55a2SKris Kennaway channel_forwarded_auth_socket_dir = NULL; 2145fcee55a2SKris Kennaway return 0; 2146fcee55a2SKris Kennaway } 2147511b41d2SMark Murray snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", 2148511b41d2SMark Murray channel_forwarded_auth_socket_dir, (int) getpid()); 2149511b41d2SMark Murray 2150511b41d2SMark Murray if (atexit(cleanup_socket) < 0) { 2151511b41d2SMark Murray int saved = errno; 2152511b41d2SMark Murray cleanup_socket(); 2153511b41d2SMark Murray packet_disconnect("socket: %.100s", strerror(saved)); 2154511b41d2SMark Murray } 2155511b41d2SMark Murray /* Create the socket. */ 2156511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 2157511b41d2SMark Murray if (sock < 0) 2158511b41d2SMark Murray packet_disconnect("socket: %.100s", strerror(errno)); 2159511b41d2SMark Murray 2160511b41d2SMark Murray /* Bind it to the name. */ 2161511b41d2SMark Murray memset(&sunaddr, 0, sizeof(sunaddr)); 2162511b41d2SMark Murray sunaddr.sun_family = AF_UNIX; 2163511b41d2SMark Murray strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, 2164511b41d2SMark Murray sizeof(sunaddr.sun_path)); 2165511b41d2SMark Murray 2166511b41d2SMark Murray if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) 2167511b41d2SMark Murray packet_disconnect("bind: %.100s", strerror(errno)); 2168511b41d2SMark Murray 2169511b41d2SMark Murray /* Restore the privileged uid. */ 2170511b41d2SMark Murray restore_uid(); 2171511b41d2SMark Murray 2172511b41d2SMark Murray /* Start listening on the socket. */ 2173511b41d2SMark Murray if (listen(sock, 5) < 0) 2174511b41d2SMark Murray packet_disconnect("listen: %.100s", strerror(errno)); 2175511b41d2SMark Murray 2176511b41d2SMark Murray /* Allocate a channel for the authentication agent socket. */ 2177511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, 2178511b41d2SMark Murray xstrdup("auth socket")); 2179511b41d2SMark Murray strlcpy(channels[newch].path, channel_forwarded_auth_socket_name, 2180511b41d2SMark Murray sizeof(channels[newch].path)); 2181fcee55a2SKris Kennaway return 1; 2182511b41d2SMark Murray } 2183511b41d2SMark Murray 2184511b41d2SMark Murray /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ 2185511b41d2SMark Murray 2186511b41d2SMark Murray void 21875b9b2fafSBrian Feldman auth_input_open_request(int type, int plen, void *ctxt) 2188511b41d2SMark Murray { 2189511b41d2SMark Murray int remch, sock, newch; 2190511b41d2SMark Murray char *dummyname; 2191511b41d2SMark Murray 2192a04a10f8SKris Kennaway packet_integrity_check(plen, 4, type); 2193a04a10f8SKris Kennaway 2194511b41d2SMark Murray /* Read the remote channel number from the message. */ 2195511b41d2SMark Murray remch = packet_get_int(); 2196511b41d2SMark Murray 2197511b41d2SMark Murray /* 2198511b41d2SMark Murray * Get a connection to the local authentication agent (this may again 2199511b41d2SMark Murray * get forwarded). 2200511b41d2SMark Murray */ 2201511b41d2SMark Murray sock = ssh_get_authentication_socket(); 2202511b41d2SMark Murray 2203511b41d2SMark Murray /* 2204511b41d2SMark Murray * If we could not connect the agent, send an error message back to 2205511b41d2SMark Murray * the server. This should never happen unless the agent dies, 2206511b41d2SMark Murray * because authentication forwarding is only enabled if we have an 2207511b41d2SMark Murray * agent. 2208511b41d2SMark Murray */ 2209511b41d2SMark Murray if (sock < 0) { 2210511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2211511b41d2SMark Murray packet_put_int(remch); 2212511b41d2SMark Murray packet_send(); 2213511b41d2SMark Murray return; 2214511b41d2SMark Murray } 2215511b41d2SMark Murray debug("Forwarding authentication connection."); 2216511b41d2SMark Murray 2217511b41d2SMark Murray /* 2218511b41d2SMark Murray * Dummy host name. This will be freed when the channel is freed; it 2219511b41d2SMark Murray * will still be valid in the packet_put_string below since the 2220511b41d2SMark Murray * channel cannot yet be freed at that point. 2221511b41d2SMark Murray */ 2222511b41d2SMark Murray dummyname = xstrdup("authentication agent connection"); 2223511b41d2SMark Murray 2224511b41d2SMark Murray newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); 2225511b41d2SMark Murray channels[newch].remote_id = remch; 2226511b41d2SMark Murray 2227511b41d2SMark Murray /* Send a confirmation to the remote host. */ 2228511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 2229511b41d2SMark Murray packet_put_int(remch); 2230511b41d2SMark Murray packet_put_int(newch); 2231511b41d2SMark Murray packet_send(); 2232511b41d2SMark Murray } 2233a04a10f8SKris Kennaway 2234a04a10f8SKris Kennaway void 2235a04a10f8SKris Kennaway channel_start_open(int id) 2236a04a10f8SKris Kennaway { 2237a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2238a04a10f8SKris Kennaway if (c == NULL) { 2239a04a10f8SKris Kennaway log("channel_open: %d: bad id", id); 2240a04a10f8SKris Kennaway return; 2241a04a10f8SKris Kennaway } 2242a04a10f8SKris Kennaway debug("send channel open %d", id); 2243a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 2244a04a10f8SKris Kennaway packet_put_cstring(c->ctype); 2245a04a10f8SKris Kennaway packet_put_int(c->self); 2246a04a10f8SKris Kennaway packet_put_int(c->local_window); 2247a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 2248a04a10f8SKris Kennaway } 2249a04a10f8SKris Kennaway void 2250a04a10f8SKris Kennaway channel_open(int id) 2251a04a10f8SKris Kennaway { 2252a04a10f8SKris Kennaway /* XXX REMOVE ME */ 2253a04a10f8SKris Kennaway channel_start_open(id); 2254a04a10f8SKris Kennaway packet_send(); 2255a04a10f8SKris Kennaway } 2256a04a10f8SKris Kennaway void 2257a04a10f8SKris Kennaway channel_request(int id, char *service, int wantconfirm) 2258a04a10f8SKris Kennaway { 2259a04a10f8SKris Kennaway channel_request_start(id, service, wantconfirm); 2260a04a10f8SKris Kennaway packet_send(); 2261a04a10f8SKris Kennaway debug("channel request %d: %s", id, service) ; 2262a04a10f8SKris Kennaway } 2263a04a10f8SKris Kennaway void 2264a04a10f8SKris Kennaway channel_request_start(int id, char *service, int wantconfirm) 2265a04a10f8SKris Kennaway { 2266a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2267a04a10f8SKris Kennaway if (c == NULL) { 2268a04a10f8SKris Kennaway log("channel_request: %d: bad id", id); 2269a04a10f8SKris Kennaway return; 2270a04a10f8SKris Kennaway } 2271a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_REQUEST); 2272a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2273a04a10f8SKris Kennaway packet_put_cstring(service); 2274a04a10f8SKris Kennaway packet_put_char(wantconfirm); 2275a04a10f8SKris Kennaway } 2276a04a10f8SKris Kennaway void 2277a04a10f8SKris Kennaway channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg) 2278a04a10f8SKris Kennaway { 2279a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2280a04a10f8SKris Kennaway if (c == NULL) { 2281a04a10f8SKris Kennaway log("channel_register_callback: %d: bad id", id); 2282a04a10f8SKris Kennaway return; 2283a04a10f8SKris Kennaway } 2284a04a10f8SKris Kennaway c->cb_event = mtype; 2285a04a10f8SKris Kennaway c->cb_fn = fn; 2286a04a10f8SKris Kennaway c->cb_arg = arg; 2287a04a10f8SKris Kennaway } 2288a04a10f8SKris Kennaway void 2289a04a10f8SKris Kennaway channel_register_cleanup(int id, channel_callback_fn *fn) 2290a04a10f8SKris Kennaway { 2291a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2292a04a10f8SKris Kennaway if (c == NULL) { 2293a04a10f8SKris Kennaway log("channel_register_cleanup: %d: bad id", id); 2294a04a10f8SKris Kennaway return; 2295a04a10f8SKris Kennaway } 2296a04a10f8SKris Kennaway c->dettach_user = fn; 2297a04a10f8SKris Kennaway } 2298a04a10f8SKris Kennaway void 2299a04a10f8SKris Kennaway channel_cancel_cleanup(int id) 2300a04a10f8SKris Kennaway { 2301a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2302a04a10f8SKris Kennaway if (c == NULL) { 2303a04a10f8SKris Kennaway log("channel_cancel_cleanup: %d: bad id", id); 2304a04a10f8SKris Kennaway return; 2305a04a10f8SKris Kennaway } 2306a04a10f8SKris Kennaway c->dettach_user = NULL; 2307a04a10f8SKris Kennaway } 2308b66f2d16SKris Kennaway void 2309b66f2d16SKris Kennaway channel_register_filter(int id, channel_filter_fn *fn) 2310b66f2d16SKris Kennaway { 2311b66f2d16SKris Kennaway Channel *c = channel_lookup(id); 2312b66f2d16SKris Kennaway if (c == NULL) { 2313b66f2d16SKris Kennaway log("channel_register_filter: %d: bad id", id); 2314b66f2d16SKris Kennaway return; 2315b66f2d16SKris Kennaway } 2316b66f2d16SKris Kennaway c->input_filter = fn; 2317b66f2d16SKris Kennaway } 2318a04a10f8SKris Kennaway 2319a04a10f8SKris Kennaway void 23205b9b2fafSBrian Feldman channel_set_fds(int id, int rfd, int wfd, int efd, 23215b9b2fafSBrian Feldman int extusage, int nonblock) 2322a04a10f8SKris Kennaway { 2323a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2324a04a10f8SKris Kennaway if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 2325a04a10f8SKris Kennaway fatal("channel_activate for non-larval channel %d.", id); 23265b9b2fafSBrian Feldman channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); 2327a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 2328a04a10f8SKris Kennaway /* XXX window size? */ 2329b66f2d16SKris Kennaway c->local_window = c->local_window_max = c->local_maxpacket * 2; 2330a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 2331a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2332a04a10f8SKris Kennaway packet_put_int(c->local_window); 2333a04a10f8SKris Kennaway packet_send(); 2334a04a10f8SKris Kennaway } 2335