1*ca86bcf2SDag-Erling Smørgrav /* $OpenBSD: channels.c,v 1.356 2016/10/18 17:32:54 dtucker 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" 43a04a10f8SKris Kennaway 44333ee039SDag-Erling Smørgrav #include <sys/types.h> 45a0ee8cc6SDag-Erling Smørgrav #include <sys/stat.h> 46333ee039SDag-Erling Smørgrav #include <sys/ioctl.h> 47333ee039SDag-Erling Smørgrav #include <sys/un.h> 48333ee039SDag-Erling Smørgrav #include <sys/socket.h> 49333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 50333ee039SDag-Erling Smørgrav # include <sys/time.h> 51333ee039SDag-Erling Smørgrav #endif 52333ee039SDag-Erling Smørgrav 53333ee039SDag-Erling Smørgrav #include <netinet/in.h> 54333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 55333ee039SDag-Erling Smørgrav 56333ee039SDag-Erling Smørgrav #include <errno.h> 57b15c8340SDag-Erling Smørgrav #include <fcntl.h> 58333ee039SDag-Erling Smørgrav #include <netdb.h> 59bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H 60bc5531deSDag-Erling Smørgrav #include <stdint.h> 61bc5531deSDag-Erling Smørgrav #endif 62333ee039SDag-Erling Smørgrav #include <stdio.h> 63333ee039SDag-Erling Smørgrav #include <stdlib.h> 64333ee039SDag-Erling Smørgrav #include <string.h> 65333ee039SDag-Erling Smørgrav #include <termios.h> 66333ee039SDag-Erling Smørgrav #include <unistd.h> 67333ee039SDag-Erling Smørgrav #include <stdarg.h> 68333ee039SDag-Erling Smørgrav 69d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 70333ee039SDag-Erling Smørgrav #include "xmalloc.h" 71ca3176e7SBrian Feldman #include "ssh.h" 72ca3176e7SBrian Feldman #include "ssh1.h" 73ca3176e7SBrian Feldman #include "ssh2.h" 74*ca86bcf2SDag-Erling Smørgrav #include "ssherr.h" 75ca3176e7SBrian Feldman #include "packet.h" 76ca3176e7SBrian Feldman #include "log.h" 77ca3176e7SBrian Feldman #include "misc.h" 78333ee039SDag-Erling Smørgrav #include "buffer.h" 79ca3176e7SBrian Feldman #include "channels.h" 80ca3176e7SBrian Feldman #include "compat.h" 81ca3176e7SBrian Feldman #include "canohost.h" 82b66f2d16SKris Kennaway #include "key.h" 83b66f2d16SKris Kennaway #include "authfd.h" 84af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 85511b41d2SMark Murray 86af12a3e7SDag-Erling Smørgrav /* -- channel core */ 87511b41d2SMark Murray 88511b41d2SMark Murray /* 89511b41d2SMark Murray * Pointer to an array containing all allocated channels. The array is 90511b41d2SMark Murray * dynamically extended as needed. 91511b41d2SMark Murray */ 92af12a3e7SDag-Erling Smørgrav static Channel **channels = NULL; 93511b41d2SMark Murray 94511b41d2SMark Murray /* 95511b41d2SMark Murray * Size of the channel array. All slots of the array must always be 96af12a3e7SDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 97511b41d2SMark Murray */ 9821e764dfSDag-Erling Smørgrav static u_int channels_alloc = 0; 99511b41d2SMark Murray 100511b41d2SMark Murray /* 101511b41d2SMark Murray * Maximum file descriptor value used in any of the channels. This is 102af12a3e7SDag-Erling Smørgrav * updated in channel_new. 103511b41d2SMark Murray */ 104ca3176e7SBrian Feldman static int channel_max_fd = 0; 105511b41d2SMark Murray 106511b41d2SMark Murray 107af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 108511b41d2SMark Murray 109511b41d2SMark Murray /* 110511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 111511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 112511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 113511b41d2SMark Murray * network (which might be behind a firewall). 114511b41d2SMark Murray */ 115a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */ 116a0ee8cc6SDag-Erling Smørgrav /* Overload host_to_connect; we could just make this match Forward */ 117a0ee8cc6SDag-Erling Smørgrav /* XXX - can we use listen_host instead of listen_path? */ 118511b41d2SMark Murray typedef struct { 119a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 120a0ee8cc6SDag-Erling Smørgrav int port_to_connect; /* Connect to 'port'. */ 121a0ee8cc6SDag-Erling Smørgrav char *listen_host; /* Remote side should listen address. */ 122a0ee8cc6SDag-Erling Smørgrav char *listen_path; /* Remote side should listen path. */ 123a0ee8cc6SDag-Erling Smørgrav int listen_port; /* Remote side should listen port. */ 124*ca86bcf2SDag-Erling Smørgrav Channel *downstream; /* Downstream mux*/ 125511b41d2SMark Murray } ForwardPermission; 126511b41d2SMark Murray 127333ee039SDag-Erling Smørgrav /* List of all permitted host/port pairs to connect by the user. */ 128e2f6069cSDag-Erling Smørgrav static ForwardPermission *permitted_opens = NULL; 129af12a3e7SDag-Erling Smørgrav 130333ee039SDag-Erling Smørgrav /* List of all permitted host/port pairs to connect by the admin. */ 131e2f6069cSDag-Erling Smørgrav static ForwardPermission *permitted_adm_opens = NULL; 132333ee039SDag-Erling Smørgrav 133333ee039SDag-Erling Smørgrav /* Number of permitted host/port pairs in the array permitted by the user. */ 134511b41d2SMark Murray static int num_permitted_opens = 0; 135333ee039SDag-Erling Smørgrav 136333ee039SDag-Erling Smørgrav /* Number of permitted host/port pair in the array permitted by the admin. */ 137333ee039SDag-Erling Smørgrav static int num_adm_permitted_opens = 0; 138333ee039SDag-Erling Smørgrav 139462c32cbSDag-Erling Smørgrav /* special-case port number meaning allow any port */ 140462c32cbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT 0 141462c32cbSDag-Erling Smørgrav 142076ad2f8SDag-Erling Smørgrav /* special-case wildcard meaning allow any host */ 143076ad2f8SDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST "*" 144076ad2f8SDag-Erling Smørgrav 145511b41d2SMark Murray /* 146511b41d2SMark Murray * If this is true, all opens are permitted. This is the case on the server 147511b41d2SMark Murray * on which we have to trust the client anyway, and the user could do 148511b41d2SMark Murray * anything after logging in anyway. 149511b41d2SMark Murray */ 150511b41d2SMark Murray static int all_opens_permitted = 0; 151511b41d2SMark Murray 152af12a3e7SDag-Erling Smørgrav 153af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 154af12a3e7SDag-Erling Smørgrav 155af12a3e7SDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 156af12a3e7SDag-Erling Smørgrav #define MAX_DISPLAYS 1000 157af12a3e7SDag-Erling Smørgrav 158d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 159d4ecd108SDag-Erling Smørgrav static char *x11_saved_display = NULL; 160d4ecd108SDag-Erling Smørgrav 161af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 162af12a3e7SDag-Erling Smørgrav static char *x11_saved_proto = NULL; 163af12a3e7SDag-Erling Smørgrav 164af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 165af12a3e7SDag-Erling Smørgrav static char *x11_saved_data = NULL; 166af12a3e7SDag-Erling Smørgrav static u_int x11_saved_data_len = 0; 167af12a3e7SDag-Erling Smørgrav 168557f75e5SDag-Erling Smørgrav /* Deadline after which all X11 connections are refused */ 169557f75e5SDag-Erling Smørgrav static u_int x11_refuse_time; 170557f75e5SDag-Erling Smørgrav 171af12a3e7SDag-Erling Smørgrav /* 172af12a3e7SDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be sending us; 173af12a3e7SDag-Erling Smørgrav * we should replace any occurrences of this by the real data. 174af12a3e7SDag-Erling Smørgrav */ 175333ee039SDag-Erling Smørgrav static u_char *x11_fake_data = NULL; 176af12a3e7SDag-Erling Smørgrav static u_int x11_fake_data_len; 177af12a3e7SDag-Erling Smørgrav 178af12a3e7SDag-Erling Smørgrav 179af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 180af12a3e7SDag-Erling Smørgrav 181af12a3e7SDag-Erling Smørgrav #define NUM_SOCKS 10 182af12a3e7SDag-Erling Smørgrav 183ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 184989dd127SDag-Erling Smørgrav static int IPv4or6 = AF_UNSPEC; 185ca3176e7SBrian Feldman 186af12a3e7SDag-Erling Smørgrav /* helper */ 187af12a3e7SDag-Erling Smørgrav static void port_open_helper(Channel *c, char *rtype); 188*ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host); 189ca3176e7SBrian Feldman 190d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 191d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 192d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 193d4af9e69SDag-Erling Smørgrav 194af12a3e7SDag-Erling Smørgrav /* -- channel core */ 195a04a10f8SKris Kennaway 196a04a10f8SKris Kennaway Channel * 197b74df5b2SDag-Erling Smørgrav channel_by_id(int id) 198a04a10f8SKris Kennaway { 199a04a10f8SKris Kennaway Channel *c; 200af12a3e7SDag-Erling Smørgrav 20121e764dfSDag-Erling Smørgrav if (id < 0 || (u_int)id >= channels_alloc) { 202b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id", id); 203a04a10f8SKris Kennaway return NULL; 204a04a10f8SKris Kennaway } 205af12a3e7SDag-Erling Smørgrav c = channels[id]; 206af12a3e7SDag-Erling Smørgrav if (c == NULL) { 207b74df5b2SDag-Erling Smørgrav logit("channel_by_id: %d: bad id: channel free", id); 208a04a10f8SKris Kennaway return NULL; 209a04a10f8SKris Kennaway } 210a04a10f8SKris Kennaway return c; 211a04a10f8SKris Kennaway } 212a04a10f8SKris Kennaway 213*ca86bcf2SDag-Erling Smørgrav Channel * 214*ca86bcf2SDag-Erling Smørgrav channel_by_remote_id(int remote_id) 215*ca86bcf2SDag-Erling Smørgrav { 216*ca86bcf2SDag-Erling Smørgrav Channel *c; 217*ca86bcf2SDag-Erling Smørgrav u_int i; 218*ca86bcf2SDag-Erling Smørgrav 219*ca86bcf2SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 220*ca86bcf2SDag-Erling Smørgrav c = channels[i]; 221*ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->remote_id == remote_id) 222*ca86bcf2SDag-Erling Smørgrav return c; 223*ca86bcf2SDag-Erling Smørgrav } 224*ca86bcf2SDag-Erling Smørgrav return NULL; 225*ca86bcf2SDag-Erling Smørgrav } 226*ca86bcf2SDag-Erling Smørgrav 227a04a10f8SKris Kennaway /* 228b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 229b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 230b74df5b2SDag-Erling Smørgrav */ 231b74df5b2SDag-Erling Smørgrav Channel * 232b74df5b2SDag-Erling Smørgrav channel_lookup(int id) 233b74df5b2SDag-Erling Smørgrav { 234b74df5b2SDag-Erling Smørgrav Channel *c; 235b74df5b2SDag-Erling Smørgrav 236b74df5b2SDag-Erling Smørgrav if ((c = channel_by_id(id)) == NULL) 237b74df5b2SDag-Erling Smørgrav return (NULL); 238b74df5b2SDag-Erling Smørgrav 239b74df5b2SDag-Erling Smørgrav switch (c->type) { 240b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 241b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 242b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 243b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 244b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 245b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 246b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 247b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 248e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 249*ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 250b74df5b2SDag-Erling Smørgrav return (c); 251b74df5b2SDag-Erling Smørgrav } 252b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 253b74df5b2SDag-Erling Smørgrav return (NULL); 254b74df5b2SDag-Erling Smørgrav } 255b74df5b2SDag-Erling Smørgrav 256b74df5b2SDag-Erling Smørgrav /* 257a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 258a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 259a04a10f8SKris Kennaway */ 260af12a3e7SDag-Erling Smørgrav static void 2615b9b2fafSBrian Feldman channel_register_fds(Channel *c, int rfd, int wfd, int efd, 262d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 263a04a10f8SKris Kennaway { 264a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 265*ca86bcf2SDag-Erling Smørgrav channel_max_fd = MAXIMUM(channel_max_fd, rfd); 266*ca86bcf2SDag-Erling Smørgrav channel_max_fd = MAXIMUM(channel_max_fd, wfd); 267*ca86bcf2SDag-Erling Smørgrav channel_max_fd = MAXIMUM(channel_max_fd, efd); 268ca3176e7SBrian Feldman 269b15c8340SDag-Erling Smørgrav if (rfd != -1) 270b15c8340SDag-Erling Smørgrav fcntl(rfd, F_SETFD, FD_CLOEXEC); 271b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 272b15c8340SDag-Erling Smørgrav fcntl(wfd, F_SETFD, FD_CLOEXEC); 273b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 274b15c8340SDag-Erling Smørgrav fcntl(efd, F_SETFD, FD_CLOEXEC); 275a04a10f8SKris Kennaway 276a04a10f8SKris Kennaway c->rfd = rfd; 277a04a10f8SKris Kennaway c->wfd = wfd; 278a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 279a04a10f8SKris Kennaway c->efd = efd; 280a04a10f8SKris Kennaway c->extended_usage = extusage; 2815b9b2fafSBrian Feldman 282d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 283221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 284e4a9863fSDag-Erling Smørgrav #ifdef _AIX 285e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 286d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 287e4a9863fSDag-Erling Smørgrav #endif 288e0fbb1d2SBrian Feldman 2895b9b2fafSBrian Feldman /* enable nonblocking mode */ 2905b9b2fafSBrian Feldman if (nonblock) { 291a04a10f8SKris Kennaway if (rfd != -1) 292a04a10f8SKris Kennaway set_nonblock(rfd); 293a04a10f8SKris Kennaway if (wfd != -1) 294a04a10f8SKris Kennaway set_nonblock(wfd); 295a04a10f8SKris Kennaway if (efd != -1) 296a04a10f8SKris Kennaway set_nonblock(efd); 297a04a10f8SKris Kennaway } 2985b9b2fafSBrian Feldman } 299a04a10f8SKris Kennaway 300511b41d2SMark Murray /* 301511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 302511b41d2SMark Murray * remote_name to be freed. 303511b41d2SMark Murray */ 304af12a3e7SDag-Erling Smørgrav Channel * 305a04a10f8SKris Kennaway channel_new(char *ctype, int type, int rfd, int wfd, int efd, 306a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 307511b41d2SMark Murray { 30821e764dfSDag-Erling Smørgrav int found; 30921e764dfSDag-Erling Smørgrav u_int i; 310511b41d2SMark Murray Channel *c; 311511b41d2SMark Murray 312511b41d2SMark Murray /* Do initial allocation if this is the first call. */ 313511b41d2SMark Murray if (channels_alloc == 0) { 314511b41d2SMark Murray channels_alloc = 10; 315333ee039SDag-Erling Smørgrav channels = xcalloc(channels_alloc, sizeof(Channel *)); 316511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) 317af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 318511b41d2SMark Murray } 319511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 320511b41d2SMark Murray for (found = -1, i = 0; i < channels_alloc; i++) 321af12a3e7SDag-Erling Smørgrav if (channels[i] == NULL) { 322511b41d2SMark Murray /* Found a free slot. */ 32321e764dfSDag-Erling Smørgrav found = (int)i; 324511b41d2SMark Murray break; 325511b41d2SMark Murray } 32621e764dfSDag-Erling Smørgrav if (found < 0) { 327511b41d2SMark Murray /* There are no free slots. Take last+1 slot and expand the array. */ 328511b41d2SMark Murray found = channels_alloc; 329a82e551fSDag-Erling Smørgrav if (channels_alloc > 10000) 330a82e551fSDag-Erling Smørgrav fatal("channel_new: internal error: channels_alloc %d " 331a82e551fSDag-Erling Smørgrav "too big.", channels_alloc); 332557f75e5SDag-Erling Smørgrav channels = xreallocarray(channels, channels_alloc + 10, 333333ee039SDag-Erling Smørgrav sizeof(Channel *)); 3343533e7e5SJosef Karthauser channels_alloc += 10; 3355b9b2fafSBrian Feldman debug2("channel: expanding %d", channels_alloc); 336511b41d2SMark Murray for (i = found; i < channels_alloc; i++) 337af12a3e7SDag-Erling Smørgrav channels[i] = NULL; 338511b41d2SMark Murray } 339af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 340333ee039SDag-Erling Smørgrav c = channels[found] = xcalloc(1, sizeof(Channel)); 341511b41d2SMark Murray buffer_init(&c->input); 342511b41d2SMark Murray buffer_init(&c->output); 343a04a10f8SKris Kennaway buffer_init(&c->extended); 344cce7d346SDag-Erling Smørgrav c->path = NULL; 345462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 346462c32cbSDag-Erling Smørgrav c->listening_port = 0; 347af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 348af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 349af12a3e7SDag-Erling Smørgrav c->flags = 0; 350d4af9e69SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); 351462c32cbSDag-Erling Smørgrav c->notbefore = 0; 352511b41d2SMark Murray c->self = found; 353511b41d2SMark Murray c->type = type; 354a04a10f8SKris Kennaway c->ctype = ctype; 355a04a10f8SKris Kennaway c->local_window = window; 356a04a10f8SKris Kennaway c->local_window_max = window; 357a04a10f8SKris Kennaway c->local_consumed = 0; 358a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 359511b41d2SMark Murray c->remote_id = -1; 360221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 361a04a10f8SKris Kennaway c->remote_window = 0; 362a04a10f8SKris Kennaway c->remote_maxpacket = 0; 363af12a3e7SDag-Erling Smørgrav c->force_drain = 0; 364af12a3e7SDag-Erling Smørgrav c->single_connection = 0; 365af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 366b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 367d4af9e69SDag-Erling Smørgrav c->open_confirm = NULL; 368d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = NULL; 369b66f2d16SKris Kennaway c->input_filter = NULL; 370b74df5b2SDag-Erling Smørgrav c->output_filter = NULL; 371d4af9e69SDag-Erling Smørgrav c->filter_ctx = NULL; 372d4af9e69SDag-Erling Smørgrav c->filter_cleanup = NULL; 373b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 374b15c8340SDag-Erling Smørgrav c->mux_rcb = NULL; 375b15c8340SDag-Erling Smørgrav c->mux_ctx = NULL; 376e2f6069cSDag-Erling Smørgrav c->mux_pause = 0; 377b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 378d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 379511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 380af12a3e7SDag-Erling Smørgrav return c; 381a04a10f8SKris Kennaway } 382511b41d2SMark Murray 383af12a3e7SDag-Erling Smørgrav static int 384af12a3e7SDag-Erling Smørgrav channel_find_maxfd(void) 385af12a3e7SDag-Erling Smørgrav { 38621e764dfSDag-Erling Smørgrav u_int i; 38721e764dfSDag-Erling Smørgrav int max = 0; 388af12a3e7SDag-Erling Smørgrav Channel *c; 389af12a3e7SDag-Erling Smørgrav 390af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 391af12a3e7SDag-Erling Smørgrav c = channels[i]; 392af12a3e7SDag-Erling Smørgrav if (c != NULL) { 393*ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->rfd); 394*ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->wfd); 395*ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->efd); 396af12a3e7SDag-Erling Smørgrav } 397af12a3e7SDag-Erling Smørgrav } 398af12a3e7SDag-Erling Smørgrav return max; 399af12a3e7SDag-Erling Smørgrav } 400af12a3e7SDag-Erling Smørgrav 401af12a3e7SDag-Erling Smørgrav int 402af12a3e7SDag-Erling Smørgrav channel_close_fd(int *fdp) 403af12a3e7SDag-Erling Smørgrav { 404af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 405af12a3e7SDag-Erling Smørgrav 406af12a3e7SDag-Erling Smørgrav if (fd != -1) { 407af12a3e7SDag-Erling Smørgrav ret = close(fd); 408af12a3e7SDag-Erling Smørgrav *fdp = -1; 409af12a3e7SDag-Erling Smørgrav if (fd == channel_max_fd) 410af12a3e7SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 411af12a3e7SDag-Erling Smørgrav } 412af12a3e7SDag-Erling Smørgrav return ret; 413af12a3e7SDag-Erling Smørgrav } 414a04a10f8SKris Kennaway 415a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 416af12a3e7SDag-Erling Smørgrav static void 417a04a10f8SKris Kennaway channel_close_fds(Channel *c) 418511b41d2SMark Murray { 419af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 420af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->rfd); 421af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->wfd); 422af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 423a04a10f8SKris Kennaway } 424511b41d2SMark Murray 425a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 426a04a10f8SKris Kennaway void 427af12a3e7SDag-Erling Smørgrav channel_free(Channel *c) 428a04a10f8SKris Kennaway { 429af12a3e7SDag-Erling Smørgrav char *s; 43021e764dfSDag-Erling Smørgrav u_int i, n; 431*ca86bcf2SDag-Erling Smørgrav Channel *other; 432d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 433ca3176e7SBrian Feldman 434*ca86bcf2SDag-Erling Smørgrav for (n = 0, i = 0; i < channels_alloc; i++) { 435*ca86bcf2SDag-Erling Smørgrav if ((other = channels[i]) != NULL) { 436af12a3e7SDag-Erling Smørgrav n++; 437*ca86bcf2SDag-Erling Smørgrav 438*ca86bcf2SDag-Erling Smørgrav /* detach from mux client and prepare for closing */ 439*ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT && 440*ca86bcf2SDag-Erling Smørgrav other->type == SSH_CHANNEL_MUX_PROXY && 441*ca86bcf2SDag-Erling Smørgrav other->mux_ctx == c) { 442*ca86bcf2SDag-Erling Smørgrav other->mux_ctx = NULL; 443*ca86bcf2SDag-Erling Smørgrav other->type = SSH_CHANNEL_OPEN; 444*ca86bcf2SDag-Erling Smørgrav other->istate = CHAN_INPUT_CLOSED; 445*ca86bcf2SDag-Erling Smørgrav other->ostate = CHAN_OUTPUT_CLOSED; 446*ca86bcf2SDag-Erling Smørgrav } 447*ca86bcf2SDag-Erling Smørgrav } 448*ca86bcf2SDag-Erling Smørgrav } 44921e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 450af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 451af12a3e7SDag-Erling Smørgrav 452*ca86bcf2SDag-Erling Smørgrav /* XXX more MUX cleanup: remove remote forwardings */ 453*ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT) { 454*ca86bcf2SDag-Erling Smørgrav for (i = 0; i < (u_int)num_permitted_opens; i++) { 455*ca86bcf2SDag-Erling Smørgrav if (permitted_opens[i].downstream != c) 456*ca86bcf2SDag-Erling Smørgrav continue; 457*ca86bcf2SDag-Erling Smørgrav /* cancel on the server, since mux client is gone */ 458*ca86bcf2SDag-Erling Smørgrav debug("channel %d: cleanup remote forward for %s:%u", 459*ca86bcf2SDag-Erling Smørgrav c->self, 460*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_host, 461*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_port); 462*ca86bcf2SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 463*ca86bcf2SDag-Erling Smørgrav packet_put_cstring("cancel-tcpip-forward"); 464*ca86bcf2SDag-Erling Smørgrav packet_put_char(0); 465*ca86bcf2SDag-Erling Smørgrav packet_put_cstring(channel_rfwd_bind_host( 466*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_host)); 467*ca86bcf2SDag-Erling Smørgrav packet_put_int(permitted_opens[i].listen_port); 468*ca86bcf2SDag-Erling Smørgrav packet_send(); 469*ca86bcf2SDag-Erling Smørgrav /* unregister */ 470*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 471*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 472*ca86bcf2SDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 473*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 474*ca86bcf2SDag-Erling Smørgrav free(permitted_opens[i].listen_host); 475*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_host = NULL; 476*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].listen_path = NULL; 477*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].downstream = NULL; 478*ca86bcf2SDag-Erling Smørgrav } 479*ca86bcf2SDag-Erling Smørgrav } 480*ca86bcf2SDag-Erling Smørgrav 481af12a3e7SDag-Erling Smørgrav s = channel_open_message(); 482221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 483e4a9863fSDag-Erling Smørgrav free(s); 484ca3176e7SBrian Feldman 485a04a10f8SKris Kennaway if (c->sock != -1) 486a04a10f8SKris Kennaway shutdown(c->sock, SHUT_RDWR); 487a04a10f8SKris Kennaway channel_close_fds(c); 488a04a10f8SKris Kennaway buffer_free(&c->input); 489a04a10f8SKris Kennaway buffer_free(&c->output); 490a04a10f8SKris Kennaway buffer_free(&c->extended); 491e4a9863fSDag-Erling Smørgrav free(c->remote_name); 492a04a10f8SKris Kennaway c->remote_name = NULL; 493e4a9863fSDag-Erling Smørgrav free(c->path); 494cce7d346SDag-Erling Smørgrav c->path = NULL; 495e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 496462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 497d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 498d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 499d4af9e69SDag-Erling Smørgrav cc->abandon_cb(c, cc->ctx); 500d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 501b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 502e4a9863fSDag-Erling Smørgrav free(cc); 503d4af9e69SDag-Erling Smørgrav } 504d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 505d4af9e69SDag-Erling Smørgrav c->filter_cleanup(c->self, c->filter_ctx); 506af12a3e7SDag-Erling Smørgrav channels[c->self] = NULL; 507e4a9863fSDag-Erling Smørgrav free(c); 508af12a3e7SDag-Erling Smørgrav } 509af12a3e7SDag-Erling Smørgrav 510af12a3e7SDag-Erling Smørgrav void 511af12a3e7SDag-Erling Smørgrav channel_free_all(void) 512af12a3e7SDag-Erling Smørgrav { 51321e764dfSDag-Erling Smørgrav u_int i; 514af12a3e7SDag-Erling Smørgrav 515af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 516af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 517af12a3e7SDag-Erling Smørgrav channel_free(channels[i]); 518af12a3e7SDag-Erling Smørgrav } 519af12a3e7SDag-Erling Smørgrav 520af12a3e7SDag-Erling Smørgrav /* 521af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 522af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 523af12a3e7SDag-Erling Smørgrav */ 524af12a3e7SDag-Erling Smørgrav void 525af12a3e7SDag-Erling Smørgrav channel_close_all(void) 526af12a3e7SDag-Erling Smørgrav { 52721e764dfSDag-Erling Smørgrav u_int i; 528af12a3e7SDag-Erling Smørgrav 529af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) 530af12a3e7SDag-Erling Smørgrav if (channels[i] != NULL) 531af12a3e7SDag-Erling Smørgrav channel_close_fds(channels[i]); 532af12a3e7SDag-Erling Smørgrav } 533af12a3e7SDag-Erling Smørgrav 534af12a3e7SDag-Erling Smørgrav /* 535af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 536af12a3e7SDag-Erling Smørgrav */ 537af12a3e7SDag-Erling Smørgrav void 538af12a3e7SDag-Erling Smørgrav channel_stop_listening(void) 539af12a3e7SDag-Erling Smørgrav { 54021e764dfSDag-Erling Smørgrav u_int i; 541af12a3e7SDag-Erling Smørgrav Channel *c; 542af12a3e7SDag-Erling Smørgrav 543af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 544af12a3e7SDag-Erling Smørgrav c = channels[i]; 545af12a3e7SDag-Erling Smørgrav if (c != NULL) { 546af12a3e7SDag-Erling Smørgrav switch (c->type) { 547af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 548af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 549af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 550af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 551a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 552a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 553af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 554af12a3e7SDag-Erling Smørgrav channel_free(c); 555af12a3e7SDag-Erling Smørgrav break; 556af12a3e7SDag-Erling Smørgrav } 557af12a3e7SDag-Erling Smørgrav } 558af12a3e7SDag-Erling Smørgrav } 559af12a3e7SDag-Erling Smørgrav } 560af12a3e7SDag-Erling Smørgrav 561af12a3e7SDag-Erling Smørgrav /* 562af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 563af12a3e7SDag-Erling Smørgrav * more channel is overfull. 564af12a3e7SDag-Erling Smørgrav */ 565af12a3e7SDag-Erling Smørgrav int 566af12a3e7SDag-Erling Smørgrav channel_not_very_much_buffered_data(void) 567af12a3e7SDag-Erling Smørgrav { 568af12a3e7SDag-Erling Smørgrav u_int i; 569af12a3e7SDag-Erling Smørgrav Channel *c; 570af12a3e7SDag-Erling Smørgrav 571af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 572af12a3e7SDag-Erling Smørgrav c = channels[i]; 573af12a3e7SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_OPEN) { 574af12a3e7SDag-Erling Smørgrav #if 0 575af12a3e7SDag-Erling Smørgrav if (!compat20 && 576af12a3e7SDag-Erling Smørgrav buffer_len(&c->input) > packet_get_maxsize()) { 577e73e9afaSDag-Erling Smørgrav debug2("channel %d: big input buffer %d", 578af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->input)); 579af12a3e7SDag-Erling Smørgrav return 0; 580af12a3e7SDag-Erling Smørgrav } 581af12a3e7SDag-Erling Smørgrav #endif 582af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->output) > packet_get_maxsize()) { 583221552e4SDag-Erling Smørgrav debug2("channel %d: big output buffer %u > %u", 584af12a3e7SDag-Erling Smørgrav c->self, buffer_len(&c->output), 585af12a3e7SDag-Erling Smørgrav packet_get_maxsize()); 586af12a3e7SDag-Erling Smørgrav return 0; 587af12a3e7SDag-Erling Smørgrav } 588af12a3e7SDag-Erling Smørgrav } 589af12a3e7SDag-Erling Smørgrav } 590af12a3e7SDag-Erling Smørgrav return 1; 591af12a3e7SDag-Erling Smørgrav } 592af12a3e7SDag-Erling Smørgrav 593af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 594af12a3e7SDag-Erling Smørgrav int 595af12a3e7SDag-Erling Smørgrav channel_still_open(void) 596af12a3e7SDag-Erling Smørgrav { 59721e764dfSDag-Erling Smørgrav u_int i; 598af12a3e7SDag-Erling Smørgrav Channel *c; 599af12a3e7SDag-Erling Smørgrav 600af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 601af12a3e7SDag-Erling Smørgrav c = channels[i]; 602af12a3e7SDag-Erling Smørgrav if (c == NULL) 603af12a3e7SDag-Erling Smørgrav continue; 604af12a3e7SDag-Erling Smørgrav switch (c->type) { 605af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 606af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 607af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 608b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 609af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 610af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 611af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 612af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 613af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 614e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 615a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 616a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 617af12a3e7SDag-Erling Smørgrav continue; 618af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 619af12a3e7SDag-Erling Smørgrav if (!compat20) 620af12a3e7SDag-Erling Smørgrav fatal("cannot happen: SSH_CHANNEL_LARVAL"); 621af12a3e7SDag-Erling Smørgrav continue; 622af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 623af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 624af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 625b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 626*ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 627af12a3e7SDag-Erling Smørgrav return 1; 628af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 629af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 630af12a3e7SDag-Erling Smørgrav if (!compat13) 631af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 632af12a3e7SDag-Erling Smørgrav return 1; 633af12a3e7SDag-Erling Smørgrav default: 634af12a3e7SDag-Erling Smørgrav fatal("channel_still_open: bad channel type %d", c->type); 635af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 636af12a3e7SDag-Erling Smørgrav } 637af12a3e7SDag-Erling Smørgrav } 638af12a3e7SDag-Erling Smørgrav return 0; 639af12a3e7SDag-Erling Smørgrav } 640af12a3e7SDag-Erling Smørgrav 641af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 642af12a3e7SDag-Erling Smørgrav int 643af12a3e7SDag-Erling Smørgrav channel_find_open(void) 644af12a3e7SDag-Erling Smørgrav { 64521e764dfSDag-Erling Smørgrav u_int i; 646af12a3e7SDag-Erling Smørgrav Channel *c; 647af12a3e7SDag-Erling Smørgrav 648af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 649af12a3e7SDag-Erling Smørgrav c = channels[i]; 65021e764dfSDag-Erling Smørgrav if (c == NULL || c->remote_id < 0) 651af12a3e7SDag-Erling Smørgrav continue; 652af12a3e7SDag-Erling Smørgrav switch (c->type) { 653af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 654af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 655af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 656af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 657af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 658b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 659b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 660*ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 661af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 662af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 663af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 664e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 665a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 666a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 667af12a3e7SDag-Erling Smørgrav continue; 668af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 669af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 670af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 671af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 672af12a3e7SDag-Erling Smørgrav return i; 673af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 674af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 675af12a3e7SDag-Erling Smørgrav if (!compat13) 676af12a3e7SDag-Erling Smørgrav fatal("cannot happen: OUT_DRAIN"); 677af12a3e7SDag-Erling Smørgrav return i; 678af12a3e7SDag-Erling Smørgrav default: 679af12a3e7SDag-Erling Smørgrav fatal("channel_find_open: bad channel type %d", c->type); 680af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 681af12a3e7SDag-Erling Smørgrav } 682af12a3e7SDag-Erling Smørgrav } 683af12a3e7SDag-Erling Smørgrav return -1; 684af12a3e7SDag-Erling Smørgrav } 685af12a3e7SDag-Erling Smørgrav 686af12a3e7SDag-Erling Smørgrav /* 687af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 688af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 689af12a3e7SDag-Erling Smørgrav * newlines. 690af12a3e7SDag-Erling Smørgrav */ 691af12a3e7SDag-Erling Smørgrav char * 692af12a3e7SDag-Erling Smørgrav channel_open_message(void) 693af12a3e7SDag-Erling Smørgrav { 694af12a3e7SDag-Erling Smørgrav Buffer buffer; 695af12a3e7SDag-Erling Smørgrav Channel *c; 696af12a3e7SDag-Erling Smørgrav char buf[1024], *cp; 69721e764dfSDag-Erling Smørgrav u_int i; 698af12a3e7SDag-Erling Smørgrav 699af12a3e7SDag-Erling Smørgrav buffer_init(&buffer); 700af12a3e7SDag-Erling Smørgrav snprintf(buf, sizeof buf, "The following connections are open:\r\n"); 701af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 702af12a3e7SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 703af12a3e7SDag-Erling Smørgrav c = channels[i]; 704af12a3e7SDag-Erling Smørgrav if (c == NULL) 705af12a3e7SDag-Erling Smørgrav continue; 706af12a3e7SDag-Erling Smørgrav switch (c->type) { 707af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 708af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 709af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 710af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 711af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 712af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 713e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 714b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 715a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 716a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 717af12a3e7SDag-Erling Smørgrav continue; 718af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 719af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 720af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 721af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 722af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 723af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 724af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_INPUT_DRAINING: 725af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OUTPUT_DRAINING: 726*ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 727*ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 72821e764dfSDag-Erling Smørgrav snprintf(buf, sizeof buf, 729acc1a9efSDag-Erling Smørgrav " #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n", 730af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 731af12a3e7SDag-Erling Smørgrav c->type, c->remote_id, 732af12a3e7SDag-Erling Smørgrav c->istate, buffer_len(&c->input), 733af12a3e7SDag-Erling Smørgrav c->ostate, buffer_len(&c->output), 734b15c8340SDag-Erling Smørgrav c->rfd, c->wfd, c->ctl_chan); 735af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, buf, strlen(buf)); 736af12a3e7SDag-Erling Smørgrav continue; 737af12a3e7SDag-Erling Smørgrav default: 738af12a3e7SDag-Erling Smørgrav fatal("channel_open_message: bad channel type %d", c->type); 739af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 740af12a3e7SDag-Erling Smørgrav } 741af12a3e7SDag-Erling Smørgrav } 742af12a3e7SDag-Erling Smørgrav buffer_append(&buffer, "\0", 1); 743bc5531deSDag-Erling Smørgrav cp = xstrdup((char *)buffer_ptr(&buffer)); 744af12a3e7SDag-Erling Smørgrav buffer_free(&buffer); 745af12a3e7SDag-Erling Smørgrav return cp; 746af12a3e7SDag-Erling Smørgrav } 747af12a3e7SDag-Erling Smørgrav 748af12a3e7SDag-Erling Smørgrav void 749af12a3e7SDag-Erling Smørgrav channel_send_open(int id) 750af12a3e7SDag-Erling Smørgrav { 751af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 752f388f5efSDag-Erling Smørgrav 753af12a3e7SDag-Erling Smørgrav if (c == NULL) { 754221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 755af12a3e7SDag-Erling Smørgrav return; 756af12a3e7SDag-Erling Smørgrav } 757e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 758af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 759af12a3e7SDag-Erling Smørgrav packet_put_cstring(c->ctype); 760af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 761af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 762af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 763af12a3e7SDag-Erling Smørgrav packet_send(); 764af12a3e7SDag-Erling Smørgrav } 765af12a3e7SDag-Erling Smørgrav 766af12a3e7SDag-Erling Smørgrav void 767e73e9afaSDag-Erling Smørgrav channel_request_start(int id, char *service, int wantconfirm) 768af12a3e7SDag-Erling Smørgrav { 769e73e9afaSDag-Erling Smørgrav Channel *c = channel_lookup(id); 770f388f5efSDag-Erling Smørgrav 771af12a3e7SDag-Erling Smørgrav if (c == NULL) { 772221552e4SDag-Erling Smørgrav logit("channel_request_start: %d: unknown channel id", id); 773af12a3e7SDag-Erling Smørgrav return; 774af12a3e7SDag-Erling Smørgrav } 77521e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 776af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_REQUEST); 777af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 778af12a3e7SDag-Erling Smørgrav packet_put_cstring(service); 779af12a3e7SDag-Erling Smørgrav packet_put_char(wantconfirm); 780af12a3e7SDag-Erling Smørgrav } 781333ee039SDag-Erling Smørgrav 782af12a3e7SDag-Erling Smørgrav void 783d4af9e69SDag-Erling Smørgrav channel_register_status_confirm(int id, channel_confirm_cb *cb, 784d4af9e69SDag-Erling Smørgrav channel_confirm_abandon_cb *abandon_cb, void *ctx) 785d4af9e69SDag-Erling Smørgrav { 786d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 787d4af9e69SDag-Erling Smørgrav Channel *c; 788d4af9e69SDag-Erling Smørgrav 789d4af9e69SDag-Erling Smørgrav if ((c = channel_lookup(id)) == NULL) 790d4af9e69SDag-Erling Smørgrav fatal("channel_register_expect: %d: bad id", id); 791d4af9e69SDag-Erling Smørgrav 7920a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 793d4af9e69SDag-Erling Smørgrav cc->cb = cb; 794d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 795d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 796d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 797d4af9e69SDag-Erling Smørgrav } 798d4af9e69SDag-Erling Smørgrav 799d4af9e69SDag-Erling Smørgrav void 800e2f6069cSDag-Erling Smørgrav channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) 801af12a3e7SDag-Erling Smørgrav { 802af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 803f388f5efSDag-Erling Smørgrav 804af12a3e7SDag-Erling Smørgrav if (c == NULL) { 805cce7d346SDag-Erling Smørgrav logit("channel_register_open_confirm: %d: bad id", id); 806af12a3e7SDag-Erling Smørgrav return; 807af12a3e7SDag-Erling Smørgrav } 808d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 809d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 810af12a3e7SDag-Erling Smørgrav } 811333ee039SDag-Erling Smørgrav 812af12a3e7SDag-Erling Smørgrav void 813b74df5b2SDag-Erling Smørgrav channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) 814af12a3e7SDag-Erling Smørgrav { 815b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 816f388f5efSDag-Erling Smørgrav 817af12a3e7SDag-Erling Smørgrav if (c == NULL) { 818221552e4SDag-Erling Smørgrav logit("channel_register_cleanup: %d: bad id", id); 819af12a3e7SDag-Erling Smørgrav return; 820af12a3e7SDag-Erling Smørgrav } 821af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 822b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 823af12a3e7SDag-Erling Smørgrav } 824333ee039SDag-Erling Smørgrav 825af12a3e7SDag-Erling Smørgrav void 826af12a3e7SDag-Erling Smørgrav channel_cancel_cleanup(int id) 827af12a3e7SDag-Erling Smørgrav { 828b74df5b2SDag-Erling Smørgrav Channel *c = channel_by_id(id); 829f388f5efSDag-Erling Smørgrav 830af12a3e7SDag-Erling Smørgrav if (c == NULL) { 831221552e4SDag-Erling Smørgrav logit("channel_cancel_cleanup: %d: bad id", id); 832af12a3e7SDag-Erling Smørgrav return; 833af12a3e7SDag-Erling Smørgrav } 834af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 835b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 836af12a3e7SDag-Erling Smørgrav } 837333ee039SDag-Erling Smørgrav 838af12a3e7SDag-Erling Smørgrav void 839b74df5b2SDag-Erling Smørgrav channel_register_filter(int id, channel_infilter_fn *ifn, 840d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 841af12a3e7SDag-Erling Smørgrav { 842af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 843f388f5efSDag-Erling Smørgrav 844af12a3e7SDag-Erling Smørgrav if (c == NULL) { 845221552e4SDag-Erling Smørgrav logit("channel_register_filter: %d: bad id", id); 846af12a3e7SDag-Erling Smørgrav return; 847af12a3e7SDag-Erling Smørgrav } 848b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 849b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 850d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 851d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 852af12a3e7SDag-Erling Smørgrav } 853af12a3e7SDag-Erling Smørgrav 854af12a3e7SDag-Erling Smørgrav void 855af12a3e7SDag-Erling Smørgrav channel_set_fds(int id, int rfd, int wfd, int efd, 856d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 857af12a3e7SDag-Erling Smørgrav { 858af12a3e7SDag-Erling Smørgrav Channel *c = channel_lookup(id); 859f388f5efSDag-Erling Smørgrav 860af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 861af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 862d4af9e69SDag-Erling Smørgrav channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); 863af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 864af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 865af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 866af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 867af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 868af12a3e7SDag-Erling Smørgrav packet_send(); 869511b41d2SMark Murray } 870511b41d2SMark Murray 871511b41d2SMark Murray /* 872a04a10f8SKris Kennaway * 'channel_pre*' are called just before select() to add any bits relevant to 873a04a10f8SKris Kennaway * channels in the select bitmasks. 874511b41d2SMark Murray */ 875a04a10f8SKris Kennaway /* 876a04a10f8SKris Kennaway * 'channel_post*': perform any appropriate operations for channels which 877a04a10f8SKris Kennaway * have events pending. 878a04a10f8SKris Kennaway */ 879a04a10f8SKris Kennaway typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); 880a04a10f8SKris Kennaway chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; 881a04a10f8SKris Kennaway chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; 882511b41d2SMark Murray 883333ee039SDag-Erling Smørgrav /* ARGSUSED */ 884af12a3e7SDag-Erling Smørgrav static void 885a04a10f8SKris Kennaway channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) 886511b41d2SMark Murray { 887a04a10f8SKris Kennaway FD_SET(c->sock, readset); 888a04a10f8SKris Kennaway } 889a04a10f8SKris Kennaway 890333ee039SDag-Erling Smørgrav /* ARGSUSED */ 891af12a3e7SDag-Erling Smørgrav static void 892ca3176e7SBrian Feldman channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) 893ca3176e7SBrian Feldman { 894ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 895ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 896ca3176e7SBrian Feldman } 897ca3176e7SBrian Feldman 898af12a3e7SDag-Erling Smørgrav static void 899a04a10f8SKris Kennaway channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) 900a04a10f8SKris Kennaway { 901a04a10f8SKris Kennaway if (buffer_len(&c->input) < packet_get_maxsize()) 902a04a10f8SKris Kennaway FD_SET(c->sock, readset); 903a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) 904a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 905a04a10f8SKris Kennaway } 906a04a10f8SKris Kennaway 907af12a3e7SDag-Erling Smørgrav static void 908af12a3e7SDag-Erling Smørgrav channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) 909a04a10f8SKris Kennaway { 91060c59fadSDag-Erling Smørgrav u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); 911a04a10f8SKris Kennaway 912a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 913af12a3e7SDag-Erling Smørgrav limit > 0 && 914333ee039SDag-Erling Smørgrav buffer_len(&c->input) < limit && 915333ee039SDag-Erling Smørgrav buffer_check_alloc(&c->input, CHAN_RBUF)) 916a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 917a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 918a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 919a04a10f8SKris Kennaway if (buffer_len(&c->output) > 0) { 920a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 921a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 92280628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 92380628bacSDag-Erling Smørgrav debug2("channel %d: obuf_empty delayed efd %d/(%d)", 92480628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 92580628bacSDag-Erling Smørgrav else 926a04a10f8SKris Kennaway chan_obuf_empty(c); 927a04a10f8SKris Kennaway } 928a04a10f8SKris Kennaway } 929a04a10f8SKris Kennaway /** XXX check close conditions, too */ 930d4af9e69SDag-Erling Smørgrav if (compat20 && c->efd != -1 && 931d4af9e69SDag-Erling Smørgrav !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { 932a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 933a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) 934a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 935e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 936e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 937e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 938a04a10f8SKris Kennaway buffer_len(&c->extended) < c->remote_window) 939a04a10f8SKris Kennaway FD_SET(c->efd, readset); 940a04a10f8SKris Kennaway } 94121e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 942a04a10f8SKris Kennaway } 943a04a10f8SKris Kennaway 944333ee039SDag-Erling Smørgrav /* ARGSUSED */ 945af12a3e7SDag-Erling Smørgrav static void 946a04a10f8SKris Kennaway channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) 947a04a10f8SKris Kennaway { 948a04a10f8SKris Kennaway if (buffer_len(&c->input) == 0) { 949a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_CLOSE); 950a04a10f8SKris Kennaway packet_put_int(c->remote_id); 951a04a10f8SKris Kennaway packet_send(); 952a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 953221552e4SDag-Erling Smørgrav debug2("channel %d: closing after input drain.", c->self); 954a04a10f8SKris Kennaway } 955a04a10f8SKris Kennaway } 956a04a10f8SKris Kennaway 957333ee039SDag-Erling Smørgrav /* ARGSUSED */ 958af12a3e7SDag-Erling Smørgrav static void 959a04a10f8SKris Kennaway channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) 960a04a10f8SKris Kennaway { 961a04a10f8SKris Kennaway if (buffer_len(&c->output) == 0) 962af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 963a04a10f8SKris Kennaway else 964a04a10f8SKris Kennaway FD_SET(c->sock, writeset); 965a04a10f8SKris Kennaway } 966a04a10f8SKris Kennaway 967a04a10f8SKris Kennaway /* 968a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 969a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 970a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 971a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 972a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 973a04a10f8SKris Kennaway * XXX All this happens at the client side. 974af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 975a04a10f8SKris Kennaway */ 976af12a3e7SDag-Erling Smørgrav static int 977af12a3e7SDag-Erling Smørgrav x11_open_helper(Buffer *b) 978a04a10f8SKris Kennaway { 979ca3176e7SBrian Feldman u_char *ucp; 980ca3176e7SBrian Feldman u_int proto_len, data_len; 981511b41d2SMark Murray 982557f75e5SDag-Erling Smørgrav /* Is this being called after the refusal deadline? */ 983557f75e5SDag-Erling Smørgrav if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 984557f75e5SDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 985557f75e5SDag-Erling Smørgrav "expired"); 986557f75e5SDag-Erling Smørgrav return -1; 987557f75e5SDag-Erling Smørgrav } 988557f75e5SDag-Erling Smørgrav 989511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 990af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 12) 991a04a10f8SKris Kennaway return 0; 992511b41d2SMark Murray 993511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 994af12a3e7SDag-Erling Smørgrav ucp = buffer_ptr(b); 995511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 996511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 997511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 998511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 999511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 1000511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 1001511b41d2SMark Murray } else { 1002221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 1003511b41d2SMark Murray ucp[0]); 1004a04a10f8SKris Kennaway return -1; 1005511b41d2SMark Murray } 1006511b41d2SMark Murray 1007511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 1008af12a3e7SDag-Erling Smørgrav if (buffer_len(b) < 1009511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 1010a04a10f8SKris Kennaway return 0; 1011511b41d2SMark Murray 1012511b41d2SMark Murray /* Check if authentication protocol matches. */ 1013511b41d2SMark Murray if (proto_len != strlen(x11_saved_proto) || 1014511b41d2SMark Murray memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { 1015221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 1016a04a10f8SKris Kennaway return -1; 1017511b41d2SMark Murray } 1018511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 1019511b41d2SMark Murray if (data_len != x11_fake_data_len || 1020e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 1021511b41d2SMark Murray x11_fake_data, x11_fake_data_len) != 0) { 1022221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 1023a04a10f8SKris Kennaway return -1; 1024511b41d2SMark Murray } 1025511b41d2SMark Murray /* Check fake data length */ 1026511b41d2SMark Murray if (x11_fake_data_len != x11_saved_data_len) { 1027511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 1028511b41d2SMark Murray x11_fake_data_len, x11_saved_data_len); 1029a04a10f8SKris Kennaway return -1; 1030511b41d2SMark Murray } 1031511b41d2SMark Murray /* 1032511b41d2SMark Murray * Received authentication protocol and data match 1033511b41d2SMark Murray * our fake data. Substitute the fake data with real 1034511b41d2SMark Murray * data. 1035511b41d2SMark Murray */ 1036511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 1037511b41d2SMark Murray x11_saved_data, x11_saved_data_len); 1038a04a10f8SKris Kennaway return 1; 1039a04a10f8SKris Kennaway } 1040511b41d2SMark Murray 1041af12a3e7SDag-Erling Smørgrav static void 1042a04a10f8SKris Kennaway channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) 1043a04a10f8SKris Kennaway { 1044af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 1045f388f5efSDag-Erling Smørgrav 1046a04a10f8SKris Kennaway if (ret == 1) { 1047511b41d2SMark Murray /* Start normal processing for the channel. */ 1048a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1049a04a10f8SKris Kennaway channel_pre_open_13(c, readset, writeset); 1050a04a10f8SKris Kennaway } else if (ret == -1) { 1051511b41d2SMark Murray /* 1052511b41d2SMark Murray * We have received an X11 connection that has bad 1053511b41d2SMark Murray * authentication information. 1054511b41d2SMark Murray */ 1055221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 1056a04a10f8SKris Kennaway buffer_clear(&c->input); 1057a04a10f8SKris Kennaway buffer_clear(&c->output); 1058af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1059a04a10f8SKris Kennaway c->sock = -1; 1060a04a10f8SKris Kennaway c->type = SSH_CHANNEL_CLOSED; 1061511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE); 1062a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1063511b41d2SMark Murray packet_send(); 1064511b41d2SMark Murray } 1065511b41d2SMark Murray } 1066511b41d2SMark Murray 1067af12a3e7SDag-Erling Smørgrav static void 1068a04a10f8SKris Kennaway channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) 1069a04a10f8SKris Kennaway { 1070af12a3e7SDag-Erling Smørgrav int ret = x11_open_helper(&c->output); 1071af12a3e7SDag-Erling Smørgrav 1072af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 1073af12a3e7SDag-Erling Smørgrav 1074a04a10f8SKris Kennaway if (ret == 1) { 1075a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1076af12a3e7SDag-Erling Smørgrav channel_pre_open(c, readset, writeset); 1077a04a10f8SKris Kennaway } else if (ret == -1) { 1078221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 1079221552e4SDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); 1080af12a3e7SDag-Erling Smørgrav chan_read_failed(c); 1081af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 1082af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 1083af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1084af12a3e7SDag-Erling Smørgrav /* for proto v1, the peer will send an IEOF */ 1085af12a3e7SDag-Erling Smørgrav if (compat20) 1086a04a10f8SKris Kennaway chan_write_failed(c); 1087af12a3e7SDag-Erling Smørgrav else 1088af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1089221552e4SDag-Erling Smørgrav debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 1090a04a10f8SKris Kennaway } 1091a04a10f8SKris Kennaway } 1092a04a10f8SKris Kennaway 1093b15c8340SDag-Erling Smørgrav static void 1094b15c8340SDag-Erling Smørgrav channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1095b15c8340SDag-Erling Smørgrav { 1096e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 1097b15c8340SDag-Erling Smørgrav buffer_check_alloc(&c->input, CHAN_RBUF)) 1098b15c8340SDag-Erling Smørgrav FD_SET(c->rfd, readset); 1099b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1100b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 1101b15c8340SDag-Erling Smørgrav buffer_clear(&c->input); 1102b15c8340SDag-Erling Smørgrav chan_ibuf_empty(c); 1103b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 1104b15c8340SDag-Erling Smørgrav chan_rcvd_oclose(c); 1105b15c8340SDag-Erling Smørgrav } 1106b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1107b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1108b15c8340SDag-Erling Smørgrav if (buffer_len(&c->output) > 0) 1109b15c8340SDag-Erling Smørgrav FD_SET(c->wfd, writeset); 1110b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 1111b15c8340SDag-Erling Smørgrav chan_obuf_empty(c); 1112b15c8340SDag-Erling Smørgrav } 1113b15c8340SDag-Erling Smørgrav } 1114b15c8340SDag-Erling Smørgrav 1115ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1116333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1117af12a3e7SDag-Erling Smørgrav static int 1118ca3176e7SBrian Feldman channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) 1119ca3176e7SBrian Feldman { 1120f388f5efSDag-Erling Smørgrav char *p, *host; 1121cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1122ca3176e7SBrian Feldman char username[256]; 1123ca3176e7SBrian Feldman struct { 1124ca3176e7SBrian Feldman u_int8_t version; 1125ca3176e7SBrian Feldman u_int8_t command; 1126ca3176e7SBrian Feldman u_int16_t dest_port; 1127ca3176e7SBrian Feldman struct in_addr dest_addr; 1128ca3176e7SBrian Feldman } s4_req, s4_rsp; 1129ca3176e7SBrian Feldman 1130ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1131ca3176e7SBrian Feldman 1132ca3176e7SBrian Feldman have = buffer_len(&c->input); 1133ca3176e7SBrian Feldman len = sizeof(s4_req); 1134ca3176e7SBrian Feldman if (have < len) 1135ca3176e7SBrian Feldman return 0; 1136bc5531deSDag-Erling Smørgrav p = (char *)buffer_ptr(&c->input); 1137cce7d346SDag-Erling Smørgrav 1138cce7d346SDag-Erling Smørgrav need = 1; 1139cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1140cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1141cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1142cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1143cce7d346SDag-Erling Smørgrav need = 2; 1144cce7d346SDag-Erling Smørgrav } 1145cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1146ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1147ca3176e7SBrian Feldman if (p[i] == '\0') { 1148cce7d346SDag-Erling Smørgrav found++; 1149cce7d346SDag-Erling Smørgrav if (found == need) 1150ca3176e7SBrian Feldman break; 1151ca3176e7SBrian Feldman } 1152ca3176e7SBrian Feldman if (i > 1024) { 1153ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1154ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1155ca3176e7SBrian Feldman c->self); 1156ca3176e7SBrian Feldman return -1; 1157ca3176e7SBrian Feldman } 1158ca3176e7SBrian Feldman } 1159cce7d346SDag-Erling Smørgrav if (found < need) 1160ca3176e7SBrian Feldman return 0; 1161ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.version, 1); 1162ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.command, 1); 1163ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_port, 2); 1164ca3176e7SBrian Feldman buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); 1165ca3176e7SBrian Feldman have = buffer_len(&c->input); 1166bc5531deSDag-Erling Smørgrav p = (char *)buffer_ptr(&c->input); 1167b83788ffSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) 1168b83788ffSDag-Erling Smørgrav fatal("channel %d: decode socks4: user not nul terminated", 1169b83788ffSDag-Erling Smørgrav c->self); 1170ca3176e7SBrian Feldman len = strlen(p); 1171ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1172cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1173ca3176e7SBrian Feldman if (len > have) 1174ca3176e7SBrian Feldman fatal("channel %d: decode socks4: len %d > have %d", 1175ca3176e7SBrian Feldman c->self, len, have); 1176ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 1177ca3176e7SBrian Feldman buffer_consume(&c->input, len); 1178ca3176e7SBrian Feldman 1179e4a9863fSDag-Erling Smørgrav free(c->path); 1180cce7d346SDag-Erling Smørgrav c->path = NULL; 1181cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1182ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1183cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1184cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 1185cce7d346SDag-Erling Smørgrav have = buffer_len(&c->input); 1186bc5531deSDag-Erling Smørgrav p = (char *)buffer_ptr(&c->input); 1187cce7d346SDag-Erling Smørgrav len = strlen(p); 1188cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1189cce7d346SDag-Erling Smørgrav c->self, p, len); 1190cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1191cce7d346SDag-Erling Smørgrav if (len > have) 1192cce7d346SDag-Erling Smørgrav fatal("channel %d: decode socks4a: len %d > have %d", 1193cce7d346SDag-Erling Smørgrav c->self, len, have); 1194cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1195cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1196cce7d346SDag-Erling Smørgrav c->self, p); 1197cce7d346SDag-Erling Smørgrav return -1; 1198cce7d346SDag-Erling Smørgrav } 1199cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 1200cce7d346SDag-Erling Smørgrav buffer_consume(&c->input, len); 1201cce7d346SDag-Erling Smørgrav } 1202ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1203ca3176e7SBrian Feldman 1204221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1205cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1206ca3176e7SBrian Feldman 1207ca3176e7SBrian Feldman if (s4_req.command != 1) { 1208cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1209cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1210ca3176e7SBrian Feldman return -1; 1211ca3176e7SBrian Feldman } 1212ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1213ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1214ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1215ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 1216333ee039SDag-Erling Smørgrav buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); 1217ca3176e7SBrian Feldman return 1; 1218ca3176e7SBrian Feldman } 1219ca3176e7SBrian Feldman 1220221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1221221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1222221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1223221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1224221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1225221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1226221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1227221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1228221552e4SDag-Erling Smørgrav 1229333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1230221552e4SDag-Erling Smørgrav static int 1231221552e4SDag-Erling Smørgrav channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) 1232221552e4SDag-Erling Smørgrav { 1233221552e4SDag-Erling Smørgrav struct { 1234221552e4SDag-Erling Smørgrav u_int8_t version; 1235221552e4SDag-Erling Smørgrav u_int8_t command; 1236221552e4SDag-Erling Smørgrav u_int8_t reserved; 1237221552e4SDag-Erling Smørgrav u_int8_t atyp; 1238221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1239221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1240e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 1241e4a9863fSDag-Erling Smørgrav u_char *p; 1242333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 1243221552e4SDag-Erling Smørgrav 1244221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 1245221552e4SDag-Erling Smørgrav p = buffer_ptr(&c->input); 1246221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1247221552e4SDag-Erling Smørgrav return -1; 1248221552e4SDag-Erling Smørgrav have = buffer_len(&c->input); 1249221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1250221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1251221552e4SDag-Erling Smørgrav if (have < 2) 1252221552e4SDag-Erling Smørgrav return 0; 1253221552e4SDag-Erling Smørgrav nmethods = p[1]; 1254221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1255221552e4SDag-Erling Smørgrav return 0; 1256221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1257221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1258221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1259221552e4SDag-Erling Smørgrav found = 1; 1260221552e4SDag-Erling Smørgrav break; 1261221552e4SDag-Erling Smørgrav } 1262221552e4SDag-Erling Smørgrav } 1263221552e4SDag-Erling Smørgrav if (!found) { 1264221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1265221552e4SDag-Erling Smørgrav c->self); 1266221552e4SDag-Erling Smørgrav return -1; 1267221552e4SDag-Erling Smørgrav } 1268221552e4SDag-Erling Smørgrav buffer_consume(&c->input, nmethods + 2); 1269221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, 0x05); /* version */ 1270221552e4SDag-Erling Smørgrav buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ 1271221552e4SDag-Erling Smørgrav FD_SET(c->sock, writeset); 1272221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1273221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1274221552e4SDag-Erling Smørgrav return 0; /* need more */ 1275221552e4SDag-Erling Smørgrav } 1276221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1277221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1278221552e4SDag-Erling Smørgrav return 0; /* need more */ 1279333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1280221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1281221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1282221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1283221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1284221552e4SDag-Erling Smørgrav return -1; 1285221552e4SDag-Erling Smørgrav } 1286221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1287221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1288221552e4SDag-Erling Smørgrav addrlen = 4; 1289221552e4SDag-Erling Smørgrav af = AF_INET; 1290221552e4SDag-Erling Smørgrav break; 1291221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1292221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1293221552e4SDag-Erling Smørgrav af = -1; 1294221552e4SDag-Erling Smørgrav break; 1295221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1296221552e4SDag-Erling Smørgrav addrlen = 16; 1297221552e4SDag-Erling Smørgrav af = AF_INET6; 1298221552e4SDag-Erling Smørgrav break; 1299221552e4SDag-Erling Smørgrav default: 1300221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1301221552e4SDag-Erling Smørgrav return -1; 1302221552e4SDag-Erling Smørgrav } 1303333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1304333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1305333ee039SDag-Erling Smørgrav need++; 1306333ee039SDag-Erling Smørgrav if (have < need) 1307221552e4SDag-Erling Smørgrav return 0; 1308221552e4SDag-Erling Smørgrav buffer_consume(&c->input, sizeof(s5_req)); 1309221552e4SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1310221552e4SDag-Erling Smørgrav buffer_consume(&c->input, 1); /* host string length */ 1311e4a9863fSDag-Erling Smørgrav buffer_get(&c->input, &dest_addr, addrlen); 1312221552e4SDag-Erling Smørgrav buffer_get(&c->input, (char *)&dest_port, 2); 1313221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1314e4a9863fSDag-Erling Smørgrav free(c->path); 1315cce7d346SDag-Erling Smørgrav c->path = NULL; 1316cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1317cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1318cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1319cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1320221552e4SDag-Erling Smørgrav return -1; 1321cce7d346SDag-Erling Smørgrav } 1322cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1323cce7d346SDag-Erling Smørgrav } else { 1324cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1325cce7d346SDag-Erling Smørgrav return -1; 1326cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1327cce7d346SDag-Erling Smørgrav } 1328221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1329221552e4SDag-Erling Smørgrav 1330221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1331221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1332221552e4SDag-Erling Smørgrav 1333221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1334221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1335221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1336221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1337221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1338221552e4SDag-Erling Smørgrav 1339333ee039SDag-Erling Smørgrav buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); 1340e4a9863fSDag-Erling Smørgrav buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ 1341333ee039SDag-Erling Smørgrav buffer_append(&c->output, &dest_port, sizeof(dest_port)); 1342221552e4SDag-Erling Smørgrav return 1; 1343221552e4SDag-Erling Smørgrav } 1344221552e4SDag-Erling Smørgrav 1345b15c8340SDag-Erling Smørgrav Channel * 1346b15c8340SDag-Erling Smørgrav channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, 1347b15c8340SDag-Erling Smørgrav int in, int out) 1348b15c8340SDag-Erling Smørgrav { 1349b15c8340SDag-Erling Smørgrav Channel *c; 1350b15c8340SDag-Erling Smørgrav 1351b15c8340SDag-Erling Smørgrav debug("channel_connect_stdio_fwd %s:%d", host_to_connect, 1352b15c8340SDag-Erling Smørgrav port_to_connect); 1353b15c8340SDag-Erling Smørgrav 1354b15c8340SDag-Erling Smørgrav c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, 1355b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1356b15c8340SDag-Erling Smørgrav 0, "stdio-forward", /*nonblock*/0); 1357b15c8340SDag-Erling Smørgrav 1358b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1359b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1360b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1361b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1362b15c8340SDag-Erling Smørgrav 1363b15c8340SDag-Erling Smørgrav channel_register_fds(c, in, out, -1, 0, 1, 0); 1364b15c8340SDag-Erling Smørgrav port_open_helper(c, "direct-tcpip"); 1365b15c8340SDag-Erling Smørgrav 1366b15c8340SDag-Erling Smørgrav return c; 1367b15c8340SDag-Erling Smørgrav } 1368b15c8340SDag-Erling Smørgrav 1369ca3176e7SBrian Feldman /* dynamic port forwarding */ 1370af12a3e7SDag-Erling Smørgrav static void 1371ca3176e7SBrian Feldman channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) 1372ca3176e7SBrian Feldman { 1373ca3176e7SBrian Feldman u_char *p; 1374d4ecd108SDag-Erling Smørgrav u_int have; 1375d4ecd108SDag-Erling Smørgrav int ret; 1376ca3176e7SBrian Feldman 1377ca3176e7SBrian Feldman have = buffer_len(&c->input); 1378ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 1379ca3176e7SBrian Feldman /* buffer_dump(&c->input); */ 1380ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1381221552e4SDag-Erling Smørgrav if (have < 3) { 1382ca3176e7SBrian Feldman /* need more */ 1383ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1384ca3176e7SBrian Feldman return; 1385ca3176e7SBrian Feldman } 1386ca3176e7SBrian Feldman /* try to guess the protocol */ 1387ca3176e7SBrian Feldman p = buffer_ptr(&c->input); 1388ca3176e7SBrian Feldman switch (p[0]) { 1389ca3176e7SBrian Feldman case 0x04: 1390ca3176e7SBrian Feldman ret = channel_decode_socks4(c, readset, writeset); 1391ca3176e7SBrian Feldman break; 1392221552e4SDag-Erling Smørgrav case 0x05: 1393221552e4SDag-Erling Smørgrav ret = channel_decode_socks5(c, readset, writeset); 1394221552e4SDag-Erling Smørgrav break; 1395ca3176e7SBrian Feldman default: 1396ca3176e7SBrian Feldman ret = -1; 1397ca3176e7SBrian Feldman break; 1398ca3176e7SBrian Feldman } 1399ca3176e7SBrian Feldman if (ret < 0) { 1400af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1401ca3176e7SBrian Feldman } else if (ret == 0) { 1402ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1403ca3176e7SBrian Feldman /* need more */ 1404ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1405ca3176e7SBrian Feldman } else { 1406ca3176e7SBrian Feldman /* switch to the next state */ 1407ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 1408ca3176e7SBrian Feldman port_open_helper(c, "direct-tcpip"); 1409ca3176e7SBrian Feldman } 1410ca3176e7SBrian Feldman } 1411ca3176e7SBrian Feldman 1412a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1413333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1414af12a3e7SDag-Erling Smørgrav static void 1415a04a10f8SKris Kennaway channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) 1416511b41d2SMark Murray { 1417af12a3e7SDag-Erling Smørgrav Channel *nc; 1418d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1419e4a9863fSDag-Erling Smørgrav int newsock, oerrno; 1420511b41d2SMark Murray socklen_t addrlen; 1421ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1422a04a10f8SKris Kennaway int remote_port; 1423511b41d2SMark Murray 1424a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1425511b41d2SMark Murray debug("X11 connection requested."); 1426511b41d2SMark Murray addrlen = sizeof(addr); 1427d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1428af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1429e4a9863fSDag-Erling Smørgrav oerrno = errno; 1430221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 1431af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->sock); 1432af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1433e4a9863fSDag-Erling Smørgrav errno = oerrno; 1434af12a3e7SDag-Erling Smørgrav } 1435511b41d2SMark Murray if (newsock < 0) { 1436e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1437e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1438511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1439462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1440e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1441a04a10f8SKris Kennaway return; 1442511b41d2SMark Murray } 1443af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1444ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1445a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1446511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1447ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1448a04a10f8SKris Kennaway 1449af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted x11 socket", 1450a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1451221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 1452a04a10f8SKris Kennaway if (compat20) { 1453a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN); 1454a04a10f8SKris Kennaway packet_put_cstring("x11"); 1455af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1456af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_window_max); 1457af12a3e7SDag-Erling Smørgrav packet_put_int(nc->local_maxpacket); 1458ca3176e7SBrian Feldman /* originator ipaddr and port */ 1459ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1460a04a10f8SKris Kennaway if (datafellows & SSH_BUG_X11FWD) { 1461221552e4SDag-Erling Smørgrav debug2("ssh2 x11 bug compat mode"); 1462a04a10f8SKris Kennaway } else { 1463a04a10f8SKris Kennaway packet_put_int(remote_port); 1464a04a10f8SKris Kennaway } 1465a04a10f8SKris Kennaway packet_send(); 1466a04a10f8SKris Kennaway } else { 1467511b41d2SMark Murray packet_start(SSH_SMSG_X11_OPEN); 1468af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1469af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1470af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1471af12a3e7SDag-Erling Smørgrav packet_put_cstring(buf); 1472511b41d2SMark Murray packet_send(); 1473511b41d2SMark Murray } 1474e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1475a04a10f8SKris Kennaway } 1476a04a10f8SKris Kennaway } 1477511b41d2SMark Murray 1478af12a3e7SDag-Erling Smørgrav static void 1479ca3176e7SBrian Feldman port_open_helper(Channel *c, char *rtype) 1480ca3176e7SBrian Feldman { 1481ca3176e7SBrian Feldman char buf[1024]; 1482f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1483076ad2f8SDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 1484ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1485d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 1486ca3176e7SBrian Feldman 1487b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1488b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1489e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1490b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1491b15c8340SDag-Erling Smørgrav remote_port = 65535; 1492b15c8340SDag-Erling Smørgrav } 1493b15c8340SDag-Erling Smørgrav 1494ca3176e7SBrian Feldman snprintf(buf, sizeof buf, 1495ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1496f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1497ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1498f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1499ca3176e7SBrian Feldman 1500e4a9863fSDag-Erling Smørgrav free(c->remote_name); 1501ca3176e7SBrian Feldman c->remote_name = xstrdup(buf); 1502ca3176e7SBrian Feldman 1503ca3176e7SBrian Feldman if (compat20) { 1504ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1505ca3176e7SBrian Feldman packet_put_cstring(rtype); 1506ca3176e7SBrian Feldman packet_put_int(c->self); 1507ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1508ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1509a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1510ca3176e7SBrian Feldman /* target host, port */ 1511ca3176e7SBrian Feldman packet_put_cstring(c->path); 1512ca3176e7SBrian Feldman packet_put_int(c->host_port); 1513a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1514a0ee8cc6SDag-Erling Smørgrav /* target path */ 1515a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(c->path); 1516a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1517a0ee8cc6SDag-Erling Smørgrav /* listen path */ 1518a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(c->path); 1519ca3176e7SBrian Feldman } else { 1520ca3176e7SBrian Feldman /* listen address, port */ 1521ca3176e7SBrian Feldman packet_put_cstring(c->path); 1522f7167e0eSDag-Erling Smørgrav packet_put_int(local_port); 1523ca3176e7SBrian Feldman } 1524a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1525a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 1526a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(""); 1527a0ee8cc6SDag-Erling Smørgrav } else { 1528ca3176e7SBrian Feldman /* originator host and port */ 1529ca3176e7SBrian Feldman packet_put_cstring(remote_ipaddr); 1530d4ecd108SDag-Erling Smørgrav packet_put_int((u_int)remote_port); 1531a0ee8cc6SDag-Erling Smørgrav } 1532ca3176e7SBrian Feldman packet_send(); 1533ca3176e7SBrian Feldman } else { 1534ca3176e7SBrian Feldman packet_start(SSH_MSG_PORT_OPEN); 1535ca3176e7SBrian Feldman packet_put_int(c->self); 1536ca3176e7SBrian Feldman packet_put_cstring(c->path); 1537ca3176e7SBrian Feldman packet_put_int(c->host_port); 1538af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & 1539af12a3e7SDag-Erling Smørgrav SSH_PROTOFLAG_HOST_IN_FWD_OPEN) 1540ca3176e7SBrian Feldman packet_put_cstring(c->remote_name); 1541ca3176e7SBrian Feldman packet_send(); 1542ca3176e7SBrian Feldman } 1543e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1544f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1545ca3176e7SBrian Feldman } 1546ca3176e7SBrian Feldman 1547b74df5b2SDag-Erling Smørgrav static void 1548b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(int fd) 1549b74df5b2SDag-Erling Smørgrav { 1550b74df5b2SDag-Erling Smørgrav int on = 1; 1551b74df5b2SDag-Erling Smørgrav 1552b74df5b2SDag-Erling Smørgrav /* 1553b74df5b2SDag-Erling Smørgrav * Set socket options. 1554b74df5b2SDag-Erling Smørgrav * Allow local port reuse in TIME_WAIT. 1555b74df5b2SDag-Erling Smørgrav */ 1556b74df5b2SDag-Erling Smørgrav if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 1557b74df5b2SDag-Erling Smørgrav error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 1558b74df5b2SDag-Erling Smørgrav } 1559b74df5b2SDag-Erling Smørgrav 1560557f75e5SDag-Erling Smørgrav void 1561557f75e5SDag-Erling Smørgrav channel_set_x11_refuse_time(u_int refuse_time) 1562557f75e5SDag-Erling Smørgrav { 1563557f75e5SDag-Erling Smørgrav x11_refuse_time = refuse_time; 1564557f75e5SDag-Erling Smørgrav } 1565557f75e5SDag-Erling Smørgrav 1566511b41d2SMark Murray /* 1567a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1568511b41d2SMark Murray */ 1569333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1570af12a3e7SDag-Erling Smørgrav static void 1571a04a10f8SKris Kennaway channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) 1572a04a10f8SKris Kennaway { 1573ca3176e7SBrian Feldman Channel *nc; 1574d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1575af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1576a04a10f8SKris Kennaway socklen_t addrlen; 1577ca3176e7SBrian Feldman char *rtype; 1578a04a10f8SKris Kennaway 1579a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1580a04a10f8SKris Kennaway debug("Connection to port %d forwarding " 1581a04a10f8SKris Kennaway "to %.100s port %d requested.", 1582a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1583ca3176e7SBrian Feldman 1584af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1585af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1586af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1587a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1588a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1589a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1590a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1591a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1592a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1593a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1594af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1595af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1596af12a3e7SDag-Erling Smørgrav } else { 1597af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1598af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1599af12a3e7SDag-Erling Smørgrav } 1600ca3176e7SBrian Feldman 1601511b41d2SMark Murray addrlen = sizeof(addr); 1602d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1603511b41d2SMark Murray if (newsock < 0) { 1604e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1605e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1606511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1607462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1608e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1609a04a10f8SKris Kennaway return; 1610511b41d2SMark Murray } 1611a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1612af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1613221552e4SDag-Erling Smørgrav nc = channel_new(rtype, nextstate, newsock, newsock, -1, 1614221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1615ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1616ca3176e7SBrian Feldman nc->host_port = c->host_port; 1617cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1618cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 1619ca3176e7SBrian Feldman 1620b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 1621ca3176e7SBrian Feldman port_open_helper(nc, rtype); 1622a04a10f8SKris Kennaway } 1623a04a10f8SKris Kennaway } 1624511b41d2SMark Murray 1625511b41d2SMark Murray /* 1626a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1627a04a10f8SKris Kennaway * clients. 1628511b41d2SMark Murray */ 1629333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1630af12a3e7SDag-Erling Smørgrav static void 1631a04a10f8SKris Kennaway channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) 1632a04a10f8SKris Kennaway { 1633af12a3e7SDag-Erling Smørgrav Channel *nc; 1634af12a3e7SDag-Erling Smørgrav int newsock; 1635d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1636a04a10f8SKris Kennaway socklen_t addrlen; 1637a04a10f8SKris Kennaway 1638a04a10f8SKris Kennaway if (FD_ISSET(c->sock, readset)) { 1639511b41d2SMark Murray addrlen = sizeof(addr); 1640d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1641511b41d2SMark Murray if (newsock < 0) { 1642462c32cbSDag-Erling Smørgrav error("accept from auth socket: %.100s", 1643462c32cbSDag-Erling Smørgrav strerror(errno)); 1644462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1645e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1646a04a10f8SKris Kennaway return; 1647511b41d2SMark Murray } 1648af12a3e7SDag-Erling Smørgrav nc = channel_new("accepted auth socket", 1649ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1650ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1651221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 1652ca3176e7SBrian Feldman if (compat20) { 1653ca3176e7SBrian Feldman packet_start(SSH2_MSG_CHANNEL_OPEN); 1654ca3176e7SBrian Feldman packet_put_cstring("auth-agent@openssh.com"); 1655af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1656ca3176e7SBrian Feldman packet_put_int(c->local_window_max); 1657ca3176e7SBrian Feldman packet_put_int(c->local_maxpacket); 1658ca3176e7SBrian Feldman } else { 1659511b41d2SMark Murray packet_start(SSH_SMSG_AGENT_OPEN); 1660af12a3e7SDag-Erling Smørgrav packet_put_int(nc->self); 1661ca3176e7SBrian Feldman } 1662511b41d2SMark Murray packet_send(); 1663511b41d2SMark Murray } 1664a04a10f8SKris Kennaway } 1665511b41d2SMark Murray 1666333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1667af12a3e7SDag-Erling Smørgrav static void 1668ca3176e7SBrian Feldman channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) 1669ca3176e7SBrian Feldman { 1670d4af9e69SDag-Erling Smørgrav int err = 0, sock; 1671af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1672af12a3e7SDag-Erling Smørgrav 1673af12a3e7SDag-Erling Smørgrav if (FD_ISSET(c->sock, writeset)) { 1674af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1675af12a3e7SDag-Erling Smørgrav err = errno; 1676af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1677af12a3e7SDag-Erling Smørgrav } 1678ca3176e7SBrian Feldman if (err == 0) { 1679d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 1680d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 1681d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1682af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1683af12a3e7SDag-Erling Smørgrav if (compat20) { 1684af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1685af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1686af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1687af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_window); 1688af12a3e7SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1689af12a3e7SDag-Erling Smørgrav } else { 1690af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1691af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1692af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 1693af12a3e7SDag-Erling Smørgrav } 1694ca3176e7SBrian Feldman } else { 1695d4af9e69SDag-Erling Smørgrav debug("channel %d: connection failed: %s", 1696ca3176e7SBrian Feldman c->self, strerror(err)); 1697d4af9e69SDag-Erling Smørgrav /* Try next address, if any */ 1698d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&c->connect_ctx)) > 0) { 1699d4af9e69SDag-Erling Smørgrav close(c->sock); 1700d4af9e69SDag-Erling Smørgrav c->sock = c->rfd = c->wfd = sock; 1701d4af9e69SDag-Erling Smørgrav channel_max_fd = channel_find_maxfd(); 1702d4af9e69SDag-Erling Smørgrav return; 1703d4af9e69SDag-Erling Smørgrav } 1704d4af9e69SDag-Erling Smørgrav /* Exhausted all addresses */ 1705d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 1706d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 1707d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1708af12a3e7SDag-Erling Smørgrav if (compat20) { 1709af12a3e7SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1710af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1711af12a3e7SDag-Erling Smørgrav packet_put_int(SSH2_OPEN_CONNECT_FAILED); 1712af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1713af12a3e7SDag-Erling Smørgrav packet_put_cstring(strerror(err)); 1714af12a3e7SDag-Erling Smørgrav packet_put_cstring(""); 1715ca3176e7SBrian Feldman } 1716af12a3e7SDag-Erling Smørgrav } else { 1717af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1718af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 1719ca3176e7SBrian Feldman } 1720af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1721af12a3e7SDag-Erling Smørgrav } 1722af12a3e7SDag-Erling Smørgrav packet_send(); 1723ca3176e7SBrian Feldman } 1724ca3176e7SBrian Feldman } 1725ca3176e7SBrian Feldman 1726333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1727af12a3e7SDag-Erling Smørgrav static int 1728a04a10f8SKris Kennaway channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 1729a04a10f8SKris Kennaway { 1730aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1731d4af9e69SDag-Erling Smørgrav int len, force; 1732511b41d2SMark Murray 1733d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 1734d4af9e69SDag-Erling Smørgrav if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { 1735333ee039SDag-Erling Smørgrav errno = 0; 1736a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1737d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || 1738d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 1739a04a10f8SKris Kennaway return 1; 1740333ee039SDag-Erling Smørgrav #ifndef PTY_ZEROREAD 17410c82706bSBrian Feldman if (len <= 0) { 1742333ee039SDag-Erling Smørgrav #else 1743333ee039SDag-Erling Smørgrav if ((!c->isatty && len <= 0) || 1744333ee039SDag-Erling Smørgrav (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1745333ee039SDag-Erling Smørgrav #endif 1746221552e4SDag-Erling Smørgrav debug2("channel %d: read<=0 rfd %d len %d", 1747a04a10f8SKris Kennaway c->self, c->rfd, len); 1748ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1749221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1750af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1751ca3176e7SBrian Feldman return -1; 1752ca3176e7SBrian Feldman } else if (compat13) { 1753af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1754a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1755221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1756a04a10f8SKris Kennaway } else { 1757a04a10f8SKris Kennaway chan_read_failed(c); 1758a04a10f8SKris Kennaway } 1759a04a10f8SKris Kennaway return -1; 1760a04a10f8SKris Kennaway } 1761b66f2d16SKris Kennaway if (c->input_filter != NULL) { 1762b66f2d16SKris Kennaway if (c->input_filter(c, buf, len) == -1) { 1763221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1764b66f2d16SKris Kennaway chan_read_failed(c); 1765b66f2d16SKris Kennaway } 1766b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1767b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->input, buf, len); 1768b66f2d16SKris Kennaway } else { 1769a04a10f8SKris Kennaway buffer_append(&c->input, buf, len); 1770a04a10f8SKris Kennaway } 1771b66f2d16SKris Kennaway } 1772a04a10f8SKris Kennaway return 1; 1773a04a10f8SKris Kennaway } 1774333ee039SDag-Erling Smørgrav 1775333ee039SDag-Erling Smørgrav /* ARGSUSED */ 1776af12a3e7SDag-Erling Smørgrav static int 1777a04a10f8SKris Kennaway channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) 1778a04a10f8SKris Kennaway { 1779ca3176e7SBrian Feldman struct termios tio; 1780b74df5b2SDag-Erling Smørgrav u_char *data = NULL, *buf; 1781e2f6069cSDag-Erling Smørgrav u_int dlen, olen = 0; 1782a04a10f8SKris Kennaway int len; 1783a04a10f8SKris Kennaway 1784a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 1785a04a10f8SKris Kennaway if (c->wfd != -1 && 1786a04a10f8SKris Kennaway FD_ISSET(c->wfd, writeset) && 1787a04a10f8SKris Kennaway buffer_len(&c->output) > 0) { 1788e2f6069cSDag-Erling Smørgrav olen = buffer_len(&c->output); 1789b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 1790b74df5b2SDag-Erling Smørgrav if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { 1791b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1792b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1793b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1794b74df5b2SDag-Erling Smørgrav else 1795b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1796b74df5b2SDag-Erling Smørgrav return -1; 1797b74df5b2SDag-Erling Smørgrav } 1798b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 1799b74df5b2SDag-Erling Smørgrav buf = data = buffer_get_string(&c->output, &dlen); 1800b74df5b2SDag-Erling Smørgrav } else { 1801b74df5b2SDag-Erling Smørgrav buf = data = buffer_ptr(&c->output); 1802af12a3e7SDag-Erling Smørgrav dlen = buffer_len(&c->output); 1803b74df5b2SDag-Erling Smørgrav } 1804b74df5b2SDag-Erling Smørgrav 1805b74df5b2SDag-Erling Smørgrav if (c->datagram) { 1806b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 1807b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1808e4a9863fSDag-Erling Smørgrav free(data); 1809d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 1810d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 1811b74df5b2SDag-Erling Smørgrav return 1; 1812b74df5b2SDag-Erling Smørgrav if (len <= 0) { 1813b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 1814b74df5b2SDag-Erling Smørgrav chan_mark_dead(c); 1815b74df5b2SDag-Erling Smørgrav else 1816b74df5b2SDag-Erling Smørgrav chan_write_failed(c); 1817b74df5b2SDag-Erling Smørgrav return -1; 1818b74df5b2SDag-Erling Smørgrav } 1819e2f6069cSDag-Erling Smørgrav goto out; 1820b74df5b2SDag-Erling Smørgrav } 1821f388f5efSDag-Erling Smørgrav #ifdef _AIX 1822f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 1823476cd3b2SDag-Erling Smørgrav if (compat20 && c->wfd_isatty) 1824476cd3b2SDag-Erling Smørgrav dlen = MIN(dlen, 8*1024); 1825f388f5efSDag-Erling Smørgrav #endif 1826b74df5b2SDag-Erling Smørgrav 1827b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1828d4af9e69SDag-Erling Smørgrav if (len < 0 && 1829d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 1830a04a10f8SKris Kennaway return 1; 1831511b41d2SMark Murray if (len <= 0) { 1832ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1833221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 1834af12a3e7SDag-Erling Smørgrav chan_mark_dead(c); 1835ca3176e7SBrian Feldman return -1; 1836ca3176e7SBrian Feldman } else if (compat13) { 1837af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 1838221552e4SDag-Erling Smørgrav debug2("channel %d: input draining.", c->self); 1839a04a10f8SKris Kennaway c->type = SSH_CHANNEL_INPUT_DRAINING; 1840511b41d2SMark Murray } else { 1841a04a10f8SKris Kennaway chan_write_failed(c); 1842511b41d2SMark Murray } 1843a04a10f8SKris Kennaway return -1; 1844511b41d2SMark Murray } 18457aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 1846b74df5b2SDag-Erling Smørgrav if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { 1847e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 1848e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 1849e0fbb1d2SBrian Feldman /* 1850e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 1851ca3176e7SBrian Feldman * traffic analysis. We need to match the 1852ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 1853b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 1854e0fbb1d2SBrian Feldman */ 1855ca3176e7SBrian Feldman packet_send_ignore(4 + len); 1856e0fbb1d2SBrian Feldman packet_send(); 1857e0fbb1d2SBrian Feldman } 1858e0fbb1d2SBrian Feldman } 18597aee6ffeSDag-Erling Smørgrav #endif 1860a04a10f8SKris Kennaway buffer_consume(&c->output, len); 1861511b41d2SMark Murray } 1862e2f6069cSDag-Erling Smørgrav out: 1863e2f6069cSDag-Erling Smørgrav if (compat20 && olen > 0) 1864e2f6069cSDag-Erling Smørgrav c->local_consumed += olen - buffer_len(&c->output); 1865a04a10f8SKris Kennaway return 1; 1866511b41d2SMark Murray } 1867333ee039SDag-Erling Smørgrav 1868af12a3e7SDag-Erling Smørgrav static int 1869a04a10f8SKris Kennaway channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 1870a04a10f8SKris Kennaway { 1871aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1872a04a10f8SKris Kennaway int len; 1873511b41d2SMark Murray 1874a04a10f8SKris Kennaway /** XXX handle drain efd, too */ 1875a04a10f8SKris Kennaway if (c->efd != -1) { 1876a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 1877a04a10f8SKris Kennaway FD_ISSET(c->efd, writeset) && 1878a04a10f8SKris Kennaway buffer_len(&c->extended) > 0) { 1879a04a10f8SKris Kennaway len = write(c->efd, buffer_ptr(&c->extended), 1880a04a10f8SKris Kennaway buffer_len(&c->extended)); 18815b9b2fafSBrian Feldman debug2("channel %d: written %d to efd %d", 1882a04a10f8SKris Kennaway c->self, len, c->efd); 1883d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 1884d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 1885ca3176e7SBrian Feldman return 1; 1886ca3176e7SBrian Feldman if (len <= 0) { 1887ca3176e7SBrian Feldman debug2("channel %d: closing write-efd %d", 1888ca3176e7SBrian Feldman c->self, c->efd); 1889af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1890ca3176e7SBrian Feldman } else { 1891a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 1892a04a10f8SKris Kennaway c->local_consumed += len; 1893a04a10f8SKris Kennaway } 1894e2f6069cSDag-Erling Smørgrav } else if (c->efd != -1 && 1895e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1896e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 1897d4af9e69SDag-Erling Smørgrav (c->detach_close || FD_ISSET(c->efd, readset))) { 1898a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 18995b9b2fafSBrian Feldman debug2("channel %d: read %d from efd %d", 1900a04a10f8SKris Kennaway c->self, len, c->efd); 1901d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 1902d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK) && !c->detach_close))) 1903ca3176e7SBrian Feldman return 1; 1904ca3176e7SBrian Feldman if (len <= 0) { 1905ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 1906a04a10f8SKris Kennaway c->self, c->efd); 1907af12a3e7SDag-Erling Smørgrav channel_close_fd(&c->efd); 1908ca3176e7SBrian Feldman } else { 1909e2f6069cSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_IGNORE) { 1910e2f6069cSDag-Erling Smørgrav debug3("channel %d: discard efd", 1911e2f6069cSDag-Erling Smørgrav c->self); 1912e2f6069cSDag-Erling Smørgrav } else 1913a04a10f8SKris Kennaway buffer_append(&c->extended, buf, len); 1914a04a10f8SKris Kennaway } 1915a04a10f8SKris Kennaway } 1916ca3176e7SBrian Feldman } 1917a04a10f8SKris Kennaway return 1; 1918a04a10f8SKris Kennaway } 1919333ee039SDag-Erling Smørgrav 192021e764dfSDag-Erling Smørgrav static int 1921ca3176e7SBrian Feldman channel_check_window(Channel *c) 1922a04a10f8SKris Kennaway { 1923ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 1924ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 1925d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 1926d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 1927d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 1928a04a10f8SKris Kennaway c->local_consumed > 0) { 1929a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); 1930a04a10f8SKris Kennaway packet_put_int(c->remote_id); 193160c59fadSDag-Erling Smørgrav packet_put_int(c->local_consumed); 1932a04a10f8SKris Kennaway packet_send(); 19335b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 1934a04a10f8SKris Kennaway c->self, c->local_window, 1935a04a10f8SKris Kennaway c->local_consumed); 193660c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 1937a04a10f8SKris Kennaway c->local_consumed = 0; 1938a04a10f8SKris Kennaway } 1939a04a10f8SKris Kennaway return 1; 1940a04a10f8SKris Kennaway } 1941a04a10f8SKris Kennaway 1942af12a3e7SDag-Erling Smørgrav static void 1943af12a3e7SDag-Erling Smørgrav channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) 1944a04a10f8SKris Kennaway { 1945a04a10f8SKris Kennaway channel_handle_rfd(c, readset, writeset); 1946a04a10f8SKris Kennaway channel_handle_wfd(c, readset, writeset); 1947af12a3e7SDag-Erling Smørgrav if (!compat20) 1948af12a3e7SDag-Erling Smørgrav return; 1949a04a10f8SKris Kennaway channel_handle_efd(c, readset, writeset); 1950ca3176e7SBrian Feldman channel_check_window(c); 1951a04a10f8SKris Kennaway } 1952a04a10f8SKris Kennaway 1953b15c8340SDag-Erling Smørgrav static u_int 1954b15c8340SDag-Erling Smørgrav read_mux(Channel *c, u_int need) 1955b15c8340SDag-Erling Smørgrav { 1956b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 1957b15c8340SDag-Erling Smørgrav int len; 1958b15c8340SDag-Erling Smørgrav u_int rlen; 1959b15c8340SDag-Erling Smørgrav 1960b15c8340SDag-Erling Smørgrav if (buffer_len(&c->input) < need) { 1961b15c8340SDag-Erling Smørgrav rlen = need - buffer_len(&c->input); 1962*ca86bcf2SDag-Erling Smørgrav len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); 1963acc1a9efSDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 1964acc1a9efSDag-Erling Smørgrav return buffer_len(&c->input); 1965b15c8340SDag-Erling Smørgrav if (len <= 0) { 1966b15c8340SDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %d", 1967b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 1968b15c8340SDag-Erling Smørgrav chan_read_failed(c); 1969b15c8340SDag-Erling Smørgrav return 0; 1970b15c8340SDag-Erling Smørgrav } else 1971b15c8340SDag-Erling Smørgrav buffer_append(&c->input, buf, len); 1972b15c8340SDag-Erling Smørgrav } 1973b15c8340SDag-Erling Smørgrav return buffer_len(&c->input); 1974b15c8340SDag-Erling Smørgrav } 1975b15c8340SDag-Erling Smørgrav 1976b15c8340SDag-Erling Smørgrav static void 1977b15c8340SDag-Erling Smørgrav channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) 1978b15c8340SDag-Erling Smørgrav { 1979b15c8340SDag-Erling Smørgrav u_int need; 1980b15c8340SDag-Erling Smørgrav ssize_t len; 1981b15c8340SDag-Erling Smørgrav 1982b15c8340SDag-Erling Smørgrav if (!compat20) 1983b15c8340SDag-Erling Smørgrav fatal("%s: entered with !compat20", __func__); 1984b15c8340SDag-Erling Smørgrav 1985e2f6069cSDag-Erling Smørgrav if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && 1986b15c8340SDag-Erling Smørgrav (c->istate == CHAN_INPUT_OPEN || 1987b15c8340SDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN)) { 1988b15c8340SDag-Erling Smørgrav /* 1989b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 1990b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 1991b15c8340SDag-Erling Smørgrav */ 1992b15c8340SDag-Erling Smørgrav if (read_mux(c, 4) < 4) /* read header */ 1993b15c8340SDag-Erling Smørgrav return; 1994b15c8340SDag-Erling Smørgrav need = get_u32(buffer_ptr(&c->input)); 1995b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 1996b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 1997b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 1998b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 1999b15c8340SDag-Erling Smørgrav chan_rcvd_oclose(c); 2000b15c8340SDag-Erling Smørgrav return; 2001b15c8340SDag-Erling Smørgrav } 2002b15c8340SDag-Erling Smørgrav if (read_mux(c, need + 4) < need + 4) /* read body */ 2003b15c8340SDag-Erling Smørgrav return; 2004b15c8340SDag-Erling Smørgrav if (c->mux_rcb(c) != 0) { 2005b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 2006b15c8340SDag-Erling Smørgrav chan_mark_dead(c); 2007b15c8340SDag-Erling Smørgrav return; 2008b15c8340SDag-Erling Smørgrav } 2009b15c8340SDag-Erling Smørgrav } 2010b15c8340SDag-Erling Smørgrav 2011b15c8340SDag-Erling Smørgrav if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && 2012b15c8340SDag-Erling Smørgrav buffer_len(&c->output) > 0) { 2013b15c8340SDag-Erling Smørgrav len = write(c->wfd, buffer_ptr(&c->output), 2014b15c8340SDag-Erling Smørgrav buffer_len(&c->output)); 2015b15c8340SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 2016b15c8340SDag-Erling Smørgrav return; 2017b15c8340SDag-Erling Smørgrav if (len <= 0) { 2018b15c8340SDag-Erling Smørgrav chan_mark_dead(c); 2019b15c8340SDag-Erling Smørgrav return; 2020b15c8340SDag-Erling Smørgrav } 2021b15c8340SDag-Erling Smørgrav buffer_consume(&c->output, len); 2022b15c8340SDag-Erling Smørgrav } 2023b15c8340SDag-Erling Smørgrav } 2024b15c8340SDag-Erling Smørgrav 2025b15c8340SDag-Erling Smørgrav static void 2026b15c8340SDag-Erling Smørgrav channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) 2027b15c8340SDag-Erling Smørgrav { 2028b15c8340SDag-Erling Smørgrav Channel *nc; 2029b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 2030b15c8340SDag-Erling Smørgrav socklen_t addrlen; 2031b15c8340SDag-Erling Smørgrav int newsock; 2032b15c8340SDag-Erling Smørgrav uid_t euid; 2033b15c8340SDag-Erling Smørgrav gid_t egid; 2034b15c8340SDag-Erling Smørgrav 2035b15c8340SDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 2036b15c8340SDag-Erling Smørgrav return; 2037b15c8340SDag-Erling Smørgrav 2038b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 2039b15c8340SDag-Erling Smørgrav 2040b15c8340SDag-Erling Smørgrav /* 2041b15c8340SDag-Erling Smørgrav * Accept connection on control socket 2042b15c8340SDag-Erling Smørgrav */ 2043b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 2044b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 2045b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 2046b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 2047b15c8340SDag-Erling Smørgrav error("%s accept: %s", __func__, strerror(errno)); 2048462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2049e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2050b15c8340SDag-Erling Smørgrav return; 2051b15c8340SDag-Erling Smørgrav } 2052b15c8340SDag-Erling Smørgrav 2053b15c8340SDag-Erling Smørgrav if (getpeereid(newsock, &euid, &egid) < 0) { 2054b15c8340SDag-Erling Smørgrav error("%s getpeereid failed: %s", __func__, 2055b15c8340SDag-Erling Smørgrav strerror(errno)); 2056b15c8340SDag-Erling Smørgrav close(newsock); 2057b15c8340SDag-Erling Smørgrav return; 2058b15c8340SDag-Erling Smørgrav } 2059b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 2060b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 2061b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 2062b15c8340SDag-Erling Smørgrav close(newsock); 2063b15c8340SDag-Erling Smørgrav return; 2064b15c8340SDag-Erling Smørgrav } 2065b15c8340SDag-Erling Smørgrav nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, 2066b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 2067b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 2068b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 2069b15c8340SDag-Erling Smørgrav debug3("%s: new mux channel %d fd %d", __func__, 2070b15c8340SDag-Erling Smørgrav nc->self, nc->sock); 2071b15c8340SDag-Erling Smørgrav /* establish state */ 2072b15c8340SDag-Erling Smørgrav nc->mux_rcb(nc); 2073b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 2074b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 2075b15c8340SDag-Erling Smørgrav } 2076b15c8340SDag-Erling Smørgrav 2077333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2078af12a3e7SDag-Erling Smørgrav static void 2079a04a10f8SKris Kennaway channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) 2080a04a10f8SKris Kennaway { 2081a04a10f8SKris Kennaway int len; 2082f388f5efSDag-Erling Smørgrav 2083511b41d2SMark Murray /* Send buffered output data to the socket. */ 2084a04a10f8SKris Kennaway if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { 2085a04a10f8SKris Kennaway len = write(c->sock, buffer_ptr(&c->output), 2086a04a10f8SKris Kennaway buffer_len(&c->output)); 2087511b41d2SMark Murray if (len <= 0) 2088af12a3e7SDag-Erling Smørgrav buffer_clear(&c->output); 2089511b41d2SMark Murray else 2090a04a10f8SKris Kennaway buffer_consume(&c->output, len); 2091511b41d2SMark Murray } 2092a04a10f8SKris Kennaway } 2093511b41d2SMark Murray 2094af12a3e7SDag-Erling Smørgrav static void 2095a04a10f8SKris Kennaway channel_handler_init_20(void) 2096a04a10f8SKris Kennaway { 2097af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 2098a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 2099a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2100ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 2101a0ee8cc6SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 2102a0ee8cc6SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 2103a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2104ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2105ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2106ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2107b15c8340SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 2108b15c8340SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2109a04a10f8SKris Kennaway 2110af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2111a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2112ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 2113a0ee8cc6SDag-Erling Smørgrav channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 2114a0ee8cc6SDag-Erling Smørgrav channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 2115a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2116ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2117ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2118af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2119b15c8340SDag-Erling Smørgrav channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 2120b15c8340SDag-Erling Smørgrav channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2121a04a10f8SKris Kennaway } 2122a04a10f8SKris Kennaway 2123af12a3e7SDag-Erling Smørgrav static void 2124a04a10f8SKris Kennaway channel_handler_init_13(void) 2125a04a10f8SKris Kennaway { 2126a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; 2127a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; 2128a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2129a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2130a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2131a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; 2132a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; 2133ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2134ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2135a04a10f8SKris Kennaway 2136af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2137a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2138a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2139a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2140a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; 2141ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2142af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2143a04a10f8SKris Kennaway } 2144a04a10f8SKris Kennaway 2145af12a3e7SDag-Erling Smørgrav static void 2146a04a10f8SKris Kennaway channel_handler_init_15(void) 2147a04a10f8SKris Kennaway { 2148af12a3e7SDag-Erling Smørgrav channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 2149a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 2150a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 2151a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 2152a04a10f8SKris Kennaway channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 2153ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 2154ca3176e7SBrian Feldman channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 2155a04a10f8SKris Kennaway 2156a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 2157a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 2158a04a10f8SKris Kennaway channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 2159af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; 2160ca3176e7SBrian Feldman channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 2161af12a3e7SDag-Erling Smørgrav channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 2162a04a10f8SKris Kennaway } 2163a04a10f8SKris Kennaway 2164af12a3e7SDag-Erling Smørgrav static void 2165a04a10f8SKris Kennaway channel_handler_init(void) 2166a04a10f8SKris Kennaway { 2167a04a10f8SKris Kennaway int i; 2168f388f5efSDag-Erling Smørgrav 2169a04a10f8SKris Kennaway for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { 2170a04a10f8SKris Kennaway channel_pre[i] = NULL; 2171a04a10f8SKris Kennaway channel_post[i] = NULL; 2172a04a10f8SKris Kennaway } 2173a04a10f8SKris Kennaway if (compat20) 2174a04a10f8SKris Kennaway channel_handler_init_20(); 2175a04a10f8SKris Kennaway else if (compat13) 2176a04a10f8SKris Kennaway channel_handler_init_13(); 2177a04a10f8SKris Kennaway else 2178a04a10f8SKris Kennaway channel_handler_init_15(); 2179a04a10f8SKris Kennaway } 2180a04a10f8SKris Kennaway 2181af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2182af12a3e7SDag-Erling Smørgrav static void 2183af12a3e7SDag-Erling Smørgrav channel_garbage_collect(Channel *c) 2184af12a3e7SDag-Erling Smørgrav { 2185af12a3e7SDag-Erling Smørgrav if (c == NULL) 2186af12a3e7SDag-Erling Smørgrav return; 2187af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 2188b74df5b2SDag-Erling Smørgrav if (!chan_is_dead(c, c->detach_close)) 2189af12a3e7SDag-Erling Smørgrav return; 2190221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 2191af12a3e7SDag-Erling Smørgrav c->detach_user(c->self, NULL); 2192af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2193af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2194af12a3e7SDag-Erling Smørgrav return; 2195221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2196af12a3e7SDag-Erling Smørgrav } 2197af12a3e7SDag-Erling Smørgrav if (!chan_is_dead(c, 1)) 2198af12a3e7SDag-Erling Smørgrav return; 2199221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 2200af12a3e7SDag-Erling Smørgrav channel_free(c); 2201af12a3e7SDag-Erling Smørgrav } 2202af12a3e7SDag-Erling Smørgrav 2203af12a3e7SDag-Erling Smørgrav static void 2204462c32cbSDag-Erling Smørgrav channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, 2205462c32cbSDag-Erling Smørgrav time_t *unpause_secs) 2206a04a10f8SKris Kennaway { 2207a04a10f8SKris Kennaway static int did_init = 0; 2208b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2209a04a10f8SKris Kennaway Channel *c; 2210462c32cbSDag-Erling Smørgrav time_t now; 2211a04a10f8SKris Kennaway 2212a04a10f8SKris Kennaway if (!did_init) { 2213a04a10f8SKris Kennaway channel_handler_init(); 2214a04a10f8SKris Kennaway did_init = 1; 2215a04a10f8SKris Kennaway } 2216e4a9863fSDag-Erling Smørgrav now = monotime(); 2217462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL) 2218462c32cbSDag-Erling Smørgrav *unpause_secs = 0; 2219b15c8340SDag-Erling Smørgrav for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { 2220af12a3e7SDag-Erling Smørgrav c = channels[i]; 2221af12a3e7SDag-Erling Smørgrav if (c == NULL) 2222511b41d2SMark Murray continue; 2223b15c8340SDag-Erling Smørgrav if (c->delayed) { 2224b15c8340SDag-Erling Smørgrav if (ftab == channel_pre) 2225b15c8340SDag-Erling Smørgrav c->delayed = 0; 2226b15c8340SDag-Erling Smørgrav else 2227b15c8340SDag-Erling Smørgrav continue; 2228b15c8340SDag-Erling Smørgrav } 2229462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2230462c32cbSDag-Erling Smørgrav /* 2231462c32cbSDag-Erling Smørgrav * Run handlers that are not paused. 2232462c32cbSDag-Erling Smørgrav */ 2233462c32cbSDag-Erling Smørgrav if (c->notbefore <= now) 2234a04a10f8SKris Kennaway (*ftab[c->type])(c, readset, writeset); 2235462c32cbSDag-Erling Smørgrav else if (unpause_secs != NULL) { 2236462c32cbSDag-Erling Smørgrav /* 2237462c32cbSDag-Erling Smørgrav * Collect the time that the earliest 2238462c32cbSDag-Erling Smørgrav * channel comes off pause. 2239462c32cbSDag-Erling Smørgrav */ 2240462c32cbSDag-Erling Smørgrav debug3("%s: chan %d: skip for %d more seconds", 2241462c32cbSDag-Erling Smørgrav __func__, c->self, 2242462c32cbSDag-Erling Smørgrav (int)(c->notbefore - now)); 2243462c32cbSDag-Erling Smørgrav if (*unpause_secs == 0 || 2244462c32cbSDag-Erling Smørgrav (c->notbefore - now) < *unpause_secs) 2245462c32cbSDag-Erling Smørgrav *unpause_secs = c->notbefore - now; 2246462c32cbSDag-Erling Smørgrav } 2247462c32cbSDag-Erling Smørgrav } 2248af12a3e7SDag-Erling Smørgrav channel_garbage_collect(c); 2249511b41d2SMark Murray } 2250462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL && *unpause_secs != 0) 2251462c32cbSDag-Erling Smørgrav debug3("%s: first channel unpauses in %d seconds", 2252462c32cbSDag-Erling Smørgrav __func__, (int)*unpause_secs); 2253511b41d2SMark Murray } 2254a04a10f8SKris Kennaway 2255af12a3e7SDag-Erling Smørgrav /* 2256af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 2257af12a3e7SDag-Erling Smørgrav * select bitmasks. 2258af12a3e7SDag-Erling Smørgrav */ 2259a04a10f8SKris Kennaway void 2260ca3176e7SBrian Feldman channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2261462c32cbSDag-Erling Smørgrav u_int *nallocp, time_t *minwait_secs, int rekeying) 2262a04a10f8SKris Kennaway { 2263333ee039SDag-Erling Smørgrav u_int n, sz, nfdset; 2264ca3176e7SBrian Feldman 2265*ca86bcf2SDag-Erling Smørgrav n = MAXIMUM(*maxfdp, channel_max_fd); 2266ca3176e7SBrian Feldman 2267333ee039SDag-Erling Smørgrav nfdset = howmany(n+1, NFDBITS); 2268333ee039SDag-Erling Smørgrav /* Explicitly test here, because xrealloc isn't always called */ 2269bc5531deSDag-Erling Smørgrav if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) 2270333ee039SDag-Erling Smørgrav fatal("channel_prepare_select: max_fd (%d) is too large", n); 2271333ee039SDag-Erling Smørgrav sz = nfdset * sizeof(fd_mask); 2272333ee039SDag-Erling Smørgrav 2273af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 2274af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 2275557f75e5SDag-Erling Smørgrav *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); 2276557f75e5SDag-Erling Smørgrav *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); 2277af12a3e7SDag-Erling Smørgrav *nallocp = sz; 2278ca3176e7SBrian Feldman } 2279af12a3e7SDag-Erling Smørgrav *maxfdp = n; 2280ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 2281ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 2282ca3176e7SBrian Feldman 2283ca3176e7SBrian Feldman if (!rekeying) 2284462c32cbSDag-Erling Smørgrav channel_handler(channel_pre, *readsetp, *writesetp, 2285462c32cbSDag-Erling Smørgrav minwait_secs); 2286a04a10f8SKris Kennaway } 2287a04a10f8SKris Kennaway 2288af12a3e7SDag-Erling Smørgrav /* 2289af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 2290af12a3e7SDag-Erling Smørgrav * events pending. 2291af12a3e7SDag-Erling Smørgrav */ 2292a04a10f8SKris Kennaway void 2293a04a10f8SKris Kennaway channel_after_select(fd_set *readset, fd_set *writeset) 2294a04a10f8SKris Kennaway { 2295462c32cbSDag-Erling Smørgrav channel_handler(channel_post, readset, writeset, NULL); 2296511b41d2SMark Murray } 2297511b41d2SMark Murray 2298af12a3e7SDag-Erling Smørgrav 2299ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 2300511b41d2SMark Murray void 2301af12a3e7SDag-Erling Smørgrav channel_output_poll(void) 2302511b41d2SMark Murray { 2303a04a10f8SKris Kennaway Channel *c; 230421e764dfSDag-Erling Smørgrav u_int i, len; 2305511b41d2SMark Murray 2306511b41d2SMark Murray for (i = 0; i < channels_alloc; i++) { 2307af12a3e7SDag-Erling Smørgrav c = channels[i]; 2308af12a3e7SDag-Erling Smørgrav if (c == NULL) 2309af12a3e7SDag-Erling Smørgrav continue; 2310511b41d2SMark Murray 2311af12a3e7SDag-Erling Smørgrav /* 2312af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 2313af12a3e7SDag-Erling Smørgrav * incoming data. 2314af12a3e7SDag-Erling Smørgrav */ 2315511b41d2SMark Murray if (compat13) { 2316a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 2317a04a10f8SKris Kennaway c->type != SSH_CHANNEL_INPUT_DRAINING) 2318511b41d2SMark Murray continue; 2319511b41d2SMark Murray } else { 2320a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 2321511b41d2SMark Murray continue; 2322a04a10f8SKris Kennaway } 2323a04a10f8SKris Kennaway if (compat20 && 2324a04a10f8SKris Kennaway (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 2325ca3176e7SBrian Feldman /* XXX is this true? */ 2326af12a3e7SDag-Erling Smørgrav debug3("channel %d: will not send data after close", c->self); 2327511b41d2SMark Murray continue; 2328511b41d2SMark Murray } 2329511b41d2SMark Murray 2330511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 2331ca3176e7SBrian Feldman if ((c->istate == CHAN_INPUT_OPEN || 2332ca3176e7SBrian Feldman c->istate == CHAN_INPUT_WAIT_DRAIN) && 2333ca3176e7SBrian Feldman (len = buffer_len(&c->input)) > 0) { 2334b74df5b2SDag-Erling Smørgrav if (c->datagram) { 2335b74df5b2SDag-Erling Smørgrav if (len > 0) { 2336b74df5b2SDag-Erling Smørgrav u_char *data; 2337b74df5b2SDag-Erling Smørgrav u_int dlen; 2338b74df5b2SDag-Erling Smørgrav 2339b74df5b2SDag-Erling Smørgrav data = buffer_get_string(&c->input, 2340b74df5b2SDag-Erling Smørgrav &dlen); 2341e2f6069cSDag-Erling Smørgrav if (dlen > c->remote_window || 2342e2f6069cSDag-Erling Smørgrav dlen > c->remote_maxpacket) { 2343e2f6069cSDag-Erling Smørgrav debug("channel %d: datagram " 2344e2f6069cSDag-Erling Smørgrav "too big for channel", 2345e2f6069cSDag-Erling Smørgrav c->self); 2346e4a9863fSDag-Erling Smørgrav free(data); 2347e2f6069cSDag-Erling Smørgrav continue; 2348e2f6069cSDag-Erling Smørgrav } 2349b74df5b2SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_DATA); 2350b74df5b2SDag-Erling Smørgrav packet_put_int(c->remote_id); 2351b74df5b2SDag-Erling Smørgrav packet_put_string(data, dlen); 2352b74df5b2SDag-Erling Smørgrav packet_send(); 2353557f75e5SDag-Erling Smørgrav c->remote_window -= dlen; 2354e4a9863fSDag-Erling Smørgrav free(data); 2355b74df5b2SDag-Erling Smørgrav } 2356b74df5b2SDag-Erling Smørgrav continue; 2357b74df5b2SDag-Erling Smørgrav } 2358af12a3e7SDag-Erling Smørgrav /* 2359af12a3e7SDag-Erling Smørgrav * Send some data for the other side over the secure 2360af12a3e7SDag-Erling Smørgrav * connection. 2361af12a3e7SDag-Erling Smørgrav */ 2362a04a10f8SKris Kennaway if (compat20) { 2363a04a10f8SKris Kennaway if (len > c->remote_window) 2364a04a10f8SKris Kennaway len = c->remote_window; 2365a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 2366a04a10f8SKris Kennaway len = c->remote_maxpacket; 2367a04a10f8SKris Kennaway } else { 2368511b41d2SMark Murray if (packet_is_interactive()) { 2369511b41d2SMark Murray if (len > 1024) 2370511b41d2SMark Murray len = 512; 2371511b41d2SMark Murray } else { 2372511b41d2SMark Murray /* Keep the packets at reasonable size. */ 2373511b41d2SMark Murray if (len > packet_get_maxsize()/2) 2374511b41d2SMark Murray len = packet_get_maxsize()/2; 2375511b41d2SMark Murray } 2376a04a10f8SKris Kennaway } 2377a04a10f8SKris Kennaway if (len > 0) { 2378a04a10f8SKris Kennaway packet_start(compat20 ? 2379a04a10f8SKris Kennaway SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); 2380a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2381a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->input), len); 2382511b41d2SMark Murray packet_send(); 2383a04a10f8SKris Kennaway buffer_consume(&c->input, len); 2384a04a10f8SKris Kennaway c->remote_window -= len; 2385a04a10f8SKris Kennaway } 2386a04a10f8SKris Kennaway } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 2387511b41d2SMark Murray if (compat13) 2388511b41d2SMark Murray fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); 2389511b41d2SMark Murray /* 2390511b41d2SMark Murray * input-buffer is empty and read-socket shutdown: 239180628bacSDag-Erling Smørgrav * tell peer, that we will not send more data: send IEOF. 239280628bacSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still in use. 2393511b41d2SMark Murray */ 239480628bacSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 239580628bacSDag-Erling Smørgrav debug2("channel %d: ibuf_empty delayed efd %d/(%d)", 239680628bacSDag-Erling Smørgrav c->self, c->efd, buffer_len(&c->extended)); 239780628bacSDag-Erling Smørgrav else 2398a04a10f8SKris Kennaway chan_ibuf_empty(c); 2399a04a10f8SKris Kennaway } 2400a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 2401a04a10f8SKris Kennaway if (compat20 && 240280628bacSDag-Erling Smørgrav !(c->flags & CHAN_EOF_SENT) && 2403a04a10f8SKris Kennaway c->remote_window > 0 && 2404a04a10f8SKris Kennaway (len = buffer_len(&c->extended)) > 0 && 2405a04a10f8SKris Kennaway c->extended_usage == CHAN_EXTENDED_READ) { 2406a82e551fSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %u euse %d", 2407ca3176e7SBrian Feldman c->self, c->remote_window, buffer_len(&c->extended), 2408ca3176e7SBrian Feldman c->extended_usage); 2409a04a10f8SKris Kennaway if (len > c->remote_window) 2410a04a10f8SKris Kennaway len = c->remote_window; 2411a04a10f8SKris Kennaway if (len > c->remote_maxpacket) 2412a04a10f8SKris Kennaway len = c->remote_maxpacket; 2413a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); 2414a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2415a04a10f8SKris Kennaway packet_put_int(SSH2_EXTENDED_DATA_STDERR); 2416a04a10f8SKris Kennaway packet_put_string(buffer_ptr(&c->extended), len); 2417a04a10f8SKris Kennaway packet_send(); 2418a04a10f8SKris Kennaway buffer_consume(&c->extended, len); 2419a04a10f8SKris Kennaway c->remote_window -= len; 2420ca3176e7SBrian Feldman debug2("channel %d: sent ext data %d", c->self, len); 2421511b41d2SMark Murray } 2422511b41d2SMark Murray } 2423511b41d2SMark Murray } 2424511b41d2SMark Murray 2425*ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support */ 2426*ca86bcf2SDag-Erling Smørgrav 2427*ca86bcf2SDag-Erling Smørgrav /* 2428*ca86bcf2SDag-Erling Smørgrav * When multiplexing channel messages for mux clients we have to deal 2429*ca86bcf2SDag-Erling Smørgrav * with downstream messages from the mux client and upstream messages 2430*ca86bcf2SDag-Erling Smørgrav * from the ssh server: 2431*ca86bcf2SDag-Erling Smørgrav * 1) Handling downstream messages is straightforward and happens 2432*ca86bcf2SDag-Erling Smørgrav * in channel_proxy_downstream(): 2433*ca86bcf2SDag-Erling Smørgrav * - We forward all messages (mostly) unmodified to the server. 2434*ca86bcf2SDag-Erling Smørgrav * - However, in order to route messages from upstream to the correct 2435*ca86bcf2SDag-Erling Smørgrav * downstream client, we have to replace the channel IDs used by the 2436*ca86bcf2SDag-Erling Smørgrav * mux clients with a unique channel ID because the mux clients might 2437*ca86bcf2SDag-Erling Smørgrav * use conflicting channel IDs. 2438*ca86bcf2SDag-Erling Smørgrav * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and 2439*ca86bcf2SDag-Erling Smørgrav * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local 2440*ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID 2441*ca86bcf2SDag-Erling Smørgrav * with the newly allocated channel ID. 2442*ca86bcf2SDag-Erling Smørgrav * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY 2443*ca86bcf2SDag-Erling Smørgrav * channels and procesed by channel_proxy_upstream(). The local channel ID 2444*ca86bcf2SDag-Erling Smørgrav * is then translated back to the original mux client ID. 2445*ca86bcf2SDag-Erling Smørgrav * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE 2446*ca86bcf2SDag-Erling Smørgrav * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. 2447*ca86bcf2SDag-Erling Smørgrav * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the 2448*ca86bcf2SDag-Erling Smørgrav * downstream mux client are removed. 2449*ca86bcf2SDag-Erling Smørgrav * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server 2450*ca86bcf2SDag-Erling Smørgrav * requires more work, because they are not addressed to a specific 2451*ca86bcf2SDag-Erling Smørgrav * channel. E.g. client_request_forwarded_tcpip() needs to figure 2452*ca86bcf2SDag-Erling Smørgrav * out whether the request is addressed to the local client or a 2453*ca86bcf2SDag-Erling Smørgrav * specific downstream client based on the listen-address/port. 2454*ca86bcf2SDag-Erling Smørgrav * 6) Agent and X11-Forwarding have a similar problem and are currenly 2455*ca86bcf2SDag-Erling Smørgrav * not supported as the matching session/channel cannot be identified 2456*ca86bcf2SDag-Erling Smørgrav * easily. 2457*ca86bcf2SDag-Erling Smørgrav */ 2458*ca86bcf2SDag-Erling Smørgrav 2459*ca86bcf2SDag-Erling Smørgrav /* 2460*ca86bcf2SDag-Erling Smørgrav * receive packets from downstream mux clients: 2461*ca86bcf2SDag-Erling Smørgrav * channel callback fired on read from mux client, creates 2462*ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs 2463*ca86bcf2SDag-Erling Smørgrav * on channel creation. 2464*ca86bcf2SDag-Erling Smørgrav */ 2465*ca86bcf2SDag-Erling Smørgrav int 2466*ca86bcf2SDag-Erling Smørgrav channel_proxy_downstream(Channel *downstream) 2467*ca86bcf2SDag-Erling Smørgrav { 2468*ca86bcf2SDag-Erling Smørgrav Channel *c = NULL; 2469*ca86bcf2SDag-Erling Smørgrav struct ssh *ssh = active_state; 2470*ca86bcf2SDag-Erling Smørgrav struct sshbuf *original = NULL, *modified = NULL; 2471*ca86bcf2SDag-Erling Smørgrav const u_char *cp; 2472*ca86bcf2SDag-Erling Smørgrav char *ctype = NULL, *listen_host = NULL; 2473*ca86bcf2SDag-Erling Smørgrav u_char type; 2474*ca86bcf2SDag-Erling Smørgrav size_t have; 2475*ca86bcf2SDag-Erling Smørgrav int ret = -1, r, idx; 2476*ca86bcf2SDag-Erling Smørgrav u_int id, remote_id, listen_port; 2477*ca86bcf2SDag-Erling Smørgrav 2478*ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(&downstream->input, stderr); */ 2479*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(&downstream->input, &cp, &have)) 2480*ca86bcf2SDag-Erling Smørgrav != 0) { 2481*ca86bcf2SDag-Erling Smørgrav error("%s: malformed message: %s", __func__, ssh_err(r)); 2482*ca86bcf2SDag-Erling Smørgrav return -1; 2483*ca86bcf2SDag-Erling Smørgrav } 2484*ca86bcf2SDag-Erling Smørgrav if (have < 2) { 2485*ca86bcf2SDag-Erling Smørgrav error("%s: short message", __func__); 2486*ca86bcf2SDag-Erling Smørgrav return -1; 2487*ca86bcf2SDag-Erling Smørgrav } 2488*ca86bcf2SDag-Erling Smørgrav type = cp[1]; 2489*ca86bcf2SDag-Erling Smørgrav /* skip padlen + type */ 2490*ca86bcf2SDag-Erling Smørgrav cp += 2; 2491*ca86bcf2SDag-Erling Smørgrav have -= 2; 2492*ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 2493*ca86bcf2SDag-Erling Smørgrav debug3("%s: channel %u: down->up: type %u", __func__, 2494*ca86bcf2SDag-Erling Smørgrav downstream->self, type); 2495*ca86bcf2SDag-Erling Smørgrav 2496*ca86bcf2SDag-Erling Smørgrav switch (type) { 2497*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN: 2498*ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2499*ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 2500*ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2501*ca86bcf2SDag-Erling Smørgrav goto out; 2502*ca86bcf2SDag-Erling Smørgrav } 2503*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || 2504*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 2505*ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2506*ca86bcf2SDag-Erling Smørgrav goto out; 2507*ca86bcf2SDag-Erling Smørgrav } 2508*ca86bcf2SDag-Erling Smørgrav c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, 2509*ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, ctype, 1); 2510*ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 2511*ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; /* original downstream id */ 2512*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || 2513*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 2514*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 2515*ca86bcf2SDag-Erling Smørgrav error("%s: compose error %s", __func__, ssh_err(r)); 2516*ca86bcf2SDag-Erling Smørgrav channel_free(c); 2517*ca86bcf2SDag-Erling Smørgrav goto out; 2518*ca86bcf2SDag-Erling Smørgrav } 2519*ca86bcf2SDag-Erling Smørgrav break; 2520*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2521*ca86bcf2SDag-Erling Smørgrav /* 2522*ca86bcf2SDag-Erling Smørgrav * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we 2523*ca86bcf2SDag-Erling Smørgrav * need to parse 'remote_id' instead of 'ctype'. 2524*ca86bcf2SDag-Erling Smørgrav */ 2525*ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2526*ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 2527*ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2528*ca86bcf2SDag-Erling Smørgrav goto out; 2529*ca86bcf2SDag-Erling Smørgrav } 2530*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || 2531*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 2532*ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2533*ca86bcf2SDag-Erling Smørgrav goto out; 2534*ca86bcf2SDag-Erling Smørgrav } 2535*ca86bcf2SDag-Erling Smørgrav c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, 2536*ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); 2537*ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 2538*ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; 2539*ca86bcf2SDag-Erling Smørgrav c->remote_id = remote_id; 2540*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || 2541*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 2542*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 2543*ca86bcf2SDag-Erling Smørgrav error("%s: compose error %s", __func__, ssh_err(r)); 2544*ca86bcf2SDag-Erling Smørgrav channel_free(c); 2545*ca86bcf2SDag-Erling Smørgrav goto out; 2546*ca86bcf2SDag-Erling Smørgrav } 2547*ca86bcf2SDag-Erling Smørgrav break; 2548*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_GLOBAL_REQUEST: 2549*ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL) { 2550*ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2551*ca86bcf2SDag-Erling Smørgrav goto out; 2552*ca86bcf2SDag-Erling Smørgrav } 2553*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { 2554*ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2555*ca86bcf2SDag-Erling Smørgrav goto out; 2556*ca86bcf2SDag-Erling Smørgrav } 2557*ca86bcf2SDag-Erling Smørgrav if (strcmp(ctype, "tcpip-forward") != 0) { 2558*ca86bcf2SDag-Erling Smørgrav error("%s: unsupported request %s", __func__, ctype); 2559*ca86bcf2SDag-Erling Smørgrav goto out; 2560*ca86bcf2SDag-Erling Smørgrav } 2561*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u8(original, NULL)) != 0 || 2562*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || 2563*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &listen_port)) != 0) { 2564*ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2565*ca86bcf2SDag-Erling Smørgrav goto out; 2566*ca86bcf2SDag-Erling Smørgrav } 2567*ca86bcf2SDag-Erling Smørgrav if (listen_port > 65535) { 2568*ca86bcf2SDag-Erling Smørgrav error("%s: tcpip-forward for %s: bad port %u", 2569*ca86bcf2SDag-Erling Smørgrav __func__, listen_host, listen_port); 2570*ca86bcf2SDag-Erling Smørgrav goto out; 2571*ca86bcf2SDag-Erling Smørgrav } 2572*ca86bcf2SDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 2573*ca86bcf2SDag-Erling Smørgrav permitted_opens = xreallocarray(permitted_opens, 2574*ca86bcf2SDag-Erling Smørgrav num_permitted_opens + 1, sizeof(*permitted_opens)); 2575*ca86bcf2SDag-Erling Smørgrav idx = num_permitted_opens++; 2576*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].host_to_connect = xstrdup("<mux>"); 2577*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].port_to_connect = -1; 2578*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].listen_host = listen_host; 2579*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].listen_port = (int)listen_port; 2580*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].downstream = downstream; 2581*ca86bcf2SDag-Erling Smørgrav listen_host = NULL; 2582*ca86bcf2SDag-Erling Smørgrav break; 2583*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2584*ca86bcf2SDag-Erling Smørgrav if (have < 4) 2585*ca86bcf2SDag-Erling Smørgrav break; 2586*ca86bcf2SDag-Erling Smørgrav remote_id = PEEK_U32(cp); 2587*ca86bcf2SDag-Erling Smørgrav if ((c = channel_by_remote_id(remote_id)) != NULL) { 2588*ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) 2589*ca86bcf2SDag-Erling Smørgrav channel_free(c); 2590*ca86bcf2SDag-Erling Smørgrav else 2591*ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT; 2592*ca86bcf2SDag-Erling Smørgrav } 2593*ca86bcf2SDag-Erling Smørgrav break; 2594*ca86bcf2SDag-Erling Smørgrav } 2595*ca86bcf2SDag-Erling Smørgrav if (modified) { 2596*ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 2597*ca86bcf2SDag-Erling Smørgrav (r = sshpkt_putb(ssh, modified)) != 0 || 2598*ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 2599*ca86bcf2SDag-Erling Smørgrav error("%s: send %s", __func__, ssh_err(r)); 2600*ca86bcf2SDag-Erling Smørgrav goto out; 2601*ca86bcf2SDag-Erling Smørgrav } 2602*ca86bcf2SDag-Erling Smørgrav } else { 2603*ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 2604*ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put(ssh, cp, have)) != 0 || 2605*ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 2606*ca86bcf2SDag-Erling Smørgrav error("%s: send %s", __func__, ssh_err(r)); 2607*ca86bcf2SDag-Erling Smørgrav goto out; 2608*ca86bcf2SDag-Erling Smørgrav } 2609*ca86bcf2SDag-Erling Smørgrav } 2610*ca86bcf2SDag-Erling Smørgrav ret = 0; 2611*ca86bcf2SDag-Erling Smørgrav out: 2612*ca86bcf2SDag-Erling Smørgrav free(ctype); 2613*ca86bcf2SDag-Erling Smørgrav free(listen_host); 2614*ca86bcf2SDag-Erling Smørgrav sshbuf_free(original); 2615*ca86bcf2SDag-Erling Smørgrav sshbuf_free(modified); 2616*ca86bcf2SDag-Erling Smørgrav return ret; 2617*ca86bcf2SDag-Erling Smørgrav } 2618*ca86bcf2SDag-Erling Smørgrav 2619*ca86bcf2SDag-Erling Smørgrav /* 2620*ca86bcf2SDag-Erling Smørgrav * receive packets from upstream server and de-multiplex packets 2621*ca86bcf2SDag-Erling Smørgrav * to correct downstream: 2622*ca86bcf2SDag-Erling Smørgrav * implemented as a helper for channel input handlers, 2623*ca86bcf2SDag-Erling Smørgrav * replaces local (proxy) channel ID with downstream channel ID. 2624*ca86bcf2SDag-Erling Smørgrav */ 2625*ca86bcf2SDag-Erling Smørgrav int 2626*ca86bcf2SDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, void *ctxt) 2627*ca86bcf2SDag-Erling Smørgrav { 2628*ca86bcf2SDag-Erling Smørgrav struct ssh *ssh = active_state; 2629*ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 2630*ca86bcf2SDag-Erling Smørgrav Channel *downstream; 2631*ca86bcf2SDag-Erling Smørgrav const u_char *cp = NULL; 2632*ca86bcf2SDag-Erling Smørgrav size_t len; 2633*ca86bcf2SDag-Erling Smørgrav int r; 2634*ca86bcf2SDag-Erling Smørgrav 2635*ca86bcf2SDag-Erling Smørgrav /* 2636*ca86bcf2SDag-Erling Smørgrav * When receiving packets from the peer we need to check whether we 2637*ca86bcf2SDag-Erling Smørgrav * need to forward the packets to the mux client. In this case we 2638*ca86bcf2SDag-Erling Smørgrav * restore the orignal channel id and keep track of CLOSE messages, 2639*ca86bcf2SDag-Erling Smørgrav * so we can cleanup the channel. 2640*ca86bcf2SDag-Erling Smørgrav */ 2641*ca86bcf2SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) 2642*ca86bcf2SDag-Erling Smørgrav return 0; 2643*ca86bcf2SDag-Erling Smørgrav if ((downstream = c->mux_ctx) == NULL) 2644*ca86bcf2SDag-Erling Smørgrav return 0; 2645*ca86bcf2SDag-Erling Smørgrav switch (type) { 2646*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2647*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_DATA: 2648*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EOF: 2649*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EXTENDED_DATA: 2650*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2651*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_FAILURE: 2652*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 2653*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_SUCCESS: 2654*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_FAILURE: 2655*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_REQUEST: 2656*ca86bcf2SDag-Erling Smørgrav break; 2657*ca86bcf2SDag-Erling Smørgrav default: 2658*ca86bcf2SDag-Erling Smørgrav debug2("%s: channel %u: unsupported type %u", __func__, 2659*ca86bcf2SDag-Erling Smørgrav c->self, type); 2660*ca86bcf2SDag-Erling Smørgrav return 0; 2661*ca86bcf2SDag-Erling Smørgrav } 2662*ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 2663*ca86bcf2SDag-Erling Smørgrav error("%s: alloc reply", __func__); 2664*ca86bcf2SDag-Erling Smørgrav goto out; 2665*ca86bcf2SDag-Erling Smørgrav } 2666*ca86bcf2SDag-Erling Smørgrav /* get remaining payload (after id) */ 2667*ca86bcf2SDag-Erling Smørgrav cp = sshpkt_ptr(ssh, &len); 2668*ca86bcf2SDag-Erling Smørgrav if (cp == NULL) { 2669*ca86bcf2SDag-Erling Smørgrav error("%s: no packet", __func__); 2670*ca86bcf2SDag-Erling Smørgrav goto out; 2671*ca86bcf2SDag-Erling Smørgrav } 2672*ca86bcf2SDag-Erling Smørgrav /* translate id and send to muxclient */ 2673*ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 2674*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, type)) != 0 || 2675*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || 2676*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put(b, cp, len)) != 0 || 2677*ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_stringb(&downstream->output, b)) != 0) { 2678*ca86bcf2SDag-Erling Smørgrav error("%s: compose for muxclient %s", __func__, ssh_err(r)); 2679*ca86bcf2SDag-Erling Smørgrav goto out; 2680*ca86bcf2SDag-Erling Smørgrav } 2681*ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(b, stderr); */ 2682*ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 2683*ca86bcf2SDag-Erling Smørgrav debug3("%s: channel %u: up->down: type %u", __func__, c->self, 2684*ca86bcf2SDag-Erling Smørgrav type); 2685*ca86bcf2SDag-Erling Smørgrav out: 2686*ca86bcf2SDag-Erling Smørgrav /* update state */ 2687*ca86bcf2SDag-Erling Smørgrav switch (type) { 2688*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2689*ca86bcf2SDag-Erling Smørgrav /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ 2690*ca86bcf2SDag-Erling Smørgrav if (cp && len > 4) 2691*ca86bcf2SDag-Erling Smørgrav c->remote_id = PEEK_U32(cp); 2692*ca86bcf2SDag-Erling Smørgrav break; 2693*ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2694*ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_SENT) 2695*ca86bcf2SDag-Erling Smørgrav channel_free(c); 2696*ca86bcf2SDag-Erling Smørgrav else 2697*ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_RCVD; 2698*ca86bcf2SDag-Erling Smørgrav break; 2699*ca86bcf2SDag-Erling Smørgrav } 2700*ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 2701*ca86bcf2SDag-Erling Smørgrav return 1; 2702*ca86bcf2SDag-Erling Smørgrav } 2703af12a3e7SDag-Erling Smørgrav 2704af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 2705511b41d2SMark Murray 2706333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2707bc5531deSDag-Erling Smørgrav int 2708af12a3e7SDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, void *ctxt) 2709511b41d2SMark Murray { 2710511b41d2SMark Murray int id; 2711a0ee8cc6SDag-Erling Smørgrav const u_char *data; 2712e2f6069cSDag-Erling Smørgrav u_int data_len, win_len; 2713a04a10f8SKris Kennaway Channel *c; 2714511b41d2SMark Murray 2715511b41d2SMark Murray /* Get the channel number and verify it. */ 2716511b41d2SMark Murray id = packet_get_int(); 2717a04a10f8SKris Kennaway c = channel_lookup(id); 2718a04a10f8SKris Kennaway if (c == NULL) 2719511b41d2SMark Murray packet_disconnect("Received data for nonexistent channel %d.", id); 2720*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2721*ca86bcf2SDag-Erling Smørgrav return 0; 2722511b41d2SMark Murray 2723511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 2724a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 2725a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 2726bc5531deSDag-Erling Smørgrav return 0; 2727511b41d2SMark Murray 2728511b41d2SMark Murray /* Get the data. */ 2729d4af9e69SDag-Erling Smørgrav data = packet_get_string_ptr(&data_len); 2730e2f6069cSDag-Erling Smørgrav win_len = data_len; 2731e2f6069cSDag-Erling Smørgrav if (c->datagram) 2732e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 2733a04a10f8SKris Kennaway 2734476cd3b2SDag-Erling Smørgrav /* 2735476cd3b2SDag-Erling Smørgrav * Ignore data for protocol > 1.3 if output end is no longer open. 2736476cd3b2SDag-Erling Smørgrav * For protocol 2 the sending side is reducing its window as it sends 2737476cd3b2SDag-Erling Smørgrav * data, so we must 'fake' consumption of the data in order to ensure 2738476cd3b2SDag-Erling Smørgrav * that window updates are sent back. Otherwise the connection might 2739476cd3b2SDag-Erling Smørgrav * deadlock. 2740476cd3b2SDag-Erling Smørgrav */ 2741476cd3b2SDag-Erling Smørgrav if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { 2742476cd3b2SDag-Erling Smørgrav if (compat20) { 2743e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 2744e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 2745476cd3b2SDag-Erling Smørgrav } 2746bc5531deSDag-Erling Smørgrav return 0; 2747476cd3b2SDag-Erling Smørgrav } 2748476cd3b2SDag-Erling Smørgrav 2749a04a10f8SKris Kennaway if (compat20) { 2750e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 2751221552e4SDag-Erling Smørgrav logit("channel %d: rcvd big packet %d, maxpack %d", 2752e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 2753a04a10f8SKris Kennaway } 2754e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 2755221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much data %d, win %d", 2756e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_window); 2757bc5531deSDag-Erling Smørgrav return 0; 2758a04a10f8SKris Kennaway } 2759e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 2760a04a10f8SKris Kennaway } 2761b74df5b2SDag-Erling Smørgrav if (c->datagram) 2762b74df5b2SDag-Erling Smørgrav buffer_put_string(&c->output, data, data_len); 2763b74df5b2SDag-Erling Smørgrav else 2764a04a10f8SKris Kennaway buffer_append(&c->output, data, data_len); 2765d4af9e69SDag-Erling Smørgrav packet_check_eom(); 2766bc5531deSDag-Erling Smørgrav return 0; 2767511b41d2SMark Murray } 2768af12a3e7SDag-Erling Smørgrav 2769333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2770bc5531deSDag-Erling Smørgrav int 2771af12a3e7SDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, void *ctxt) 2772a04a10f8SKris Kennaway { 2773a04a10f8SKris Kennaway int id; 2774a04a10f8SKris Kennaway char *data; 2775a82e551fSDag-Erling Smørgrav u_int data_len, tcode; 2776a04a10f8SKris Kennaway Channel *c; 2777a04a10f8SKris Kennaway 2778a04a10f8SKris Kennaway /* Get the channel number and verify it. */ 2779a04a10f8SKris Kennaway id = packet_get_int(); 2780a04a10f8SKris Kennaway c = channel_lookup(id); 2781a04a10f8SKris Kennaway 2782a04a10f8SKris Kennaway if (c == NULL) 2783a04a10f8SKris Kennaway packet_disconnect("Received extended_data for bad channel %d.", id); 2784*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2785*ca86bcf2SDag-Erling Smørgrav return 0; 2786a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 2787221552e4SDag-Erling Smørgrav logit("channel %d: ext data for non open", id); 2788bc5531deSDag-Erling Smørgrav return 0; 2789a04a10f8SKris Kennaway } 279080628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 279180628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 279280628bacSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", id); 279380628bacSDag-Erling Smørgrav else 279480628bacSDag-Erling Smørgrav packet_disconnect("Received extended_data after EOF " 279580628bacSDag-Erling Smørgrav "on channel %d.", id); 279680628bacSDag-Erling Smørgrav } 2797a04a10f8SKris Kennaway tcode = packet_get_int(); 2798a04a10f8SKris Kennaway if (c->efd == -1 || 2799a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 2800a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 2801221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 2802bc5531deSDag-Erling Smørgrav return 0; 2803a04a10f8SKris Kennaway } 2804a04a10f8SKris Kennaway data = packet_get_string(&data_len); 2805af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2806a04a10f8SKris Kennaway if (data_len > c->local_window) { 2807221552e4SDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %d, win %d", 2808a04a10f8SKris Kennaway c->self, data_len, c->local_window); 2809e4a9863fSDag-Erling Smørgrav free(data); 2810bc5531deSDag-Erling Smørgrav return 0; 2811a04a10f8SKris Kennaway } 28125b9b2fafSBrian Feldman debug2("channel %d: rcvd ext data %d", c->self, data_len); 2813a04a10f8SKris Kennaway c->local_window -= data_len; 2814a04a10f8SKris Kennaway buffer_append(&c->extended, data, data_len); 2815e4a9863fSDag-Erling Smørgrav free(data); 2816bc5531deSDag-Erling Smørgrav return 0; 2817a04a10f8SKris Kennaway } 2818a04a10f8SKris Kennaway 2819333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2820bc5531deSDag-Erling Smørgrav int 2821af12a3e7SDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, void *ctxt) 2822a04a10f8SKris Kennaway { 2823a04a10f8SKris Kennaway int id; 2824a04a10f8SKris Kennaway Channel *c; 2825a04a10f8SKris Kennaway 2826a04a10f8SKris Kennaway id = packet_get_int(); 2827af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2828a04a10f8SKris Kennaway c = channel_lookup(id); 2829a04a10f8SKris Kennaway if (c == NULL) 2830a04a10f8SKris Kennaway packet_disconnect("Received ieof for nonexistent channel %d.", id); 2831*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2832*ca86bcf2SDag-Erling Smørgrav return 0; 2833a04a10f8SKris Kennaway chan_rcvd_ieof(c); 2834af12a3e7SDag-Erling Smørgrav 2835af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 2836af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 2837af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 2838af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 2839af12a3e7SDag-Erling Smørgrav if (buffer_len(&c->input) == 0) 2840af12a3e7SDag-Erling Smørgrav chan_ibuf_empty(c); 2841af12a3e7SDag-Erling Smørgrav } 2842bc5531deSDag-Erling Smørgrav return 0; 2843a04a10f8SKris Kennaway } 2844511b41d2SMark Murray 2845333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2846bc5531deSDag-Erling Smørgrav int 2847af12a3e7SDag-Erling Smørgrav channel_input_close(int type, u_int32_t seq, void *ctxt) 2848511b41d2SMark Murray { 2849a04a10f8SKris Kennaway int id; 2850a04a10f8SKris Kennaway Channel *c; 2851511b41d2SMark Murray 2852a04a10f8SKris Kennaway id = packet_get_int(); 2853af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2854a04a10f8SKris Kennaway c = channel_lookup(id); 2855a04a10f8SKris Kennaway if (c == NULL) 2856a04a10f8SKris Kennaway packet_disconnect("Received close for nonexistent channel %d.", id); 2857*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2858*ca86bcf2SDag-Erling Smørgrav return 0; 2859511b41d2SMark Murray /* 2860511b41d2SMark Murray * Send a confirmation that we have closed the channel and no more 2861511b41d2SMark Murray * data is coming for it. 2862511b41d2SMark Murray */ 2863511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); 2864a04a10f8SKris Kennaway packet_put_int(c->remote_id); 2865511b41d2SMark Murray packet_send(); 2866511b41d2SMark Murray 2867511b41d2SMark Murray /* 2868511b41d2SMark Murray * If the channel is in closed state, we have sent a close request, 2869511b41d2SMark Murray * and the other side will eventually respond with a confirmation. 2870511b41d2SMark Murray * Thus, we cannot free the channel here, because then there would be 2871511b41d2SMark Murray * no-one to receive the confirmation. The channel gets freed when 2872511b41d2SMark Murray * the confirmation arrives. 2873511b41d2SMark Murray */ 2874a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_CLOSED) { 2875511b41d2SMark Murray /* 2876511b41d2SMark Murray * Not a closed channel - mark it as draining, which will 2877511b41d2SMark Murray * cause it to be freed later. 2878511b41d2SMark Murray */ 2879af12a3e7SDag-Erling Smørgrav buffer_clear(&c->input); 2880a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OUTPUT_DRAINING; 2881511b41d2SMark Murray } 2882bc5531deSDag-Erling Smørgrav return 0; 2883511b41d2SMark Murray } 2884511b41d2SMark Murray 2885a04a10f8SKris Kennaway /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ 2886333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2887bc5531deSDag-Erling Smørgrav int 2888af12a3e7SDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, void *ctxt) 2889a04a10f8SKris Kennaway { 2890a04a10f8SKris Kennaway int id = packet_get_int(); 2891a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2892af12a3e7SDag-Erling Smørgrav 2893a04a10f8SKris Kennaway if (c == NULL) 2894a04a10f8SKris Kennaway packet_disconnect("Received oclose for nonexistent channel %d.", id); 2895*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2896*ca86bcf2SDag-Erling Smørgrav return 0; 2897*ca86bcf2SDag-Erling Smørgrav packet_check_eom(); 2898a04a10f8SKris Kennaway chan_rcvd_oclose(c); 2899bc5531deSDag-Erling Smørgrav return 0; 2900a04a10f8SKris Kennaway } 2901511b41d2SMark Murray 2902333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2903bc5531deSDag-Erling Smørgrav int 2904af12a3e7SDag-Erling Smørgrav channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) 2905511b41d2SMark Murray { 2906a04a10f8SKris Kennaway int id = packet_get_int(); 2907a04a10f8SKris Kennaway Channel *c = channel_lookup(id); 2908a04a10f8SKris Kennaway 2909a04a10f8SKris Kennaway if (c == NULL) 2910a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2911a04a10f8SKris Kennaway "out-of-range channel %d.", id); 2912*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2913*ca86bcf2SDag-Erling Smørgrav return 0; 2914*ca86bcf2SDag-Erling Smørgrav packet_check_eom(); 2915e4a9863fSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) 2916a04a10f8SKris Kennaway packet_disconnect("Received close confirmation for " 2917a04a10f8SKris Kennaway "non-closed channel %d (type %d).", id, c->type); 2918af12a3e7SDag-Erling Smørgrav channel_free(c); 2919bc5531deSDag-Erling Smørgrav return 0; 2920a04a10f8SKris Kennaway } 2921a04a10f8SKris Kennaway 2922333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2923bc5531deSDag-Erling Smørgrav int 2924af12a3e7SDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) 2925a04a10f8SKris Kennaway { 2926a04a10f8SKris Kennaway int id, remote_id; 2927a04a10f8SKris Kennaway Channel *c; 2928a04a10f8SKris Kennaway 2929a04a10f8SKris Kennaway id = packet_get_int(); 2930a04a10f8SKris Kennaway c = channel_lookup(id); 2931a04a10f8SKris Kennaway 2932*ca86bcf2SDag-Erling Smørgrav if (c==NULL) 2933*ca86bcf2SDag-Erling Smørgrav packet_disconnect("Received open confirmation for " 2934*ca86bcf2SDag-Erling Smørgrav "unknown channel %d.", id); 2935*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2936*ca86bcf2SDag-Erling Smørgrav return 0; 2937*ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 2938a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 2939a04a10f8SKris Kennaway "non-opening channel %d.", id); 2940a04a10f8SKris Kennaway remote_id = packet_get_int(); 2941a04a10f8SKris Kennaway /* Record the remote channel number and mark that the channel is now open. */ 2942a04a10f8SKris Kennaway c->remote_id = remote_id; 2943a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 2944a04a10f8SKris Kennaway 2945a04a10f8SKris Kennaway if (compat20) { 2946a04a10f8SKris Kennaway c->remote_window = packet_get_int(); 2947a04a10f8SKris Kennaway c->remote_maxpacket = packet_get_int(); 2948d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 29495b9b2fafSBrian Feldman debug2("callback start"); 2950e2f6069cSDag-Erling Smørgrav c->open_confirm(c->self, 1, c->open_confirm_ctx); 29515b9b2fafSBrian Feldman debug2("callback done"); 2952a04a10f8SKris Kennaway } 2953221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 2954a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 2955a04a10f8SKris Kennaway } 2956af12a3e7SDag-Erling Smørgrav packet_check_eom(); 2957bc5531deSDag-Erling Smørgrav return 0; 2958af12a3e7SDag-Erling Smørgrav } 2959af12a3e7SDag-Erling Smørgrav 2960af12a3e7SDag-Erling Smørgrav static char * 2961af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 2962af12a3e7SDag-Erling Smørgrav { 2963af12a3e7SDag-Erling Smørgrav switch (reason) { 2964af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 2965af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 2966af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 2967af12a3e7SDag-Erling Smørgrav return "connect failed"; 2968af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 2969af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 2970af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 2971af12a3e7SDag-Erling Smørgrav return "resource shortage"; 2972af12a3e7SDag-Erling Smørgrav } 2973af12a3e7SDag-Erling Smørgrav return "unknown reason"; 2974a04a10f8SKris Kennaway } 2975a04a10f8SKris Kennaway 2976333ee039SDag-Erling Smørgrav /* ARGSUSED */ 2977bc5531deSDag-Erling Smørgrav int 2978af12a3e7SDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, void *ctxt) 2979a04a10f8SKris Kennaway { 2980ca3176e7SBrian Feldman int id, reason; 2981ca3176e7SBrian Feldman char *msg = NULL, *lang = NULL; 2982a04a10f8SKris Kennaway Channel *c; 2983a04a10f8SKris Kennaway 2984a04a10f8SKris Kennaway id = packet_get_int(); 2985a04a10f8SKris Kennaway c = channel_lookup(id); 2986a04a10f8SKris Kennaway 2987*ca86bcf2SDag-Erling Smørgrav if (c==NULL) 2988*ca86bcf2SDag-Erling Smørgrav packet_disconnect("Received open failure for " 2989*ca86bcf2SDag-Erling Smørgrav "unknown channel %d.", id); 2990*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 2991*ca86bcf2SDag-Erling Smørgrav return 0; 2992*ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 2993a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 2994a04a10f8SKris Kennaway "non-opening channel %d.", id); 2995a04a10f8SKris Kennaway if (compat20) { 2996ca3176e7SBrian Feldman reason = packet_get_int(); 2997af12a3e7SDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 2998ca3176e7SBrian Feldman msg = packet_get_string(NULL); 2999ca3176e7SBrian Feldman lang = packet_get_string(NULL); 3000ca3176e7SBrian Feldman } 3001221552e4SDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", id, 3002af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 3003e4a9863fSDag-Erling Smørgrav free(msg); 3004e4a9863fSDag-Erling Smørgrav free(lang); 3005e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 3006e2f6069cSDag-Erling Smørgrav debug2("callback start"); 3007e2f6069cSDag-Erling Smørgrav c->open_confirm(c->self, 0, c->open_confirm_ctx); 3008e2f6069cSDag-Erling Smørgrav debug2("callback done"); 3009e2f6069cSDag-Erling Smørgrav } 3010a04a10f8SKris Kennaway } 3011af12a3e7SDag-Erling Smørgrav packet_check_eom(); 3012cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 3013cce7d346SDag-Erling Smørgrav chan_mark_dead(c); 3014bc5531deSDag-Erling Smørgrav return 0; 3015a04a10f8SKris Kennaway } 3016a04a10f8SKris Kennaway 3017333ee039SDag-Erling Smørgrav /* ARGSUSED */ 3018bc5531deSDag-Erling Smørgrav int 3019af12a3e7SDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) 3020a04a10f8SKris Kennaway { 3021a04a10f8SKris Kennaway Channel *c; 3022a82e551fSDag-Erling Smørgrav int id; 3023557f75e5SDag-Erling Smørgrav u_int adjust, tmp; 3024a04a10f8SKris Kennaway 3025a04a10f8SKris Kennaway if (!compat20) 3026bc5531deSDag-Erling Smørgrav return 0; 3027511b41d2SMark Murray 3028511b41d2SMark Murray /* Get the channel number and verify it. */ 3029a04a10f8SKris Kennaway id = packet_get_int(); 3030a04a10f8SKris Kennaway c = channel_lookup(id); 3031511b41d2SMark Murray 3032b74df5b2SDag-Erling Smørgrav if (c == NULL) { 3033b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 3034bc5531deSDag-Erling Smørgrav return 0; 3035511b41d2SMark Murray } 3036*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 3037*ca86bcf2SDag-Erling Smørgrav return 0; 3038a04a10f8SKris Kennaway adjust = packet_get_int(); 3039af12a3e7SDag-Erling Smørgrav packet_check_eom(); 3040a82e551fSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", id, adjust); 3041557f75e5SDag-Erling Smørgrav if ((tmp = c->remote_window + adjust) < c->remote_window) 3042557f75e5SDag-Erling Smørgrav fatal("channel %d: adjust %u overflows remote window %u", 3043557f75e5SDag-Erling Smørgrav id, adjust, c->remote_window); 3044557f75e5SDag-Erling Smørgrav c->remote_window = tmp; 3045bc5531deSDag-Erling Smørgrav return 0; 3046511b41d2SMark Murray } 3047511b41d2SMark Murray 3048333ee039SDag-Erling Smørgrav /* ARGSUSED */ 3049bc5531deSDag-Erling Smørgrav int 3050af12a3e7SDag-Erling Smørgrav channel_input_port_open(int type, u_int32_t seq, void *ctxt) 3051af12a3e7SDag-Erling Smørgrav { 3052af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 3053af12a3e7SDag-Erling Smørgrav u_short host_port; 3054af12a3e7SDag-Erling Smørgrav char *host, *originator_string; 3055d4af9e69SDag-Erling Smørgrav int remote_id; 3056af12a3e7SDag-Erling Smørgrav 3057af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 3058af12a3e7SDag-Erling Smørgrav host = packet_get_string(NULL); 3059af12a3e7SDag-Erling Smørgrav host_port = packet_get_int(); 3060af12a3e7SDag-Erling Smørgrav 3061af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 3062af12a3e7SDag-Erling Smørgrav originator_string = packet_get_string(NULL); 3063af12a3e7SDag-Erling Smørgrav } else { 3064af12a3e7SDag-Erling Smørgrav originator_string = xstrdup("unknown (remote did not supply name)"); 3065af12a3e7SDag-Erling Smørgrav } 3066af12a3e7SDag-Erling Smørgrav packet_check_eom(); 3067a0ee8cc6SDag-Erling Smørgrav c = channel_connect_to_port(host, host_port, 3068d4af9e69SDag-Erling Smørgrav "connected socket", originator_string); 3069e4a9863fSDag-Erling Smørgrav free(originator_string); 3070e4a9863fSDag-Erling Smørgrav free(host); 3071221552e4SDag-Erling Smørgrav if (c == NULL) { 3072af12a3e7SDag-Erling Smørgrav packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 3073af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 3074af12a3e7SDag-Erling Smørgrav packet_send(); 3075d4af9e69SDag-Erling Smørgrav } else 3076d4af9e69SDag-Erling Smørgrav c->remote_id = remote_id; 3077bc5531deSDag-Erling Smørgrav return 0; 3078af12a3e7SDag-Erling Smørgrav } 3079af12a3e7SDag-Erling Smørgrav 3080d4af9e69SDag-Erling Smørgrav /* ARGSUSED */ 3081bc5531deSDag-Erling Smørgrav int 3082d4af9e69SDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) 3083d4af9e69SDag-Erling Smørgrav { 3084d4af9e69SDag-Erling Smørgrav Channel *c; 3085d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 3086cce7d346SDag-Erling Smørgrav int id; 3087d4af9e69SDag-Erling Smørgrav 3088d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 30897aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 3090d4af9e69SDag-Erling Smørgrav 3091cce7d346SDag-Erling Smørgrav id = packet_get_int(); 3092cce7d346SDag-Erling Smørgrav debug2("channel_input_status_confirm: type %d id %d", type, id); 3093d4af9e69SDag-Erling Smørgrav 3094cce7d346SDag-Erling Smørgrav if ((c = channel_lookup(id)) == NULL) { 3095cce7d346SDag-Erling Smørgrav logit("channel_input_status_confirm: %d: unknown", id); 3096bc5531deSDag-Erling Smørgrav return 0; 3097d4af9e69SDag-Erling Smørgrav } 3098*ca86bcf2SDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ctxt)) 3099*ca86bcf2SDag-Erling Smørgrav return 0; 3100*ca86bcf2SDag-Erling Smørgrav packet_check_eom(); 3101d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 3102bc5531deSDag-Erling Smørgrav return 0; 3103d4af9e69SDag-Erling Smørgrav cc->cb(type, c, cc->ctx); 3104d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 3105b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 3106e4a9863fSDag-Erling Smørgrav free(cc); 3107bc5531deSDag-Erling Smørgrav return 0; 3108d4af9e69SDag-Erling Smørgrav } 3109af12a3e7SDag-Erling Smørgrav 3110af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 3111511b41d2SMark Murray 3112511b41d2SMark Murray void 3113af12a3e7SDag-Erling Smørgrav channel_set_af(int af) 3114511b41d2SMark Murray { 3115af12a3e7SDag-Erling Smørgrav IPv4or6 = af; 3116511b41d2SMark Murray } 3117511b41d2SMark Murray 311889986192SBrooks Davis 3119462c32cbSDag-Erling Smørgrav /* 3120462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 3121462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 3122462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 3123462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 3124462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 3125462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 3126462c32cbSDag-Erling Smørgrav * 3127462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 3128462c32cbSDag-Erling Smørgrav * 3129462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 3130462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 3131462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 3132a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 3133462c32cbSDag-Erling Smørgrav */ 3134462c32cbSDag-Erling Smørgrav static const char * 3135462c32cbSDag-Erling Smørgrav channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, 3136a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 3137462c32cbSDag-Erling Smørgrav { 3138462c32cbSDag-Erling Smørgrav const char *addr = NULL; 3139462c32cbSDag-Erling Smørgrav int wildcard = 0; 3140462c32cbSDag-Erling Smørgrav 3141462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 3142462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 3143a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 3144462c32cbSDag-Erling Smørgrav wildcard = 1; 3145a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 3146462c32cbSDag-Erling Smørgrav if (((datafellows & SSH_OLD_FORWARD_ADDR) && 3147462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 3148462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 3149a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 3150462c32cbSDag-Erling Smørgrav wildcard = 1; 3151f7167e0eSDag-Erling Smørgrav /* 3152f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 3153f7167e0eSDag-Erling Smørgrav * address and it was overridden. 3154f7167e0eSDag-Erling Smørgrav */ 3155f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 3156f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 3157f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 3158f7167e0eSDag-Erling Smørgrav packet_send_debug("Forwarding listen address " 3159f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 3160f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 3161f7167e0eSDag-Erling Smørgrav } 3162a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 3163a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 3164a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3165a0ee8cc6SDag-Erling Smørgrav /* Accept localhost address when GatewayPorts=yes */ 3166a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 3167f7167e0eSDag-Erling Smørgrav } 3168a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 3169a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3170a0ee8cc6SDag-Erling Smørgrav /* 3171a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 3172a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 3173a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 3174a0ee8cc6SDag-Erling Smørgrav */ 3175462c32cbSDag-Erling Smørgrav addr = listen_addr; 3176462c32cbSDag-Erling Smørgrav } 3177462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 3178462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 3179462c32cbSDag-Erling Smørgrav return addr; 3180462c32cbSDag-Erling Smørgrav } 3181462c32cbSDag-Erling Smørgrav 3182af12a3e7SDag-Erling Smørgrav static int 3183a0ee8cc6SDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, 3184a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 3185511b41d2SMark Murray { 3186af12a3e7SDag-Erling Smørgrav Channel *c; 3187b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 3188511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3189aa49c926SDag-Erling Smørgrav const char *host, *addr; 3190af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 3191cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 3192511b41d2SMark Murray 3193aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 3194511b41d2SMark Murray 3195557f75e5SDag-Erling Smørgrav if (is_client && fwd->connect_path != NULL) { 3196557f75e5SDag-Erling Smørgrav host = fwd->connect_path; 3197557f75e5SDag-Erling Smørgrav } else { 3198557f75e5SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 3199557f75e5SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 3200af12a3e7SDag-Erling Smørgrav if (host == NULL) { 3201af12a3e7SDag-Erling Smørgrav error("No forward host name."); 3202d4ecd108SDag-Erling Smørgrav return 0; 3203ca3176e7SBrian Feldman } 3204cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 3205ca3176e7SBrian Feldman error("Forward host name too long."); 3206d4ecd108SDag-Erling Smørgrav return 0; 3207ca3176e7SBrian Feldman } 3208557f75e5SDag-Erling Smørgrav } 3209ca3176e7SBrian Feldman 3210462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 3211a0ee8cc6SDag-Erling Smørgrav addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, 3212a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 3213a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d wildcard %d addr %s", __func__, 3214aa49c926SDag-Erling Smørgrav type, wildcard, (addr == NULL) ? "NULL" : addr); 3215aa49c926SDag-Erling Smørgrav 3216aa49c926SDag-Erling Smørgrav /* 3217511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 3218511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 3219511b41d2SMark Murray */ 3220511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 3221511b41d2SMark Murray hints.ai_family = IPv4or6; 3222aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 3223511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3224a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 3225aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 3226aa49c926SDag-Erling Smørgrav if (addr == NULL) { 3227aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 3228aa49c926SDag-Erling Smørgrav packet_disconnect("getaddrinfo: fatal error: %s", 3229d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3230aa49c926SDag-Erling Smørgrav } else { 3231a0ee8cc6SDag-Erling Smørgrav error("%s: getaddrinfo(%.64s): %s", __func__, addr, 3232d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3233aa49c926SDag-Erling Smørgrav } 3234d4ecd108SDag-Erling Smørgrav return 0; 3235aa49c926SDag-Erling Smørgrav } 3236cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 3237cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 3238511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3239cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 3240cce7d346SDag-Erling Smørgrav case AF_INET: 3241cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 3242cce7d346SDag-Erling Smørgrav sin_port; 3243cce7d346SDag-Erling Smørgrav break; 3244cce7d346SDag-Erling Smørgrav case AF_INET6: 3245cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 3246cce7d346SDag-Erling Smørgrav sin6_port; 3247cce7d346SDag-Erling Smørgrav break; 3248cce7d346SDag-Erling Smørgrav default: 3249511b41d2SMark Murray continue; 3250cce7d346SDag-Erling Smørgrav } 3251cce7d346SDag-Erling Smørgrav /* 3252cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 3253cce7d346SDag-Erling Smørgrav * same port for all address families. 3254cce7d346SDag-Erling Smørgrav */ 3255a0ee8cc6SDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 3256cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && *allocated_listen_port > 0) 3257cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 3258cce7d346SDag-Erling Smørgrav 3259511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 3260511b41d2SMark Murray strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3261a0ee8cc6SDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 3262511b41d2SMark Murray continue; 3263511b41d2SMark Murray } 3264511b41d2SMark Murray /* Create a port to listen for the host. */ 3265221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 3266511b41d2SMark Murray if (sock < 0) { 3267511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 3268511b41d2SMark Murray verbose("socket: %.100s", strerror(errno)); 3269511b41d2SMark Murray continue; 3270511b41d2SMark Murray } 3271b74df5b2SDag-Erling Smørgrav 3272b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 3273b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3274b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3275f388f5efSDag-Erling Smørgrav 3276cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 3277cce7d346SDag-Erling Smørgrav ntop, strport); 3278511b41d2SMark Murray 3279511b41d2SMark Murray /* Bind the socket to the address. */ 3280511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 3281511b41d2SMark Murray /* address can be in use ipv6 address is already bound */ 3282989dd127SDag-Erling Smørgrav if (!ai->ai_next) 3283989dd127SDag-Erling Smørgrav error("bind: %.100s", strerror(errno)); 3284989dd127SDag-Erling Smørgrav else 3285511b41d2SMark Murray verbose("bind: %.100s", strerror(errno)); 3286989dd127SDag-Erling Smørgrav 3287511b41d2SMark Murray close(sock); 3288511b41d2SMark Murray continue; 3289511b41d2SMark Murray } 3290511b41d2SMark Murray /* Start listening for connections on the socket. */ 3291476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 3292511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 3293511b41d2SMark Murray close(sock); 3294511b41d2SMark Murray continue; 3295511b41d2SMark Murray } 3296cce7d346SDag-Erling Smørgrav 3297cce7d346SDag-Erling Smørgrav /* 3298a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 3299cce7d346SDag-Erling Smørgrav * record what we got. 3300cce7d346SDag-Erling Smørgrav */ 3301a0ee8cc6SDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && 3302cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 3303cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 3304076ad2f8SDag-Erling Smørgrav *allocated_listen_port = get_local_port(sock); 3305cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 3306cce7d346SDag-Erling Smørgrav *allocated_listen_port); 3307cce7d346SDag-Erling Smørgrav } 3308cce7d346SDag-Erling Smørgrav 330960c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 3310af12a3e7SDag-Erling Smørgrav c = channel_new("port listener", type, sock, sock, -1, 3311a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3312221552e4SDag-Erling Smørgrav 0, "port listener", 1); 3313cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 3314a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 3315462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 3316a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 3317462c32cbSDag-Erling Smørgrav !(datafellows & SSH_BUG_DYNAMIC_RPORT)) 3318462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 3319462c32cbSDag-Erling Smørgrav else 3320a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 3321511b41d2SMark Murray success = 1; 3322511b41d2SMark Murray } 3323511b41d2SMark Murray if (success == 0) 3324a0ee8cc6SDag-Erling Smørgrav error("%s: cannot listen to port: %d", __func__, 3325a0ee8cc6SDag-Erling Smørgrav fwd->listen_port); 3326511b41d2SMark Murray freeaddrinfo(aitop); 3327ca3176e7SBrian Feldman return success; 3328511b41d2SMark Murray } 3329511b41d2SMark Murray 3330a0ee8cc6SDag-Erling Smørgrav static int 3331a0ee8cc6SDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, 3332a0ee8cc6SDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3333a0ee8cc6SDag-Erling Smørgrav { 3334a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 3335a0ee8cc6SDag-Erling Smørgrav const char *path; 3336a0ee8cc6SDag-Erling Smørgrav Channel *c; 3337a0ee8cc6SDag-Erling Smørgrav int port, sock; 3338a0ee8cc6SDag-Erling Smørgrav mode_t omask; 3339a0ee8cc6SDag-Erling Smørgrav 3340a0ee8cc6SDag-Erling Smørgrav switch (type) { 3341a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 3342a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3343a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 3344a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 3345a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 3346a0ee8cc6SDag-Erling Smørgrav return 0; 3347a0ee8cc6SDag-Erling Smørgrav } 3348a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 3349a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3350a0ee8cc6SDag-Erling Smørgrav } else { 3351a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 3352a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 3353a0ee8cc6SDag-Erling Smørgrav return 0; 3354a0ee8cc6SDag-Erling Smørgrav } 3355a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 3356a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 3357a0ee8cc6SDag-Erling Smørgrav return 0; 3358a0ee8cc6SDag-Erling Smørgrav } 3359a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 3360a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 3361a0ee8cc6SDag-Erling Smørgrav } 3362a0ee8cc6SDag-Erling Smørgrav break; 3363a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 3364a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 3365a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3366a0ee8cc6SDag-Erling Smørgrav break; 3367a0ee8cc6SDag-Erling Smørgrav default: 3368a0ee8cc6SDag-Erling Smørgrav error("%s: unexpected channel type %d", __func__, type); 3369a0ee8cc6SDag-Erling Smørgrav return 0; 3370a0ee8cc6SDag-Erling Smørgrav } 3371a0ee8cc6SDag-Erling Smørgrav 3372a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 3373a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 3374a0ee8cc6SDag-Erling Smørgrav return 0; 3375a0ee8cc6SDag-Erling Smørgrav } 3376a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 3377a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 3378a0ee8cc6SDag-Erling Smørgrav return 0; 3379a0ee8cc6SDag-Erling Smørgrav } 3380a0ee8cc6SDag-Erling Smørgrav 3381a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d path %s", __func__, type, fwd->listen_path); 3382a0ee8cc6SDag-Erling Smørgrav 3383a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 3384a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 3385a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 3386a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 3387a0ee8cc6SDag-Erling Smørgrav umask(omask); 3388a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 3389a0ee8cc6SDag-Erling Smørgrav return 0; 3390a0ee8cc6SDag-Erling Smørgrav 3391a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 3392a0ee8cc6SDag-Erling Smørgrav 3393a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 3394a0ee8cc6SDag-Erling Smørgrav c = channel_new("unix listener", type, sock, sock, -1, 3395a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3396a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 3397a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 3398a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 3399a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 3400a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 3401a0ee8cc6SDag-Erling Smørgrav return 1; 3402a0ee8cc6SDag-Erling Smørgrav } 3403a0ee8cc6SDag-Erling Smørgrav 3404a0ee8cc6SDag-Erling Smørgrav static int 3405a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(const char *host, u_short port) 340621e764dfSDag-Erling Smørgrav { 340721e764dfSDag-Erling Smørgrav u_int i; 340821e764dfSDag-Erling Smørgrav int found = 0; 340921e764dfSDag-Erling Smørgrav 341021e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 341121e764dfSDag-Erling Smørgrav Channel *c = channels[i]; 3412462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3413462c32cbSDag-Erling Smørgrav continue; 3414462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 3415462c32cbSDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3416462c32cbSDag-Erling Smørgrav channel_free(c); 3417462c32cbSDag-Erling Smørgrav found = 1; 3418462c32cbSDag-Erling Smørgrav } 3419462c32cbSDag-Erling Smørgrav } 342021e764dfSDag-Erling Smørgrav 3421462c32cbSDag-Erling Smørgrav return (found); 3422462c32cbSDag-Erling Smørgrav } 3423462c32cbSDag-Erling Smørgrav 3424a0ee8cc6SDag-Erling Smørgrav static int 3425a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(const char *path) 3426462c32cbSDag-Erling Smørgrav { 3427462c32cbSDag-Erling Smørgrav u_int i; 3428462c32cbSDag-Erling Smørgrav int found = 0; 3429a0ee8cc6SDag-Erling Smørgrav 3430a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3431a0ee8cc6SDag-Erling Smørgrav Channel *c = channels[i]; 3432a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3433a0ee8cc6SDag-Erling Smørgrav continue; 3434a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 3435a0ee8cc6SDag-Erling Smørgrav continue; 3436a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 3437a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3438a0ee8cc6SDag-Erling Smørgrav channel_free(c); 3439a0ee8cc6SDag-Erling Smørgrav found = 1; 3440a0ee8cc6SDag-Erling Smørgrav } 3441a0ee8cc6SDag-Erling Smørgrav } 3442a0ee8cc6SDag-Erling Smørgrav 3443a0ee8cc6SDag-Erling Smørgrav return (found); 3444a0ee8cc6SDag-Erling Smørgrav } 3445a0ee8cc6SDag-Erling Smørgrav 3446a0ee8cc6SDag-Erling Smørgrav int 3447a0ee8cc6SDag-Erling Smørgrav channel_cancel_rport_listener(struct Forward *fwd) 3448a0ee8cc6SDag-Erling Smørgrav { 3449a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) 3450a0ee8cc6SDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(fwd->listen_path); 3451a0ee8cc6SDag-Erling Smørgrav else 3452a0ee8cc6SDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); 3453a0ee8cc6SDag-Erling Smørgrav } 3454a0ee8cc6SDag-Erling Smørgrav 3455a0ee8cc6SDag-Erling Smørgrav static int 3456a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, 3457a0ee8cc6SDag-Erling Smørgrav int cport, struct ForwardOptions *fwd_opts) 3458a0ee8cc6SDag-Erling Smørgrav { 3459a0ee8cc6SDag-Erling Smørgrav u_int i; 3460a0ee8cc6SDag-Erling Smørgrav int found = 0; 3461a0ee8cc6SDag-Erling Smørgrav const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); 3462462c32cbSDag-Erling Smørgrav 3463462c32cbSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3464462c32cbSDag-Erling Smørgrav Channel *c = channels[i]; 3465462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 3466462c32cbSDag-Erling Smørgrav continue; 3467462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 3468462c32cbSDag-Erling Smørgrav continue; 3469462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 3470462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 3471462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 3472462c32cbSDag-Erling Smørgrav continue; 3473462c32cbSDag-Erling Smørgrav } else { 3474462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 3475462c32cbSDag-Erling Smørgrav continue; 3476462c32cbSDag-Erling Smørgrav } 3477462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 3478462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 3479462c32cbSDag-Erling Smørgrav continue; 3480462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 3481aa49c926SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 348221e764dfSDag-Erling Smørgrav channel_free(c); 348321e764dfSDag-Erling Smørgrav found = 1; 348421e764dfSDag-Erling Smørgrav } 348521e764dfSDag-Erling Smørgrav } 348621e764dfSDag-Erling Smørgrav 348721e764dfSDag-Erling Smørgrav return (found); 348821e764dfSDag-Erling Smørgrav } 348921e764dfSDag-Erling Smørgrav 3490a0ee8cc6SDag-Erling Smørgrav static int 3491a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(const char *path) 3492a0ee8cc6SDag-Erling Smørgrav { 3493a0ee8cc6SDag-Erling Smørgrav u_int i; 3494a0ee8cc6SDag-Erling Smørgrav int found = 0; 3495a0ee8cc6SDag-Erling Smørgrav 3496a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 3497a0ee8cc6SDag-Erling Smørgrav error("%s: no path specified.", __func__); 3498a0ee8cc6SDag-Erling Smørgrav return 0; 3499a0ee8cc6SDag-Erling Smørgrav } 3500a0ee8cc6SDag-Erling Smørgrav 3501a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 3502a0ee8cc6SDag-Erling Smørgrav Channel *c = channels[i]; 3503a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 3504a0ee8cc6SDag-Erling Smørgrav continue; 3505a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 3506a0ee8cc6SDag-Erling Smørgrav continue; 3507a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 3508a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 3509a0ee8cc6SDag-Erling Smørgrav channel_free(c); 3510a0ee8cc6SDag-Erling Smørgrav found = 1; 3511a0ee8cc6SDag-Erling Smørgrav } 3512a0ee8cc6SDag-Erling Smørgrav } 3513a0ee8cc6SDag-Erling Smørgrav 3514a0ee8cc6SDag-Erling Smørgrav return (found); 3515a0ee8cc6SDag-Erling Smørgrav } 3516a0ee8cc6SDag-Erling Smørgrav 3517a0ee8cc6SDag-Erling Smørgrav int 3518a0ee8cc6SDag-Erling Smørgrav channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 3519a0ee8cc6SDag-Erling Smørgrav { 3520a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) 3521a0ee8cc6SDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(fwd->listen_path); 3522a0ee8cc6SDag-Erling Smørgrav else 3523a0ee8cc6SDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); 3524a0ee8cc6SDag-Erling Smørgrav } 3525a0ee8cc6SDag-Erling Smørgrav 3526af12a3e7SDag-Erling Smørgrav /* protocol local port fwd, used by ssh (and sshd in v1) */ 3527af12a3e7SDag-Erling Smørgrav int 3528a0ee8cc6SDag-Erling Smørgrav channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) 3529af12a3e7SDag-Erling Smørgrav { 3530a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3531a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal( 3532a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 3533a0ee8cc6SDag-Erling Smørgrav } else { 3534a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, 3535a0ee8cc6SDag-Erling Smørgrav fwd, NULL, fwd_opts); 3536a0ee8cc6SDag-Erling Smørgrav } 3537af12a3e7SDag-Erling Smørgrav } 3538af12a3e7SDag-Erling Smørgrav 3539af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 3540af12a3e7SDag-Erling Smørgrav int 3541a0ee8cc6SDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct Forward *fwd, 3542a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 3543af12a3e7SDag-Erling Smørgrav { 3544a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3545a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal( 3546a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 3547a0ee8cc6SDag-Erling Smørgrav } else { 3548a0ee8cc6SDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip( 3549a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 3550a0ee8cc6SDag-Erling Smørgrav fwd_opts); 3551a0ee8cc6SDag-Erling Smørgrav } 3552af12a3e7SDag-Erling Smørgrav } 3553af12a3e7SDag-Erling Smørgrav 3554511b41d2SMark Murray /* 3555462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 3556462c32cbSDag-Erling Smørgrav * this server. 3557462c32cbSDag-Erling Smørgrav */ 3558462c32cbSDag-Erling Smørgrav static const char * 3559462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 3560462c32cbSDag-Erling Smørgrav { 3561462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 3562462c32cbSDag-Erling Smørgrav if (datafellows & SSH_BUG_RFWD_ADDR) 3563462c32cbSDag-Erling Smørgrav return "127.0.0.1"; 3564462c32cbSDag-Erling Smørgrav else 3565462c32cbSDag-Erling Smørgrav return "localhost"; 3566462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 3567462c32cbSDag-Erling Smørgrav if (datafellows & SSH_BUG_RFWD_ADDR) 3568462c32cbSDag-Erling Smørgrav return "0.0.0.0"; 3569462c32cbSDag-Erling Smørgrav else 3570462c32cbSDag-Erling Smørgrav return ""; 3571462c32cbSDag-Erling Smørgrav } else 3572462c32cbSDag-Erling Smørgrav return listen_host; 3573462c32cbSDag-Erling Smørgrav } 3574462c32cbSDag-Erling Smørgrav 3575462c32cbSDag-Erling Smørgrav /* 3576511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 3577511b41d2SMark Murray * the secure channel to host:port from local side. 3578462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 3579462c32cbSDag-Erling Smørgrav * channel_update_permitted_opens(). 3580511b41d2SMark Murray */ 3581333ee039SDag-Erling Smørgrav int 3582a0ee8cc6SDag-Erling Smørgrav channel_request_remote_forwarding(struct Forward *fwd) 3583511b41d2SMark Murray { 3584462c32cbSDag-Erling Smørgrav int type, success = 0, idx = -1; 3585ca3176e7SBrian Feldman 3586511b41d2SMark Murray /* Send the forward request to the remote side. */ 3587a04a10f8SKris Kennaway if (compat20) { 3588a04a10f8SKris Kennaway packet_start(SSH2_MSG_GLOBAL_REQUEST); 3589a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3590a0ee8cc6SDag-Erling Smørgrav packet_put_cstring("streamlocal-forward@openssh.com"); 3591a0ee8cc6SDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 3592a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(fwd->listen_path); 3593a0ee8cc6SDag-Erling Smørgrav } else { 3594a04a10f8SKris Kennaway packet_put_cstring("tcpip-forward"); 359580628bacSDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 3596a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); 3597a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->listen_port); 3598a0ee8cc6SDag-Erling Smørgrav } 3599ca3176e7SBrian Feldman packet_send(); 3600ca3176e7SBrian Feldman packet_write_wait(); 3601ca3176e7SBrian Feldman /* Assume that server accepts the request */ 3602ca3176e7SBrian Feldman success = 1; 3603a0ee8cc6SDag-Erling Smørgrav } else if (fwd->listen_path == NULL) { 3604511b41d2SMark Murray packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); 3605a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->listen_port); 3606a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(fwd->connect_host); 3607a0ee8cc6SDag-Erling Smørgrav packet_put_int(fwd->connect_port); 3608511b41d2SMark Murray packet_send(); 3609511b41d2SMark Murray packet_write_wait(); 3610ca3176e7SBrian Feldman 3611ca3176e7SBrian Feldman /* Wait for response from the remote side. */ 3612af12a3e7SDag-Erling Smørgrav type = packet_read(); 3613ca3176e7SBrian Feldman switch (type) { 3614ca3176e7SBrian Feldman case SSH_SMSG_SUCCESS: 3615ca3176e7SBrian Feldman success = 1; 3616ca3176e7SBrian Feldman break; 3617ca3176e7SBrian Feldman case SSH_SMSG_FAILURE: 3618ca3176e7SBrian Feldman break; 3619ca3176e7SBrian Feldman default: 3620ca3176e7SBrian Feldman /* Unknown packet */ 3621ca3176e7SBrian Feldman packet_disconnect("Protocol error for port forward request:" 3622ca3176e7SBrian Feldman "received packet type %d.", type); 3623ca3176e7SBrian Feldman } 3624a0ee8cc6SDag-Erling Smørgrav } else { 3625a0ee8cc6SDag-Erling Smørgrav logit("Warning: Server does not support remote stream local forwarding."); 3626ca3176e7SBrian Feldman } 3627ca3176e7SBrian Feldman if (success) { 3628e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 3629557f75e5SDag-Erling Smørgrav permitted_opens = xreallocarray(permitted_opens, 3630e2f6069cSDag-Erling Smørgrav num_permitted_opens + 1, sizeof(*permitted_opens)); 3631462c32cbSDag-Erling Smørgrav idx = num_permitted_opens++; 3632a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3633a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].host_to_connect = 3634a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->connect_path); 3635a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 3636a0ee8cc6SDag-Erling Smørgrav PORT_STREAMLOCAL; 3637a0ee8cc6SDag-Erling Smørgrav } else { 3638a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].host_to_connect = 3639a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->connect_host); 3640a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 3641a0ee8cc6SDag-Erling Smørgrav fwd->connect_port; 3642a0ee8cc6SDag-Erling Smørgrav } 3643a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3644a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = NULL; 3645a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = 3646a0ee8cc6SDag-Erling Smørgrav xstrdup(fwd->listen_path); 3647a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_port = PORT_STREAMLOCAL; 3648a0ee8cc6SDag-Erling Smørgrav } else { 3649a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = 3650a0ee8cc6SDag-Erling Smørgrav fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; 3651a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = NULL; 3652a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_port = fwd->listen_port; 3653a0ee8cc6SDag-Erling Smørgrav } 3654*ca86bcf2SDag-Erling Smørgrav permitted_opens[idx].downstream = NULL; 3655511b41d2SMark Murray } 3656462c32cbSDag-Erling Smørgrav return (idx); 3657a04a10f8SKris Kennaway } 3658511b41d2SMark Murray 3659a0ee8cc6SDag-Erling Smørgrav static int 3660a0ee8cc6SDag-Erling Smørgrav open_match(ForwardPermission *allowed_open, const char *requestedhost, 3661a0ee8cc6SDag-Erling Smørgrav int requestedport) 3662a0ee8cc6SDag-Erling Smørgrav { 3663a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3664a0ee8cc6SDag-Erling Smørgrav return 0; 3665a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 3666a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 3667a0ee8cc6SDag-Erling Smørgrav return 0; 3668076ad2f8SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 3669076ad2f8SDag-Erling Smørgrav strcmp(allowed_open->host_to_connect, requestedhost) != 0) 3670a0ee8cc6SDag-Erling Smørgrav return 0; 3671a0ee8cc6SDag-Erling Smørgrav return 1; 3672a0ee8cc6SDag-Erling Smørgrav } 3673a0ee8cc6SDag-Erling Smørgrav 3674a0ee8cc6SDag-Erling Smørgrav /* 3675a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 3676a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 3677a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 3678a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 3679a0ee8cc6SDag-Erling Smørgrav */ 3680a0ee8cc6SDag-Erling Smørgrav static int 3681a0ee8cc6SDag-Erling Smørgrav open_listen_match_tcpip(ForwardPermission *allowed_open, 3682a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 3683a0ee8cc6SDag-Erling Smørgrav { 3684a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 3685a0ee8cc6SDag-Erling Smørgrav 3686a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3687a0ee8cc6SDag-Erling Smørgrav return 0; 3688a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 3689a0ee8cc6SDag-Erling Smørgrav return 0; 3690a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 3691a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 3692a0ee8cc6SDag-Erling Smørgrav return 1; 3693a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 3694a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 3695a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 3696a0ee8cc6SDag-Erling Smørgrav if (allowed_host == NULL || 3697a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 3698a0ee8cc6SDag-Erling Smørgrav return 0; 3699a0ee8cc6SDag-Erling Smørgrav return 1; 3700a0ee8cc6SDag-Erling Smørgrav } 3701a0ee8cc6SDag-Erling Smørgrav 3702a0ee8cc6SDag-Erling Smørgrav static int 3703a0ee8cc6SDag-Erling Smørgrav open_listen_match_streamlocal(ForwardPermission *allowed_open, 3704a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 3705a0ee8cc6SDag-Erling Smørgrav { 3706a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3707a0ee8cc6SDag-Erling Smørgrav return 0; 3708a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 3709a0ee8cc6SDag-Erling Smørgrav return 0; 3710a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 3711a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 3712a0ee8cc6SDag-Erling Smørgrav return 0; 3713a0ee8cc6SDag-Erling Smørgrav return 1; 3714a0ee8cc6SDag-Erling Smørgrav } 3715a0ee8cc6SDag-Erling Smørgrav 3716511b41d2SMark Murray /* 371721e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 371821e764dfSDag-Erling Smørgrav * local side. 371921e764dfSDag-Erling Smørgrav */ 3720a0ee8cc6SDag-Erling Smørgrav static int 3721a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(const char *host, u_short port) 372221e764dfSDag-Erling Smørgrav { 372321e764dfSDag-Erling Smørgrav int i; 372421e764dfSDag-Erling Smørgrav 372521e764dfSDag-Erling Smørgrav if (!compat20) 3726462c32cbSDag-Erling Smørgrav return -1; 372721e764dfSDag-Erling Smørgrav 372821e764dfSDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3729a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) 373021e764dfSDag-Erling Smørgrav break; 373121e764dfSDag-Erling Smørgrav } 373221e764dfSDag-Erling Smørgrav if (i >= num_permitted_opens) { 373321e764dfSDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3734462c32cbSDag-Erling Smørgrav return -1; 373521e764dfSDag-Erling Smørgrav } 373621e764dfSDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 373721e764dfSDag-Erling Smørgrav packet_put_cstring("cancel-tcpip-forward"); 373821e764dfSDag-Erling Smørgrav packet_put_char(0); 3739462c32cbSDag-Erling Smørgrav packet_put_cstring(channel_rfwd_bind_host(host)); 374021e764dfSDag-Erling Smørgrav packet_put_int(port); 374121e764dfSDag-Erling Smørgrav packet_send(); 374221e764dfSDag-Erling Smørgrav 374321e764dfSDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 374421e764dfSDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 3745e4a9863fSDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 374621e764dfSDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 3747a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_host); 3748a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_host = NULL; 3749a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_path = NULL; 3750*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].downstream = NULL; 3751462c32cbSDag-Erling Smørgrav 3752462c32cbSDag-Erling Smørgrav return 0; 375321e764dfSDag-Erling Smørgrav } 375421e764dfSDag-Erling Smørgrav 375521e764dfSDag-Erling Smørgrav /* 3756a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 3757a0ee8cc6SDag-Erling Smørgrav * path from local side. 3758a0ee8cc6SDag-Erling Smørgrav */ 3759a0ee8cc6SDag-Erling Smørgrav static int 3760a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(const char *path) 3761a0ee8cc6SDag-Erling Smørgrav { 3762a0ee8cc6SDag-Erling Smørgrav int i; 3763a0ee8cc6SDag-Erling Smørgrav 3764a0ee8cc6SDag-Erling Smørgrav if (!compat20) 3765a0ee8cc6SDag-Erling Smørgrav return -1; 3766a0ee8cc6SDag-Erling Smørgrav 3767a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3768a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_streamlocal(&permitted_opens[i], path)) 3769a0ee8cc6SDag-Erling Smørgrav break; 3770a0ee8cc6SDag-Erling Smørgrav } 3771a0ee8cc6SDag-Erling Smørgrav if (i >= num_permitted_opens) { 3772a0ee8cc6SDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3773a0ee8cc6SDag-Erling Smørgrav return -1; 3774a0ee8cc6SDag-Erling Smørgrav } 3775a0ee8cc6SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 3776a0ee8cc6SDag-Erling Smørgrav packet_put_cstring("cancel-streamlocal-forward@openssh.com"); 3777a0ee8cc6SDag-Erling Smørgrav packet_put_char(0); 3778a0ee8cc6SDag-Erling Smørgrav packet_put_cstring(path); 3779a0ee8cc6SDag-Erling Smørgrav packet_send(); 3780a0ee8cc6SDag-Erling Smørgrav 3781a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_port = 0; 3782a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].port_to_connect = 0; 3783a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 3784a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].host_to_connect = NULL; 3785a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_host = NULL; 3786a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_path); 3787a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].listen_path = NULL; 3788*ca86bcf2SDag-Erling Smørgrav permitted_opens[i].downstream = NULL; 3789a0ee8cc6SDag-Erling Smørgrav 3790a0ee8cc6SDag-Erling Smørgrav return 0; 3791a0ee8cc6SDag-Erling Smørgrav } 3792a0ee8cc6SDag-Erling Smørgrav 3793a0ee8cc6SDag-Erling Smørgrav /* 3794a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 3795a0ee8cc6SDag-Erling Smørgrav */ 3796a0ee8cc6SDag-Erling Smørgrav int 3797a0ee8cc6SDag-Erling Smørgrav channel_request_rforward_cancel(struct Forward *fwd) 3798a0ee8cc6SDag-Erling Smørgrav { 3799a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 3800a0ee8cc6SDag-Erling Smørgrav return (channel_request_rforward_cancel_streamlocal( 3801a0ee8cc6SDag-Erling Smørgrav fwd->listen_path)); 3802a0ee8cc6SDag-Erling Smørgrav } else { 3803a0ee8cc6SDag-Erling Smørgrav return (channel_request_rforward_cancel_tcpip(fwd->listen_host, 3804a0ee8cc6SDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); 3805a0ee8cc6SDag-Erling Smørgrav } 3806a0ee8cc6SDag-Erling Smørgrav } 3807a0ee8cc6SDag-Erling Smørgrav 3808a0ee8cc6SDag-Erling Smørgrav /* 3809ca3176e7SBrian Feldman * Permits opening to any host/port if permitted_opens[] is empty. This is 3810ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 3811ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 3812ca3176e7SBrian Feldman */ 3813ca3176e7SBrian Feldman void 3814af12a3e7SDag-Erling Smørgrav channel_permit_all_opens(void) 3815ca3176e7SBrian Feldman { 3816ca3176e7SBrian Feldman if (num_permitted_opens == 0) 3817ca3176e7SBrian Feldman all_opens_permitted = 1; 3818ca3176e7SBrian Feldman } 3819ca3176e7SBrian Feldman 3820ca3176e7SBrian Feldman void 3821ca3176e7SBrian Feldman channel_add_permitted_opens(char *host, int port) 3822ca3176e7SBrian Feldman { 3823ca3176e7SBrian Feldman debug("allow port forwarding to host %s port %d", host, port); 3824ca3176e7SBrian Feldman 3825557f75e5SDag-Erling Smørgrav permitted_opens = xreallocarray(permitted_opens, 3826e2f6069cSDag-Erling Smørgrav num_permitted_opens + 1, sizeof(*permitted_opens)); 3827ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); 3828ca3176e7SBrian Feldman permitted_opens[num_permitted_opens].port_to_connect = port; 3829a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_host = NULL; 3830a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_path = NULL; 3831a0ee8cc6SDag-Erling Smørgrav permitted_opens[num_permitted_opens].listen_port = 0; 3832*ca86bcf2SDag-Erling Smørgrav permitted_opens[num_permitted_opens].downstream = NULL; 3833ca3176e7SBrian Feldman num_permitted_opens++; 3834ca3176e7SBrian Feldman 3835ca3176e7SBrian Feldman all_opens_permitted = 0; 3836ca3176e7SBrian Feldman } 3837ca3176e7SBrian Feldman 3838462c32cbSDag-Erling Smørgrav /* 3839462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 3840462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 3841462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 3842462c32cbSDag-Erling Smørgrav */ 3843462c32cbSDag-Erling Smørgrav void 3844462c32cbSDag-Erling Smørgrav channel_update_permitted_opens(int idx, int newport) 3845462c32cbSDag-Erling Smørgrav { 3846462c32cbSDag-Erling Smørgrav if (idx < 0 || idx >= num_permitted_opens) { 3847462c32cbSDag-Erling Smørgrav debug("channel_update_permitted_opens: index out of range:" 3848462c32cbSDag-Erling Smørgrav " %d num_permitted_opens %d", idx, num_permitted_opens); 3849462c32cbSDag-Erling Smørgrav return; 3850462c32cbSDag-Erling Smørgrav } 3851462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 3852462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 3853462c32cbSDag-Erling Smørgrav newport, 3854462c32cbSDag-Erling Smørgrav permitted_opens[idx].host_to_connect, 3855462c32cbSDag-Erling Smørgrav permitted_opens[idx].port_to_connect); 3856462c32cbSDag-Erling Smørgrav if (newport >= 0) { 3857462c32cbSDag-Erling Smørgrav permitted_opens[idx].listen_port = 3858462c32cbSDag-Erling Smørgrav (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 3859462c32cbSDag-Erling Smørgrav } else { 3860462c32cbSDag-Erling Smørgrav permitted_opens[idx].listen_port = 0; 3861462c32cbSDag-Erling Smørgrav permitted_opens[idx].port_to_connect = 0; 3862e4a9863fSDag-Erling Smørgrav free(permitted_opens[idx].host_to_connect); 3863462c32cbSDag-Erling Smørgrav permitted_opens[idx].host_to_connect = NULL; 3864a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[idx].listen_host); 3865a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_host = NULL; 3866a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[idx].listen_path); 3867a0ee8cc6SDag-Erling Smørgrav permitted_opens[idx].listen_path = NULL; 3868462c32cbSDag-Erling Smørgrav } 3869462c32cbSDag-Erling Smørgrav } 3870462c32cbSDag-Erling Smørgrav 3871333ee039SDag-Erling Smørgrav int 3872333ee039SDag-Erling Smørgrav channel_add_adm_permitted_opens(char *host, int port) 3873333ee039SDag-Erling Smørgrav { 3874333ee039SDag-Erling Smørgrav debug("config allows port forwarding to host %s port %d", host, port); 3875333ee039SDag-Erling Smørgrav 3876557f75e5SDag-Erling Smørgrav permitted_adm_opens = xreallocarray(permitted_adm_opens, 3877e2f6069cSDag-Erling Smørgrav num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); 3878333ee039SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].host_to_connect 3879333ee039SDag-Erling Smørgrav = xstrdup(host); 3880333ee039SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; 3881a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; 3882a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; 3883a0ee8cc6SDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; 3884333ee039SDag-Erling Smørgrav return ++num_adm_permitted_opens; 3885333ee039SDag-Erling Smørgrav } 3886333ee039SDag-Erling Smørgrav 3887ca3176e7SBrian Feldman void 3888462c32cbSDag-Erling Smørgrav channel_disable_adm_local_opens(void) 3889462c32cbSDag-Erling Smørgrav { 38906888a9beSDag-Erling Smørgrav channel_clear_adm_permitted_opens(); 3891557f75e5SDag-Erling Smørgrav permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1); 38926888a9beSDag-Erling Smørgrav permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; 3893462c32cbSDag-Erling Smørgrav num_adm_permitted_opens = 1; 3894462c32cbSDag-Erling Smørgrav } 3895462c32cbSDag-Erling Smørgrav 3896462c32cbSDag-Erling Smørgrav void 3897ca3176e7SBrian Feldman channel_clear_permitted_opens(void) 3898ca3176e7SBrian Feldman { 3899ca3176e7SBrian Feldman int i; 3900ca3176e7SBrian Feldman 3901a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 3902e4a9863fSDag-Erling Smørgrav free(permitted_opens[i].host_to_connect); 3903a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_host); 3904a0ee8cc6SDag-Erling Smørgrav free(permitted_opens[i].listen_path); 3905a0ee8cc6SDag-Erling Smørgrav } 3906e4a9863fSDag-Erling Smørgrav free(permitted_opens); 3907e2f6069cSDag-Erling Smørgrav permitted_opens = NULL; 3908ca3176e7SBrian Feldman num_permitted_opens = 0; 3909ca3176e7SBrian Feldman } 3910ca3176e7SBrian Feldman 3911333ee039SDag-Erling Smørgrav void 3912333ee039SDag-Erling Smørgrav channel_clear_adm_permitted_opens(void) 3913333ee039SDag-Erling Smørgrav { 3914333ee039SDag-Erling Smørgrav int i; 3915333ee039SDag-Erling Smørgrav 3916a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) { 3917e4a9863fSDag-Erling Smørgrav free(permitted_adm_opens[i].host_to_connect); 3918a0ee8cc6SDag-Erling Smørgrav free(permitted_adm_opens[i].listen_host); 3919a0ee8cc6SDag-Erling Smørgrav free(permitted_adm_opens[i].listen_path); 3920a0ee8cc6SDag-Erling Smørgrav } 3921e4a9863fSDag-Erling Smørgrav free(permitted_adm_opens); 3922e2f6069cSDag-Erling Smørgrav permitted_adm_opens = NULL; 3923333ee039SDag-Erling Smørgrav num_adm_permitted_opens = 0; 3924333ee039SDag-Erling Smørgrav } 3925ca3176e7SBrian Feldman 3926d4af9e69SDag-Erling Smørgrav void 3927d4af9e69SDag-Erling Smørgrav channel_print_adm_permitted_opens(void) 3928511b41d2SMark Murray { 3929d4af9e69SDag-Erling Smørgrav int i; 3930511b41d2SMark Murray 3931cce7d346SDag-Erling Smørgrav printf("permitopen"); 3932cce7d346SDag-Erling Smørgrav if (num_adm_permitted_opens == 0) { 3933cce7d346SDag-Erling Smørgrav printf(" any\n"); 3934cce7d346SDag-Erling Smørgrav return; 3935cce7d346SDag-Erling Smørgrav } 3936d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 3937462c32cbSDag-Erling Smørgrav if (permitted_adm_opens[i].host_to_connect == NULL) 3938462c32cbSDag-Erling Smørgrav printf(" none"); 3939462c32cbSDag-Erling Smørgrav else 3940d4af9e69SDag-Erling Smørgrav printf(" %s:%d", permitted_adm_opens[i].host_to_connect, 3941d4af9e69SDag-Erling Smørgrav permitted_adm_opens[i].port_to_connect); 3942cce7d346SDag-Erling Smørgrav printf("\n"); 3943511b41d2SMark Murray } 3944d4af9e69SDag-Erling Smørgrav 3945462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 3946462c32cbSDag-Erling Smørgrav int 3947462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 3948462c32cbSDag-Erling Smørgrav { 3949462c32cbSDag-Erling Smørgrav int port; 3950462c32cbSDag-Erling Smørgrav 3951462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 3952462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 3953462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 3954462c32cbSDag-Erling Smørgrav return port; 3955462c32cbSDag-Erling Smørgrav return -1; 3956462c32cbSDag-Erling Smørgrav } 3957462c32cbSDag-Erling Smørgrav 3958d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 3959d4af9e69SDag-Erling Smørgrav static int 3960d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 3961d4af9e69SDag-Erling Smørgrav { 3962d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 3963a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 3964*ca86bcf2SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))]; 3965d4af9e69SDag-Erling Smørgrav 3966d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 3967a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 3968a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 3969a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 3970a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 3971a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 3972a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 3973a0ee8cc6SDag-Erling Smørgrav break; 3974a0ee8cc6SDag-Erling Smørgrav case AF_INET: 3975a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 3976d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 3977d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 3978d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3979d4af9e69SDag-Erling Smørgrav error("connect_next: getnameinfo failed"); 3980511b41d2SMark Murray continue; 3981511b41d2SMark Murray } 3982a0ee8cc6SDag-Erling Smørgrav break; 3983a0ee8cc6SDag-Erling Smørgrav default: 3984a0ee8cc6SDag-Erling Smørgrav continue; 3985a0ee8cc6SDag-Erling Smørgrav } 3986d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 3987d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 3988d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 3989511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 3990e73e9afaSDag-Erling Smørgrav else 3991e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 3992511b41d2SMark Murray continue; 3993511b41d2SMark Murray } 399421e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 399521e764dfSDag-Erling Smørgrav fatal("%s: set_nonblock(%d)", __func__, sock); 3996d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 3997d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 3998d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s): " 3999d4af9e69SDag-Erling Smørgrav "%.100s", cctx->host, ntop, strport, 4000511b41d2SMark Murray strerror(errno)); 4001d4af9e69SDag-Erling Smørgrav saved_errno = errno; 4002511b41d2SMark Murray close(sock); 4003d4af9e69SDag-Erling Smørgrav errno = saved_errno; 4004511b41d2SMark Murray continue; /* fail -- try next */ 4005511b41d2SMark Murray } 4006a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 4007a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 4008d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s) " 4009d4af9e69SDag-Erling Smørgrav "in progress, fd=%d", cctx->host, ntop, strport, sock); 4010d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 4011a04a10f8SKris Kennaway return sock; 4012a04a10f8SKris Kennaway } 4013ca3176e7SBrian Feldman return -1; 4014ca3176e7SBrian Feldman } 4015ca3176e7SBrian Feldman 4016d4af9e69SDag-Erling Smørgrav static void 4017d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 4018d4af9e69SDag-Erling Smørgrav { 4019e4a9863fSDag-Erling Smørgrav free(cctx->host); 4020a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 4021a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 4022a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 4023a0ee8cc6SDag-Erling Smørgrav else 4024d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 4025a0ee8cc6SDag-Erling Smørgrav } 4026b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 4027d4af9e69SDag-Erling Smørgrav } 4028d4af9e69SDag-Erling Smørgrav 4029a0ee8cc6SDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 4030d4af9e69SDag-Erling Smørgrav static Channel * 4031a0ee8cc6SDag-Erling Smørgrav connect_to(const char *name, int port, char *ctype, char *rname) 4032d4af9e69SDag-Erling Smørgrav { 4033d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 4034d4af9e69SDag-Erling Smørgrav int gaierr; 4035d4af9e69SDag-Erling Smørgrav int sock = -1; 4036d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 4037d4af9e69SDag-Erling Smørgrav struct channel_connect cctx; 4038d4af9e69SDag-Erling Smørgrav Channel *c; 4039d4af9e69SDag-Erling Smørgrav 4040d4af9e69SDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 4041a0ee8cc6SDag-Erling Smørgrav 4042a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 4043a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 4044a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 4045a0ee8cc6SDag-Erling Smørgrav 4046a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 4047a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 4048a0ee8cc6SDag-Erling Smørgrav return (NULL); 4049a0ee8cc6SDag-Erling Smørgrav } 4050a0ee8cc6SDag-Erling Smørgrav 4051a0ee8cc6SDag-Erling Smørgrav /* 4052a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 4053a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 4054a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 4055a0ee8cc6SDag-Erling Smørgrav */ 4056a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 4057a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 4058a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 4059a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 4060a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 4061a0ee8cc6SDag-Erling Smørgrav ai->ai_socktype = SOCK_STREAM; 4062a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 4063a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 4064a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 4065a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 4066a0ee8cc6SDag-Erling Smørgrav cctx.aitop = ai; 4067a0ee8cc6SDag-Erling Smørgrav } else { 4068d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 4069d4af9e69SDag-Erling Smørgrav hints.ai_family = IPv4or6; 4070d4af9e69SDag-Erling Smørgrav hints.ai_socktype = SOCK_STREAM; 4071d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 4072a0ee8cc6SDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { 4073a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 4074d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 4075d4af9e69SDag-Erling Smørgrav return NULL; 4076d4af9e69SDag-Erling Smørgrav } 4077a0ee8cc6SDag-Erling Smørgrav } 4078d4af9e69SDag-Erling Smørgrav 4079a0ee8cc6SDag-Erling Smørgrav cctx.host = xstrdup(name); 4080d4af9e69SDag-Erling Smørgrav cctx.port = port; 4081d4af9e69SDag-Erling Smørgrav cctx.ai = cctx.aitop; 4082d4af9e69SDag-Erling Smørgrav 4083d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&cctx)) == -1) { 4084d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 4085a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 4086d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 4087d4af9e69SDag-Erling Smørgrav return NULL; 4088d4af9e69SDag-Erling Smørgrav } 4089d4af9e69SDag-Erling Smørgrav c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 4090d4af9e69SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 4091d4af9e69SDag-Erling Smørgrav c->connect_ctx = cctx; 4092d4af9e69SDag-Erling Smørgrav return c; 4093d4af9e69SDag-Erling Smørgrav } 4094d4af9e69SDag-Erling Smørgrav 4095*ca86bcf2SDag-Erling Smørgrav /* 4096*ca86bcf2SDag-Erling Smørgrav * returns either the newly connected channel or the downstream channel 4097*ca86bcf2SDag-Erling Smørgrav * that needs to deal with this connection. 4098*ca86bcf2SDag-Erling Smørgrav */ 4099d4af9e69SDag-Erling Smørgrav Channel * 4100a0ee8cc6SDag-Erling Smørgrav channel_connect_by_listen_address(const char *listen_host, 4101a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 4102d4af9e69SDag-Erling Smørgrav { 4103d4af9e69SDag-Erling Smørgrav int i; 4104d4af9e69SDag-Erling Smørgrav 4105d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 4106a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_tcpip(&permitted_opens[i], listen_host, 4107a0ee8cc6SDag-Erling Smørgrav listen_port, 1)) { 4108*ca86bcf2SDag-Erling Smørgrav if (permitted_opens[i].downstream) 4109*ca86bcf2SDag-Erling Smørgrav return permitted_opens[i].downstream; 4110d4af9e69SDag-Erling Smørgrav return connect_to( 4111d4af9e69SDag-Erling Smørgrav permitted_opens[i].host_to_connect, 4112d4af9e69SDag-Erling Smørgrav permitted_opens[i].port_to_connect, ctype, rname); 4113d4af9e69SDag-Erling Smørgrav } 4114d4af9e69SDag-Erling Smørgrav } 4115d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 4116d4af9e69SDag-Erling Smørgrav listen_port); 4117d4af9e69SDag-Erling Smørgrav return NULL; 4118d4af9e69SDag-Erling Smørgrav } 4119d4af9e69SDag-Erling Smørgrav 4120a0ee8cc6SDag-Erling Smørgrav Channel * 4121a0ee8cc6SDag-Erling Smørgrav channel_connect_by_listen_path(const char *path, char *ctype, char *rname) 4122a0ee8cc6SDag-Erling Smørgrav { 4123a0ee8cc6SDag-Erling Smørgrav int i; 4124a0ee8cc6SDag-Erling Smørgrav 4125a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) { 4126a0ee8cc6SDag-Erling Smørgrav if (open_listen_match_streamlocal(&permitted_opens[i], path)) { 4127a0ee8cc6SDag-Erling Smørgrav return connect_to( 4128a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].host_to_connect, 4129a0ee8cc6SDag-Erling Smørgrav permitted_opens[i].port_to_connect, ctype, rname); 4130a0ee8cc6SDag-Erling Smørgrav } 4131a0ee8cc6SDag-Erling Smørgrav } 4132a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 4133a0ee8cc6SDag-Erling Smørgrav path); 4134a0ee8cc6SDag-Erling Smørgrav return NULL; 4135a0ee8cc6SDag-Erling Smørgrav } 4136a0ee8cc6SDag-Erling Smørgrav 4137ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 4138d4af9e69SDag-Erling Smørgrav Channel * 4139a0ee8cc6SDag-Erling Smørgrav channel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) 4140ca3176e7SBrian Feldman { 4141333ee039SDag-Erling Smørgrav int i, permit, permit_adm = 1; 4142ca3176e7SBrian Feldman 4143ca3176e7SBrian Feldman permit = all_opens_permitted; 4144ca3176e7SBrian Feldman if (!permit) { 4145ca3176e7SBrian Feldman for (i = 0; i < num_permitted_opens; i++) 4146a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_opens[i], host, port)) { 4147ca3176e7SBrian Feldman permit = 1; 4148a0ee8cc6SDag-Erling Smørgrav break; 4149a0ee8cc6SDag-Erling Smørgrav } 4150ca3176e7SBrian Feldman } 4151333ee039SDag-Erling Smørgrav 4152333ee039SDag-Erling Smørgrav if (num_adm_permitted_opens > 0) { 4153333ee039SDag-Erling Smørgrav permit_adm = 0; 4154333ee039SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 4155a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_adm_opens[i], host, port)) { 4156333ee039SDag-Erling Smørgrav permit_adm = 1; 4157a0ee8cc6SDag-Erling Smørgrav break; 4158a0ee8cc6SDag-Erling Smørgrav } 4159333ee039SDag-Erling Smørgrav } 4160333ee039SDag-Erling Smørgrav 4161333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 4162221552e4SDag-Erling Smørgrav logit("Received request to connect to host %.100s port %d, " 4163ca3176e7SBrian Feldman "but the request was denied.", host, port); 4164d4af9e69SDag-Erling Smørgrav return NULL; 4165ca3176e7SBrian Feldman } 4166d4af9e69SDag-Erling Smørgrav return connect_to(host, port, ctype, rname); 4167ca3176e7SBrian Feldman } 4168ca3176e7SBrian Feldman 4169a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 4170a0ee8cc6SDag-Erling Smørgrav Channel * 4171a0ee8cc6SDag-Erling Smørgrav channel_connect_to_path(const char *path, char *ctype, char *rname) 4172a0ee8cc6SDag-Erling Smørgrav { 4173a0ee8cc6SDag-Erling Smørgrav int i, permit, permit_adm = 1; 4174a0ee8cc6SDag-Erling Smørgrav 4175a0ee8cc6SDag-Erling Smørgrav permit = all_opens_permitted; 4176a0ee8cc6SDag-Erling Smørgrav if (!permit) { 4177a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_permitted_opens; i++) 4178a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { 4179a0ee8cc6SDag-Erling Smørgrav permit = 1; 4180a0ee8cc6SDag-Erling Smørgrav break; 4181a0ee8cc6SDag-Erling Smørgrav } 4182a0ee8cc6SDag-Erling Smørgrav } 4183a0ee8cc6SDag-Erling Smørgrav 4184a0ee8cc6SDag-Erling Smørgrav if (num_adm_permitted_opens > 0) { 4185a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 4186a0ee8cc6SDag-Erling Smørgrav for (i = 0; i < num_adm_permitted_opens; i++) 4187a0ee8cc6SDag-Erling Smørgrav if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { 4188a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 4189a0ee8cc6SDag-Erling Smørgrav break; 4190a0ee8cc6SDag-Erling Smørgrav } 4191a0ee8cc6SDag-Erling Smørgrav } 4192a0ee8cc6SDag-Erling Smørgrav 4193a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 4194a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 4195a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 4196a0ee8cc6SDag-Erling Smørgrav return NULL; 4197a0ee8cc6SDag-Erling Smørgrav } 4198a0ee8cc6SDag-Erling Smørgrav return connect_to(path, PORT_STREAMLOCAL, ctype, rname); 4199a0ee8cc6SDag-Erling Smørgrav } 4200a0ee8cc6SDag-Erling Smørgrav 420121e764dfSDag-Erling Smørgrav void 420221e764dfSDag-Erling Smørgrav channel_send_window_changes(void) 420321e764dfSDag-Erling Smørgrav { 420421e764dfSDag-Erling Smørgrav u_int i; 420521e764dfSDag-Erling Smørgrav struct winsize ws; 420621e764dfSDag-Erling Smørgrav 420721e764dfSDag-Erling Smørgrav for (i = 0; i < channels_alloc; i++) { 4208aa49c926SDag-Erling Smørgrav if (channels[i] == NULL || !channels[i]->client_tty || 420921e764dfSDag-Erling Smørgrav channels[i]->type != SSH_CHANNEL_OPEN) 421021e764dfSDag-Erling Smørgrav continue; 421121e764dfSDag-Erling Smørgrav if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 421221e764dfSDag-Erling Smørgrav continue; 421321e764dfSDag-Erling Smørgrav channel_request_start(i, "window-change", 0); 4214333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_col); 4215333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_row); 4216333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_xpixel); 4217333ee039SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_ypixel); 421821e764dfSDag-Erling Smørgrav packet_send(); 421921e764dfSDag-Erling Smørgrav } 422021e764dfSDag-Erling Smørgrav } 422121e764dfSDag-Erling Smørgrav 4222af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 4223511b41d2SMark Murray 4224511b41d2SMark Murray /* 4225511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 4226a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 4227a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 4228511b41d2SMark Murray */ 4229af12a3e7SDag-Erling Smørgrav int 4230af12a3e7SDag-Erling Smørgrav x11_create_display_inet(int x11_display_offset, int x11_use_localhost, 4231d4ecd108SDag-Erling Smørgrav int single_connection, u_int *display_numberp, int **chanids) 4232511b41d2SMark Murray { 4233af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 4234511b41d2SMark Murray int display_number, sock; 4235511b41d2SMark Murray u_short port; 4236511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4237511b41d2SMark Murray char strport[NI_MAXSERV]; 4238511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 4239511b41d2SMark Murray 4240b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 4241b74df5b2SDag-Erling Smørgrav return -1; 4242b74df5b2SDag-Erling Smørgrav 4243511b41d2SMark Murray for (display_number = x11_display_offset; 4244511b41d2SMark Murray display_number < MAX_DISPLAYS; 4245511b41d2SMark Murray display_number++) { 4246511b41d2SMark Murray port = 6000 + display_number; 4247511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 4248511b41d2SMark Murray hints.ai_family = IPv4or6; 4249af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 4250511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4251511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 4252511b41d2SMark Murray if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { 4253d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 4254af12a3e7SDag-Erling Smørgrav return -1; 4255511b41d2SMark Murray } 4256511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 4257511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 4258511b41d2SMark Murray continue; 4259221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 4260221552e4SDag-Erling Smørgrav ai->ai_protocol); 4261511b41d2SMark Murray if (sock < 0) { 42628ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 42638ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 42648ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 42658ad9b54aSDag-Erling Smørgrav #endif 42668ad9b54aSDag-Erling Smørgrav ) { 4267511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 426821e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 4269af12a3e7SDag-Erling Smørgrav return -1; 4270989dd127SDag-Erling Smørgrav } else { 4271989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 4272989dd127SDag-Erling Smørgrav ai->ai_family); 4273989dd127SDag-Erling Smørgrav continue; 4274511b41d2SMark Murray } 4275989dd127SDag-Erling Smørgrav } 4276b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 4277b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 4278d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 4279b74df5b2SDag-Erling Smørgrav channel_set_reuseaddr(sock); 4280511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 4281221552e4SDag-Erling Smørgrav debug2("bind port %d: %.100s", port, strerror(errno)); 4282511b41d2SMark Murray close(sock); 4283989dd127SDag-Erling Smørgrav 4284511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4285511b41d2SMark Murray close(socks[n]); 4286511b41d2SMark Murray } 4287511b41d2SMark Murray num_socks = 0; 4288511b41d2SMark Murray break; 4289511b41d2SMark Murray } 4290511b41d2SMark Murray socks[num_socks++] = sock; 4291511b41d2SMark Murray if (num_socks == NUM_SOCKS) 4292511b41d2SMark Murray break; 4293511b41d2SMark Murray } 4294ca3176e7SBrian Feldman freeaddrinfo(aitop); 4295511b41d2SMark Murray if (num_socks > 0) 4296511b41d2SMark Murray break; 4297511b41d2SMark Murray } 4298511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 4299511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 4300af12a3e7SDag-Erling Smørgrav return -1; 4301511b41d2SMark Murray } 4302511b41d2SMark Murray /* Start listening for connections on the socket. */ 4303511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4304511b41d2SMark Murray sock = socks[n]; 4305476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 4306511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 4307511b41d2SMark Murray close(sock); 4308af12a3e7SDag-Erling Smørgrav return -1; 4309511b41d2SMark Murray } 4310511b41d2SMark Murray } 4311511b41d2SMark Murray 4312511b41d2SMark Murray /* Allocate a channel for each socket. */ 4313333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 4314511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4315511b41d2SMark Murray sock = socks[n]; 4316af12a3e7SDag-Erling Smørgrav nc = channel_new("x11 listener", 4317a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 4318a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 4319221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 4320af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 4321d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 4322511b41d2SMark Murray } 4323d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 4324511b41d2SMark Murray 4325af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 4326a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 4327a82e551fSDag-Erling Smørgrav return (0); 4328511b41d2SMark Murray } 4329511b41d2SMark Murray 4330af12a3e7SDag-Erling Smørgrav static int 4331cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 4332511b41d2SMark Murray { 4333511b41d2SMark Murray int sock; 4334511b41d2SMark Murray struct sockaddr_un addr; 4335511b41d2SMark Murray 4336511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 4337511b41d2SMark Murray if (sock < 0) 4338511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4339511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 4340511b41d2SMark Murray addr.sun_family = AF_UNIX; 4341cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 4342511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 4343511b41d2SMark Murray return sock; 4344511b41d2SMark Murray close(sock); 4345511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 4346511b41d2SMark Murray return -1; 4347511b41d2SMark Murray } 4348511b41d2SMark Murray 4349cce7d346SDag-Erling Smørgrav static int 4350cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 4351cce7d346SDag-Erling Smørgrav { 4352cce7d346SDag-Erling Smørgrav char buf[1024]; 4353cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 4354cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 4355cce7d346SDag-Erling Smørgrav } 4356cce7d346SDag-Erling Smørgrav 4357a04a10f8SKris Kennaway int 4358a04a10f8SKris Kennaway x11_connect_display(void) 4359511b41d2SMark Murray { 4360333ee039SDag-Erling Smørgrav u_int display_number; 4361511b41d2SMark Murray const char *display; 4362a04a10f8SKris Kennaway char buf[1024], *cp; 4363511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4364511b41d2SMark Murray char strport[NI_MAXSERV]; 4365333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 4366511b41d2SMark Murray 4367511b41d2SMark Murray /* Try to open a socket for the local X server. */ 4368511b41d2SMark Murray display = getenv("DISPLAY"); 4369511b41d2SMark Murray if (!display) { 4370511b41d2SMark Murray error("DISPLAY not set."); 4371a04a10f8SKris Kennaway return -1; 4372511b41d2SMark Murray } 4373511b41d2SMark Murray /* 4374511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 4375511b41d2SMark Murray * connection to the real X server. 4376511b41d2SMark Murray */ 4377511b41d2SMark Murray 4378cce7d346SDag-Erling Smørgrav /* Check if the display is from launchd. */ 4379cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 4380cce7d346SDag-Erling Smørgrav if (strncmp(display, "/tmp/launch", 11) == 0) { 4381cce7d346SDag-Erling Smørgrav sock = connect_local_xsocket_path(display); 4382cce7d346SDag-Erling Smørgrav if (sock < 0) 4383cce7d346SDag-Erling Smørgrav return -1; 4384cce7d346SDag-Erling Smørgrav 4385cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 4386cce7d346SDag-Erling Smørgrav return sock; 4387cce7d346SDag-Erling Smørgrav } 4388cce7d346SDag-Erling Smørgrav #endif 4389511b41d2SMark Murray /* 4390511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 4391511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 4392511b41d2SMark Murray */ 4393511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 4394511b41d2SMark Murray display[0] == ':') { 4395511b41d2SMark Murray /* Connect to the unix domain socket. */ 4396333ee039SDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { 4397511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 4398511b41d2SMark Murray display); 4399a04a10f8SKris Kennaway return -1; 4400511b41d2SMark Murray } 4401511b41d2SMark Murray /* Create a socket. */ 4402511b41d2SMark Murray sock = connect_local_xsocket(display_number); 4403511b41d2SMark Murray if (sock < 0) 4404a04a10f8SKris Kennaway return -1; 4405511b41d2SMark Murray 4406511b41d2SMark Murray /* OK, we now have a connection to the display. */ 4407a04a10f8SKris Kennaway return sock; 4408511b41d2SMark Murray } 4409511b41d2SMark Murray /* 4410511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 4411511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 4412511b41d2SMark Murray */ 4413af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 4414511b41d2SMark Murray cp = strchr(buf, ':'); 4415511b41d2SMark Murray if (!cp) { 4416511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 4417a04a10f8SKris Kennaway return -1; 4418511b41d2SMark Murray } 4419511b41d2SMark Murray *cp = 0; 4420511b41d2SMark Murray /* buf now contains the host name. But first we parse the display number. */ 4421333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 4422511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 4423511b41d2SMark Murray display); 4424a04a10f8SKris Kennaway return -1; 4425511b41d2SMark Murray } 4426511b41d2SMark Murray 4427511b41d2SMark Murray /* Look up the host address */ 4428511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 4429511b41d2SMark Murray hints.ai_family = IPv4or6; 4430511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4431333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 4432511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 4433d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 4434d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 4435a04a10f8SKris Kennaway return -1; 4436511b41d2SMark Murray } 4437511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 4438511b41d2SMark Murray /* Create a socket. */ 4439221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 4440511b41d2SMark Murray if (sock < 0) { 4441221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 4442511b41d2SMark Murray continue; 4443511b41d2SMark Murray } 4444511b41d2SMark Murray /* Connect it to the display. */ 4445511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 4446333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 4447a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 4448511b41d2SMark Murray close(sock); 4449511b41d2SMark Murray continue; 4450511b41d2SMark Murray } 4451511b41d2SMark Murray /* Success */ 4452511b41d2SMark Murray break; 4453a04a10f8SKris Kennaway } 4454511b41d2SMark Murray freeaddrinfo(aitop); 4455511b41d2SMark Murray if (!ai) { 4456333ee039SDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 6000 + display_number, 4457511b41d2SMark Murray strerror(errno)); 4458a04a10f8SKris Kennaway return -1; 4459511b41d2SMark Murray } 4460af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 4461a04a10f8SKris Kennaway return sock; 4462a04a10f8SKris Kennaway } 4463511b41d2SMark Murray 4464a04a10f8SKris Kennaway /* 4465a04a10f8SKris Kennaway * This is called when SSH_SMSG_X11_OPEN is received. The packet contains 4466a04a10f8SKris Kennaway * the remote channel number. We should do whatever we want, and respond 4467a04a10f8SKris Kennaway * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. 4468a04a10f8SKris Kennaway */ 4469a04a10f8SKris Kennaway 4470333ee039SDag-Erling Smørgrav /* ARGSUSED */ 4471bc5531deSDag-Erling Smørgrav int 4472af12a3e7SDag-Erling Smørgrav x11_input_open(int type, u_int32_t seq, void *ctxt) 4473a04a10f8SKris Kennaway { 4474af12a3e7SDag-Erling Smørgrav Channel *c = NULL; 4475af12a3e7SDag-Erling Smørgrav int remote_id, sock = 0; 4476a04a10f8SKris Kennaway char *remote_host; 4477a04a10f8SKris Kennaway 4478a04a10f8SKris Kennaway debug("Received X11 open request."); 4479af12a3e7SDag-Erling Smørgrav 4480af12a3e7SDag-Erling Smørgrav remote_id = packet_get_int(); 4481af12a3e7SDag-Erling Smørgrav 4482af12a3e7SDag-Erling Smørgrav if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { 4483af12a3e7SDag-Erling Smørgrav remote_host = packet_get_string(NULL); 4484af12a3e7SDag-Erling Smørgrav } else { 4485af12a3e7SDag-Erling Smørgrav remote_host = xstrdup("unknown (remote did not supply name)"); 4486af12a3e7SDag-Erling Smørgrav } 4487af12a3e7SDag-Erling Smørgrav packet_check_eom(); 4488a04a10f8SKris Kennaway 4489a04a10f8SKris Kennaway /* Obtain a connection to the real X display. */ 4490a04a10f8SKris Kennaway sock = x11_connect_display(); 4491af12a3e7SDag-Erling Smørgrav if (sock != -1) { 4492af12a3e7SDag-Erling Smørgrav /* Allocate a channel for this connection. */ 4493af12a3e7SDag-Erling Smørgrav c = channel_new("connected x11 socket", 4494af12a3e7SDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, 4495af12a3e7SDag-Erling Smørgrav remote_host, 1); 4496af12a3e7SDag-Erling Smørgrav c->remote_id = remote_id; 4497af12a3e7SDag-Erling Smørgrav c->force_drain = 1; 4498af12a3e7SDag-Erling Smørgrav } 4499e4a9863fSDag-Erling Smørgrav free(remote_host); 4500af12a3e7SDag-Erling Smørgrav if (c == NULL) { 4501a04a10f8SKris Kennaway /* Send refusal to the remote host. */ 4502a04a10f8SKris Kennaway packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 4503af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 4504a04a10f8SKris Kennaway } else { 4505511b41d2SMark Murray /* Send a confirmation to the remote host. */ 4506511b41d2SMark Murray packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 4507af12a3e7SDag-Erling Smørgrav packet_put_int(remote_id); 4508af12a3e7SDag-Erling Smørgrav packet_put_int(c->self); 4509a04a10f8SKris Kennaway } 4510af12a3e7SDag-Erling Smørgrav packet_send(); 4511bc5531deSDag-Erling Smørgrav return 0; 4512511b41d2SMark Murray } 4513511b41d2SMark Murray 45145b9b2fafSBrian Feldman /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ 4515333ee039SDag-Erling Smørgrav /* ARGSUSED */ 4516bc5531deSDag-Erling Smørgrav int 4517af12a3e7SDag-Erling Smørgrav deny_input_open(int type, u_int32_t seq, void *ctxt) 45185b9b2fafSBrian Feldman { 45195b9b2fafSBrian Feldman int rchan = packet_get_int(); 4520f388f5efSDag-Erling Smørgrav 45215b9b2fafSBrian Feldman switch (type) { 45225b9b2fafSBrian Feldman case SSH_SMSG_AGENT_OPEN: 45235b9b2fafSBrian Feldman error("Warning: ssh server tried agent forwarding."); 45245b9b2fafSBrian Feldman break; 45255b9b2fafSBrian Feldman case SSH_SMSG_X11_OPEN: 45265b9b2fafSBrian Feldman error("Warning: ssh server tried X11 forwarding."); 45275b9b2fafSBrian Feldman break; 45285b9b2fafSBrian Feldman default: 4529af12a3e7SDag-Erling Smørgrav error("deny_input_open: type %d", type); 45305b9b2fafSBrian Feldman break; 45315b9b2fafSBrian Feldman } 4532b74df5b2SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a malicious server."); 45335b9b2fafSBrian Feldman packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 45345b9b2fafSBrian Feldman packet_put_int(rchan); 45355b9b2fafSBrian Feldman packet_send(); 4536bc5531deSDag-Erling Smørgrav return 0; 45375b9b2fafSBrian Feldman } 45385b9b2fafSBrian Feldman 4539511b41d2SMark Murray /* 4540511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 4541511b41d2SMark Murray * data, and enables authentication spoofing. 4542af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 4543511b41d2SMark Murray */ 4544511b41d2SMark Murray void 4545d4ecd108SDag-Erling Smørgrav x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, 4546e146993eSDag-Erling Smørgrav const char *proto, const char *data, int want_reply) 4547511b41d2SMark Murray { 4548ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 4549d4ecd108SDag-Erling Smørgrav u_int i, value; 4550511b41d2SMark Murray char *new_data; 4551511b41d2SMark Murray int screen_number; 4552511b41d2SMark Murray const char *cp; 4553511b41d2SMark Murray 4554d4ecd108SDag-Erling Smørgrav if (x11_saved_display == NULL) 4555d4ecd108SDag-Erling Smørgrav x11_saved_display = xstrdup(disp); 4556d4ecd108SDag-Erling Smørgrav else if (strcmp(disp, x11_saved_display) != 0) { 4557d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 4558d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 4559d4ecd108SDag-Erling Smørgrav return; 4560d4ecd108SDag-Erling Smørgrav } 4561d4ecd108SDag-Erling Smørgrav 4562d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 4563511b41d2SMark Murray if (cp) 4564511b41d2SMark Murray cp = strchr(cp, '.'); 4565511b41d2SMark Murray if (cp) 4566333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 4567511b41d2SMark Murray else 4568511b41d2SMark Murray screen_number = 0; 4569511b41d2SMark Murray 4570d4ecd108SDag-Erling Smørgrav if (x11_saved_proto == NULL) { 4571511b41d2SMark Murray /* Save protocol name. */ 4572511b41d2SMark Murray x11_saved_proto = xstrdup(proto); 4573*ca86bcf2SDag-Erling Smørgrav 4574*ca86bcf2SDag-Erling Smørgrav /* Extract real authentication data. */ 4575511b41d2SMark Murray x11_saved_data = xmalloc(data_len); 4576511b41d2SMark Murray for (i = 0; i < data_len; i++) { 4577511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 4578d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 4579d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 4580511b41d2SMark Murray x11_saved_data[i] = value; 4581511b41d2SMark Murray } 4582511b41d2SMark Murray x11_saved_data_len = data_len; 4583*ca86bcf2SDag-Erling Smørgrav 4584*ca86bcf2SDag-Erling Smørgrav /* Generate fake data of the same length. */ 4585*ca86bcf2SDag-Erling Smørgrav x11_fake_data = xmalloc(data_len); 4586*ca86bcf2SDag-Erling Smørgrav arc4random_buf(x11_fake_data, data_len); 4587511b41d2SMark Murray x11_fake_data_len = data_len; 4588d4ecd108SDag-Erling Smørgrav } 4589511b41d2SMark Murray 4590511b41d2SMark Murray /* Convert the fake data into hex. */ 4591d4ecd108SDag-Erling Smørgrav new_data = tohex(x11_fake_data, data_len); 4592511b41d2SMark Murray 4593511b41d2SMark Murray /* Send the request packet. */ 4594a04a10f8SKris Kennaway if (compat20) { 4595e146993eSDag-Erling Smørgrav channel_request_start(client_session_id, "x11-req", want_reply); 4596a04a10f8SKris Kennaway packet_put_char(0); /* XXX bool single connection */ 4597a04a10f8SKris Kennaway } else { 4598511b41d2SMark Murray packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); 4599a04a10f8SKris Kennaway } 4600a04a10f8SKris Kennaway packet_put_cstring(proto); 4601a04a10f8SKris Kennaway packet_put_cstring(new_data); 4602511b41d2SMark Murray packet_put_int(screen_number); 4603511b41d2SMark Murray packet_send(); 4604511b41d2SMark Murray packet_write_wait(); 4605e4a9863fSDag-Erling Smørgrav free(new_data); 4606511b41d2SMark Murray } 4607511b41d2SMark Murray 4608af12a3e7SDag-Erling Smørgrav 4609af12a3e7SDag-Erling Smørgrav /* -- agent forwarding */ 4610af12a3e7SDag-Erling Smørgrav 4611511b41d2SMark Murray /* Sends a message to the server to request authentication fd forwarding. */ 4612511b41d2SMark Murray 4613511b41d2SMark Murray void 4614af12a3e7SDag-Erling Smørgrav auth_request_forwarding(void) 4615511b41d2SMark Murray { 4616511b41d2SMark Murray packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); 4617511b41d2SMark Murray packet_send(); 4618511b41d2SMark Murray packet_write_wait(); 4619511b41d2SMark Murray } 4620