1*a0ee8cc6SDag-Erling Smørgrav /* $OpenBSD: channels.c,v 1.336 2014/07/15 15:54:14 millert Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * This file contains functions for generic socket connection forwarding. 7511b41d2SMark Murray * There is also code for initiating connection forwarding for X11 connections, 8511b41d2SMark Murray * arbitrary tcp/ip connections, and the authentication agent connection. 9511b41d2SMark Murray * 10b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 11b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 12b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 13b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 14b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 15b66f2d16SKris Kennaway * 16a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 17af12a3e7SDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001, 2002 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" 43cf783db1SDag-Erling Smørgrav __RCSID("$FreeBSD$"); 44a04a10f8SKris Kennaway 45333ee039SDag-Erling Smørgrav #include <sys/types.h> 46*a0ee8cc6SDag-Erling Smørgrav #include <sys/stat.h> 47333ee039SDag-Erling Smørgrav #include <sys/ioctl.h> 48333ee039SDag-Erling Smørgrav #include <sys/un.h> 49333ee039SDag-Erling Smørgrav #include <sys/socket.h> 50333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 51333ee039SDag-Erling Smørgrav # include <sys/time.h> 52333ee039SDag-Erling Smørgrav #endif 53333ee039SDag-Erling Smørgrav 54333ee039SDag-Erling Smørgrav #include <netinet/in.h> 55333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 56333ee039SDag-Erling Smørgrav 57333ee039SDag-Erling Smørgrav #include <errno.h> 58b15c8340SDag-Erling Smørgrav #include <fcntl.h> 59333ee039SDag-Erling Smørgrav #include <netdb.h> 60333ee039SDag-Erling Smørgrav #include <stdio.h> 61333ee039SDag-Erling Smørgrav #include <stdlib.h> 62333ee039SDag-Erling Smørgrav #include <string.h> 63333ee039SDag-Erling Smørgrav #include <termios.h> 64333ee039SDag-Erling Smørgrav #include <unistd.h> 65333ee039SDag-Erling Smørgrav #include <stdarg.h> 66333ee039SDag-Erling Smørgrav 67d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 68333ee039SDag-Erling Smørgrav #include "xmalloc.h" 69ca3176e7SBrian Feldman #include "ssh.h" 70ca3176e7SBrian Feldman #include "ssh1.h" 71ca3176e7SBrian Feldman #include "ssh2.h" 72ca3176e7SBrian Feldman #include "packet.h" 73ca3176e7SBrian Feldman #include "log.h" 74ca3176e7SBrian Feldman #include "misc.h" 75333ee039SDag-Erling Smørgrav #include "buffer.h" 76ca3176e7SBrian Feldman #include "channels.h" 77ca3176e7SBrian Feldman #include "compat.h" 78ca3176e7SBrian Feldman #include "canohost.h" 79b66f2d16SKris Kennaway #include "key.h" 80b66f2d16SKris Kennaway #include "authfd.h" 81af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 82511b41d2SMark Murray 83af12a3e7SDag-Erling Smørgrav /* -- channel core */ 84511b41d2SMark Murray 85511b41d2SMark Murray /* 86511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 87511b41d2SMark Murray * dynamically extended as needed. 88511b41d2SMark Murray */ 89af12a3e7SDag-Erling Smørgrav static Channel **channels = NULL; 90511b41d2SMark Murray 91511b41d2SMark Murray /* 92511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 93af12a3e7SDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 94511b41d2SMark Murray */ 9521e764dfSDag-Erling Smørgrav static u_int channels_alloc = 0; 96511b41d2SMark Murray 97511b41d2SMark Murray /* 98511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 99af12a3e7SDag-Erling Smørgrav * updated in channel_new. 100511b41d2SMark Murray */ 101ca3176e7SBrian Feldman static int channel_max_fd = 0; 102511b41d2SMark Murray 103511b41d2SMark Murray 104af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 105511b41d2SMark Murray 106511b41d2SMark Murray /* 107511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 108511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 109511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 110511b41d2SMark Murray * network (which might be behind a firewall). 111511b41d2SMark Murray */ 112*a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */ 113*a0ee8cc6SDag-Erling Smørgrav /* Overload host_to_connect; we could just make this match Forward */ 114*a0ee8cc6SDag-Erling Smørgrav /* XXX - can we use listen_host instead of listen_path? */ 115511b41d2SMark Murray typedef struct { 116a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 117*a0ee8cc6SDag-Erling Smørgrav int port_to_connect; /* Connect to 'port'. */ 118*a0ee8cc6SDag-Erling Smørgrav char *listen_host; /* Remote side should listen address. */ 119*a0ee8cc6SDag-Erling Smørgrav char *listen_path; /* Remote side should listen path. */ 120*a0ee8cc6SDag-Erling Smørgrav int listen_port; /* Remote side should listen port. */ 121511b41d2SMark Murray } ForwardPermission; 122511b41d2SMark Murray 123333ee039SDag-Erling Smørgrav /* List of all permitted host/port pairs to connect by the user. */ 124e2f6069cSDag-Erling Smørgrav static ForwardPermission *permitted_opens = NULL; 125af12a3e7SDag-Erling Smørgrav 126333ee039SDag-Erling Smørgrav /* List of all permitted host/port pairs to connect by the admin. */ 127e2f6069cSDag-Erling Smørgrav static ForwardPermission *permitted_adm_opens = NULL; 128333ee039SDag-Erling Smørgrav 129333ee039SDag-Erling Smørgrav /* Number of permitted host/port pairs in the array permitted by the user. */ 130511b41d2SMark Murray static int num_permitted_opens = 0; 131333ee039SDag-Erling Smørgrav 132333ee039SDag-Erling Smørgrav /* Number of permitted host/port pair in the array permitted by the admin. */ 133333ee039SDag-Erling Smørgrav static int num_adm_permitted_opens = 0; 134333ee039SDag-Erling Smørgrav 135462c32cbSDag-Erling Smørgrav /* special-case port number meaning allow any port */ 136462c32cbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT 0 137462c32cbSDag-Erling Smørgrav 138511b41d2SMark Murray /* 139511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 140511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 141511b41d2SMark Murray * anything after logging in anyway. 142511b41d2SMark Murray */ 143511b41d2SMark Murray static int all_opens_permitted = 0; 144511b41d2SMark Murray 145af12a3e7SDag-Erling Smørgrav 146af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 147af12a3e7SDag-Erling Smørgrav 148af12a3e7SDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 149af12a3e7SDag-Erling Smørgrav #define MAX_DISPLAYS 1000 150af12a3e7SDag-Erling Smørgrav 151d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 152d4ecd108SDag-Erling Smørgrav static char *x11_saved_display = NULL; 153d4ecd108SDag-Erling Smørgrav 154af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 155af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 156af12a3e7SDag-Erling Smørgrav 157af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 158af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 159af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 160af12a3e7SDag-Erling Smørgrav 161af12a3e7SDag-Erling Smørgrav /* 162af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 163af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 164af12a3e7SDag-Erling Smørgrav */ 165333ee039SDag-Erling Smørgrav static u_char *x11_fake_data = NULL; 166af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 167af12a3e7SDag-Erling Smørgrav 168af12a3e7SDag-Erling Smørgrav 169af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 170af12a3e7SDag-Erling Smørgrav 171af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 172af12a3e7SDag-Erling Smørgrav 173ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 174989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 175ca3176e7SBrian Feldman 176af12a3e7SDag-Erling Smørgrav /* helper */ 177af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 178ca3176e7SBrian Feldman 179d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 180d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 181d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 182d4af9e69SDag-Erling Smørgrav 183af12a3e7SDag-Erling Smørgrav /* -- channel core */ 184a04a10f8SKris Kennaway 185a04a10f8SKris Kennaway Channel * 186b74df5b2SDag-Erling Smørgrav channel_by_id(int id) 187a04a10f8SKris Kennaway { 188a04a10f8SKris Kennaway Channel *c; 189af12a3e7SDag-Erling Smørgrav 19021e764dfSDag-Erling Smørgrav if (id < 0 || (u_int)id >= channels_alloc) { 191b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id", id); 192a04a10f8SKris Kennaway return NULL; 193a04a10f8SKris Kennaway } 194af12a3e7SDag-Erling Smørgrav c = channels[id]; 195af12a3e7SDag-Erling Smørgrav if (c == NULL) { 196b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id: channel free", id); 197a04a10f8SKris Kennaway return NULL; 198a04a10f8SKris Kennaway } 199a04a10f8SKris Kennaway return c; 200a04a10f8SKris Kennaway } 201a04a10f8SKris Kennaway 202a04a10f8SKris Kennaway /* 203b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 204b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 205b74df5b2SDag-Erling Smørgrav */ 206b74df5b2SDag-Erling Smørgrav Channel * 207b74df5b2SDag-Erling Smørgrav channel_lookup(int id) 208b74df5b2SDag-Erling Smørgrav { 209b74df5b2SDag-Erling Smørgrav Channel *c; 210b74df5b2SDag-Erling Smørgrav 211b74df5b2SDag-Erling Smørgrav if ((c = channel_by_id(id)) == NULL) 212b74df5b2SDag-Erling Smørgrav return (NULL); 213b74df5b2SDag-Erling Smørgrav 214b74df5b2SDag-Erling Smørgrav switch (c->type) { 215b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 216b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 217b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 218b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 219b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 220b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 221b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 222b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 223e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 224b74df5b2SDag-Erling Smørgrav return (c); 225b74df5b2SDag-Erling Smørgrav } 226b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 227b74df5b2SDag-Erling Smørgrav return (NULL); 228b74df5b2SDag-Erling Smørgrav } 229b74df5b2SDag-Erling Smørgrav 230b74df5b2SDag-Erling Smørgrav /* 231a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 232a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 233a04a10f8SKris Kennaway */ 234af12a3e7SDag-Erling Smørgrav static void 2355b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 236d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 237a04a10f8SKris Kennaway { 238a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 239ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, rfd); 240ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, wfd); 241ca3176e7SBrian Feldman channel_max_fd = MAX(channel_max_fd, efd); 242ca3176e7SBrian Feldman 243b15c8340SDag-Erling Smørgrav if (rfd != -1) 244b15c8340SDag-Erling Smørgrav fcntl(rfd, F_SETFD, FD_CLOEXEC); 245b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 246b15c8340SDag-Erling Smørgrav fcntl(wfd, F_SETFD, FD_CLOEXEC); 247b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 248b15c8340SDag-Erling Smørgrav fcntl(efd, F_SETFD, FD_CLOEXEC); 249a04a10f8SKris Kennaway 250a04a10f8SKris Kennaway c->rfd = rfd; 251a04a10f8SKris Kennaway c->wfd = wfd; 252a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 253a04a10f8SKris Kennaway c->efd = efd; 254a04a10f8SKris Kennaway c->extended_usage = extusage; 2555b9b2fafSBrian Feldman 256d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 257221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 258e4a9863fSDag-Erling Smørgrav #ifdef _AIX 259e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 260d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 261e4a9863fSDag-Erling Smørgrav #endif 262e0fbb1d2SBrian Feldman 2635b9b2fafSBrian Feldman /* enable nonblocking mode */ 2645b9b2fafSBrian Feldman if (nonblock) { 265a04a10f8SKris Kennaway if (rfd != -1) 266a04a10f8SKris Kennaway set_nonblock(rfd); 267a04a10f8SKris Kennaway if (wfd != -1) 268a04a10f8SKris Kennaway set_nonblock(wfd); 269a04a10f8SKris Kennaway if (efd != -1) 270a04a10f8SKris Kennaway set_nonblock(efd); 271a04a10f8SKris Kennaway } 2725b9b2fafSBrian Feldman } 273a04a10f8SKris Kennaway 274511b41d2SMark Murray /* 275511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 276511b41d2SMark Murray * remote_name to be freed. 277511b41d2SMark Murray */ 278af12a3e7SDag-Erling Smørgrav Channel * 279a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 280a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 281511b41d2SMark Murray { 28221e764dfSDag-Erling Smørgrav int found; 28321e764dfSDag-Erling Smørgrav u_int i; 284511b41d2SMark Murray Channel *c; 285511b41d2SMark Murray 286511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 287511b41d2SMark Murray if (channels_alloc == 0) { 288511b41d2SMark Murray channels_alloc = 10; 289333ee039SDag-Erling Smørgrav channels = xcalloc(channels_alloc, sizeof(Channel *)); 290511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 291af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 292511b41d2SMark Murray } 293511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 294511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 295af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 296511b41d2SMark Murray /* Found a free slot. */ 29721e764dfSDag-Erling Smørgrav found = (int)i; 298511b41d2SMark Murray break; 299511b41d2SMark Murray } 30021e764dfSDag-Erling Smørgrav if (found < 0) { 301511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 302511b41d2SMark Murray found = channels_alloc; 303a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 304a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 305a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 306333ee039SDag-Erling Smørgrav channels = xrealloc(channels, channels_alloc + 10, 307333ee039SDag-Erling Smørgrav sizeof(Channel *)); 3083533e7e5SJosef Karthauser channels_alloc += 10; 3095b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 310511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 311af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 312511b41d2SMark Murray } 313af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 314333ee039SDag-Erling Smørgrav c = channels[found] = xcalloc(1, sizeof(Channel)); 315511b41d2SMark Murray buffer_init(&c->input); 316511b41d2SMark Murray buffer_init(&c->output); 317a04a10f8SKris Kennaway buffer_init(&c->extended); 318cce7d346SDag-Erling Smørgrav c->path = NULL; 319462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 320462c32cbSDag-Erling Smørgrav c->listening_port = 0; 321af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 322af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 323af12a3e7SDag-Erling Smørgrav c->flags = 0; 324d4af9e69SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 325462c32cbSDag-Erling Smørgrav c->notbefore = 0; 326511b41d2SMark Murray c->self = found; 327511b41d2SMark Murray c->type = type; 328a04a10f8SKris Kennaway c->ctype = ctype; 329a04a10f8SKris Kennaway c->local_window = window; 330a04a10f8SKris Kennaway c->local_window_max = window; 331a04a10f8SKris Kennaway c->local_consumed = 0; 332a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 333511b41d2SMark Murray c->remote_id = -1; 334221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 335a04a10f8SKris Kennaway c->remote_window = 0; 336a04a10f8SKris Kennaway c->remote_maxpacket = 0; 337af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 338af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 339af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 340b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 341d4af9e69SDag-Erling Smørgrav c->open_confirm = NULL; 342d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 343b66f2d16SKris Kennaway c->input_filter = NULL; 344b74df5b2SDag-Erling Smørgrav c->output_filter = NULL; 345d4af9e69SDag-Erling Smørgrav c->filter_ctx = NULL; 346d4af9e69SDag-Erling Smørgrav c->filter_cleanup = NULL; 347b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 348b15c8340SDag-Erling Smørgrav c->mux_rcb = NULL; 349b15c8340SDag-Erling Smørgrav c->mux_ctx = NULL; 350e2f6069cSDag-Erling Smørgrav c->mux_pause = 0; 351b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 352d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 353511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 354af12a3e7SDag-Erling Smørgrav return c; 355a04a10f8SKris Kennaway } 356511b41d2SMark Murray 357af12a3e7SDag-Erling Smørgrav static int 358af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 359af12a3e7SDag-Erling Smørgrav { 36021e764dfSDag-Erling Smørgrav u_int i; 36121e764dfSDag-Erling Smørgrav int max = 0; 362af12a3e7SDag-Erling Smørgrav Channel *c; 363af12a3e7SDag-Erling Smørgrav 364af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 365af12a3e7SDag-Erling Smørgrav c = channels[i]; 366af12a3e7SDag-Erling Smørgrav if (c != NULL) { 367af12a3e7SDag-Erling Smørgrav max = MAX(max, c->rfd); 368af12a3e7SDag-Erling Smørgrav max = MAX(max, c->wfd); 369af12a3e7SDag-Erling Smørgrav max = MAX(max, c->efd); 370af12a3e7SDag-Erling Smørgrav } 371af12a3e7SDag-Erling Smørgrav } 372af12a3e7SDag-Erling Smørgrav return max; 373af12a3e7SDag-Erling Smørgrav } 374af12a3e7SDag-Erling Smørgrav 375af12a3e7SDag-Erling Smørgrav int 376af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 377af12a3e7SDag-Erling Smørgrav { 378af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 379af12a3e7SDag-Erling Smørgrav 380af12a3e7SDag-Erling Smørgrav if (fd != -1) { 381af12a3e7SDag-Erling Smørgrav ret = close(fd); 382af12a3e7SDag-Erling Smørgrav *fdp = -1; 383af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 384af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 385af12a3e7SDag-Erling Smørgrav } 386af12a3e7SDag-Erling Smørgrav return ret; 387af12a3e7SDag-Erling Smørgrav } 388a04a10f8SKris Kennaway 389a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 390af12a3e7SDag-Erling Smørgrav static void 391a04a10f8SKris Kennaway channel_close_fds(Channel *c) 392511b41d2SMark Murray { 393af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 394af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 395af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 396af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 397a04a10f8SKris Kennaway } 398511b41d2SMark Murray 399a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 400a04a10f8SKris Kennaway void 401af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 402a04a10f8SKris Kennaway { 403af12a3e7SDag-Erling Smørgrav char *s; 40421e764dfSDag-Erling Smørgrav u_int i, n; 405d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 406ca3176e7SBrian Feldman 407af12a3e7SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) 408af12a3e7SDag-Erling Smørgrav if (channels[i]) 409af12a3e7SDag-Erling Smørgrav n++; 41021e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 411af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 412af12a3e7SDag-Erling Smørgrav 413af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 414221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 415e4a9863fSDag-Erling Smørgrav free(s); 416ca3176e7SBrian Feldman 417a04a10f8SKris Kennaway if (c->sock != -1) 418a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 419a04a10f8SKris Kennaway channel_close_fds(c); 420a04a10f8SKris Kennaway buffer_free(&c->input); 421a04a10f8SKris Kennaway buffer_free(&c->output); 422a04a10f8SKris Kennaway buffer_free(&c->extended); 423e4a9863fSDag-Erling Smørgrav free(c->remote_name); 424a04a10f8SKris Kennaway c->remote_name = NULL; 425e4a9863fSDag-Erling Smørgrav free(c->path); 426cce7d346SDag-Erling Smørgrav c->path = NULL; 427e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 428462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 429d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 430d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 431d4af9e69SDag-Erling Smørgrav cc->abandon_cb(c, cc->ctx); 432d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 433b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 434e4a9863fSDag-Erling Smørgrav free(cc); 435d4af9e69SDag-Erling Smørgrav } 436d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 437d4af9e69SDag-Erling Smørgrav c->filter_cleanup(c->self, c->filter_ctx); 438af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 439e4a9863fSDag-Erling Smørgrav free(c); 440af12a3e7SDag-Erling Smørgrav } 441af12a3e7SDag-Erling Smørgrav 442af12a3e7SDag-Erling Smørgrav void 443af12a3e7SDag-Erling Smørgrav channel_free_all(void) 444af12a3e7SDag-Erling Smørgrav { 44521e764dfSDag-Erling Smørgrav u_int i; 446af12a3e7SDag-Erling Smørgrav 447af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 448af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 449af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 450af12a3e7SDag-Erling Smørgrav } 451af12a3e7SDag-Erling Smørgrav 452af12a3e7SDag-Erling Smørgrav /* 453af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 454af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 455af12a3e7SDag-Erling Smørgrav */ 456af12a3e7SDag-Erling Smørgrav void 457af12a3e7SDag-Erling Smørgrav channel_close_all(void) 458af12a3e7SDag-Erling Smørgrav { 45921e764dfSDag-Erling Smørgrav u_int i; 460af12a3e7SDag-Erling Smørgrav 461af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 462af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 463af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 464af12a3e7SDag-Erling Smørgrav } 465af12a3e7SDag-Erling Smørgrav 466af12a3e7SDag-Erling Smørgrav /* 467af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 468af12a3e7SDag-Erling Smørgrav */ 469af12a3e7SDag-Erling Smørgrav void 470af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 471af12a3e7SDag-Erling Smørgrav { 47221e764dfSDag-Erling Smørgrav u_int i; 473af12a3e7SDag-Erling Smørgrav Channel *c; 474af12a3e7SDag-Erling Smørgrav 475af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 476af12a3e7SDag-Erling Smørgrav c = channels[i]; 477af12a3e7SDag-Erling Smørgrav if (c != NULL) { 478af12a3e7SDag-Erling Smørgrav switch (c->type) { 479af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 480af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 481af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 482af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 483*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 484*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 485af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 486af12a3e7SDag-Erling Smørgrav channel_free(c); 487af12a3e7SDag-Erling Smørgrav break; 488af12a3e7SDag-Erling Smørgrav } 489af12a3e7SDag-Erling Smørgrav } 490af12a3e7SDag-Erling Smørgrav } 491af12a3e7SDag-Erling Smørgrav } 492af12a3e7SDag-Erling Smørgrav 493af12a3e7SDag-Erling Smørgrav /* 494af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 495af12a3e7SDag-Erling Smørgrav * more channel is overfull. 496af12a3e7SDag-Erling Smørgrav */ 497af12a3e7SDag-Erling Smørgrav int 498af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 499af12a3e7SDag-Erling Smørgrav { 500af12a3e7SDag-Erling Smørgrav u_int i; 501af12a3e7SDag-Erling Smørgrav Channel *c; 502af12a3e7SDag-Erling Smørgrav 503af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 504af12a3e7SDag-Erling Smørgrav c = channels[i]; 505af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 506af12a3e7SDag-Erling Smørgrav #if 0 507af12a3e7SDag-Erling Smørgrav if (!compat20 && 508af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 509e73e9afaSDag-Erling Smørgrav debug2("channel %d: big input buffer %d", 510af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 511af12a3e7SDag-Erling Smørgrav return 0; 512af12a3e7SDag-Erling Smørgrav } 513af12a3e7SDag-Erling Smørgrav #endif 514af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 515221552e4SDag-Erling Smørgrav debug2("channel %d: big output buffer %u > %u", 516af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 517af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 518af12a3e7SDag-Erling Smørgrav return 0; 519af12a3e7SDag-Erling Smørgrav } 520af12a3e7SDag-Erling Smørgrav } 521af12a3e7SDag-Erling Smørgrav } 522af12a3e7SDag-Erling Smørgrav return 1; 523af12a3e7SDag-Erling Smørgrav } 524af12a3e7SDag-Erling Smørgrav 525af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 526af12a3e7SDag-Erling Smørgrav int 527af12a3e7SDag-Erling Smørgrav channel_still_open(void) 528af12a3e7SDag-Erling Smørgrav { 52921e764dfSDag-Erling Smørgrav u_int i; 530af12a3e7SDag-Erling Smørgrav Channel *c; 531af12a3e7SDag-Erling Smørgrav 532af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 533af12a3e7SDag-Erling Smørgrav c = channels[i]; 534af12a3e7SDag-Erling Smørgrav if (c == NULL) 535af12a3e7SDag-Erling Smørgrav continue; 536af12a3e7SDag-Erling Smørgrav switch (c->type) { 537af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 538af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 539af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 540b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 541af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 542af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 543af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 544af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 545af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 546e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 547*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 548*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 549af12a3e7SDag-Erling Smørgrav continue; 550af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 551af12a3e7SDag-Erling Smørgrav if (!compat20) 552af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 553af12a3e7SDag-Erling Smørgrav continue; 554af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 555af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 556af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 557b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 558af12a3e7SDag-Erling Smørgrav return 1; 559af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 560af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 561af12a3e7SDag-Erling Smørgrav if (!compat13) 562af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 563af12a3e7SDag-Erling Smørgrav return 1; 564af12a3e7SDag-Erling Smørgrav default: 565af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 566af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 567af12a3e7SDag-Erling Smørgrav } 568af12a3e7SDag-Erling Smørgrav } 569af12a3e7SDag-Erling Smørgrav return 0; 570af12a3e7SDag-Erling Smørgrav } 571af12a3e7SDag-Erling Smørgrav 572af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 573af12a3e7SDag-Erling Smørgrav int 574af12a3e7SDag-Erling Smørgrav channel_find_open(void) 575af12a3e7SDag-Erling Smørgrav { 57621e764dfSDag-Erling Smørgrav u_int i; 577af12a3e7SDag-Erling Smørgrav Channel *c; 578af12a3e7SDag-Erling Smørgrav 579af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 580af12a3e7SDag-Erling Smørgrav c = channels[i]; 58121e764dfSDag-Erling Smørgrav if (c == NULL || c->remote_id < 0) 582af12a3e7SDag-Erling Smørgrav continue; 583af12a3e7SDag-Erling Smørgrav switch (c->type) { 584af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 585af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 586af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 587af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 588af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 589b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 590b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 591af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 592af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 593af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 594e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 595*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 596*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 597af12a3e7SDag-Erling Smørgrav continue; 598af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 599af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 600af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 601af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 602af12a3e7SDag-Erling Smørgrav return i; 603af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 604af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 605af12a3e7SDag-Erling Smørgrav if (!compat13) 606af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 607af12a3e7SDag-Erling Smørgrav return i; 608af12a3e7SDag-Erling Smørgrav default: 609af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 610af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 611af12a3e7SDag-Erling Smørgrav } 612af12a3e7SDag-Erling Smørgrav } 613af12a3e7SDag-Erling Smørgrav return -1; 614af12a3e7SDag-Erling Smørgrav } 615af12a3e7SDag-Erling Smørgrav 616af12a3e7SDag-Erling Smørgrav 617af12a3e7SDag-Erling Smørgrav /* 618af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 619af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 620af12a3e7SDag-Erling Smørgrav * newlines. 621af12a3e7SDag-Erling Smørgrav */ 622af12a3e7SDag-Erling Smørgrav char * 623af12a3e7SDag-Erling Smørgrav channel_open_message(void) 624af12a3e7SDag-Erling Smørgrav { 625af12a3e7SDag-Erling Smørgrav Buffer buffer; 626af12a3e7SDag-Erling Smørgrav Channel *c; 627af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 62821e764dfSDag-Erling Smørgrav u_int i; 629af12a3e7SDag-Erling Smørgrav 630af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 631af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 632af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 633af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 634af12a3e7SDag-Erling Smørgrav c = channels[i]; 635af12a3e7SDag-Erling Smørgrav if (c == NULL) 636af12a3e7SDag-Erling Smørgrav continue; 637af12a3e7SDag-Erling Smørgrav switch (c->type) { 638af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 639af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 640af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 641af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 642af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 643af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 644e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 645b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 646b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 647*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 648*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 649af12a3e7SDag-Erling Smørgrav continue; 650af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 651af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 652af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 653af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 654af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 655af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 656af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 657af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 65821e764dfSDag-Erling Smørgrav snprintf(buf, sizeof buf, 659b15c8340SDag-Erling Smørgrav " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n", 660af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 661af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 662af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 663af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 664b15c8340SDag-Erling Smørgrav c->rfd, c->wfd, c->ctl_chan); 665af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 666af12a3e7SDag-Erling Smørgrav continue; 667af12a3e7SDag-Erling Smørgrav default: 668af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 669af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 670af12a3e7SDag-Erling Smørgrav } 671af12a3e7SDag-Erling Smørgrav } 672af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 673af12a3e7SDag-Erling Smørgrav cp = xstrdup(buffer_ptr(&buffer)); 674af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 675af12a3e7SDag-Erling Smørgrav return cp; 676af12a3e7SDag-Erling Smørgrav } 677af12a3e7SDag-Erling Smørgrav 678af12a3e7SDag-Erling Smørgrav void 679af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 680af12a3e7SDag-Erling Smørgrav { 681af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 682f388f5efSDag-Erling Smørgrav 683af12a3e7SDag-Erling Smørgrav if (c == NULL) { 684221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 685af12a3e7SDag-Erling Smørgrav return; 686af12a3e7SDag-Erling Smørgrav } 687e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 688af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 689af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 690af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 691af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 692af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 693af12a3e7SDag-Erling Smørgrav packet_send(); 694af12a3e7SDag-Erling Smørgrav } 695af12a3e7SDag-Erling Smørgrav 696af12a3e7SDag-Erling Smørgrav void 697e73e9afaSDag-Erling Smørgrav channel_request_start(int id, char *service, int wantconfirm) 698af12a3e7SDag-Erling Smørgrav { 699e73e9afaSDag-Erling Smørgrav Channel *c = channel_lookup(id); 700f388f5efSDag-Erling Smørgrav 701af12a3e7SDag-Erling Smørgrav if (c == NULL) { 702221552e4SDag-Erling Smørgrav logit("channel_request_start: %d: unknown channel id", id); 703af12a3e7SDag-Erling Smørgrav return; 704af12a3e7SDag-Erling Smørgrav } 70521e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 706af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 707af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 708af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 709af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 710af12a3e7SDag-Erling Smørgrav } 711333ee039SDag-Erling Smørgrav 712af12a3e7SDag-Erling Smørgrav void 713d4af9e69SDag-Erling Smørgrav channel_register_status_confirm(int id, channel_confirm_cb *cb, 714d4af9e69SDag-Erling Smørgrav channel_confirm_abandon_cb *abandon_cb, void *ctx) 715d4af9e69SDag-Erling Smørgrav { 716d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 717d4af9e69SDag-Erling Smørgrav Channel *c; 718d4af9e69SDag-Erling Smørgrav 719d4af9e69SDag-Erling Smørgrav if ((c = channel_lookup(id)) == NULL) 720d4af9e69SDag-Erling Smørgrav fatal("channel_register_expect: %d: bad id", id); 721d4af9e69SDag-Erling Smørgrav 7220a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 723d4af9e69SDag-Erling Smørgrav cc->cb = cb; 724d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 725d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 726d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 727d4af9e69SDag-Erling Smørgrav } 728d4af9e69SDag-Erling Smørgrav 729d4af9e69SDag-Erling Smørgrav void 730e2f6069cSDag-Erling Smørgrav channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) 731af12a3e7SDag-Erling Smørgrav { 732af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 733f388f5efSDag-Erling Smørgrav 734af12a3e7SDag-Erling Smørgrav if (c == NULL) { 735cce7d346SDag-Erling Smørgrav logit("channel_register_open_confirm: %d: bad id", id); 736af12a3e7SDag-Erling Smørgrav return; 737af12a3e7SDag-Erling Smørgrav } 738d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 739d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 740af12a3e7SDag-Erling Smørgrav } 741333ee039SDag-Erling Smørgrav 742af12a3e7SDag-Erling Smørgrav void 743b74df5b2SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 744af12a3e7SDag-Erling Smørgrav { 745b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 746f388f5efSDag-Erling Smørgrav 747af12a3e7SDag-Erling Smørgrav if (c == NULL) { 748221552e4SDag-Erling Smørgrav logit("channel_register_cleanup: %d: bad id", id); 749af12a3e7SDag-Erling Smørgrav return; 750af12a3e7SDag-Erling Smørgrav } 751af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 752b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 753af12a3e7SDag-Erling Smørgrav } 754333ee039SDag-Erling Smørgrav 755af12a3e7SDag-Erling Smørgrav void 756af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 757af12a3e7SDag-Erling Smørgrav { 758b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 759f388f5efSDag-Erling Smørgrav 760af12a3e7SDag-Erling Smørgrav if (c == NULL) { 761221552e4SDag-Erling Smørgrav logit("channel_cancel_cleanup: %d: bad id", id); 762af12a3e7SDag-Erling Smørgrav return; 763af12a3e7SDag-Erling Smørgrav } 764af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 765b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 766af12a3e7SDag-Erling Smørgrav } 767333ee039SDag-Erling Smørgrav 768af12a3e7SDag-Erling Smørgrav void 769b74df5b2SDag-Erling Smørgrav channel_register_filter(int id, channel_infilter_fn *ifn, 770d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 771af12a3e7SDag-Erling Smørgrav { 772af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 773f388f5efSDag-Erling Smørgrav 774af12a3e7SDag-Erling Smørgrav if (c == NULL) { 775221552e4SDag-Erling Smørgrav logit("channel_register_filter: %d: bad id", id); 776af12a3e7SDag-Erling Smørgrav return; 777af12a3e7SDag-Erling Smørgrav } 778b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 779b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 780d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 781d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 782af12a3e7SDag-Erling Smørgrav } 783af12a3e7SDag-Erling Smørgrav 784af12a3e7SDag-Erling Smørgrav void 785af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 786d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 787af12a3e7SDag-Erling Smørgrav { 788af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 789f388f5efSDag-Erling Smørgrav 790af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 791af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 792d4af9e69SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 793af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 794af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 795af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 796af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 797af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 798af12a3e7SDag-Erling Smørgrav packet_send(); 799511b41d2SMark Murray } 800511b41d2SMark Murray 801511b41d2SMark Murray /* 802a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 803a04a10f8SKris Kennaway * channels in the select bitmasks. 804511b41d2SMark Murray */ 805a04a10f8SKris Kennaway /* 806a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 807a04a10f8SKris Kennaway * have events pending. 808a04a10f8SKris Kennaway */ 809a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 810a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 811a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 812511b41d2SMark Murray 813333ee039SDag-Erling Smørgrav /* ARGSUSED */ 814af12a3e7SDag-Erling Smørgrav static void 815a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 816511b41d2SMark Murray { 817a04a10f8SKris Kennaway FD_SET(c->sock, readset); 818a04a10f8SKris Kennaway } 819a04a10f8SKris Kennaway 820333ee039SDag-Erling Smørgrav /* ARGSUSED */ 821af12a3e7SDag-Erling Smørgrav static void 822ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 823ca3176e7SBrian Feldman { 824ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 825ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 826ca3176e7SBrian Feldman } 827ca3176e7SBrian Feldman 828af12a3e7SDag-Erling Smørgrav static void 829a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 830a04a10f8SKris Kennaway { 831a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 832a04a10f8SKris Kennaway FD_SET(c->sock, readset); 833a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 834a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 835a04a10f8SKris Kennaway } 836a04a10f8SKris Kennaway 837af12a3e7SDag-Erling Smørgrav static void 838af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 839a04a10f8SKris Kennaway { 84060c59fadSDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 841a04a10f8SKris Kennaway 842a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 843af12a3e7SDag-Erling Smørgrav limit > 0 && 844333ee039SDag-Erling Smørgrav buffer_len(&c->input) < limit && 845333ee039SDag-Erling Smørgrav buffer_check_alloc(&c->input, CHAN_RBUF)) 846a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 847a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 848a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 849a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 850a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 851a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 85280628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 85380628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 85480628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 85580628bacSDag-Erling Smørgrav else 856a04a10f8SKris Kennaway chan_obuf_empty(c); 857a04a10f8SKris Kennaway } 858a04a10f8SKris Kennaway } 859a04a10f8SKris Kennaway /** XXX check close conditions, too */ 860d4af9e69SDag-Erling Smørgrav if (compat20 && c->efd != -1 && 861d4af9e69SDag-Erling Smørgrav !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 862a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 863a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 864a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 865e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 866e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 867e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 868a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 869a04a10f8SKris Kennaway FD_SET(c->efd, readset); 870a04a10f8SKris Kennaway } 87121e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 872a04a10f8SKris Kennaway } 873a04a10f8SKris Kennaway 874333ee039SDag-Erling Smørgrav /* ARGSUSED */ 875af12a3e7SDag-Erling Smørgrav static void 876a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 877a04a10f8SKris Kennaway { 878a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 879a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 880a04a10f8SKris Kennaway packet_put_int(c->remote_id); 881a04a10f8SKris Kennaway packet_send(); 882a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 883221552e4SDag-Erling Smørgrav debug2("channel %d: closing after input drain.", c->self); 884a04a10f8SKris Kennaway } 885a04a10f8SKris Kennaway } 886a04a10f8SKris Kennaway 887333ee039SDag-Erling Smørgrav /* ARGSUSED */ 888af12a3e7SDag-Erling Smørgrav static void 889a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 890a04a10f8SKris Kennaway { 891a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 892af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 893a04a10f8SKris Kennaway else 894a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 895a04a10f8SKris Kennaway } 896a04a10f8SKris Kennaway 897a04a10f8SKris Kennaway /* 898a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 899a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 900a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 901a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 902a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 903a04a10f8SKris Kennaway * XXX All this happens at the client side. 904af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 905a04a10f8SKris Kennaway */ 906af12a3e7SDag-Erling Smørgrav static int 907af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 908a04a10f8SKris Kennaway { 909ca3176e7SBrian Feldman u_char *ucp; 910ca3176e7SBrian Feldman u_int proto_len, data_len; 911511b41d2SMark Murray 912511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 913af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 914a04a10f8SKris Kennaway return 0; 915511b41d2SMark Murray 916511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 917af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 918511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 919511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 920511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 921511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 922511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 923511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 924511b41d2SMark Murray } else { 925221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 926511b41d2SMark Murray ucp[0]); 927a04a10f8SKris Kennaway return -1; 928511b41d2SMark Murray } 929511b41d2SMark Murray 930511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 931af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 932511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 933a04a10f8SKris Kennaway return 0; 934511b41d2SMark Murray 935511b41d2SMark Murray /* Check if authentication protocol matches. */ 936511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 937511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 938221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 939a04a10f8SKris Kennaway return -1; 940511b41d2SMark Murray } 941511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 942511b41d2SMark Murray if (data_len != x11_fake_data_len || 943e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 944511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 945221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 946a04a10f8SKris Kennaway return -1; 947511b41d2SMark Murray } 948511b41d2SMark Murray /* Check fake data length */ 949511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 950511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 951511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 952a04a10f8SKris Kennaway return -1; 953511b41d2SMark Murray } 954511b41d2SMark Murray /* 955511b41d2SMark Murray * Received authentication protocol and data match 956511b41d2SMark Murray * our fake data. Substitute the fake data with real 957511b41d2SMark Murray * data. 958511b41d2SMark Murray */ 959511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 960511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 961a04a10f8SKris Kennaway return 1; 962a04a10f8SKris Kennaway } 963511b41d2SMark Murray 964af12a3e7SDag-Erling Smørgrav static void 965a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 966a04a10f8SKris Kennaway { 967af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 968f388f5efSDag-Erling Smørgrav 969a04a10f8SKris Kennaway if (ret == 1) { 970511b41d2SMark Murray /* Start normal processing for the channel. */ 971a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 972a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 973a04a10f8SKris Kennaway } else if (ret == -1) { 974511b41d2SMark Murray /* 975511b41d2SMark Murray * We have received an X11 connection that has bad 976511b41d2SMark Murray * authentication information. 977511b41d2SMark Murray */ 978221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 979a04a10f8SKris Kennaway buffer_clear(&c->input); 980a04a10f8SKris Kennaway buffer_clear(&c->output); 981af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 982a04a10f8SKris Kennaway c->sock = -1; 983a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 984511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 985a04a10f8SKris Kennaway packet_put_int(c->remote_id); 986511b41d2SMark Murray packet_send(); 987511b41d2SMark Murray } 988511b41d2SMark Murray } 989511b41d2SMark Murray 990af12a3e7SDag-Erling Smørgrav static void 991a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 992a04a10f8SKris Kennaway { 993af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 994af12a3e7SDag-Erling Smørgrav 995af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 996af12a3e7SDag-Erling Smørgrav 997a04a10f8SKris Kennaway if (ret == 1) { 998a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 999af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 1000a04a10f8SKris Kennaway } else if (ret == -1) { 1001221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 1002221552e4SDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 1003af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 1004af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1005af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1006af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1007af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 1008af12a3e7SDag-Erling Smørgrav if (compat20) 1009a04a10f8SKris Kennaway chan_write_failed(c); 1010af12a3e7SDag-Erling Smørgrav else 1011af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1012221552e4SDag-Erling Smørgrav debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 1013a04a10f8SKris Kennaway } 1014a04a10f8SKris Kennaway } 1015a04a10f8SKris Kennaway 1016b15c8340SDag-Erling Smørgrav static void 1017b15c8340SDag-Erling Smørgrav channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1018b15c8340SDag-Erling Smørgrav { 1019e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 1020b15c8340SDag-Erling Smørgrav buffer_check_alloc(&c->input, CHAN_RBUF)) 1021b15c8340SDag-Erling Smørgrav FD_SET(c->rfd, readset); 1022b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1023b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 1024b15c8340SDag-Erling Smørgrav buffer_clear(&c->input); 1025b15c8340SDag-Erling Smørgrav chan_ibuf_empty(c); 1026b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 1027b15c8340SDag-Erling Smørgrav chan_rcvd_oclose(c); 1028b15c8340SDag-Erling Smørgrav } 1029b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1030b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1031b15c8340SDag-Erling Smørgrav if (buffer_len(&c->output) > 0) 1032b15c8340SDag-Erling Smørgrav FD_SET(c->wfd, writeset); 1033b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 1034b15c8340SDag-Erling Smørgrav chan_obuf_empty(c); 1035b15c8340SDag-Erling Smørgrav } 1036b15c8340SDag-Erling Smørgrav } 1037b15c8340SDag-Erling Smørgrav 1038ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1039333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1040af12a3e7SDag-Erling Smørgrav static int 1041ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 1042ca3176e7SBrian Feldman { 1043f388f5efSDag-Erling Smørgrav char *p, *host; 1044cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1045ca3176e7SBrian Feldman char username[256]; 1046ca3176e7SBrian Feldman struct { 1047ca3176e7SBrian Feldman u_int8_t version; 1048ca3176e7SBrian Feldman u_int8_t command; 1049ca3176e7SBrian Feldman u_int16_t dest_port; 1050ca3176e7SBrian Feldman struct in_addr dest_addr; 1051ca3176e7SBrian Feldman } s4_req, s4_rsp; 1052ca3176e7SBrian Feldman 1053ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1054ca3176e7SBrian Feldman 1055ca3176e7SBrian Feldman have = buffer_len(&c->input); 1056ca3176e7SBrian Feldman len = sizeof(s4_req); 1057ca3176e7SBrian Feldman if (have < len) 1058ca3176e7SBrian Feldman return 0; 1059ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1060cce7d346SDag-Erling Smørgrav 1061cce7d346SDag-Erling Smørgrav need = 1; 1062cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1063cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1064cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1065cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1066cce7d346SDag-Erling Smørgrav need = 2; 1067cce7d346SDag-Erling Smørgrav } 1068cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1069ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1070ca3176e7SBrian Feldman if (p[i] == '\0') { 1071cce7d346SDag-Erling Smørgrav found++; 1072cce7d346SDag-Erling Smørgrav if (found == need) 1073ca3176e7SBrian Feldman break; 1074ca3176e7SBrian Feldman } 1075ca3176e7SBrian Feldman if (i > 1024) { 1076ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1077ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1078ca3176e7SBrian Feldman c->self); 1079ca3176e7SBrian Feldman return -1; 1080ca3176e7SBrian Feldman } 1081ca3176e7SBrian Feldman } 1082cce7d346SDag-Erling Smørgrav if (found < need) 1083ca3176e7SBrian Feldman return 0; 1084ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 1085ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 1086ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 1087ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 1088ca3176e7SBrian Feldman have = buffer_len(&c->input); 1089ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1090b83788ffSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) 1091b83788ffSDag-Erling Smørgrav fatal("channel %d: decode socks4: user not nul terminated", 1092b83788ffSDag-Erling Smørgrav c->self); 1093ca3176e7SBrian Feldman len = strlen(p); 1094ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1095cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1096ca3176e7SBrian Feldman if (len > have) 1097ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 1098ca3176e7SBrian Feldman c->self, len, have); 1099ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 1100ca3176e7SBrian Feldman buffer_consume(&c->input, len); 1101ca3176e7SBrian Feldman 1102e4a9863fSDag-Erling Smørgrav free(c->path); 1103cce7d346SDag-Erling Smørgrav c->path = NULL; 1104cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1105ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1106cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1107cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 1108cce7d346SDag-Erling Smørgrav have = buffer_len(&c->input); 1109cce7d346SDag-Erling Smørgrav p = buffer_ptr(&c->input); 1110cce7d346SDag-Erling Smørgrav len = strlen(p); 1111cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1112cce7d346SDag-Erling Smørgrav c->self, p, len); 1113cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1114cce7d346SDag-Erling Smørgrav if (len > have) 1115cce7d346SDag-Erling Smørgrav fatal("channel %d: decode socks4a: len %d > have %d", 1116cce7d346SDag-Erling Smørgrav c->self, len, have); 1117cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1118cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1119cce7d346SDag-Erling Smørgrav c->self, p); 1120cce7d346SDag-Erling Smørgrav return -1; 1121cce7d346SDag-Erling Smørgrav } 1122cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 1123cce7d346SDag-Erling Smørgrav buffer_consume(&c->input, len); 1124cce7d346SDag-Erling Smørgrav } 1125ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1126ca3176e7SBrian Feldman 1127221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1128cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1129ca3176e7SBrian Feldman 1130ca3176e7SBrian Feldman if (s4_req.command != 1) { 1131cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1132cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1133ca3176e7SBrian Feldman return -1; 1134ca3176e7SBrian Feldman } 1135ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1136ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1137ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1138ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1139333ee039SDag-Erling Smørgrav buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 1140ca3176e7SBrian Feldman return 1; 1141ca3176e7SBrian Feldman } 1142ca3176e7SBrian Feldman 1143221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1144221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1145221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1146221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1147221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1148221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1149221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1150221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1151221552e4SDag-Erling Smørgrav 1152333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1153221552e4SDag-Erling Smørgrav static int 1154221552e4SDag-Erling Smørgrav channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1155221552e4SDag-Erling Smørgrav { 1156221552e4SDag-Erling Smørgrav struct { 1157221552e4SDag-Erling Smørgrav u_int8_t version; 1158221552e4SDag-Erling Smørgrav u_int8_t command; 1159221552e4SDag-Erling Smørgrav u_int8_t reserved; 1160221552e4SDag-Erling Smørgrav u_int8_t atyp; 1161221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1162221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1163e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1164e4a9863fSDag-Erling Smørgrav u_char *p; 1165333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 1166221552e4SDag-Erling Smørgrav 1167221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 1168221552e4SDag-Erling Smørgrav p = buffer_ptr(&c->input); 1169221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1170221552e4SDag-Erling Smørgrav return -1; 1171221552e4SDag-Erling Smørgrav have = buffer_len(&c->input); 1172221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1173221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1174221552e4SDag-Erling Smørgrav if (have < 2) 1175221552e4SDag-Erling Smørgrav return 0; 1176221552e4SDag-Erling Smørgrav nmethods = p[1]; 1177221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1178221552e4SDag-Erling Smørgrav return 0; 1179221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1180221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1181221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1182221552e4SDag-Erling Smørgrav found = 1; 1183221552e4SDag-Erling Smørgrav break; 1184221552e4SDag-Erling Smørgrav } 1185221552e4SDag-Erling Smørgrav } 1186221552e4SDag-Erling Smørgrav if (!found) { 1187221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1188221552e4SDag-Erling Smørgrav c->self); 1189221552e4SDag-Erling Smørgrav return -1; 1190221552e4SDag-Erling Smørgrav } 1191221552e4SDag-Erling Smørgrav buffer_consume(&c->input, nmethods + 2); 1192221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, 0x05); /* version */ 1193221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1194221552e4SDag-Erling Smørgrav FD_SET(c->sock, writeset); 1195221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1196221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1197221552e4SDag-Erling Smørgrav return 0; /* need more */ 1198221552e4SDag-Erling Smørgrav } 1199221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1200221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1201221552e4SDag-Erling Smørgrav return 0; /* need more */ 1202333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1203221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1204221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1205221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1206221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1207221552e4SDag-Erling Smørgrav return -1; 1208221552e4SDag-Erling Smørgrav } 1209221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1210221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1211221552e4SDag-Erling Smørgrav addrlen = 4; 1212221552e4SDag-Erling Smørgrav af = AF_INET; 1213221552e4SDag-Erling Smørgrav break; 1214221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1215221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1216221552e4SDag-Erling Smørgrav af = -1; 1217221552e4SDag-Erling Smørgrav break; 1218221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1219221552e4SDag-Erling Smørgrav addrlen = 16; 1220221552e4SDag-Erling Smørgrav af = AF_INET6; 1221221552e4SDag-Erling Smørgrav break; 1222221552e4SDag-Erling Smørgrav default: 1223221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1224221552e4SDag-Erling Smørgrav return -1; 1225221552e4SDag-Erling Smørgrav } 1226333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1227333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1228333ee039SDag-Erling Smørgrav need++; 1229333ee039SDag-Erling Smørgrav if (have < need) 1230221552e4SDag-Erling Smørgrav return 0; 1231221552e4SDag-Erling Smørgrav buffer_consume(&c->input, sizeof(s5_req)); 1232221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1233221552e4SDag-Erling Smørgrav buffer_consume(&c->input, 1); /* host string length */ 1234e4a9863fSDag-Erling Smørgrav buffer_get(&c->input, &dest_addr, addrlen); 1235221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_port, 2); 1236221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1237e4a9863fSDag-Erling Smørgrav free(c->path); 1238cce7d346SDag-Erling Smørgrav c->path = NULL; 1239cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1240cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1241cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1242cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1243221552e4SDag-Erling Smørgrav return -1; 1244cce7d346SDag-Erling Smørgrav } 1245cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1246cce7d346SDag-Erling Smørgrav } else { 1247cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1248cce7d346SDag-Erling Smørgrav return -1; 1249cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1250cce7d346SDag-Erling Smørgrav } 1251221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1252221552e4SDag-Erling Smørgrav 1253221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1254221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1255221552e4SDag-Erling Smørgrav 1256221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1257221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1258221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1259221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1260221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1261221552e4SDag-Erling Smørgrav 1262333ee039SDag-Erling Smørgrav buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1263e4a9863fSDag-Erling Smørgrav buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ 1264333ee039SDag-Erling Smørgrav buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1265221552e4SDag-Erling Smørgrav return 1; 1266221552e4SDag-Erling Smørgrav } 1267221552e4SDag-Erling Smørgrav 1268b15c8340SDag-Erling Smørgrav Channel * 1269b15c8340SDag-Erling Smørgrav channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, 1270b15c8340SDag-Erling Smørgrav int in, int out) 1271b15c8340SDag-Erling Smørgrav { 1272b15c8340SDag-Erling Smørgrav Channel *c; 1273b15c8340SDag-Erling Smørgrav 1274b15c8340SDag-Erling Smørgrav debug("channel_connect_stdio_fwd %s:%d", host_to_connect, 1275b15c8340SDag-Erling Smørgrav port_to_connect); 1276b15c8340SDag-Erling Smørgrav 1277b15c8340SDag-Erling Smørgrav c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, 1278b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1279b15c8340SDag-Erling Smørgrav 0, "stdio-forward", /*nonblock*/0); 1280b15c8340SDag-Erling Smørgrav 1281b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1282b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1283b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1284b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1285b15c8340SDag-Erling Smørgrav 1286b15c8340SDag-Erling Smørgrav channel_register_fds(c, in, out, -1, 0, 1, 0); 1287b15c8340SDag-Erling Smørgrav port_open_helper(c, "direct-tcpip"); 1288b15c8340SDag-Erling Smørgrav 1289b15c8340SDag-Erling Smørgrav return c; 1290b15c8340SDag-Erling Smørgrav } 1291b15c8340SDag-Erling Smørgrav 1292ca3176e7SBrian Feldman /* dynamic port forwarding */ 1293af12a3e7SDag-Erling Smørgrav static void 1294ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 1295ca3176e7SBrian Feldman { 1296ca3176e7SBrian Feldman u_char *p; 1297d4ecd108SDag-Erling Smørgrav u_int have; 1298d4ecd108SDag-Erling Smørgrav int ret; 1299ca3176e7SBrian Feldman 1300ca3176e7SBrian Feldman have = buffer_len(&c->input); 1301ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 1302ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 1303ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1304221552e4SDag-Erling Smørgrav if (have < 3) { 1305ca3176e7SBrian Feldman /* need more */ 1306ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1307ca3176e7SBrian Feldman return; 1308ca3176e7SBrian Feldman } 1309ca3176e7SBrian Feldman /* try to guess the protocol */ 1310ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1311ca3176e7SBrian Feldman switch (p[0]) { 1312ca3176e7SBrian Feldman case 0x04: 1313ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 1314ca3176e7SBrian Feldman break; 1315221552e4SDag-Erling Smørgrav case 0x05: 1316221552e4SDag-Erling Smørgrav ret = channel_decode_socks5(c, readset, writeset); 1317221552e4SDag-Erling Smørgrav break; 1318ca3176e7SBrian Feldman default: 1319ca3176e7SBrian Feldman ret = -1; 1320ca3176e7SBrian Feldman break; 1321ca3176e7SBrian Feldman } 1322ca3176e7SBrian Feldman if (ret < 0) { 1323af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1324ca3176e7SBrian Feldman } else if (ret == 0) { 1325ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1326ca3176e7SBrian Feldman /* need more */ 1327ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1328ca3176e7SBrian Feldman } else { 1329ca3176e7SBrian Feldman /* switch to the next state */ 1330ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 1331ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 1332ca3176e7SBrian Feldman } 1333ca3176e7SBrian Feldman } 1334ca3176e7SBrian Feldman 1335a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1336333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1337af12a3e7SDag-Erling Smørgrav static void 1338a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 1339511b41d2SMark Murray { 1340af12a3e7SDag-Erling Smørgrav Channel *nc; 1341d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1342e4a9863fSDag-Erling Smørgrav int newsock, oerrno; 1343511b41d2SMark Murray socklen_t addrlen; 1344ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1345a04a10f8SKris Kennaway int remote_port; 1346511b41d2SMark Murray 1347a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1348511b41d2SMark Murray debug("X11 connection requested."); 1349511b41d2SMark Murray addrlen = sizeof(addr); 1350d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1351af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1352e4a9863fSDag-Erling Smørgrav oerrno = errno; 1353221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 1354af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1355af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1356e4a9863fSDag-Erling Smørgrav errno = oerrno; 1357af12a3e7SDag-Erling Smørgrav } 1358511b41d2SMark Murray if (newsock < 0) { 1359e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1360e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1361511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1362462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1363e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1364a04a10f8SKris Kennaway return; 1365511b41d2SMark Murray } 1366af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1367ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1368a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1369511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1370ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1371a04a10f8SKris Kennaway 1372af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1373a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1374221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 1375a04a10f8SKris Kennaway if (compat20) { 1376a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1377a04a10f8SKris Kennaway packet_put_cstring("x11"); 1378af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1379af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1380af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1381ca3176e7SBrian Feldman /* originator ipaddr and port */ 1382ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1383a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1384221552e4SDag-Erling Smørgrav debug2("ssh2 x11 bug compat mode"); 1385a04a10f8SKris Kennaway } else { 1386a04a10f8SKris Kennaway packet_put_int(remote_port); 1387a04a10f8SKris Kennaway } 1388a04a10f8SKris Kennaway packet_send(); 1389a04a10f8SKris Kennaway } else { 1390511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1391af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1392af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1393af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1394af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1395511b41d2SMark Murray packet_send(); 1396511b41d2SMark Murray } 1397e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1398a04a10f8SKris Kennaway } 1399a04a10f8SKris Kennaway } 1400511b41d2SMark Murray 1401af12a3e7SDag-Erling Smørgrav static void 1402ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1403ca3176e7SBrian Feldman { 1404ca3176e7SBrian Feldman char buf[1024]; 1405f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1406b83788ffSDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_sock_port(c->sock, 1); 1407ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1408d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 1409ca3176e7SBrian Feldman 1410b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1411b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1412e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1413b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1414b15c8340SDag-Erling Smørgrav remote_port = 65535; 1415b15c8340SDag-Erling Smørgrav } 1416b15c8340SDag-Erling Smørgrav 1417ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1418ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1419f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1420ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1421f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1422ca3176e7SBrian Feldman 1423e4a9863fSDag-Erling Smørgrav free(c->remote_name); 1424ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1425ca3176e7SBrian Feldman 1426ca3176e7SBrian Feldman if (compat20) { 1427ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1428ca3176e7SBrian Feldman packet_put_cstring(rtype); 1429ca3176e7SBrian Feldman packet_put_int(c->self); 1430ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1431ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1432*a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1433ca3176e7SBrian Feldman /* target host, port */ 1434ca3176e7SBrian Feldman packet_put_cstring(c->path); 1435ca3176e7SBrian Feldman packet_put_int(c->host_port); 1436*a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1437*a0ee8cc6SDag-Erling Smørgrav /* target path */ 1438*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(c->path); 1439*a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1440*a0ee8cc6SDag-Erling Smørgrav /* listen path */ 1441*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(c->path); 1442ca3176e7SBrian Feldman } else { 1443ca3176e7SBrian Feldman /* listen address, port */ 1444ca3176e7SBrian Feldman packet_put_cstring(c->path); 1445f7167e0eSDag-Erling Smørgrav packet_put_int(local_port); 1446ca3176e7SBrian Feldman } 1447*a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1448*a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 1449*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(""); 1450*a0ee8cc6SDag-Erling Smørgrav } else { 1451ca3176e7SBrian Feldman /* originator host and port */ 1452ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1453d4ecd108SDag-Erling Smørgrav packet_put_int((u_int)remote_port); 1454*a0ee8cc6SDag-Erling Smørgrav } 1455ca3176e7SBrian Feldman packet_send(); 1456ca3176e7SBrian Feldman } else { 1457ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1458ca3176e7SBrian Feldman packet_put_int(c->self); 1459ca3176e7SBrian Feldman packet_put_cstring(c->path); 1460ca3176e7SBrian Feldman packet_put_int(c->host_port); 1461af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1462af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1463ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1464ca3176e7SBrian Feldman packet_send(); 1465ca3176e7SBrian Feldman } 1466e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1467f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1468ca3176e7SBrian Feldman } 1469ca3176e7SBrian Feldman 1470b74df5b2SDag-Erling Smørgrav static void 1471b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(int fd) 1472b74df5b2SDag-Erling Smørgrav { 1473b74df5b2SDag-Erling Smørgrav int on = 1; 1474b74df5b2SDag-Erling Smørgrav 1475b74df5b2SDag-Erling Smørgrav /* 1476b74df5b2SDag-Erling Smørgrav * Set socket options. 1477b74df5b2SDag-Erling Smørgrav * Allow local port reuse in TIME_WAIT. 1478b74df5b2SDag-Erling Smørgrav */ 1479b74df5b2SDag-Erling Smørgrav if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1480b74df5b2SDag-Erling Smørgrav error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1481b74df5b2SDag-Erling Smørgrav } 1482b74df5b2SDag-Erling Smørgrav 1483511b41d2SMark Murray /* 1484a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1485511b41d2SMark Murray */ 1486333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1487af12a3e7SDag-Erling Smørgrav static void 1488a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 1489a04a10f8SKris Kennaway { 1490ca3176e7SBrian Feldman Channel *nc; 1491d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1492af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1493a04a10f8SKris Kennaway socklen_t addrlen; 1494ca3176e7SBrian Feldman char *rtype; 1495a04a10f8SKris Kennaway 1496a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1497a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1498a04a10f8SKris Kennaway "to %.100s port %d requested.", 1499a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1500ca3176e7SBrian Feldman 1501af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1502af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1503af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1504*a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1505*a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1506*a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1507*a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1508*a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1509*a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1510*a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1511af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1512af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1513af12a3e7SDag-Erling Smørgrav } else { 1514af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1515af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1516af12a3e7SDag-Erling Smørgrav } 1517ca3176e7SBrian Feldman 1518511b41d2SMark Murray addrlen = sizeof(addr); 1519d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1520511b41d2SMark Murray if (newsock < 0) { 1521e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1522e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1523511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1524462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1525e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1526a04a10f8SKris Kennaway return; 1527511b41d2SMark Murray } 1528*a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1529af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1530221552e4SDag-Erling Smørgrav nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1531221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1532ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1533ca3176e7SBrian Feldman nc->host_port = c->host_port; 1534cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1535cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 1536ca3176e7SBrian Feldman 1537b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 1538ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1539a04a10f8SKris Kennaway } 1540a04a10f8SKris Kennaway } 1541511b41d2SMark Murray 1542511b41d2SMark Murray /* 1543a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1544a04a10f8SKris Kennaway * clients. 1545511b41d2SMark Murray */ 1546333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1547af12a3e7SDag-Erling Smørgrav static void 1548a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 1549a04a10f8SKris Kennaway { 1550af12a3e7SDag-Erling Smørgrav Channel *nc; 1551af12a3e7SDag-Erling Smørgrav int newsock; 1552d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1553a04a10f8SKris Kennaway socklen_t addrlen; 1554a04a10f8SKris Kennaway 1555a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1556511b41d2SMark Murray addrlen = sizeof(addr); 1557d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1558511b41d2SMark Murray if (newsock < 0) { 1559462c32cbSDag-Erling Smørgrav error("accept from auth socket: %.100s", 1560462c32cbSDag-Erling Smørgrav strerror(errno)); 1561462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1562e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1563a04a10f8SKris Kennaway return; 1564511b41d2SMark Murray } 1565af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1566ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1567ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1568221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 1569ca3176e7SBrian Feldman if (compat20) { 1570ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1571ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1572af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1573ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1574ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1575ca3176e7SBrian Feldman } else { 1576511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1577af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1578ca3176e7SBrian Feldman } 1579511b41d2SMark Murray packet_send(); 1580511b41d2SMark Murray } 1581a04a10f8SKris Kennaway } 1582511b41d2SMark Murray 1583333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1584af12a3e7SDag-Erling Smørgrav static void 1585ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 1586ca3176e7SBrian Feldman { 1587d4af9e69SDag-Erling Smørgrav int err = 0, sock; 1588af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1589af12a3e7SDag-Erling Smørgrav 1590af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1591af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1592af12a3e7SDag-Erling Smørgrav err = errno; 1593af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1594af12a3e7SDag-Erling Smørgrav } 1595ca3176e7SBrian Feldman if (err == 0) { 1596d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 1597d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 1598d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1599af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1600af12a3e7SDag-Erling Smørgrav if (compat20) { 1601af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1602af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1603af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1604af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1605af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1606af12a3e7SDag-Erling Smørgrav } else { 1607af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1608af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1609af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1610af12a3e7SDag-Erling Smørgrav } 1611ca3176e7SBrian Feldman } else { 1612d4af9e69SDag-Erling Smørgrav debug("channel %d: connection failed: %s", 1613ca3176e7SBrian Feldman c->self, strerror(err)); 1614d4af9e69SDag-Erling Smørgrav /* Try next address, if any */ 1615d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&c->connect_ctx)) > 0) { 1616d4af9e69SDag-Erling Smørgrav close(c->sock); 1617d4af9e69SDag-Erling Smørgrav c->sock = c->rfd = c->wfd = sock; 1618d4af9e69SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 1619d4af9e69SDag-Erling Smørgrav return; 1620d4af9e69SDag-Erling Smørgrav } 1621d4af9e69SDag-Erling Smørgrav /* Exhausted all addresses */ 1622d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 1623d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 1624d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1625af12a3e7SDag-Erling Smørgrav if (compat20) { 1626af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1627af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1628af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1629af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1630af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1631af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1632ca3176e7SBrian Feldman } 1633af12a3e7SDag-Erling Smørgrav } else { 1634af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1635af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1636ca3176e7SBrian Feldman } 1637af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1638af12a3e7SDag-Erling Smørgrav } 1639af12a3e7SDag-Erling Smørgrav packet_send(); 1640ca3176e7SBrian Feldman } 1641ca3176e7SBrian Feldman } 1642ca3176e7SBrian Feldman 1643333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1644af12a3e7SDag-Erling Smørgrav static int 1645a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 1646a04a10f8SKris Kennaway { 1647aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1648d4af9e69SDag-Erling Smørgrav int len, force; 1649511b41d2SMark Murray 1650d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1651d4af9e69SDag-Erling Smørgrav if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1652333ee039SDag-Erling Smørgrav errno = 0; 1653a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1654d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || 1655d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 1656a04a10f8SKris Kennaway return 1; 1657333ee039SDag-Erling Smørgrav #ifndef PTY_ZEROREAD 16580c82706bSBrian Feldman if (len <= 0) { 1659333ee039SDag-Erling Smørgrav #else 1660333ee039SDag-Erling Smørgrav if ((!c->isatty && len <= 0) || 1661333ee039SDag-Erling Smørgrav (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1662333ee039SDag-Erling Smørgrav #endif 1663221552e4SDag-Erling Smørgrav debug2("channel %d: read<=0 rfd %d len %d", 1664a04a10f8SKris Kennaway c->self, c->rfd, len); 1665ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1666221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1667af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1668ca3176e7SBrian Feldman return -1; 1669ca3176e7SBrian Feldman } else if (compat13) { 1670af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1671a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1672221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1673a04a10f8SKris Kennaway } else { 1674a04a10f8SKris Kennaway chan_read_failed(c); 1675a04a10f8SKris Kennaway } 1676a04a10f8SKris Kennaway return -1; 1677a04a10f8SKris Kennaway } 1678b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1679b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1680221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1681b66f2d16SKris Kennaway chan_read_failed(c); 1682b66f2d16SKris Kennaway } 1683b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1684b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->input, buf, len); 1685b66f2d16SKris Kennaway } else { 1686a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1687a04a10f8SKris Kennaway } 1688b66f2d16SKris Kennaway } 1689a04a10f8SKris Kennaway return 1; 1690a04a10f8SKris Kennaway } 1691333ee039SDag-Erling Smørgrav 1692333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1693af12a3e7SDag-Erling Smørgrav static int 1694a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 1695a04a10f8SKris Kennaway { 1696ca3176e7SBrian Feldman struct termios tio; 1697b74df5b2SDag-Erling Smørgrav u_char *data = NULL, *buf; 1698e2f6069cSDag-Erling Smørgrav u_int dlen, olen = 0; 1699a04a10f8SKris Kennaway int len; 1700a04a10f8SKris Kennaway 1701a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1702a04a10f8SKris Kennaway if (c->wfd != -1 && 1703a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1704a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1705e2f6069cSDag-Erling Smørgrav olen = buffer_len(&c->output); 1706b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 1707b74df5b2SDag-Erling Smørgrav if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1708b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1709b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1710b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1711b74df5b2SDag-Erling Smørgrav else 1712b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1713b74df5b2SDag-Erling Smørgrav return -1; 1714b74df5b2SDag-Erling Smørgrav } 1715b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1716b74df5b2SDag-Erling Smørgrav buf = data = buffer_get_string(&c->output, &dlen); 1717b74df5b2SDag-Erling Smørgrav } else { 1718b74df5b2SDag-Erling Smørgrav buf = data = buffer_ptr(&c->output); 1719af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1720b74df5b2SDag-Erling Smørgrav } 1721b74df5b2SDag-Erling Smørgrav 1722b74df5b2SDag-Erling Smørgrav if (c->datagram) { 1723b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 1724b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1725e4a9863fSDag-Erling Smørgrav free(data); 1726d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 1727d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 1728b74df5b2SDag-Erling Smørgrav return 1; 1729b74df5b2SDag-Erling Smørgrav if (len <= 0) { 1730b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1731b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1732b74df5b2SDag-Erling Smørgrav else 1733b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1734b74df5b2SDag-Erling Smørgrav return -1; 1735b74df5b2SDag-Erling Smørgrav } 1736e2f6069cSDag-Erling Smørgrav goto out; 1737b74df5b2SDag-Erling Smørgrav } 1738f388f5efSDag-Erling Smørgrav #ifdef _AIX 1739f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 1740476cd3b2SDag-Erling Smørgrav if (compat20 && c->wfd_isatty) 1741476cd3b2SDag-Erling Smørgrav dlen = MIN(dlen, 8*1024); 1742f388f5efSDag-Erling Smørgrav #endif 1743b74df5b2SDag-Erling Smørgrav 1744b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1745d4af9e69SDag-Erling Smørgrav if (len < 0 && 1746d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 1747a04a10f8SKris Kennaway return 1; 1748511b41d2SMark Murray if (len <= 0) { 1749ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1750221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1751af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1752ca3176e7SBrian Feldman return -1; 1753ca3176e7SBrian Feldman } else if (compat13) { 1754af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1755221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1756a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1757511b41d2SMark Murray } else { 1758a04a10f8SKris Kennaway chan_write_failed(c); 1759511b41d2SMark Murray } 1760a04a10f8SKris Kennaway return -1; 1761511b41d2SMark Murray } 17627aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 1763b74df5b2SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 1764e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1765e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1766e0fbb1d2SBrian Feldman /* 1767e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1768ca3176e7SBrian Feldman * traffic analysis. We need to match the 1769ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1770b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 1771e0fbb1d2SBrian Feldman */ 1772ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1773e0fbb1d2SBrian Feldman packet_send(); 1774e0fbb1d2SBrian Feldman } 1775e0fbb1d2SBrian Feldman } 17767aee6ffeSDag-Erling Smørgrav #endif 1777a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1778511b41d2SMark Murray } 1779e2f6069cSDag-Erling Smørgrav out: 1780e2f6069cSDag-Erling Smørgrav if (compat20 && olen > 0) 1781e2f6069cSDag-Erling Smørgrav c->local_consumed += olen - buffer_len(&c->output); 1782a04a10f8SKris Kennaway return 1; 1783511b41d2SMark Murray } 1784333ee039SDag-Erling Smørgrav 1785af12a3e7SDag-Erling Smørgrav static int 1786a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 1787a04a10f8SKris Kennaway { 1788aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1789a04a10f8SKris Kennaway int len; 1790511b41d2SMark Murray 1791a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1792a04a10f8SKris Kennaway if (c->efd != -1) { 1793a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1794a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1795a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1796a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1797a04a10f8SKris Kennaway buffer_len(&c->extended)); 17985b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1799a04a10f8SKris Kennaway c->self, len, c->efd); 1800d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 1801d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 1802ca3176e7SBrian Feldman return 1; 1803ca3176e7SBrian Feldman if (len <= 0) { 1804ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1805ca3176e7SBrian Feldman c->self, c->efd); 1806af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1807ca3176e7SBrian Feldman } else { 1808a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1809a04a10f8SKris Kennaway c->local_consumed += len; 1810a04a10f8SKris Kennaway } 1811e2f6069cSDag-Erling Smørgrav } else if (c->efd != -1 && 1812e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1813e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 1814d4af9e69SDag-Erling Smørgrav (c->detach_close || FD_ISSET(c->efd, readset))) { 1815a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 18165b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1817a04a10f8SKris Kennaway c->self, len, c->efd); 1818d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1819d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK) && !c->detach_close))) 1820ca3176e7SBrian Feldman return 1; 1821ca3176e7SBrian Feldman if (len <= 0) { 1822ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1823a04a10f8SKris Kennaway c->self, c->efd); 1824af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1825ca3176e7SBrian Feldman } else { 1826e2f6069cSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_IGNORE) { 1827e2f6069cSDag-Erling Smørgrav debug3("channel %d: discard efd", 1828e2f6069cSDag-Erling Smørgrav c->self); 1829e2f6069cSDag-Erling Smørgrav } else 1830a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1831a04a10f8SKris Kennaway } 1832a04a10f8SKris Kennaway } 1833ca3176e7SBrian Feldman } 1834a04a10f8SKris Kennaway return 1; 1835a04a10f8SKris Kennaway } 1836333ee039SDag-Erling Smørgrav 183721e764dfSDag-Erling Smørgrav static int 1838ca3176e7SBrian Feldman channel_check_window(Channel *c) 1839a04a10f8SKris Kennaway { 1840ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1841ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1842d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 1843d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 1844d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 1845a04a10f8SKris Kennaway c->local_consumed > 0) { 1846a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1847a04a10f8SKris Kennaway packet_put_int(c->remote_id); 184860c59fadSDag-Erling Smørgrav packet_put_int(c->local_consumed); 1849a04a10f8SKris Kennaway packet_send(); 18505b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1851a04a10f8SKris Kennaway c->self, c->local_window, 1852a04a10f8SKris Kennaway c->local_consumed); 185360c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 1854a04a10f8SKris Kennaway c->local_consumed = 0; 1855a04a10f8SKris Kennaway } 1856a04a10f8SKris Kennaway return 1; 1857a04a10f8SKris Kennaway } 1858a04a10f8SKris Kennaway 1859af12a3e7SDag-Erling Smørgrav static void 1860af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 1861a04a10f8SKris Kennaway { 1862a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1863a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1864af12a3e7SDag-Erling Smørgrav if (!compat20) 1865af12a3e7SDag-Erling Smørgrav return; 1866a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1867ca3176e7SBrian Feldman channel_check_window(c); 1868a04a10f8SKris Kennaway } 1869a04a10f8SKris Kennaway 1870b15c8340SDag-Erling Smørgrav static u_int 1871b15c8340SDag-Erling Smørgrav read_mux(Channel *c, u_int need) 1872b15c8340SDag-Erling Smørgrav { 1873b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1874b15c8340SDag-Erling Smørgrav int len; 1875b15c8340SDag-Erling Smørgrav u_int rlen; 1876b15c8340SDag-Erling Smørgrav 1877b15c8340SDag-Erling Smørgrav if (buffer_len(&c->input) < need) { 1878b15c8340SDag-Erling Smørgrav rlen = need - buffer_len(&c->input); 1879b15c8340SDag-Erling Smørgrav len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); 1880b15c8340SDag-Erling Smørgrav if (len <= 0) { 1881b15c8340SDag-Erling Smørgrav if (errno != EINTR && errno != EAGAIN) { 1882b15c8340SDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %d", 1883b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 1884b15c8340SDag-Erling Smørgrav chan_read_failed(c); 1885b15c8340SDag-Erling Smørgrav return 0; 1886b15c8340SDag-Erling Smørgrav } 1887b15c8340SDag-Erling Smørgrav } else 1888b15c8340SDag-Erling Smørgrav buffer_append(&c->input, buf, len); 1889b15c8340SDag-Erling Smørgrav } 1890b15c8340SDag-Erling Smørgrav return buffer_len(&c->input); 1891b15c8340SDag-Erling Smørgrav } 1892b15c8340SDag-Erling Smørgrav 1893b15c8340SDag-Erling Smørgrav static void 1894b15c8340SDag-Erling Smørgrav channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1895b15c8340SDag-Erling Smørgrav { 1896b15c8340SDag-Erling Smørgrav u_int need; 1897b15c8340SDag-Erling Smørgrav ssize_t len; 1898b15c8340SDag-Erling Smørgrav 1899b15c8340SDag-Erling Smørgrav if (!compat20) 1900b15c8340SDag-Erling Smørgrav fatal("%s: entered with !compat20", __func__); 1901b15c8340SDag-Erling Smørgrav 1902e2f6069cSDag-Erling Smørgrav if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && 1903b15c8340SDag-Erling Smørgrav (c->istate == CHAN_INPUT_OPEN || 1904b15c8340SDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN)) { 1905b15c8340SDag-Erling Smørgrav /* 1906b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 1907b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 1908b15c8340SDag-Erling Smørgrav */ 1909b15c8340SDag-Erling Smørgrav if (read_mux(c, 4) < 4) /* read header */ 1910b15c8340SDag-Erling Smørgrav return; 1911b15c8340SDag-Erling Smørgrav need = get_u32(buffer_ptr(&c->input)); 1912b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 1913b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 1914b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 1915b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 1916b15c8340SDag-Erling Smørgrav chan_rcvd_oclose(c); 1917b15c8340SDag-Erling Smørgrav return; 1918b15c8340SDag-Erling Smørgrav } 1919b15c8340SDag-Erling Smørgrav if (read_mux(c, need + 4) < need + 4) /* read body */ 1920b15c8340SDag-Erling Smørgrav return; 1921b15c8340SDag-Erling Smørgrav if (c->mux_rcb(c) != 0) { 1922b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 1923b15c8340SDag-Erling Smørgrav chan_mark_dead(c); 1924b15c8340SDag-Erling Smørgrav return; 1925b15c8340SDag-Erling Smørgrav } 1926b15c8340SDag-Erling Smørgrav } 1927b15c8340SDag-Erling Smørgrav 1928b15c8340SDag-Erling Smørgrav if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && 1929b15c8340SDag-Erling Smørgrav buffer_len(&c->output) > 0) { 1930b15c8340SDag-Erling Smørgrav len = write(c->wfd, buffer_ptr(&c->output), 1931b15c8340SDag-Erling Smørgrav buffer_len(&c->output)); 1932b15c8340SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1933b15c8340SDag-Erling Smørgrav return; 1934b15c8340SDag-Erling Smørgrav if (len <= 0) { 1935b15c8340SDag-Erling Smørgrav chan_mark_dead(c); 1936b15c8340SDag-Erling Smørgrav return; 1937b15c8340SDag-Erling Smørgrav } 1938b15c8340SDag-Erling Smørgrav buffer_consume(&c->output, len); 1939b15c8340SDag-Erling Smørgrav } 1940b15c8340SDag-Erling Smørgrav } 1941b15c8340SDag-Erling Smørgrav 1942b15c8340SDag-Erling Smørgrav static void 1943b15c8340SDag-Erling Smørgrav channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) 1944b15c8340SDag-Erling Smørgrav { 1945b15c8340SDag-Erling Smørgrav Channel *nc; 1946b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 1947b15c8340SDag-Erling Smørgrav socklen_t addrlen; 1948b15c8340SDag-Erling Smørgrav int newsock; 1949b15c8340SDag-Erling Smørgrav uid_t euid; 1950b15c8340SDag-Erling Smørgrav gid_t egid; 1951b15c8340SDag-Erling Smørgrav 1952b15c8340SDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 1953b15c8340SDag-Erling Smørgrav return; 1954b15c8340SDag-Erling Smørgrav 1955b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 1956b15c8340SDag-Erling Smørgrav 1957b15c8340SDag-Erling Smørgrav /* 1958b15c8340SDag-Erling Smørgrav * Accept connection on control socket 1959b15c8340SDag-Erling Smørgrav */ 1960b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 1961b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 1962b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 1963b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 1964b15c8340SDag-Erling Smørgrav error("%s accept: %s", __func__, strerror(errno)); 1965462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1966e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1967b15c8340SDag-Erling Smørgrav return; 1968b15c8340SDag-Erling Smørgrav } 1969b15c8340SDag-Erling Smørgrav 1970b15c8340SDag-Erling Smørgrav if (getpeereid(newsock, &euid, &egid) < 0) { 1971b15c8340SDag-Erling Smørgrav error("%s getpeereid failed: %s", __func__, 1972b15c8340SDag-Erling Smørgrav strerror(errno)); 1973b15c8340SDag-Erling Smørgrav close(newsock); 1974b15c8340SDag-Erling Smørgrav return; 1975b15c8340SDag-Erling Smørgrav } 1976b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 1977b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 1978b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 1979b15c8340SDag-Erling Smørgrav close(newsock); 1980b15c8340SDag-Erling Smørgrav return; 1981b15c8340SDag-Erling Smørgrav } 1982b15c8340SDag-Erling Smørgrav nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, 1983b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 1984b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 1985b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 1986b15c8340SDag-Erling Smørgrav debug3("%s: new mux channel %d fd %d", __func__, 1987b15c8340SDag-Erling Smørgrav nc->self, nc->sock); 1988b15c8340SDag-Erling Smørgrav /* establish state */ 1989b15c8340SDag-Erling Smørgrav nc->mux_rcb(nc); 1990b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 1991b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 1992b15c8340SDag-Erling Smørgrav } 1993b15c8340SDag-Erling Smørgrav 1994333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1995af12a3e7SDag-Erling Smørgrav static void 1996a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 1997a04a10f8SKris Kennaway { 1998a04a10f8SKris Kennaway int len; 1999f388f5efSDag-Erling Smørgrav 2000511b41d2SMark Murray /* Send buffered output data to the socket. */ 2001a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 2002a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 2003a04a10f8SKris Kennaway buffer_len(&c->output)); 2004511b41d2SMark Murray if (len <= 0) 2005af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 2006511b41d2SMark Murray else 2007a04a10f8SKris Kennaway buffer_consume(&c->output, len); 2008511b41d2SMark Murray } 2009a04a10f8SKris Kennaway } 2010511b41d2SMark Murray 2011af12a3e7SDag-Erling Smørgrav static void 2012a04a10f8SKris Kennaway channel_handler_init_20(void) 2013a04a10f8SKris Kennaway { 2014af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 2015a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 2016a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2017ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 2018*a0ee8cc6SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 2019*a0ee8cc6SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 2020a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2021ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2022ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2023ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2024b15c8340SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 2025b15c8340SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2026a04a10f8SKris Kennaway 2027af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2028a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2029ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 2030*a0ee8cc6SDag-Erling Smørgrav channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 2031*a0ee8cc6SDag-Erling Smørgrav channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 2032a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2033ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2034ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2035af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2036b15c8340SDag-Erling Smørgrav channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 2037b15c8340SDag-Erling Smørgrav channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2038a04a10f8SKris Kennaway } 2039a04a10f8SKris Kennaway 2040af12a3e7SDag-Erling Smørgrav static void 2041a04a10f8SKris Kennaway channel_handler_init_13(void) 2042a04a10f8SKris Kennaway { 2043a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 2044a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 2045a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2046a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2047a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2048a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 2049a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 2050ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2051ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2052a04a10f8SKris Kennaway 2053af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2054a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2055a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2056a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2057a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 2058ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2059af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2060a04a10f8SKris Kennaway } 2061a04a10f8SKris Kennaway 2062af12a3e7SDag-Erling Smørgrav static void 2063a04a10f8SKris Kennaway channel_handler_init_15(void) 2064a04a10f8SKris Kennaway { 2065af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 2066a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 2067a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2068a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2069a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2070ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2071ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2072a04a10f8SKris Kennaway 2073a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2074a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2075a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2076af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2077ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2078af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2079a04a10f8SKris Kennaway } 2080a04a10f8SKris Kennaway 2081af12a3e7SDag-Erling Smørgrav static void 2082a04a10f8SKris Kennaway channel_handler_init(void) 2083a04a10f8SKris Kennaway { 2084a04a10f8SKris Kennaway int i; 2085f388f5efSDag-Erling Smørgrav 2086a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 2087a04a10f8SKris Kennaway channel_pre[i] = NULL; 2088a04a10f8SKris Kennaway channel_post[i] = NULL; 2089a04a10f8SKris Kennaway } 2090a04a10f8SKris Kennaway if (compat20) 2091a04a10f8SKris Kennaway channel_handler_init_20(); 2092a04a10f8SKris Kennaway else if (compat13) 2093a04a10f8SKris Kennaway channel_handler_init_13(); 2094a04a10f8SKris Kennaway else 2095a04a10f8SKris Kennaway channel_handler_init_15(); 2096a04a10f8SKris Kennaway } 2097a04a10f8SKris Kennaway 2098af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2099af12a3e7SDag-Erling Smørgrav static void 2100af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 2101af12a3e7SDag-Erling Smørgrav { 2102af12a3e7SDag-Erling Smørgrav if (c == NULL) 2103af12a3e7SDag-Erling Smørgrav return; 2104af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 2105b74df5b2SDag-Erling Smørgrav if (!chan_is_dead(c, c->detach_close)) 2106af12a3e7SDag-Erling Smørgrav return; 2107221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 2108af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 2109af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2110af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2111af12a3e7SDag-Erling Smørgrav return; 2112221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2113af12a3e7SDag-Erling Smørgrav } 2114af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 2115af12a3e7SDag-Erling Smørgrav return; 2116221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 2117af12a3e7SDag-Erling Smørgrav channel_free(c); 2118af12a3e7SDag-Erling Smørgrav } 2119af12a3e7SDag-Erling Smørgrav 2120af12a3e7SDag-Erling Smørgrav static void 2121462c32cbSDag-Erling Smørgrav channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, 2122462c32cbSDag-Erling Smørgrav time_t *unpause_secs) 2123a04a10f8SKris Kennaway { 2124a04a10f8SKris Kennaway static int did_init = 0; 2125b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2126a04a10f8SKris Kennaway Channel *c; 2127462c32cbSDag-Erling Smørgrav time_t now; 2128a04a10f8SKris Kennaway 2129a04a10f8SKris Kennaway if (!did_init) { 2130a04a10f8SKris Kennaway channel_handler_init(); 2131a04a10f8SKris Kennaway did_init = 1; 2132a04a10f8SKris Kennaway } 2133e4a9863fSDag-Erling Smørgrav now = monotime(); 2134462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL) 2135462c32cbSDag-Erling Smørgrav *unpause_secs = 0; 2136b15c8340SDag-Erling Smørgrav for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { 2137af12a3e7SDag-Erling Smørgrav c = channels[i]; 2138af12a3e7SDag-Erling Smørgrav if (c == NULL) 2139511b41d2SMark Murray continue; 2140b15c8340SDag-Erling Smørgrav if (c->delayed) { 2141b15c8340SDag-Erling Smørgrav if (ftab == channel_pre) 2142b15c8340SDag-Erling Smørgrav c->delayed = 0; 2143b15c8340SDag-Erling Smørgrav else 2144b15c8340SDag-Erling Smørgrav continue; 2145b15c8340SDag-Erling Smørgrav } 2146462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2147462c32cbSDag-Erling Smørgrav /* 2148462c32cbSDag-Erling Smørgrav * Run handlers that are not paused. 2149462c32cbSDag-Erling Smørgrav */ 2150462c32cbSDag-Erling Smørgrav if (c->notbefore <= now) 2151a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 2152462c32cbSDag-Erling Smørgrav else if (unpause_secs != NULL) { 2153462c32cbSDag-Erling Smørgrav /* 2154462c32cbSDag-Erling Smørgrav * Collect the time that the earliest 2155462c32cbSDag-Erling Smørgrav * channel comes off pause. 2156462c32cbSDag-Erling Smørgrav */ 2157462c32cbSDag-Erling Smørgrav debug3("%s: chan %d: skip for %d more seconds", 2158462c32cbSDag-Erling Smørgrav __func__, c->self, 2159462c32cbSDag-Erling Smørgrav (int)(c->notbefore - now)); 2160462c32cbSDag-Erling Smørgrav if (*unpause_secs == 0 || 2161462c32cbSDag-Erling Smørgrav (c->notbefore - now) < *unpause_secs) 2162462c32cbSDag-Erling Smørgrav *unpause_secs = c->notbefore - now; 2163462c32cbSDag-Erling Smørgrav } 2164462c32cbSDag-Erling Smørgrav } 2165af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 2166511b41d2SMark Murray } 2167462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL && *unpause_secs != 0) 2168462c32cbSDag-Erling Smørgrav debug3("%s: first channel unpauses in %d seconds", 2169462c32cbSDag-Erling Smørgrav __func__, (int)*unpause_secs); 2170511b41d2SMark Murray } 2171a04a10f8SKris Kennaway 2172af12a3e7SDag-Erling Smørgrav /* 2173af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 2174af12a3e7SDag-Erling Smørgrav * select bitmasks. 2175af12a3e7SDag-Erling Smørgrav */ 2176a04a10f8SKris Kennaway void 2177ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2178462c32cbSDag-Erling Smørgrav u_int *nallocp, time_t *minwait_secs, int rekeying) 2179a04a10f8SKris Kennaway { 2180333ee039SDag-Erling Smørgrav u_int n, sz, nfdset; 2181ca3176e7SBrian Feldman 2182ca3176e7SBrian Feldman n = MAX(*maxfdp, channel_max_fd); 2183ca3176e7SBrian Feldman 2184333ee039SDag-Erling Smørgrav nfdset = howmany(n+1, NFDBITS); 2185333ee039SDag-Erling Smørgrav /* Explicitly test here, because xrealloc isn't always called */ 2186333ee039SDag-Erling Smørgrav if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) 2187333ee039SDag-Erling Smørgrav fatal("channel_prepare_select: max_fd (%d) is too large", n); 2188333ee039SDag-Erling Smørgrav sz = nfdset * sizeof(fd_mask); 2189333ee039SDag-Erling Smørgrav 2190af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 2191af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 2192333ee039SDag-Erling Smørgrav *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); 2193333ee039SDag-Erling Smørgrav *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); 2194af12a3e7SDag-Erling Smørgrav *nallocp = sz; 2195ca3176e7SBrian Feldman } 2196af12a3e7SDag-Erling Smørgrav *maxfdp = n; 2197ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 2198ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 2199ca3176e7SBrian Feldman 2200ca3176e7SBrian Feldman if (!rekeying) 2201462c32cbSDag-Erling Smørgrav channel_handler(channel_pre, *readsetp, *writesetp, 2202462c32cbSDag-Erling Smørgrav minwait_secs); 2203a04a10f8SKris Kennaway } 2204a04a10f8SKris Kennaway 2205af12a3e7SDag-Erling Smørgrav /* 2206af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 2207af12a3e7SDag-Erling Smørgrav * events pending. 2208af12a3e7SDag-Erling Smørgrav */ 2209a04a10f8SKris Kennaway void 2210a04a10f8SKris Kennaway channel_after_select(fd_set *readset, fd_set *writeset) 2211a04a10f8SKris Kennaway { 2212462c32cbSDag-Erling Smørgrav channel_handler(channel_post, readset, writeset, NULL); 2213511b41d2SMark Murray } 2214511b41d2SMark Murray 2215af12a3e7SDag-Erling Smørgrav 2216ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 2217511b41d2SMark Murray void 2218af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 2219511b41d2SMark Murray { 2220a04a10f8SKris Kennaway Channel *c; 222121e764dfSDag-Erling Smørgrav u_int i, len; 2222511b41d2SMark Murray 2223511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 2224af12a3e7SDag-Erling Smørgrav c = channels[i]; 2225af12a3e7SDag-Erling Smørgrav if (c == NULL) 2226af12a3e7SDag-Erling Smørgrav continue; 2227511b41d2SMark Murray 2228af12a3e7SDag-Erling Smørgrav /* 2229af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 2230af12a3e7SDag-Erling Smørgrav * incoming data. 2231af12a3e7SDag-Erling Smørgrav */ 2232511b41d2SMark Murray if (compat13) { 2233a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 2234a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 2235511b41d2SMark Murray continue; 2236511b41d2SMark Murray } else { 2237a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 2238511b41d2SMark Murray continue; 2239a04a10f8SKris Kennaway } 2240a04a10f8SKris Kennaway if (compat20 && 2241a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 2242ca3176e7SBrian Feldman /* XXX is this true? */ 2243af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 2244511b41d2SMark Murray continue; 2245511b41d2SMark Murray } 2246511b41d2SMark Murray 2247511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 2248ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 2249ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 2250ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 2251b74df5b2SDag-Erling Smørgrav if (c->datagram) { 2252b74df5b2SDag-Erling Smørgrav if (len > 0) { 2253b74df5b2SDag-Erling Smørgrav u_char *data; 2254b74df5b2SDag-Erling Smørgrav u_int dlen; 2255b74df5b2SDag-Erling Smørgrav 2256b74df5b2SDag-Erling Smørgrav data = buffer_get_string(&c->input, 2257b74df5b2SDag-Erling Smørgrav &dlen); 2258e2f6069cSDag-Erling Smørgrav if (dlen > c->remote_window || 2259e2f6069cSDag-Erling Smørgrav dlen > c->remote_maxpacket) { 2260e2f6069cSDag-Erling Smørgrav debug("channel %d: datagram " 2261e2f6069cSDag-Erling Smørgrav "too big for channel", 2262e2f6069cSDag-Erling Smørgrav c->self); 2263e4a9863fSDag-Erling Smørgrav free(data); 2264e2f6069cSDag-Erling Smørgrav continue; 2265e2f6069cSDag-Erling Smørgrav } 2266b74df5b2SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_DATA); 2267b74df5b2SDag-Erling Smørgrav packet_put_int(c->remote_id); 2268b74df5b2SDag-Erling Smørgrav packet_put_string(data, dlen); 2269b74df5b2SDag-Erling Smørgrav packet_send(); 2270b74df5b2SDag-Erling Smørgrav c->remote_window -= dlen + 4; 2271e4a9863fSDag-Erling Smørgrav free(data); 2272b74df5b2SDag-Erling Smørgrav } 2273b74df5b2SDag-Erling Smørgrav continue; 2274b74df5b2SDag-Erling Smørgrav } 2275af12a3e7SDag-Erling Smørgrav /* 2276af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 2277af12a3e7SDag-Erling Smørgrav * connection. 2278af12a3e7SDag-Erling Smørgrav */ 2279a04a10f8SKris Kennaway if (compat20) { 2280a04a10f8SKris Kennaway if (len > c->remote_window) 2281a04a10f8SKris Kennaway len = c->remote_window; 2282a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 2283a04a10f8SKris Kennaway len = c->remote_maxpacket; 2284a04a10f8SKris Kennaway } else { 2285511b41d2SMark Murray if (packet_is_interactive()) { 2286511b41d2SMark Murray if (len > 1024) 2287511b41d2SMark Murray len = 512; 2288511b41d2SMark Murray } else { 2289511b41d2SMark Murray /* Keep the packets at reasonable size. */ 2290511b41d2SMark Murray if (len > packet_get_maxsize()/2) 2291511b41d2SMark Murray len = packet_get_maxsize()/2; 2292511b41d2SMark Murray } 2293a04a10f8SKris Kennaway } 2294a04a10f8SKris Kennaway if (len > 0) { 2295a04a10f8SKris Kennaway packet_start(compat20 ? 2296a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 2297a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2298a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 2299511b41d2SMark Murray packet_send(); 2300a04a10f8SKris Kennaway buffer_consume(&c->input, len); 2301a04a10f8SKris Kennaway c->remote_window -= len; 2302a04a10f8SKris Kennaway } 2303a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 2304511b41d2SMark Murray if (compat13) 2305511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 2306511b41d2SMark Murray /* 2307511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 230880628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 230980628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 2310511b41d2SMark Murray */ 231180628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 231280628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 231380628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 231480628bacSDag-Erling Smørgrav else 2315a04a10f8SKris Kennaway chan_ibuf_empty(c); 2316a04a10f8SKris Kennaway } 2317a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 2318a04a10f8SKris Kennaway if (compat20 && 231980628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 2320a04a10f8SKris Kennaway c->remote_window > 0 && 2321a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 2322a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 2323a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 2324ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 2325ca3176e7SBrian Feldman c->extended_usage); 2326a04a10f8SKris Kennaway if (len > c->remote_window) 2327a04a10f8SKris Kennaway len = c->remote_window; 2328a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 2329a04a10f8SKris Kennaway len = c->remote_maxpacket; 2330a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 2331a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2332a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 2333a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 2334a04a10f8SKris Kennaway packet_send(); 2335a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 2336a04a10f8SKris Kennaway c->remote_window -= len; 2337ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 2338511b41d2SMark Murray } 2339511b41d2SMark Murray } 2340511b41d2SMark Murray } 2341511b41d2SMark Murray 2342af12a3e7SDag-Erling Smørgrav 2343af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 2344511b41d2SMark Murray 2345333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2346511b41d2SMark Murray void 2347af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 2348511b41d2SMark Murray { 2349511b41d2SMark Murray int id; 2350*a0ee8cc6SDag-Erling Smørgrav const u_char *data; 2351e2f6069cSDag-Erling Smørgrav u_int data_len, win_len; 2352a04a10f8SKris Kennaway Channel *c; 2353511b41d2SMark Murray 2354511b41d2SMark Murray /* Get the channel number and verify it. */ 2355511b41d2SMark Murray id = packet_get_int(); 2356a04a10f8SKris Kennaway c = channel_lookup(id); 2357a04a10f8SKris Kennaway if (c == NULL) 2358511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 2359511b41d2SMark Murray 2360511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 2361a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 2362a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 2363511b41d2SMark Murray return; 2364511b41d2SMark Murray 2365511b41d2SMark Murray /* Get the data. */ 2366d4af9e69SDag-Erling Smørgrav data = packet_get_string_ptr(&data_len); 2367e2f6069cSDag-Erling Smørgrav win_len = data_len; 2368e2f6069cSDag-Erling Smørgrav if (c->datagram) 2369e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 2370a04a10f8SKris Kennaway 2371476cd3b2SDag-Erling Smørgrav /* 2372476cd3b2SDag-Erling Smørgrav * Ignore data for protocol > 1.3 if output end is no longer open. 2373476cd3b2SDag-Erling Smørgrav * For protocol 2 the sending side is reducing its window as it sends 2374476cd3b2SDag-Erling Smørgrav * data, so we must 'fake' consumption of the data in order to ensure 2375476cd3b2SDag-Erling Smørgrav * that window updates are sent back. Otherwise the connection might 2376476cd3b2SDag-Erling Smørgrav * deadlock. 2377476cd3b2SDag-Erling Smørgrav */ 2378476cd3b2SDag-Erling Smørgrav if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2379476cd3b2SDag-Erling Smørgrav if (compat20) { 2380e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 2381e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 2382476cd3b2SDag-Erling Smørgrav } 2383476cd3b2SDag-Erling Smørgrav return; 2384476cd3b2SDag-Erling Smørgrav } 2385476cd3b2SDag-Erling Smørgrav 2386a04a10f8SKris Kennaway if (compat20) { 2387e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 2388221552e4SDag-Erling Smørgrav logit("channel %d: rcvd big packet %d, maxpack %d", 2389e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 2390a04a10f8SKris Kennaway } 2391e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 2392221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much data %d, win %d", 2393e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_window); 2394a04a10f8SKris Kennaway return; 2395a04a10f8SKris Kennaway } 2396e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 2397a04a10f8SKris Kennaway } 2398b74df5b2SDag-Erling Smørgrav if (c->datagram) 2399b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->output, data, data_len); 2400b74df5b2SDag-Erling Smørgrav else 2401a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 2402d4af9e69SDag-Erling Smørgrav packet_check_eom(); 2403511b41d2SMark Murray } 2404af12a3e7SDag-Erling Smørgrav 2405333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2406a04a10f8SKris Kennaway void 2407af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 2408a04a10f8SKris Kennaway { 2409a04a10f8SKris Kennaway int id; 2410a04a10f8SKris Kennaway char *data; 2411a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 2412a04a10f8SKris Kennaway Channel *c; 2413a04a10f8SKris Kennaway 2414a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 2415a04a10f8SKris Kennaway id = packet_get_int(); 2416a04a10f8SKris Kennaway c = channel_lookup(id); 2417a04a10f8SKris Kennaway 2418a04a10f8SKris Kennaway if (c == NULL) 2419a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 2420a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 2421221552e4SDag-Erling Smørgrav logit("channel %d: ext data for non open", id); 2422a04a10f8SKris Kennaway return; 2423a04a10f8SKris Kennaway } 242480628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 242580628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 242680628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 242780628bacSDag-Erling Smørgrav else 242880628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 242980628bacSDag-Erling Smørgrav "on channel %d.", id); 243080628bacSDag-Erling Smørgrav } 2431a04a10f8SKris Kennaway tcode = packet_get_int(); 2432a04a10f8SKris Kennaway if (c->efd == -1 || 2433a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 2434a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 2435221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 2436a04a10f8SKris Kennaway return; 2437a04a10f8SKris Kennaway } 2438a04a10f8SKris Kennaway data = packet_get_string(&data_len); 2439af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2440a04a10f8SKris Kennaway if (data_len > c->local_window) { 2441221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %d, win %d", 2442a04a10f8SKris Kennaway c->self, data_len, c->local_window); 2443e4a9863fSDag-Erling Smørgrav free(data); 2444a04a10f8SKris Kennaway return; 2445a04a10f8SKris Kennaway } 24465b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 2447a04a10f8SKris Kennaway c->local_window -= data_len; 2448a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 2449e4a9863fSDag-Erling Smørgrav free(data); 2450a04a10f8SKris Kennaway } 2451a04a10f8SKris Kennaway 2452333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2453a04a10f8SKris Kennaway void 2454af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 2455a04a10f8SKris Kennaway { 2456a04a10f8SKris Kennaway int id; 2457a04a10f8SKris Kennaway Channel *c; 2458a04a10f8SKris Kennaway 2459a04a10f8SKris Kennaway id = packet_get_int(); 2460af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2461a04a10f8SKris Kennaway c = channel_lookup(id); 2462a04a10f8SKris Kennaway if (c == NULL) 2463a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 2464a04a10f8SKris Kennaway chan_rcvd_ieof(c); 2465af12a3e7SDag-Erling Smørgrav 2466af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 2467af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 2468af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 2469af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 2470af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 2471af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 2472af12a3e7SDag-Erling Smørgrav } 2473af12a3e7SDag-Erling Smørgrav 2474a04a10f8SKris Kennaway } 2475511b41d2SMark Murray 2476333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2477511b41d2SMark Murray void 2478af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 2479511b41d2SMark Murray { 2480a04a10f8SKris Kennaway int id; 2481a04a10f8SKris Kennaway Channel *c; 2482511b41d2SMark Murray 2483a04a10f8SKris Kennaway id = packet_get_int(); 2484af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2485a04a10f8SKris Kennaway c = channel_lookup(id); 2486a04a10f8SKris Kennaway if (c == NULL) 2487a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 2488511b41d2SMark Murray 2489511b41d2SMark Murray /* 2490511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 2491511b41d2SMark Murray * data is coming for it. 2492511b41d2SMark Murray */ 2493511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 2494a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2495511b41d2SMark Murray packet_send(); 2496511b41d2SMark Murray 2497511b41d2SMark Murray /* 2498511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 2499511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 2500511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 2501511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 2502511b41d2SMark Murray * the confirmation arrives. 2503511b41d2SMark Murray */ 2504a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 2505511b41d2SMark Murray /* 2506511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 2507511b41d2SMark Murray * cause it to be freed later. 2508511b41d2SMark Murray */ 2509af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 2510a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 2511511b41d2SMark Murray } 2512511b41d2SMark Murray } 2513511b41d2SMark Murray 2514a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2515333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2516a04a10f8SKris Kennaway void 2517af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 2518a04a10f8SKris Kennaway { 2519a04a10f8SKris Kennaway int id = packet_get_int(); 2520a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2521af12a3e7SDag-Erling Smørgrav 2522af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2523a04a10f8SKris Kennaway if (c == NULL) 2524a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 2525a04a10f8SKris Kennaway chan_rcvd_oclose(c); 2526a04a10f8SKris Kennaway } 2527511b41d2SMark Murray 2528333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2529511b41d2SMark Murray void 2530af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 2531511b41d2SMark Murray { 2532a04a10f8SKris Kennaway int id = packet_get_int(); 2533a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2534a04a10f8SKris Kennaway 2535af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2536a04a10f8SKris Kennaway if (c == NULL) 2537a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2538a04a10f8SKris Kennaway "out-of-range channel %d.", id); 2539e4a9863fSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) 2540a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2541a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 2542af12a3e7SDag-Erling Smørgrav channel_free(c); 2543a04a10f8SKris Kennaway } 2544a04a10f8SKris Kennaway 2545333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2546a04a10f8SKris Kennaway void 2547af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 2548a04a10f8SKris Kennaway { 2549a04a10f8SKris Kennaway int id, remote_id; 2550a04a10f8SKris Kennaway Channel *c; 2551a04a10f8SKris Kennaway 2552a04a10f8SKris Kennaway id = packet_get_int(); 2553a04a10f8SKris Kennaway c = channel_lookup(id); 2554a04a10f8SKris Kennaway 2555a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 2556a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 2557a04a10f8SKris Kennaway "non-opening channel %d.", id); 2558a04a10f8SKris Kennaway remote_id = packet_get_int(); 2559a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 2560a04a10f8SKris Kennaway c->remote_id = remote_id; 2561a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 2562a04a10f8SKris Kennaway 2563a04a10f8SKris Kennaway if (compat20) { 2564a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 2565a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 2566d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 25675b9b2fafSBrian Feldman debug2("callback start"); 2568e2f6069cSDag-Erling Smørgrav c->open_confirm(c->self, 1, c->open_confirm_ctx); 25695b9b2fafSBrian Feldman debug2("callback done"); 2570a04a10f8SKris Kennaway } 2571221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 2572a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 2573a04a10f8SKris Kennaway } 2574af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2575af12a3e7SDag-Erling Smørgrav } 2576af12a3e7SDag-Erling Smørgrav 2577af12a3e7SDag-Erling Smørgrav static char * 2578af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 2579af12a3e7SDag-Erling Smørgrav { 2580af12a3e7SDag-Erling Smørgrav switch (reason) { 2581af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 2582af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 2583af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 2584af12a3e7SDag-Erling Smørgrav return "connect failed"; 2585af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 2586af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 2587af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 2588af12a3e7SDag-Erling Smørgrav return "resource shortage"; 2589af12a3e7SDag-Erling Smørgrav } 2590af12a3e7SDag-Erling Smørgrav return "unknown reason"; 2591a04a10f8SKris Kennaway } 2592a04a10f8SKris Kennaway 2593333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2594a04a10f8SKris Kennaway void 2595af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 2596a04a10f8SKris Kennaway { 2597ca3176e7SBrian Feldman int id, reason; 2598ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 2599a04a10f8SKris Kennaway Channel *c; 2600a04a10f8SKris Kennaway 2601a04a10f8SKris Kennaway id = packet_get_int(); 2602a04a10f8SKris Kennaway c = channel_lookup(id); 2603a04a10f8SKris Kennaway 2604a04a10f8SKris Kennaway if (c==NULL || c->type != SSH_CHANNEL_OPENING) 2605a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 2606a04a10f8SKris Kennaway "non-opening channel %d.", id); 2607a04a10f8SKris Kennaway if (compat20) { 2608ca3176e7SBrian Feldman reason = packet_get_int(); 2609af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 2610ca3176e7SBrian Feldman msg = packet_get_string(NULL); 2611ca3176e7SBrian Feldman lang = packet_get_string(NULL); 2612ca3176e7SBrian Feldman } 2613221552e4SDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", id, 2614af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 2615e4a9863fSDag-Erling Smørgrav free(msg); 2616e4a9863fSDag-Erling Smørgrav free(lang); 2617e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 2618e2f6069cSDag-Erling Smørgrav debug2("callback start"); 2619e2f6069cSDag-Erling Smørgrav c->open_confirm(c->self, 0, c->open_confirm_ctx); 2620e2f6069cSDag-Erling Smørgrav debug2("callback done"); 2621e2f6069cSDag-Erling Smørgrav } 2622a04a10f8SKris Kennaway } 2623af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2624cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 2625cce7d346SDag-Erling Smørgrav chan_mark_dead(c); 2626a04a10f8SKris Kennaway } 2627a04a10f8SKris Kennaway 2628333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2629a04a10f8SKris Kennaway void 2630af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 2631a04a10f8SKris Kennaway { 2632a04a10f8SKris Kennaway Channel *c; 2633a82e551fSDag-Erling Smørgrav int id; 2634a82e551fSDag-Erling Smørgrav u_int adjust; 2635a04a10f8SKris Kennaway 2636a04a10f8SKris Kennaway if (!compat20) 2637a04a10f8SKris Kennaway return; 2638511b41d2SMark Murray 2639511b41d2SMark Murray /* Get the channel number and verify it. */ 2640a04a10f8SKris Kennaway id = packet_get_int(); 2641a04a10f8SKris Kennaway c = channel_lookup(id); 2642511b41d2SMark Murray 2643b74df5b2SDag-Erling Smørgrav if (c == NULL) { 2644b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 2645511b41d2SMark Murray return; 2646511b41d2SMark Murray } 2647a04a10f8SKris Kennaway adjust = packet_get_int(); 2648af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2649a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 2650a04a10f8SKris Kennaway c->remote_window += adjust; 2651511b41d2SMark Murray } 2652511b41d2SMark Murray 2653333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2654af12a3e7SDag-Erling Smørgrav void 2655af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 2656af12a3e7SDag-Erling Smørgrav { 2657af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 2658af12a3e7SDag-Erling Smørgrav u_short host_port; 2659af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 2660d4af9e69SDag-Erling Smørgrav int remote_id; 2661af12a3e7SDag-Erling Smørgrav 2662af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 2663af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 2664af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 2665af12a3e7SDag-Erling Smørgrav 2666af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 2667af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 2668af12a3e7SDag-Erling Smørgrav } else { 2669af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 2670af12a3e7SDag-Erling Smørgrav } 2671af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2672*a0ee8cc6SDag-Erling Smørgrav c = channel_connect_to_port(host, host_port, 2673d4af9e69SDag-Erling Smørgrav "connected socket", originator_string); 2674e4a9863fSDag-Erling Smørgrav free(originator_string); 2675e4a9863fSDag-Erling Smørgrav free(host); 2676221552e4SDag-Erling Smørgrav if (c == NULL) { 2677af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 2678af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 2679af12a3e7SDag-Erling Smørgrav packet_send(); 2680d4af9e69SDag-Erling Smørgrav } else 2681d4af9e69SDag-Erling Smørgrav c->remote_id = remote_id; 2682af12a3e7SDag-Erling Smørgrav } 2683af12a3e7SDag-Erling Smørgrav 2684d4af9e69SDag-Erling Smørgrav /* ARGSUSED */ 2685d4af9e69SDag-Erling Smørgrav void 2686d4af9e69SDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 2687d4af9e69SDag-Erling Smørgrav { 2688d4af9e69SDag-Erling Smørgrav Channel *c; 2689d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 2690cce7d346SDag-Erling Smørgrav int id; 2691d4af9e69SDag-Erling Smørgrav 2692d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 26937aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 2694d4af9e69SDag-Erling Smørgrav 2695cce7d346SDag-Erling Smørgrav id = packet_get_int(); 2696d4af9e69SDag-Erling Smørgrav packet_check_eom(); 2697d4af9e69SDag-Erling Smørgrav 2698cce7d346SDag-Erling Smørgrav debug2("channel_input_status_confirm: type %d id %d", type, id); 2699d4af9e69SDag-Erling Smørgrav 2700cce7d346SDag-Erling Smørgrav if ((c = channel_lookup(id)) == NULL) { 2701cce7d346SDag-Erling Smørgrav logit("channel_input_status_confirm: %d: unknown", id); 2702d4af9e69SDag-Erling Smørgrav return; 2703d4af9e69SDag-Erling Smørgrav } 2704d4af9e69SDag-Erling Smørgrav ; 2705d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 2706d4af9e69SDag-Erling Smørgrav return; 2707d4af9e69SDag-Erling Smørgrav cc->cb(type, c, cc->ctx); 2708d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 2709b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 2710e4a9863fSDag-Erling Smørgrav free(cc); 2711d4af9e69SDag-Erling Smørgrav } 2712af12a3e7SDag-Erling Smørgrav 2713af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 2714511b41d2SMark Murray 2715511b41d2SMark Murray void 2716af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 2717511b41d2SMark Murray { 2718af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 2719511b41d2SMark Murray } 2720511b41d2SMark Murray 272189986192SBrooks Davis 2722462c32cbSDag-Erling Smørgrav /* 2723462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 2724462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 2725462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 2726462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 2727462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 2728462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 2729462c32cbSDag-Erling Smørgrav * 2730462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 2731462c32cbSDag-Erling Smørgrav * 2732462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 2733462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 2734462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 2735*a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 2736462c32cbSDag-Erling Smørgrav */ 2737462c32cbSDag-Erling Smørgrav static const char * 2738462c32cbSDag-Erling Smørgrav channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, 2739*a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 2740462c32cbSDag-Erling Smørgrav { 2741462c32cbSDag-Erling Smørgrav const char *addr = NULL; 2742462c32cbSDag-Erling Smørgrav int wildcard = 0; 2743462c32cbSDag-Erling Smørgrav 2744462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 2745462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 2746*a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 2747462c32cbSDag-Erling Smørgrav wildcard = 1; 2748*a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 2749462c32cbSDag-Erling Smørgrav if (((datafellows & SSH_OLD_FORWARD_ADDR) && 2750462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 2751462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 2752*a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 2753462c32cbSDag-Erling Smørgrav wildcard = 1; 2754f7167e0eSDag-Erling Smørgrav /* 2755f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 2756f7167e0eSDag-Erling Smørgrav * address and it was overridden. 2757f7167e0eSDag-Erling Smørgrav */ 2758f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 2759f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 2760f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 2761f7167e0eSDag-Erling Smørgrav packet_send_debug("Forwarding listen address " 2762f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 2763f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 2764f7167e0eSDag-Erling Smørgrav } 2765*a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 2766*a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 2767*a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 2768*a0ee8cc6SDag-Erling Smørgrav /* Accept localhost address when GatewayPorts=yes */ 2769*a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 2770f7167e0eSDag-Erling Smørgrav } 2771*a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 2772*a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 2773*a0ee8cc6SDag-Erling Smørgrav /* 2774*a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 2775*a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 2776*a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 2777*a0ee8cc6SDag-Erling Smørgrav */ 2778462c32cbSDag-Erling Smørgrav addr = listen_addr; 2779462c32cbSDag-Erling Smørgrav } 2780462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 2781462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 2782462c32cbSDag-Erling Smørgrav return addr; 2783462c32cbSDag-Erling Smørgrav } 2784462c32cbSDag-Erling Smørgrav 2785af12a3e7SDag-Erling Smørgrav static int 2786*a0ee8cc6SDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, 2787*a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 2788511b41d2SMark Murray { 2789af12a3e7SDag-Erling Smørgrav Channel *c; 2790b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 2791511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 2792aa49c926SDag-Erling Smørgrav const char *host, *addr; 2793af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 2794cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 2795511b41d2SMark Murray 2796af12a3e7SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 2797*a0ee8cc6SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 2798aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 2799511b41d2SMark Murray 2800af12a3e7SDag-Erling Smørgrav if (host == NULL) { 2801af12a3e7SDag-Erling Smørgrav error("No forward host name."); 2802d4ecd108SDag-Erling Smørgrav return 0; 2803ca3176e7SBrian Feldman } 2804cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 2805ca3176e7SBrian Feldman error("Forward host name too long."); 2806d4ecd108SDag-Erling Smørgrav return 0; 2807ca3176e7SBrian Feldman } 2808ca3176e7SBrian Feldman 2809462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 2810*a0ee8cc6SDag-Erling Smørgrav addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, 2811*a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 2812*a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d wildcard %d addr %s", __func__, 2813aa49c926SDag-Erling Smørgrav type, wildcard, (addr == NULL) ? "NULL" : addr); 2814aa49c926SDag-Erling Smørgrav 2815aa49c926SDag-Erling Smørgrav /* 2816511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 2817511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 2818511b41d2SMark Murray */ 2819511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 2820511b41d2SMark Murray hints.ai_family = IPv4or6; 2821aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 2822511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 2823*a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 2824aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 2825aa49c926SDag-Erling Smørgrav if (addr == NULL) { 2826aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 2827aa49c926SDag-Erling Smørgrav packet_disconnect("getaddrinfo: fatal error: %s", 2828d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 2829aa49c926SDag-Erling Smørgrav } else { 2830*a0ee8cc6SDag-Erling Smørgrav error("%s: getaddrinfo(%.64s): %s", __func__, addr, 2831d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 2832aa49c926SDag-Erling Smørgrav } 2833d4ecd108SDag-Erling Smørgrav return 0; 2834aa49c926SDag-Erling Smørgrav } 2835cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 2836cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 2837511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 2838cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 2839cce7d346SDag-Erling Smørgrav case AF_INET: 2840cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 2841cce7d346SDag-Erling Smørgrav sin_port; 2842cce7d346SDag-Erling Smørgrav break; 2843cce7d346SDag-Erling Smørgrav case AF_INET6: 2844cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 2845cce7d346SDag-Erling Smørgrav sin6_port; 2846cce7d346SDag-Erling Smørgrav break; 2847cce7d346SDag-Erling Smørgrav default: 2848511b41d2SMark Murray continue; 2849cce7d346SDag-Erling Smørgrav } 2850cce7d346SDag-Erling Smørgrav /* 2851cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 2852cce7d346SDag-Erling Smørgrav * same port for all address families. 2853cce7d346SDag-Erling Smørgrav */ 2854*a0ee8cc6SDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 2855cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && *allocated_listen_port > 0) 2856cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 2857cce7d346SDag-Erling Smørgrav 2858511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 2859511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 2860*a0ee8cc6SDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 2861511b41d2SMark Murray continue; 2862511b41d2SMark Murray } 2863511b41d2SMark Murray /* Create a port to listen for the host. */ 2864221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 2865511b41d2SMark Murray if (sock < 0) { 2866511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 2867511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 2868511b41d2SMark Murray continue; 2869511b41d2SMark Murray } 2870b74df5b2SDag-Erling Smørgrav 2871b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 2872b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 2873b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 2874f388f5efSDag-Erling Smørgrav 2875cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 2876cce7d346SDag-Erling Smørgrav ntop, strport); 2877511b41d2SMark Murray 2878511b41d2SMark Murray /* Bind the socket to the address. */ 2879511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 2880511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 2881989dd127SDag-Erling Smørgrav if (!ai->ai_next) 2882989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 2883989dd127SDag-Erling Smørgrav else 2884511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 2885989dd127SDag-Erling Smørgrav 2886511b41d2SMark Murray close(sock); 2887511b41d2SMark Murray continue; 2888511b41d2SMark Murray } 2889511b41d2SMark Murray /* Start listening for connections on the socket. */ 2890476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 2891511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 2892511b41d2SMark Murray close(sock); 2893511b41d2SMark Murray continue; 2894511b41d2SMark Murray } 2895cce7d346SDag-Erling Smørgrav 2896cce7d346SDag-Erling Smørgrav /* 2897*a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 2898cce7d346SDag-Erling Smørgrav * record what we got. 2899cce7d346SDag-Erling Smørgrav */ 2900*a0ee8cc6SDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 2901cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 2902cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 2903cce7d346SDag-Erling Smørgrav *allocated_listen_port = get_sock_port(sock, 1); 2904cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 2905cce7d346SDag-Erling Smørgrav *allocated_listen_port); 2906cce7d346SDag-Erling Smørgrav } 2907cce7d346SDag-Erling Smørgrav 290860c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 2909af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 2910a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2911221552e4SDag-Erling Smørgrav 0, "port listener", 1); 2912cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 2913*a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 2914462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 2915*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 2916462c32cbSDag-Erling Smørgrav !(datafellows & SSH_BUG_DYNAMIC_RPORT)) 2917462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 2918462c32cbSDag-Erling Smørgrav else 2919*a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 2920511b41d2SMark Murray success = 1; 2921511b41d2SMark Murray } 2922511b41d2SMark Murray if (success == 0) 2923*a0ee8cc6SDag-Erling Smørgrav error("%s: cannot listen to port: %d", __func__, 2924*a0ee8cc6SDag-Erling Smørgrav fwd->listen_port); 2925511b41d2SMark Murray freeaddrinfo(aitop); 2926ca3176e7SBrian Feldman return success; 2927511b41d2SMark Murray } 2928511b41d2SMark Murray 2929*a0ee8cc6SDag-Erling Smørgrav static int 2930*a0ee8cc6SDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, 2931*a0ee8cc6SDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 2932*a0ee8cc6SDag-Erling Smørgrav { 2933*a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 2934*a0ee8cc6SDag-Erling Smørgrav const char *path; 2935*a0ee8cc6SDag-Erling Smørgrav Channel *c; 2936*a0ee8cc6SDag-Erling Smørgrav int port, sock; 2937*a0ee8cc6SDag-Erling Smørgrav mode_t omask; 2938*a0ee8cc6SDag-Erling Smørgrav 2939*a0ee8cc6SDag-Erling Smørgrav switch (type) { 2940*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 2941*a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 2942*a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 2943*a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 2944*a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 2945*a0ee8cc6SDag-Erling Smørgrav return 0; 2946*a0ee8cc6SDag-Erling Smørgrav } 2947*a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 2948*a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 2949*a0ee8cc6SDag-Erling Smørgrav } else { 2950*a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 2951*a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 2952*a0ee8cc6SDag-Erling Smørgrav return 0; 2953*a0ee8cc6SDag-Erling Smørgrav } 2954*a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 2955*a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 2956*a0ee8cc6SDag-Erling Smørgrav return 0; 2957*a0ee8cc6SDag-Erling Smørgrav } 2958*a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 2959*a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 2960*a0ee8cc6SDag-Erling Smørgrav } 2961*a0ee8cc6SDag-Erling Smørgrav break; 2962*a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 2963*a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 2964*a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 2965*a0ee8cc6SDag-Erling Smørgrav break; 2966*a0ee8cc6SDag-Erling Smørgrav default: 2967*a0ee8cc6SDag-Erling Smørgrav error("%s: unexpected channel type %d", __func__, type); 2968*a0ee8cc6SDag-Erling Smørgrav return 0; 2969*a0ee8cc6SDag-Erling Smørgrav } 2970*a0ee8cc6SDag-Erling Smørgrav 2971*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 2972*a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 2973*a0ee8cc6SDag-Erling Smørgrav return 0; 2974*a0ee8cc6SDag-Erling Smørgrav } 2975*a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 2976*a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 2977*a0ee8cc6SDag-Erling Smørgrav return 0; 2978*a0ee8cc6SDag-Erling Smørgrav } 2979*a0ee8cc6SDag-Erling Smørgrav 2980*a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d path %s", __func__, type, fwd->listen_path); 2981*a0ee8cc6SDag-Erling Smørgrav 2982*a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 2983*a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 2984*a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 2985*a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 2986*a0ee8cc6SDag-Erling Smørgrav umask(omask); 2987*a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 2988*a0ee8cc6SDag-Erling Smørgrav return 0; 2989*a0ee8cc6SDag-Erling Smørgrav 2990*a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 2991*a0ee8cc6SDag-Erling Smørgrav 2992*a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 2993*a0ee8cc6SDag-Erling Smørgrav c = channel_new("unix listener", type, sock, sock, -1, 2994*a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 2995*a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 2996*a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 2997*a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 2998*a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 2999*a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 3000*a0ee8cc6SDag-Erling Smørgrav return 1; 3001*a0ee8cc6SDag-Erling Smørgrav } 3002*a0ee8cc6SDag-Erling Smørgrav 3003*a0ee8cc6SDag-Erling Smørgrav static int 3004*a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(const char *host, u_short port) 300521e764dfSDag-Erling Smørgrav { 300621e764dfSDag-Erling Smørgrav u_int i; 300721e764dfSDag-Erling Smørgrav int found = 0; 300821e764dfSDag-Erling Smørgrav 300921e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 301021e764dfSDag-Erling Smørgrav Channel *c = channels[i]; 3011462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3012462c32cbSDag-Erling Smørgrav continue; 3013462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 3014462c32cbSDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3015462c32cbSDag-Erling Smørgrav channel_free(c); 3016462c32cbSDag-Erling Smørgrav found = 1; 3017462c32cbSDag-Erling Smørgrav } 3018462c32cbSDag-Erling Smørgrav } 301921e764dfSDag-Erling Smørgrav 3020462c32cbSDag-Erling Smørgrav return (found); 3021462c32cbSDag-Erling Smørgrav } 3022462c32cbSDag-Erling Smørgrav 3023*a0ee8cc6SDag-Erling Smørgrav static int 3024*a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(const char *path) 3025462c32cbSDag-Erling Smørgrav { 3026462c32cbSDag-Erling Smørgrav u_int i; 3027462c32cbSDag-Erling Smørgrav int found = 0; 3028*a0ee8cc6SDag-Erling Smørgrav 3029*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3030*a0ee8cc6SDag-Erling Smørgrav Channel *c = channels[i]; 3031*a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3032*a0ee8cc6SDag-Erling Smørgrav continue; 3033*a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 3034*a0ee8cc6SDag-Erling Smørgrav continue; 3035*a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 3036*a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3037*a0ee8cc6SDag-Erling Smørgrav channel_free(c); 3038*a0ee8cc6SDag-Erling Smørgrav found = 1; 3039*a0ee8cc6SDag-Erling Smørgrav } 3040*a0ee8cc6SDag-Erling Smørgrav } 3041*a0ee8cc6SDag-Erling Smørgrav 3042*a0ee8cc6SDag-Erling Smørgrav return (found); 3043*a0ee8cc6SDag-Erling Smørgrav } 3044*a0ee8cc6SDag-Erling Smørgrav 3045*a0ee8cc6SDag-Erling Smørgrav int 3046*a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener(struct Forward *fwd) 3047*a0ee8cc6SDag-Erling Smørgrav { 3048*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) 3049*a0ee8cc6SDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(fwd->listen_path); 3050*a0ee8cc6SDag-Erling Smørgrav else 3051*a0ee8cc6SDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); 3052*a0ee8cc6SDag-Erling Smørgrav } 3053*a0ee8cc6SDag-Erling Smørgrav 3054*a0ee8cc6SDag-Erling Smørgrav static int 3055*a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, 3056*a0ee8cc6SDag-Erling Smørgrav int cport, struct ForwardOptions *fwd_opts) 3057*a0ee8cc6SDag-Erling Smørgrav { 3058*a0ee8cc6SDag-Erling Smørgrav u_int i; 3059*a0ee8cc6SDag-Erling Smørgrav int found = 0; 3060*a0ee8cc6SDag-Erling Smørgrav const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); 3061462c32cbSDag-Erling Smørgrav 3062462c32cbSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3063462c32cbSDag-Erling Smørgrav Channel *c = channels[i]; 3064462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 3065462c32cbSDag-Erling Smørgrav continue; 3066462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 3067462c32cbSDag-Erling Smørgrav continue; 3068462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 3069462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 3070462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 3071462c32cbSDag-Erling Smørgrav continue; 3072462c32cbSDag-Erling Smørgrav } else { 3073462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 3074462c32cbSDag-Erling Smørgrav continue; 3075462c32cbSDag-Erling Smørgrav } 3076462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 3077462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 3078462c32cbSDag-Erling Smørgrav continue; 3079462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 3080aa49c926SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 308121e764dfSDag-Erling Smørgrav channel_free(c); 308221e764dfSDag-Erling Smørgrav found = 1; 308321e764dfSDag-Erling Smørgrav } 308421e764dfSDag-Erling Smørgrav } 308521e764dfSDag-Erling Smørgrav 308621e764dfSDag-Erling Smørgrav return (found); 308721e764dfSDag-Erling Smørgrav } 308821e764dfSDag-Erling Smørgrav 3089*a0ee8cc6SDag-Erling Smørgrav static int 3090*a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(const char *path) 3091*a0ee8cc6SDag-Erling Smørgrav { 3092*a0ee8cc6SDag-Erling Smørgrav u_int i; 3093*a0ee8cc6SDag-Erling Smørgrav int found = 0; 3094*a0ee8cc6SDag-Erling Smørgrav 3095*a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 3096*a0ee8cc6SDag-Erling Smørgrav error("%s: no path specified.", __func__); 3097*a0ee8cc6SDag-Erling Smørgrav return 0; 3098*a0ee8cc6SDag-Erling Smørgrav } 3099*a0ee8cc6SDag-Erling Smørgrav 3100*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3101*a0ee8cc6SDag-Erling Smørgrav Channel *c = channels[i]; 3102*a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 3103*a0ee8cc6SDag-Erling Smørgrav continue; 3104*a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 3105*a0ee8cc6SDag-Erling Smørgrav continue; 3106*a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 3107*a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3108*a0ee8cc6SDag-Erling Smørgrav channel_free(c); 3109*a0ee8cc6SDag-Erling Smørgrav found = 1; 3110*a0ee8cc6SDag-Erling Smørgrav } 3111*a0ee8cc6SDag-Erling Smørgrav } 3112*a0ee8cc6SDag-Erling Smørgrav 3113*a0ee8cc6SDag-Erling Smørgrav return (found); 3114*a0ee8cc6SDag-Erling Smørgrav } 3115*a0ee8cc6SDag-Erling Smørgrav 3116*a0ee8cc6SDag-Erling Smørgrav int 3117*a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 3118*a0ee8cc6SDag-Erling Smørgrav { 3119*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) 3120*a0ee8cc6SDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(fwd->listen_path); 3121*a0ee8cc6SDag-Erling Smørgrav else 3122*a0ee8cc6SDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); 3123*a0ee8cc6SDag-Erling Smørgrav } 3124*a0ee8cc6SDag-Erling Smørgrav 3125af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 3126af12a3e7SDag-Erling Smørgrav int 3127*a0ee8cc6SDag-Erling Smørgrav channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) 3128af12a3e7SDag-Erling Smørgrav { 3129*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3130*a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal( 3131*a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 3132*a0ee8cc6SDag-Erling Smørgrav } else { 3133*a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, 3134*a0ee8cc6SDag-Erling Smørgrav fwd, NULL, fwd_opts); 3135*a0ee8cc6SDag-Erling Smørgrav } 3136af12a3e7SDag-Erling Smørgrav } 3137af12a3e7SDag-Erling Smørgrav 3138af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 3139af12a3e7SDag-Erling Smørgrav int 3140*a0ee8cc6SDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct Forward *fwd, 3141*a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 3142af12a3e7SDag-Erling Smørgrav { 3143*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3144*a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal( 3145*a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 3146*a0ee8cc6SDag-Erling Smørgrav } else { 3147*a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip( 3148*a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 3149*a0ee8cc6SDag-Erling Smørgrav fwd_opts); 3150*a0ee8cc6SDag-Erling Smørgrav } 3151af12a3e7SDag-Erling Smørgrav } 3152af12a3e7SDag-Erling Smørgrav 3153511b41d2SMark Murray /* 3154462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 3155462c32cbSDag-Erling Smørgrav * this server. 3156462c32cbSDag-Erling Smørgrav */ 3157462c32cbSDag-Erling Smørgrav static const char * 3158462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 3159462c32cbSDag-Erling Smørgrav { 3160462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 3161462c32cbSDag-Erling Smørgrav if (datafellows & SSH_BUG_RFWD_ADDR) 3162462c32cbSDag-Erling Smørgrav return "127.0.0.1"; 3163462c32cbSDag-Erling Smørgrav else 3164462c32cbSDag-Erling Smørgrav return "localhost"; 3165462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 3166462c32cbSDag-Erling Smørgrav if (datafellows & SSH_BUG_RFWD_ADDR) 3167462c32cbSDag-Erling Smørgrav return "0.0.0.0"; 3168462c32cbSDag-Erling Smørgrav else 3169462c32cbSDag-Erling Smørgrav return ""; 3170462c32cbSDag-Erling Smørgrav } else 3171462c32cbSDag-Erling Smørgrav return listen_host; 3172462c32cbSDag-Erling Smørgrav } 3173462c32cbSDag-Erling Smørgrav 3174462c32cbSDag-Erling Smørgrav /* 3175511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 3176511b41d2SMark Murray * the secure channel to host:port from local side. 3177462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 3178462c32cbSDag-Erling Smørgrav * channel_update_permitted_opens(). 3179511b41d2SMark Murray */ 3180333ee039SDag-Erling Smørgrav int 3181*a0ee8cc6SDag-Erling Smørgrav channel_request_remote_forwarding(struct Forward *fwd) 3182511b41d2SMark Murray { 3183462c32cbSDag-Erling Smørgrav int type, success = 0, idx = -1; 3184ca3176e7SBrian Feldman 3185511b41d2SMark Murray /* Send the forward request to the remote side. */ 3186a04a10f8SKris Kennaway if (compat20) { 3187a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 3188*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3189*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring("streamlocal-forward@openssh.com"); 3190*a0ee8cc6SDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 3191*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(fwd->listen_path); 3192*a0ee8cc6SDag-Erling Smørgrav } else { 3193a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 319480628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 3195*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); 3196*a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->listen_port); 3197*a0ee8cc6SDag-Erling Smørgrav } 3198ca3176e7SBrian Feldman packet_send(); 3199ca3176e7SBrian Feldman packet_write_wait(); 3200ca3176e7SBrian Feldman /* Assume that server accepts the request */ 3201ca3176e7SBrian Feldman success = 1; 3202*a0ee8cc6SDag-Erling Smørgrav } else if (fwd->listen_path == NULL) { 3203511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 3204*a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->listen_port); 3205*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(fwd->connect_host); 3206*a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->connect_port); 3207511b41d2SMark Murray packet_send(); 3208511b41d2SMark Murray packet_write_wait(); 3209ca3176e7SBrian Feldman 3210ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 3211af12a3e7SDag-Erling Smørgrav type = packet_read(); 3212ca3176e7SBrian Feldman switch (type) { 3213ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 3214ca3176e7SBrian Feldman success = 1; 3215ca3176e7SBrian Feldman break; 3216ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 3217ca3176e7SBrian Feldman break; 3218ca3176e7SBrian Feldman default: 3219ca3176e7SBrian Feldman /* Unknown packet */ 3220ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 3221ca3176e7SBrian Feldman "received packet type %d.", type); 3222ca3176e7SBrian Feldman } 3223*a0ee8cc6SDag-Erling Smørgrav } else { 3224*a0ee8cc6SDag-Erling Smørgrav logit("Warning: Server does not support remote stream local forwarding."); 3225ca3176e7SBrian Feldman } 3226ca3176e7SBrian Feldman if (success) { 3227e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 3228e2f6069cSDag-Erling Smørgrav permitted_opens = xrealloc(permitted_opens, 3229e2f6069cSDag-Erling Smørgrav num_permitted_opens + 1, sizeof(*permitted_opens)); 3230462c32cbSDag-Erling Smørgrav idx = num_permitted_opens++; 3231*a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3232*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].host_to_connect = 3233*a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->connect_path); 3234*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 3235*a0ee8cc6SDag-Erling Smørgrav PORT_STREAMLOCAL; 3236*a0ee8cc6SDag-Erling Smørgrav } else { 3237*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].host_to_connect = 3238*a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->connect_host); 3239*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 3240*a0ee8cc6SDag-Erling Smørgrav fwd->connect_port; 3241*a0ee8cc6SDag-Erling Smørgrav } 3242*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3243*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = NULL; 3244*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = 3245*a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->listen_path); 3246*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_port = PORT_STREAMLOCAL; 3247*a0ee8cc6SDag-Erling Smørgrav } else { 3248*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = 3249*a0ee8cc6SDag-Erling Smørgrav fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; 3250*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = NULL; 3251*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_port = fwd->listen_port; 3252*a0ee8cc6SDag-Erling Smørgrav } 3253511b41d2SMark Murray } 3254462c32cbSDag-Erling Smørgrav return (idx); 3255a04a10f8SKris Kennaway } 3256511b41d2SMark Murray 3257*a0ee8cc6SDag-Erling Smørgrav static int 3258*a0ee8cc6SDag-Erling Smørgrav open_match(ForwardPermission *allowed_open, const char *requestedhost, 3259*a0ee8cc6SDag-Erling Smørgrav int requestedport) 3260*a0ee8cc6SDag-Erling Smørgrav { 3261*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3262*a0ee8cc6SDag-Erling Smørgrav return 0; 3263*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 3264*a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 3265*a0ee8cc6SDag-Erling Smørgrav return 0; 3266*a0ee8cc6SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, requestedhost) != 0) 3267*a0ee8cc6SDag-Erling Smørgrav return 0; 3268*a0ee8cc6SDag-Erling Smørgrav return 1; 3269*a0ee8cc6SDag-Erling Smørgrav } 3270*a0ee8cc6SDag-Erling Smørgrav 3271*a0ee8cc6SDag-Erling Smørgrav /* 3272*a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 3273*a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 3274*a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 3275*a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 3276*a0ee8cc6SDag-Erling Smørgrav */ 3277*a0ee8cc6SDag-Erling Smørgrav static int 3278*a0ee8cc6SDag-Erling Smørgrav open_listen_match_tcpip(ForwardPermission *allowed_open, 3279*a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 3280*a0ee8cc6SDag-Erling Smørgrav { 3281*a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 3282*a0ee8cc6SDag-Erling Smørgrav 3283*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3284*a0ee8cc6SDag-Erling Smørgrav return 0; 3285*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 3286*a0ee8cc6SDag-Erling Smørgrav return 0; 3287*a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 3288*a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 3289*a0ee8cc6SDag-Erling Smørgrav return 1; 3290*a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 3291*a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 3292*a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 3293*a0ee8cc6SDag-Erling Smørgrav if (allowed_host == NULL || 3294*a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 3295*a0ee8cc6SDag-Erling Smørgrav return 0; 3296*a0ee8cc6SDag-Erling Smørgrav return 1; 3297*a0ee8cc6SDag-Erling Smørgrav } 3298*a0ee8cc6SDag-Erling Smørgrav 3299*a0ee8cc6SDag-Erling Smørgrav static int 3300*a0ee8cc6SDag-Erling Smørgrav open_listen_match_streamlocal(ForwardPermission *allowed_open, 3301*a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 3302*a0ee8cc6SDag-Erling Smørgrav { 3303*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3304*a0ee8cc6SDag-Erling Smørgrav return 0; 3305*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 3306*a0ee8cc6SDag-Erling Smørgrav return 0; 3307*a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 3308*a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 3309*a0ee8cc6SDag-Erling Smørgrav return 0; 3310*a0ee8cc6SDag-Erling Smørgrav return 1; 3311*a0ee8cc6SDag-Erling Smørgrav } 3312*a0ee8cc6SDag-Erling Smørgrav 3313511b41d2SMark Murray /* 331421e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 331521e764dfSDag-Erling Smørgrav * local side. 331621e764dfSDag-Erling Smørgrav */ 3317*a0ee8cc6SDag-Erling Smørgrav static int 3318*a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(const char *host, u_short port) 331921e764dfSDag-Erling Smørgrav { 332021e764dfSDag-Erling Smørgrav int i; 332121e764dfSDag-Erling Smørgrav 332221e764dfSDag-Erling Smørgrav if (!compat20) 3323462c32cbSDag-Erling Smørgrav return -1; 332421e764dfSDag-Erling Smørgrav 332521e764dfSDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3326*a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) 332721e764dfSDag-Erling Smørgrav break; 332821e764dfSDag-Erling Smørgrav } 332921e764dfSDag-Erling Smørgrav if (i >= num_permitted_opens) { 333021e764dfSDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3331462c32cbSDag-Erling Smørgrav return -1; 333221e764dfSDag-Erling Smørgrav } 333321e764dfSDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 333421e764dfSDag-Erling Smørgrav packet_put_cstring("cancel-tcpip-forward"); 333521e764dfSDag-Erling Smørgrav packet_put_char(0); 3336462c32cbSDag-Erling Smørgrav packet_put_cstring(channel_rfwd_bind_host(host)); 333721e764dfSDag-Erling Smørgrav packet_put_int(port); 333821e764dfSDag-Erling Smørgrav packet_send(); 333921e764dfSDag-Erling Smørgrav 334021e764dfSDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 334121e764dfSDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 3342e4a9863fSDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 334321e764dfSDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 3344*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_host); 3345*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_host = NULL; 3346*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_path = NULL; 3347462c32cbSDag-Erling Smørgrav 3348462c32cbSDag-Erling Smørgrav return 0; 334921e764dfSDag-Erling Smørgrav } 335021e764dfSDag-Erling Smørgrav 335121e764dfSDag-Erling Smørgrav /* 3352*a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 3353*a0ee8cc6SDag-Erling Smørgrav * path from local side. 3354*a0ee8cc6SDag-Erling Smørgrav */ 3355*a0ee8cc6SDag-Erling Smørgrav static int 3356*a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(const char *path) 3357*a0ee8cc6SDag-Erling Smørgrav { 3358*a0ee8cc6SDag-Erling Smørgrav int i; 3359*a0ee8cc6SDag-Erling Smørgrav 3360*a0ee8cc6SDag-Erling Smørgrav if (!compat20) 3361*a0ee8cc6SDag-Erling Smørgrav return -1; 3362*a0ee8cc6SDag-Erling Smørgrav 3363*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3364*a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_streamlocal(&permitted_opens[i], path)) 3365*a0ee8cc6SDag-Erling Smørgrav break; 3366*a0ee8cc6SDag-Erling Smørgrav } 3367*a0ee8cc6SDag-Erling Smørgrav if (i >= num_permitted_opens) { 3368*a0ee8cc6SDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3369*a0ee8cc6SDag-Erling Smørgrav return -1; 3370*a0ee8cc6SDag-Erling Smørgrav } 3371*a0ee8cc6SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 3372*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring("cancel-streamlocal-forward@openssh.com"); 3373*a0ee8cc6SDag-Erling Smørgrav packet_put_char(0); 3374*a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(path); 3375*a0ee8cc6SDag-Erling Smørgrav packet_send(); 3376*a0ee8cc6SDag-Erling Smørgrav 3377*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 3378*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 3379*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 3380*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 3381*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_host = NULL; 3382*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_path); 3383*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_path = NULL; 3384*a0ee8cc6SDag-Erling Smørgrav 3385*a0ee8cc6SDag-Erling Smørgrav return 0; 3386*a0ee8cc6SDag-Erling Smørgrav } 3387*a0ee8cc6SDag-Erling Smørgrav 3388*a0ee8cc6SDag-Erling Smørgrav /* 3389*a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 3390*a0ee8cc6SDag-Erling Smørgrav */ 3391*a0ee8cc6SDag-Erling Smørgrav int 3392*a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel(struct Forward *fwd) 3393*a0ee8cc6SDag-Erling Smørgrav { 3394*a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3395*a0ee8cc6SDag-Erling Smørgrav return (channel_request_rforward_cancel_streamlocal( 3396*a0ee8cc6SDag-Erling Smørgrav fwd->listen_path)); 3397*a0ee8cc6SDag-Erling Smørgrav } else { 3398*a0ee8cc6SDag-Erling Smørgrav return (channel_request_rforward_cancel_tcpip(fwd->listen_host, 3399*a0ee8cc6SDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); 3400*a0ee8cc6SDag-Erling Smørgrav } 3401*a0ee8cc6SDag-Erling Smørgrav } 3402*a0ee8cc6SDag-Erling Smørgrav 3403*a0ee8cc6SDag-Erling Smørgrav /* 3404511b41d2SMark Murray * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates 3405511b41d2SMark Murray * listening for the port, and sends back a success reply (or disconnect 3406333ee039SDag-Erling Smørgrav * message if there was an error). 3407511b41d2SMark Murray */ 3408333ee039SDag-Erling Smørgrav int 3409*a0ee8cc6SDag-Erling Smørgrav channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) 3410511b41d2SMark Murray { 3411333ee039SDag-Erling Smørgrav int success = 0; 3412*a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 3413511b41d2SMark Murray 3414511b41d2SMark Murray /* Get arguments from the packet. */ 3415*a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 3416*a0ee8cc6SDag-Erling Smørgrav fwd.listen_port = packet_get_int(); 3417*a0ee8cc6SDag-Erling Smørgrav fwd.connect_host = packet_get_string(NULL); 3418*a0ee8cc6SDag-Erling Smørgrav fwd.connect_port = packet_get_int(); 3419511b41d2SMark Murray 3420989dd127SDag-Erling Smørgrav #ifndef HAVE_CYGWIN 3421511b41d2SMark Murray /* 3422511b41d2SMark Murray * Check that an unprivileged user is not trying to forward a 3423511b41d2SMark Murray * privileged port. 3424511b41d2SMark Murray */ 3425*a0ee8cc6SDag-Erling Smørgrav if (fwd.listen_port < IPPORT_RESERVED && !is_root) 3426221552e4SDag-Erling Smørgrav packet_disconnect( 3427221552e4SDag-Erling Smørgrav "Requested forwarding of port %d but user is not root.", 3428*a0ee8cc6SDag-Erling Smørgrav fwd.listen_port); 3429*a0ee8cc6SDag-Erling Smørgrav if (fwd.connect_port == 0) 3430221552e4SDag-Erling Smørgrav packet_disconnect("Dynamic forwarding denied."); 3431989dd127SDag-Erling Smørgrav #endif 3432221552e4SDag-Erling Smørgrav 3433ca3176e7SBrian Feldman /* Initiate forwarding */ 3434*a0ee8cc6SDag-Erling Smørgrav success = channel_setup_local_fwd_listener(&fwd, fwd_opts); 3435511b41d2SMark Murray 3436511b41d2SMark Murray /* Free the argument string. */ 3437*a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_host); 3438333ee039SDag-Erling Smørgrav 3439333ee039SDag-Erling Smørgrav return (success ? 0 : -1); 3440511b41d2SMark Murray } 3441511b41d2SMark Murray 3442ca3176e7SBrian Feldman /* 3443ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 3444ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 3445ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 3446ca3176e7SBrian Feldman */ 3447ca3176e7SBrian Feldman void 3448af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 3449ca3176e7SBrian Feldman { 3450ca3176e7SBrian Feldman if (num_permitted_opens == 0) 3451ca3176e7SBrian Feldman all_opens_permitted = 1; 3452ca3176e7SBrian Feldman } 3453ca3176e7SBrian Feldman 3454ca3176e7SBrian Feldman void 3455ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 3456ca3176e7SBrian Feldman { 3457ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 3458ca3176e7SBrian Feldman 3459e2f6069cSDag-Erling Smørgrav permitted_opens = xrealloc(permitted_opens, 3460e2f6069cSDag-Erling Smørgrav num_permitted_opens + 1, sizeof(*permitted_opens)); 3461ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 3462ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 3463*a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_host = NULL; 3464*a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_path = NULL; 3465*a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_port = 0; 3466ca3176e7SBrian Feldman num_permitted_opens++; 3467ca3176e7SBrian Feldman 3468ca3176e7SBrian Feldman all_opens_permitted = 0; 3469ca3176e7SBrian Feldman } 3470ca3176e7SBrian Feldman 3471462c32cbSDag-Erling Smørgrav /* 3472462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 3473462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 3474462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 3475462c32cbSDag-Erling Smørgrav */ 3476462c32cbSDag-Erling Smørgrav void 3477462c32cbSDag-Erling Smørgrav channel_update_permitted_opens(int idx, int newport) 3478462c32cbSDag-Erling Smørgrav { 3479462c32cbSDag-Erling Smørgrav if (idx < 0 || idx >= num_permitted_opens) { 3480462c32cbSDag-Erling Smørgrav debug("channel_update_permitted_opens: index out of range:" 3481462c32cbSDag-Erling Smørgrav " %d num_permitted_opens %d", idx, num_permitted_opens); 3482462c32cbSDag-Erling Smørgrav return; 3483462c32cbSDag-Erling Smørgrav } 3484462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 3485462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 3486462c32cbSDag-Erling Smørgrav newport, 3487462c32cbSDag-Erling Smørgrav permitted_opens[idx].host_to_connect, 3488462c32cbSDag-Erling Smørgrav permitted_opens[idx].port_to_connect); 3489462c32cbSDag-Erling Smørgrav if (newport >= 0) { 3490462c32cbSDag-Erling Smørgrav permitted_opens[idx].listen_port = 3491462c32cbSDag-Erling Smørgrav (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 3492462c32cbSDag-Erling Smørgrav } else { 3493462c32cbSDag-Erling Smørgrav permitted_opens[idx].listen_port = 0; 3494462c32cbSDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 0; 3495e4a9863fSDag-Erling Smørgrav free(permitted_opens[idx].host_to_connect); 3496462c32cbSDag-Erling Smørgrav permitted_opens[idx].host_to_connect = NULL; 3497*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[idx].listen_host); 3498*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = NULL; 3499*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[idx].listen_path); 3500*a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = NULL; 3501462c32cbSDag-Erling Smørgrav } 3502462c32cbSDag-Erling Smørgrav } 3503462c32cbSDag-Erling Smørgrav 3504333ee039SDag-Erling Smørgrav int 3505333ee039SDag-Erling Smørgrav channel_add_adm_permitted_opens(char *host, int port) 3506333ee039SDag-Erling Smørgrav { 3507333ee039SDag-Erling Smørgrav debug("config allows port forwarding to host %s port %d", host, port); 3508333ee039SDag-Erling Smørgrav 3509e2f6069cSDag-Erling Smørgrav permitted_adm_opens = xrealloc(permitted_adm_opens, 3510e2f6069cSDag-Erling Smørgrav num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); 3511333ee039SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].host_to_connect 3512333ee039SDag-Erling Smørgrav = xstrdup(host); 3513333ee039SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 3514*a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; 3515*a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; 3516*a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; 3517333ee039SDag-Erling Smørgrav return ++num_adm_permitted_opens; 3518333ee039SDag-Erling Smørgrav } 3519333ee039SDag-Erling Smørgrav 3520ca3176e7SBrian Feldman void 3521462c32cbSDag-Erling Smørgrav channel_disable_adm_local_opens(void) 3522462c32cbSDag-Erling Smørgrav { 35236888a9beSDag-Erling Smørgrav channel_clear_adm_permitted_opens(); 3524462c32cbSDag-Erling Smørgrav permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens)); 35256888a9beSDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; 3526462c32cbSDag-Erling Smørgrav num_adm_permitted_opens = 1; 3527462c32cbSDag-Erling Smørgrav } 3528462c32cbSDag-Erling Smørgrav 3529462c32cbSDag-Erling Smørgrav void 3530ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 3531ca3176e7SBrian Feldman { 3532ca3176e7SBrian Feldman int i; 3533ca3176e7SBrian Feldman 3534*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3535e4a9863fSDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 3536*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_host); 3537*a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_path); 3538*a0ee8cc6SDag-Erling Smørgrav } 3539e4a9863fSDag-Erling Smørgrav free(permitted_opens); 3540e2f6069cSDag-Erling Smørgrav permitted_opens = NULL; 3541ca3176e7SBrian Feldman num_permitted_opens = 0; 3542ca3176e7SBrian Feldman } 3543ca3176e7SBrian Feldman 3544333ee039SDag-Erling Smørgrav void 3545333ee039SDag-Erling Smørgrav channel_clear_adm_permitted_opens(void) 3546333ee039SDag-Erling Smørgrav { 3547333ee039SDag-Erling Smørgrav int i; 3548333ee039SDag-Erling Smørgrav 3549*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) { 3550e4a9863fSDag-Erling Smørgrav free(permitted_adm_opens[i].host_to_connect); 3551*a0ee8cc6SDag-Erling Smørgrav free(permitted_adm_opens[i].listen_host); 3552*a0ee8cc6SDag-Erling Smørgrav free(permitted_adm_opens[i].listen_path); 3553*a0ee8cc6SDag-Erling Smørgrav } 3554e4a9863fSDag-Erling Smørgrav free(permitted_adm_opens); 3555e2f6069cSDag-Erling Smørgrav permitted_adm_opens = NULL; 3556333ee039SDag-Erling Smørgrav num_adm_permitted_opens = 0; 3557333ee039SDag-Erling Smørgrav } 3558ca3176e7SBrian Feldman 3559d4af9e69SDag-Erling Smørgrav void 3560d4af9e69SDag-Erling Smørgrav channel_print_adm_permitted_opens(void) 3561511b41d2SMark Murray { 3562d4af9e69SDag-Erling Smørgrav int i; 3563511b41d2SMark Murray 3564cce7d346SDag-Erling Smørgrav printf("permitopen"); 3565cce7d346SDag-Erling Smørgrav if (num_adm_permitted_opens == 0) { 3566cce7d346SDag-Erling Smørgrav printf(" any\n"); 3567cce7d346SDag-Erling Smørgrav return; 3568cce7d346SDag-Erling Smørgrav } 3569d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 3570462c32cbSDag-Erling Smørgrav if (permitted_adm_opens[i].host_to_connect == NULL) 3571462c32cbSDag-Erling Smørgrav printf(" none"); 3572462c32cbSDag-Erling Smørgrav else 3573d4af9e69SDag-Erling Smørgrav printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 3574d4af9e69SDag-Erling Smørgrav permitted_adm_opens[i].port_to_connect); 3575cce7d346SDag-Erling Smørgrav printf("\n"); 3576511b41d2SMark Murray } 3577d4af9e69SDag-Erling Smørgrav 3578462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 3579462c32cbSDag-Erling Smørgrav int 3580462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 3581462c32cbSDag-Erling Smørgrav { 3582462c32cbSDag-Erling Smørgrav int port; 3583462c32cbSDag-Erling Smørgrav 3584462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 3585462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 3586462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 3587462c32cbSDag-Erling Smørgrav return port; 3588462c32cbSDag-Erling Smørgrav return -1; 3589462c32cbSDag-Erling Smørgrav } 3590462c32cbSDag-Erling Smørgrav 3591d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 3592d4af9e69SDag-Erling Smørgrav static int 3593d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 3594d4af9e69SDag-Erling Smørgrav { 3595d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 3596*a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 3597*a0ee8cc6SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; 3598d4af9e69SDag-Erling Smørgrav 3599d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 3600*a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 3601*a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 3602*a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 3603*a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 3604*a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 3605*a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 3606*a0ee8cc6SDag-Erling Smørgrav break; 3607*a0ee8cc6SDag-Erling Smørgrav case AF_INET: 3608*a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 3609d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 3610d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 3611d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3612d4af9e69SDag-Erling Smørgrav error("connect_next: getnameinfo failed"); 3613511b41d2SMark Murray continue; 3614511b41d2SMark Murray } 3615*a0ee8cc6SDag-Erling Smørgrav break; 3616*a0ee8cc6SDag-Erling Smørgrav default: 3617*a0ee8cc6SDag-Erling Smørgrav continue; 3618*a0ee8cc6SDag-Erling Smørgrav } 3619d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 3620d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 3621d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 3622511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 3623e73e9afaSDag-Erling Smørgrav else 3624e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 3625511b41d2SMark Murray continue; 3626511b41d2SMark Murray } 362721e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 362821e764dfSDag-Erling Smørgrav fatal("%s: set_nonblock(%d)", __func__, sock); 3629d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 3630d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 3631d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s): " 3632d4af9e69SDag-Erling Smørgrav "%.100s", cctx->host, ntop, strport, 3633511b41d2SMark Murray strerror(errno)); 3634d4af9e69SDag-Erling Smørgrav saved_errno = errno; 3635511b41d2SMark Murray close(sock); 3636d4af9e69SDag-Erling Smørgrav errno = saved_errno; 3637511b41d2SMark Murray continue; /* fail -- try next */ 3638511b41d2SMark Murray } 3639*a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 3640*a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 3641d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s) " 3642d4af9e69SDag-Erling Smørgrav "in progress, fd=%d", cctx->host, ntop, strport, sock); 3643d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 3644a04a10f8SKris Kennaway return sock; 3645a04a10f8SKris Kennaway } 3646ca3176e7SBrian Feldman return -1; 3647ca3176e7SBrian Feldman } 3648ca3176e7SBrian Feldman 3649d4af9e69SDag-Erling Smørgrav static void 3650d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 3651d4af9e69SDag-Erling Smørgrav { 3652e4a9863fSDag-Erling Smørgrav free(cctx->host); 3653*a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 3654*a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 3655*a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 3656*a0ee8cc6SDag-Erling Smørgrav else 3657d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 3658*a0ee8cc6SDag-Erling Smørgrav } 3659b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 3660d4af9e69SDag-Erling Smørgrav } 3661d4af9e69SDag-Erling Smørgrav 3662*a0ee8cc6SDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 3663d4af9e69SDag-Erling Smørgrav static Channel * 3664*a0ee8cc6SDag-Erling Smørgrav connect_to(const char *name, int port, char *ctype, char *rname) 3665d4af9e69SDag-Erling Smørgrav { 3666d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 3667d4af9e69SDag-Erling Smørgrav int gaierr; 3668d4af9e69SDag-Erling Smørgrav int sock = -1; 3669d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 3670d4af9e69SDag-Erling Smørgrav struct channel_connect cctx; 3671d4af9e69SDag-Erling Smørgrav Channel *c; 3672d4af9e69SDag-Erling Smørgrav 3673d4af9e69SDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 3674*a0ee8cc6SDag-Erling Smørgrav 3675*a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 3676*a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 3677*a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 3678*a0ee8cc6SDag-Erling Smørgrav 3679*a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 3680*a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 3681*a0ee8cc6SDag-Erling Smørgrav return (NULL); 3682*a0ee8cc6SDag-Erling Smørgrav } 3683*a0ee8cc6SDag-Erling Smørgrav 3684*a0ee8cc6SDag-Erling Smørgrav /* 3685*a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 3686*a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 3687*a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 3688*a0ee8cc6SDag-Erling Smørgrav */ 3689*a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 3690*a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 3691*a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 3692*a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 3693*a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 3694*a0ee8cc6SDag-Erling Smørgrav ai->ai_socktype = SOCK_STREAM; 3695*a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 3696*a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 3697*a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 3698*a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 3699*a0ee8cc6SDag-Erling Smørgrav cctx.aitop = ai; 3700*a0ee8cc6SDag-Erling Smørgrav } else { 3701d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 3702d4af9e69SDag-Erling Smørgrav hints.ai_family = IPv4or6; 3703d4af9e69SDag-Erling Smørgrav hints.ai_socktype = SOCK_STREAM; 3704d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 3705*a0ee8cc6SDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { 3706*a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 3707d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 3708d4af9e69SDag-Erling Smørgrav return NULL; 3709d4af9e69SDag-Erling Smørgrav } 3710*a0ee8cc6SDag-Erling Smørgrav } 3711d4af9e69SDag-Erling Smørgrav 3712*a0ee8cc6SDag-Erling Smørgrav cctx.host = xstrdup(name); 3713d4af9e69SDag-Erling Smørgrav cctx.port = port; 3714d4af9e69SDag-Erling Smørgrav cctx.ai = cctx.aitop; 3715d4af9e69SDag-Erling Smørgrav 3716d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&cctx)) == -1) { 3717d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 3718*a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 3719d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 3720d4af9e69SDag-Erling Smørgrav return NULL; 3721d4af9e69SDag-Erling Smørgrav } 3722d4af9e69SDag-Erling Smørgrav c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 3723d4af9e69SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 3724d4af9e69SDag-Erling Smørgrav c->connect_ctx = cctx; 3725d4af9e69SDag-Erling Smørgrav return c; 3726d4af9e69SDag-Erling Smørgrav } 3727d4af9e69SDag-Erling Smørgrav 3728d4af9e69SDag-Erling Smørgrav Channel * 3729*a0ee8cc6SDag-Erling Smørgrav channel_connect_by_listen_address(const char *listen_host, 3730*a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 3731d4af9e69SDag-Erling Smørgrav { 3732d4af9e69SDag-Erling Smørgrav int i; 3733d4af9e69SDag-Erling Smørgrav 3734d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3735*a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_tcpip(&permitted_opens[i], listen_host, 3736*a0ee8cc6SDag-Erling Smørgrav listen_port, 1)) { 3737d4af9e69SDag-Erling Smørgrav return connect_to( 3738d4af9e69SDag-Erling Smørgrav permitted_opens[i].host_to_connect, 3739d4af9e69SDag-Erling Smørgrav permitted_opens[i].port_to_connect, ctype, rname); 3740d4af9e69SDag-Erling Smørgrav } 3741d4af9e69SDag-Erling Smørgrav } 3742d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 3743d4af9e69SDag-Erling Smørgrav listen_port); 3744d4af9e69SDag-Erling Smørgrav return NULL; 3745d4af9e69SDag-Erling Smørgrav } 3746d4af9e69SDag-Erling Smørgrav 3747*a0ee8cc6SDag-Erling Smørgrav Channel * 3748*a0ee8cc6SDag-Erling Smørgrav channel_connect_by_listen_path(const char *path, char *ctype, char *rname) 3749*a0ee8cc6SDag-Erling Smørgrav { 3750*a0ee8cc6SDag-Erling Smørgrav int i; 3751*a0ee8cc6SDag-Erling Smørgrav 3752*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3753*a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_streamlocal(&permitted_opens[i], path)) { 3754*a0ee8cc6SDag-Erling Smørgrav return connect_to( 3755*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].host_to_connect, 3756*a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].port_to_connect, ctype, rname); 3757*a0ee8cc6SDag-Erling Smørgrav } 3758*a0ee8cc6SDag-Erling Smørgrav } 3759*a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 3760*a0ee8cc6SDag-Erling Smørgrav path); 3761*a0ee8cc6SDag-Erling Smørgrav return NULL; 3762*a0ee8cc6SDag-Erling Smørgrav } 3763*a0ee8cc6SDag-Erling Smørgrav 3764ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 3765d4af9e69SDag-Erling Smørgrav Channel * 3766*a0ee8cc6SDag-Erling Smørgrav channel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) 3767ca3176e7SBrian Feldman { 3768333ee039SDag-Erling Smørgrav int i, permit, permit_adm = 1; 3769ca3176e7SBrian Feldman 3770ca3176e7SBrian Feldman permit = all_opens_permitted; 3771ca3176e7SBrian Feldman if (!permit) { 3772ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 3773*a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_opens[i], host, port)) { 3774ca3176e7SBrian Feldman permit = 1; 3775*a0ee8cc6SDag-Erling Smørgrav break; 3776*a0ee8cc6SDag-Erling Smørgrav } 3777ca3176e7SBrian Feldman } 3778333ee039SDag-Erling Smørgrav 3779333ee039SDag-Erling Smørgrav if (num_adm_permitted_opens > 0) { 3780333ee039SDag-Erling Smørgrav permit_adm = 0; 3781333ee039SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 3782*a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_adm_opens[i], host, port)) { 3783333ee039SDag-Erling Smørgrav permit_adm = 1; 3784*a0ee8cc6SDag-Erling Smørgrav break; 3785*a0ee8cc6SDag-Erling Smørgrav } 3786333ee039SDag-Erling Smørgrav } 3787333ee039SDag-Erling Smørgrav 3788333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 3789221552e4SDag-Erling Smørgrav logit("Received request to connect to host %.100s port %d, " 3790ca3176e7SBrian Feldman "but the request was denied.", host, port); 3791d4af9e69SDag-Erling Smørgrav return NULL; 3792ca3176e7SBrian Feldman } 3793d4af9e69SDag-Erling Smørgrav return connect_to(host, port, ctype, rname); 3794ca3176e7SBrian Feldman } 3795ca3176e7SBrian Feldman 3796*a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 3797*a0ee8cc6SDag-Erling Smørgrav Channel * 3798*a0ee8cc6SDag-Erling Smørgrav channel_connect_to_path(const char *path, char *ctype, char *rname) 3799*a0ee8cc6SDag-Erling Smørgrav { 3800*a0ee8cc6SDag-Erling Smørgrav int i, permit, permit_adm = 1; 3801*a0ee8cc6SDag-Erling Smørgrav 3802*a0ee8cc6SDag-Erling Smørgrav permit = all_opens_permitted; 3803*a0ee8cc6SDag-Erling Smørgrav if (!permit) { 3804*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) 3805*a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { 3806*a0ee8cc6SDag-Erling Smørgrav permit = 1; 3807*a0ee8cc6SDag-Erling Smørgrav break; 3808*a0ee8cc6SDag-Erling Smørgrav } 3809*a0ee8cc6SDag-Erling Smørgrav } 3810*a0ee8cc6SDag-Erling Smørgrav 3811*a0ee8cc6SDag-Erling Smørgrav if (num_adm_permitted_opens > 0) { 3812*a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 3813*a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 3814*a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { 3815*a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 3816*a0ee8cc6SDag-Erling Smørgrav break; 3817*a0ee8cc6SDag-Erling Smørgrav } 3818*a0ee8cc6SDag-Erling Smørgrav } 3819*a0ee8cc6SDag-Erling Smørgrav 3820*a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 3821*a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 3822*a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 3823*a0ee8cc6SDag-Erling Smørgrav return NULL; 3824*a0ee8cc6SDag-Erling Smørgrav } 3825*a0ee8cc6SDag-Erling Smørgrav return connect_to(path, PORT_STREAMLOCAL, ctype, rname); 3826*a0ee8cc6SDag-Erling Smørgrav } 3827*a0ee8cc6SDag-Erling Smørgrav 382821e764dfSDag-Erling Smørgrav void 382921e764dfSDag-Erling Smørgrav channel_send_window_changes(void) 383021e764dfSDag-Erling Smørgrav { 383121e764dfSDag-Erling Smørgrav u_int i; 383221e764dfSDag-Erling Smørgrav struct winsize ws; 383321e764dfSDag-Erling Smørgrav 383421e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3835aa49c926SDag-Erling Smørgrav if (channels[i] == NULL || !channels[i]->client_tty || 383621e764dfSDag-Erling Smørgrav channels[i]->type != SSH_CHANNEL_OPEN) 383721e764dfSDag-Erling Smørgrav continue; 383821e764dfSDag-Erling Smørgrav if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 383921e764dfSDag-Erling Smørgrav continue; 384021e764dfSDag-Erling Smørgrav channel_request_start(i, "window-change", 0); 3841333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_col); 3842333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_row); 3843333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_xpixel); 3844333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_ypixel); 384521e764dfSDag-Erling Smørgrav packet_send(); 384621e764dfSDag-Erling Smørgrav } 384721e764dfSDag-Erling Smørgrav } 384821e764dfSDag-Erling Smørgrav 3849af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 3850511b41d2SMark Murray 3851511b41d2SMark Murray /* 3852511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 3853a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 3854a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 3855511b41d2SMark Murray */ 3856af12a3e7SDag-Erling Smørgrav int 3857af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 3858d4ecd108SDag-Erling Smørgrav int single_connection, u_int *display_numberp, int **chanids) 3859511b41d2SMark Murray { 3860af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 3861511b41d2SMark Murray int display_number, sock; 3862511b41d2SMark Murray u_short port; 3863511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3864511b41d2SMark Murray char strport[NI_MAXSERV]; 3865511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 3866511b41d2SMark Murray 3867b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 3868b74df5b2SDag-Erling Smørgrav return -1; 3869b74df5b2SDag-Erling Smørgrav 3870511b41d2SMark Murray for (display_number = x11_display_offset; 3871511b41d2SMark Murray display_number < MAX_DISPLAYS; 3872511b41d2SMark Murray display_number++) { 3873511b41d2SMark Murray port = 6000 + display_number; 3874511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 3875511b41d2SMark Murray hints.ai_family = IPv4or6; 3876af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 3877511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3878511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 3879511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 3880d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 3881af12a3e7SDag-Erling Smørgrav return -1; 3882511b41d2SMark Murray } 3883511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3884511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 3885511b41d2SMark Murray continue; 3886221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 3887221552e4SDag-Erling Smørgrav ai->ai_protocol); 3888511b41d2SMark Murray if (sock < 0) { 38898ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 38908ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 38918ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 38928ad9b54aSDag-Erling Smørgrav #endif 38938ad9b54aSDag-Erling Smørgrav ) { 3894511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 389521e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 3896af12a3e7SDag-Erling Smørgrav return -1; 3897989dd127SDag-Erling Smørgrav } else { 3898989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 3899989dd127SDag-Erling Smørgrav ai->ai_family); 3900989dd127SDag-Erling Smørgrav continue; 3901511b41d2SMark Murray } 3902989dd127SDag-Erling Smørgrav } 3903b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3904b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3905d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 3906b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 3907511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3908221552e4SDag-Erling Smørgrav debug2("bind port %d: %.100s", port, strerror(errno)); 3909511b41d2SMark Murray close(sock); 3910989dd127SDag-Erling Smørgrav 3911511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 3912511b41d2SMark Murray close(socks[n]); 3913511b41d2SMark Murray } 3914511b41d2SMark Murray num_socks = 0; 3915511b41d2SMark Murray break; 3916511b41d2SMark Murray } 3917511b41d2SMark Murray socks[num_socks++] = sock; 3918511b41d2SMark Murray if (num_socks == NUM_SOCKS) 3919511b41d2SMark Murray break; 3920511b41d2SMark Murray } 3921ca3176e7SBrian Feldman freeaddrinfo(aitop); 3922511b41d2SMark Murray if (num_socks > 0) 3923511b41d2SMark Murray break; 3924511b41d2SMark Murray } 3925511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 3926511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 3927af12a3e7SDag-Erling Smørgrav return -1; 3928511b41d2SMark Murray } 3929511b41d2SMark Murray /* Start listening for connections on the socket. */ 3930511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 3931511b41d2SMark Murray sock = socks[n]; 3932476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 3933511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 3934511b41d2SMark Murray close(sock); 3935af12a3e7SDag-Erling Smørgrav return -1; 3936511b41d2SMark Murray } 3937511b41d2SMark Murray } 3938511b41d2SMark Murray 3939511b41d2SMark Murray /* Allocate a channel for each socket. */ 3940333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 3941511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 3942511b41d2SMark Murray sock = socks[n]; 3943af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 3944a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 3945a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 3946221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 3947af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 3948d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 3949511b41d2SMark Murray } 3950d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 3951511b41d2SMark Murray 3952af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 3953a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 3954a82e551fSDag-Erling Smørgrav return (0); 3955511b41d2SMark Murray } 3956511b41d2SMark Murray 3957af12a3e7SDag-Erling Smørgrav static int 3958cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 3959511b41d2SMark Murray { 3960511b41d2SMark Murray int sock; 3961511b41d2SMark Murray struct sockaddr_un addr; 3962511b41d2SMark Murray 3963511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 3964511b41d2SMark Murray if (sock < 0) 3965511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 3966511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 3967511b41d2SMark Murray addr.sun_family = AF_UNIX; 3968cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 3969511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 3970511b41d2SMark Murray return sock; 3971511b41d2SMark Murray close(sock); 3972511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 3973511b41d2SMark Murray return -1; 3974511b41d2SMark Murray } 3975511b41d2SMark Murray 3976cce7d346SDag-Erling Smørgrav static int 3977cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 3978cce7d346SDag-Erling Smørgrav { 3979cce7d346SDag-Erling Smørgrav char buf[1024]; 3980cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 3981cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 3982cce7d346SDag-Erling Smørgrav } 3983cce7d346SDag-Erling Smørgrav 3984a04a10f8SKris Kennaway int 3985a04a10f8SKris Kennaway x11_connect_display(void) 3986511b41d2SMark Murray { 3987333ee039SDag-Erling Smørgrav u_int display_number; 3988511b41d2SMark Murray const char *display; 3989a04a10f8SKris Kennaway char buf[1024], *cp; 3990511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3991511b41d2SMark Murray char strport[NI_MAXSERV]; 3992333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 3993511b41d2SMark Murray 3994511b41d2SMark Murray /* Try to open a socket for the local X server. */ 3995511b41d2SMark Murray display = getenv("DISPLAY"); 3996511b41d2SMark Murray if (!display) { 3997511b41d2SMark Murray error("DISPLAY not set."); 3998a04a10f8SKris Kennaway return -1; 3999511b41d2SMark Murray } 4000511b41d2SMark Murray /* 4001511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 4002511b41d2SMark Murray * connection to the real X server. 4003511b41d2SMark Murray */ 4004511b41d2SMark Murray 4005cce7d346SDag-Erling Smørgrav /* Check if the display is from launchd. */ 4006cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 4007cce7d346SDag-Erling Smørgrav if (strncmp(display, "/tmp/launch", 11) == 0) { 4008cce7d346SDag-Erling Smørgrav sock = connect_local_xsocket_path(display); 4009cce7d346SDag-Erling Smørgrav if (sock < 0) 4010cce7d346SDag-Erling Smørgrav return -1; 4011cce7d346SDag-Erling Smørgrav 4012cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 4013cce7d346SDag-Erling Smørgrav return sock; 4014cce7d346SDag-Erling Smørgrav } 4015cce7d346SDag-Erling Smørgrav #endif 4016511b41d2SMark Murray /* 4017511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 4018511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 4019511b41d2SMark Murray */ 4020511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 4021511b41d2SMark Murray display[0] == ':') { 4022511b41d2SMark Murray /* Connect to the unix domain socket. */ 4023333ee039SDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 4024511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 4025511b41d2SMark Murray display); 4026a04a10f8SKris Kennaway return -1; 4027511b41d2SMark Murray } 4028511b41d2SMark Murray /* Create a socket. */ 4029511b41d2SMark Murray sock = connect_local_xsocket(display_number); 4030511b41d2SMark Murray if (sock < 0) 4031a04a10f8SKris Kennaway return -1; 4032511b41d2SMark Murray 4033511b41d2SMark Murray /* OK, we now have a connection to the display. */ 4034a04a10f8SKris Kennaway return sock; 4035511b41d2SMark Murray } 4036511b41d2SMark Murray /* 4037511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 4038511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 4039511b41d2SMark Murray */ 4040af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 4041511b41d2SMark Murray cp = strchr(buf, ':'); 4042511b41d2SMark Murray if (!cp) { 4043511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 4044a04a10f8SKris Kennaway return -1; 4045511b41d2SMark Murray } 4046511b41d2SMark Murray *cp = 0; 4047511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 4048333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 4049511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 4050511b41d2SMark Murray display); 4051a04a10f8SKris Kennaway return -1; 4052511b41d2SMark Murray } 4053511b41d2SMark Murray 4054511b41d2SMark Murray /* Look up the host address */ 4055511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 4056511b41d2SMark Murray hints.ai_family = IPv4or6; 4057511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4058333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 4059511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 4060d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 4061d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 4062a04a10f8SKris Kennaway return -1; 4063511b41d2SMark Murray } 4064511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 4065511b41d2SMark Murray /* Create a socket. */ 4066221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 4067511b41d2SMark Murray if (sock < 0) { 4068221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 4069511b41d2SMark Murray continue; 4070511b41d2SMark Murray } 4071511b41d2SMark Murray /* Connect it to the display. */ 4072511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 4073333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 4074a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 4075511b41d2SMark Murray close(sock); 4076511b41d2SMark Murray continue; 4077511b41d2SMark Murray } 4078511b41d2SMark Murray /* Success */ 4079511b41d2SMark Murray break; 4080a04a10f8SKris Kennaway } 4081511b41d2SMark Murray freeaddrinfo(aitop); 4082511b41d2SMark Murray if (!ai) { 4083333ee039SDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 4084511b41d2SMark Murray strerror(errno)); 4085a04a10f8SKris Kennaway return -1; 4086511b41d2SMark Murray } 4087af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 4088a04a10f8SKris Kennaway return sock; 4089a04a10f8SKris Kennaway } 4090511b41d2SMark Murray 4091a04a10f8SKris Kennaway /* 4092a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 4093a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 4094a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 4095a04a10f8SKris Kennaway */ 4096a04a10f8SKris Kennaway 4097333ee039SDag-Erling Smørgrav /* ARGSUSED */ 4098a04a10f8SKris Kennaway void 4099af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 4100a04a10f8SKris Kennaway { 4101af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 4102af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 4103a04a10f8SKris Kennaway char *remote_host; 4104a04a10f8SKris Kennaway 4105a04a10f8SKris Kennaway debug("Received X11 open request."); 4106af12a3e7SDag-Erling Smørgrav 4107af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 4108af12a3e7SDag-Erling Smørgrav 4109af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 4110af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 4111af12a3e7SDag-Erling Smørgrav } else { 4112af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 4113af12a3e7SDag-Erling Smørgrav } 4114af12a3e7SDag-Erling Smørgrav packet_check_eom(); 4115a04a10f8SKris Kennaway 4116a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 4117a04a10f8SKris Kennaway sock = x11_connect_display(); 4118af12a3e7SDag-Erling Smørgrav if (sock != -1) { 4119af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 4120af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 4121af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 4122af12a3e7SDag-Erling Smørgrav remote_host, 1); 4123af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 4124af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 4125af12a3e7SDag-Erling Smørgrav } 4126e4a9863fSDag-Erling Smørgrav free(remote_host); 4127af12a3e7SDag-Erling Smørgrav if (c == NULL) { 4128a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 4129a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 4130af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 4131a04a10f8SKris Kennaway } else { 4132511b41d2SMark Murray /* Send a confirmation to the remote host. */ 4133511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 4134af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 4135af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 4136a04a10f8SKris Kennaway } 4137af12a3e7SDag-Erling Smørgrav packet_send(); 4138511b41d2SMark Murray } 4139511b41d2SMark Murray 41405b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 4141333ee039SDag-Erling Smørgrav /* ARGSUSED */ 41425b9b2fafSBrian Feldman void 4143af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 41445b9b2fafSBrian Feldman { 41455b9b2fafSBrian Feldman int rchan = packet_get_int(); 4146f388f5efSDag-Erling Smørgrav 41475b9b2fafSBrian Feldman switch (type) { 41485b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 41495b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 41505b9b2fafSBrian Feldman break; 41515b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 41525b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 41535b9b2fafSBrian Feldman break; 41545b9b2fafSBrian Feldman default: 4155af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 41565b9b2fafSBrian Feldman break; 41575b9b2fafSBrian Feldman } 4158b74df5b2SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a malicious server."); 41595b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 41605b9b2fafSBrian Feldman packet_put_int(rchan); 41615b9b2fafSBrian Feldman packet_send(); 41625b9b2fafSBrian Feldman } 41635b9b2fafSBrian Feldman 4164511b41d2SMark Murray /* 4165511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 4166511b41d2SMark Murray * data, and enables authentication spoofing. 4167af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 4168511b41d2SMark Murray */ 4169511b41d2SMark Murray void 4170d4ecd108SDag-Erling Smørgrav x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 4171e146993eSDag-Erling Smørgrav const char *proto, const char *data, int want_reply) 4172511b41d2SMark Murray { 4173ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 4174d4ecd108SDag-Erling Smørgrav u_int i, value; 4175511b41d2SMark Murray char *new_data; 4176511b41d2SMark Murray int screen_number; 4177511b41d2SMark Murray const char *cp; 417821e764dfSDag-Erling Smørgrav u_int32_t rnd = 0; 4179511b41d2SMark Murray 4180d4ecd108SDag-Erling Smørgrav if (x11_saved_display == NULL) 4181d4ecd108SDag-Erling Smørgrav x11_saved_display = xstrdup(disp); 4182d4ecd108SDag-Erling Smørgrav else if (strcmp(disp, x11_saved_display) != 0) { 4183d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 4184d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 4185d4ecd108SDag-Erling Smørgrav return; 4186d4ecd108SDag-Erling Smørgrav } 4187d4ecd108SDag-Erling Smørgrav 4188d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 4189511b41d2SMark Murray if (cp) 4190511b41d2SMark Murray cp = strchr(cp, '.'); 4191511b41d2SMark Murray if (cp) 4192333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 4193511b41d2SMark Murray else 4194511b41d2SMark Murray screen_number = 0; 4195511b41d2SMark Murray 4196d4ecd108SDag-Erling Smørgrav if (x11_saved_proto == NULL) { 4197511b41d2SMark Murray /* Save protocol name. */ 4198511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 4199511b41d2SMark Murray /* 4200d4ecd108SDag-Erling Smørgrav * Extract real authentication data and generate fake data 4201d4ecd108SDag-Erling Smørgrav * of the same length. 4202511b41d2SMark Murray */ 4203511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 4204511b41d2SMark Murray x11_fake_data = xmalloc(data_len); 4205511b41d2SMark Murray for (i = 0; i < data_len; i++) { 4206511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 4207d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 4208d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 4209511b41d2SMark Murray if (i % 4 == 0) 421021e764dfSDag-Erling Smørgrav rnd = arc4random(); 4211511b41d2SMark Murray x11_saved_data[i] = value; 421221e764dfSDag-Erling Smørgrav x11_fake_data[i] = rnd & 0xff; 421321e764dfSDag-Erling Smørgrav rnd >>= 8; 4214511b41d2SMark Murray } 4215511b41d2SMark Murray x11_saved_data_len = data_len; 4216511b41d2SMark Murray x11_fake_data_len = data_len; 4217d4ecd108SDag-Erling Smørgrav } 4218511b41d2SMark Murray 4219511b41d2SMark Murray /* Convert the fake data into hex. */ 4220d4ecd108SDag-Erling Smørgrav new_data = tohex(x11_fake_data, data_len); 4221511b41d2SMark Murray 4222511b41d2SMark Murray /* Send the request packet. */ 4223a04a10f8SKris Kennaway if (compat20) { 4224e146993eSDag-Erling Smørgrav channel_request_start(client_session_id, "x11-req", want_reply); 4225a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 4226a04a10f8SKris Kennaway } else { 4227511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 4228a04a10f8SKris Kennaway } 4229a04a10f8SKris Kennaway packet_put_cstring(proto); 4230a04a10f8SKris Kennaway packet_put_cstring(new_data); 4231511b41d2SMark Murray packet_put_int(screen_number); 4232511b41d2SMark Murray packet_send(); 4233511b41d2SMark Murray packet_write_wait(); 4234e4a9863fSDag-Erling Smørgrav free(new_data); 4235511b41d2SMark Murray } 4236511b41d2SMark Murray 4237af12a3e7SDag-Erling Smørgrav 4238af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 4239af12a3e7SDag-Erling Smørgrav 4240511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 4241511b41d2SMark Murray 4242511b41d2SMark Murray void 4243af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 4244511b41d2SMark Murray { 4245511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 4246511b41d2SMark Murray packet_send(); 4247511b41d2SMark Murray packet_write_wait(); 4248511b41d2SMark Murray } 4249