1*a91a2465SEd Maste /* $OpenBSD: channels.c,v 1.437 2024/03/06 02:59:59 djm 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> 584f52dfbbSDag-Erling Smørgrav #include <limits.h> 59333ee039SDag-Erling Smørgrav #include <netdb.h> 601323ec57SEd Maste #ifdef HAVE_POLL_H 611323ec57SEd Maste #include <poll.h> 621323ec57SEd Maste #endif 634f52dfbbSDag-Erling Smørgrav #include <stdarg.h> 64bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H 65bc5531deSDag-Erling Smørgrav # include <stdint.h> 66bc5531deSDag-Erling Smørgrav #endif 67333ee039SDag-Erling Smørgrav #include <stdio.h> 68333ee039SDag-Erling Smørgrav #include <stdlib.h> 69333ee039SDag-Erling Smørgrav #include <string.h> 70333ee039SDag-Erling Smørgrav #include <termios.h> 71333ee039SDag-Erling Smørgrav #include <unistd.h> 72333ee039SDag-Erling Smørgrav 73d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 74333ee039SDag-Erling Smørgrav #include "xmalloc.h" 75ca3176e7SBrian Feldman #include "ssh.h" 76ca3176e7SBrian Feldman #include "ssh2.h" 77ca86bcf2SDag-Erling Smørgrav #include "ssherr.h" 784f52dfbbSDag-Erling Smørgrav #include "sshbuf.h" 79ca3176e7SBrian Feldman #include "packet.h" 80ca3176e7SBrian Feldman #include "log.h" 81ca3176e7SBrian Feldman #include "misc.h" 82ca3176e7SBrian Feldman #include "channels.h" 83ca3176e7SBrian Feldman #include "compat.h" 84ca3176e7SBrian Feldman #include "canohost.h" 85190cef3dSDag-Erling Smørgrav #include "sshkey.h" 86b66f2d16SKris Kennaway #include "authfd.h" 87af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 88190cef3dSDag-Erling Smørgrav #include "match.h" 89511b41d2SMark Murray 901323ec57SEd Maste /* XXX remove once we're satisfied there's no lurking bugs */ 911323ec57SEd Maste /* #define DEBUG_CHANNEL_POLL 1 */ 921323ec57SEd Maste 934f52dfbbSDag-Erling Smørgrav /* -- agent forwarding */ 944f52dfbbSDag-Erling Smørgrav #define NUM_SOCKS 10 95511b41d2SMark Murray 96af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 974f52dfbbSDag-Erling Smørgrav /* special-case port number meaning allow any port */ 984f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT 0 994f52dfbbSDag-Erling Smørgrav 1004f52dfbbSDag-Erling Smørgrav /* special-case wildcard meaning allow any host */ 1014f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST "*" 1024f52dfbbSDag-Erling Smørgrav 1034f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */ 1044f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 1054f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS 1000 106511b41d2SMark Murray 1071323ec57SEd Maste /* Per-channel callback for pre/post IO actions */ 1081323ec57SEd Maste typedef void chan_fn(struct ssh *, Channel *c); 109190cef3dSDag-Erling Smørgrav 110511b41d2SMark Murray /* 111511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 112511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 113511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 114511b41d2SMark Murray * network (which might be behind a firewall). 115511b41d2SMark Murray */ 116a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */ 117a0ee8cc6SDag-Erling Smørgrav /* Overload host_to_connect; we could just make this match Forward */ 118a0ee8cc6SDag-Erling Smørgrav /* XXX - can we use listen_host instead of listen_path? */ 119190cef3dSDag-Erling Smørgrav struct permission { 120a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 121a0ee8cc6SDag-Erling Smørgrav int port_to_connect; /* Connect to 'port'. */ 122a0ee8cc6SDag-Erling Smørgrav char *listen_host; /* Remote side should listen address. */ 123a0ee8cc6SDag-Erling Smørgrav char *listen_path; /* Remote side should listen path. */ 124a0ee8cc6SDag-Erling Smørgrav int listen_port; /* Remote side should listen port. */ 125ca86bcf2SDag-Erling Smørgrav Channel *downstream; /* Downstream mux*/ 126190cef3dSDag-Erling Smørgrav }; 127511b41d2SMark Murray 128190cef3dSDag-Erling Smørgrav /* 129190cef3dSDag-Erling Smørgrav * Stores the forwarding permission state for a single direction (local or 130190cef3dSDag-Erling Smørgrav * remote). 131190cef3dSDag-Erling Smørgrav */ 132190cef3dSDag-Erling Smørgrav struct permission_set { 133190cef3dSDag-Erling Smørgrav /* 134190cef3dSDag-Erling Smørgrav * List of all local permitted host/port pairs to allow for the 135190cef3dSDag-Erling Smørgrav * user. 136190cef3dSDag-Erling Smørgrav */ 137190cef3dSDag-Erling Smørgrav u_int num_permitted_user; 138190cef3dSDag-Erling Smørgrav struct permission *permitted_user; 139190cef3dSDag-Erling Smørgrav 140190cef3dSDag-Erling Smørgrav /* 141190cef3dSDag-Erling Smørgrav * List of all permitted host/port pairs to allow for the admin. 142190cef3dSDag-Erling Smørgrav */ 143190cef3dSDag-Erling Smørgrav u_int num_permitted_admin; 144190cef3dSDag-Erling Smørgrav struct permission *permitted_admin; 145190cef3dSDag-Erling Smørgrav 146190cef3dSDag-Erling Smørgrav /* 147190cef3dSDag-Erling Smørgrav * If this is true, all opens/listens are permitted. This is the 148190cef3dSDag-Erling Smørgrav * case on the server on which we have to trust the client anyway, 149190cef3dSDag-Erling Smørgrav * and the user could do anything after logging in. 150190cef3dSDag-Erling Smørgrav */ 151190cef3dSDag-Erling Smørgrav int all_permitted; 152190cef3dSDag-Erling Smørgrav }; 153af12a3e7SDag-Erling Smørgrav 154f374ba41SEd Maste /* Used to record timeouts per channel type */ 155f374ba41SEd Maste struct ssh_channel_timeout { 156f374ba41SEd Maste char *type_pattern; 157535af610SEd Maste int timeout_secs; 158f374ba41SEd Maste }; 159f374ba41SEd Maste 1604f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */ 1614f52dfbbSDag-Erling Smørgrav struct ssh_channels { 1624f52dfbbSDag-Erling Smørgrav /* 1634f52dfbbSDag-Erling Smørgrav * Pointer to an array containing all allocated channels. The array 1644f52dfbbSDag-Erling Smørgrav * is dynamically extended as needed. 1654f52dfbbSDag-Erling Smørgrav */ 1664f52dfbbSDag-Erling Smørgrav Channel **channels; 167076ad2f8SDag-Erling Smørgrav 168511b41d2SMark Murray /* 1694f52dfbbSDag-Erling Smørgrav * Size of the channel array. All slots of the array must always be 1704f52dfbbSDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 171511b41d2SMark Murray */ 1724f52dfbbSDag-Erling Smørgrav u_int channels_alloc; 173511b41d2SMark Murray 1744f52dfbbSDag-Erling Smørgrav /* 1751323ec57SEd Maste * 'channel_pre*' are called just before IO to add any bits 1761323ec57SEd Maste * relevant to channels in the c->io_want bitmasks. 1774f52dfbbSDag-Erling Smørgrav * 1784f52dfbbSDag-Erling Smørgrav * 'channel_post*': perform any appropriate operations for 1791323ec57SEd Maste * channels which have c->io_ready events pending. 1804f52dfbbSDag-Erling Smørgrav */ 1814f52dfbbSDag-Erling Smørgrav chan_fn **channel_pre; 1824f52dfbbSDag-Erling Smørgrav chan_fn **channel_post; 1834f52dfbbSDag-Erling Smørgrav 1844f52dfbbSDag-Erling Smørgrav /* -- tcp forwarding */ 185190cef3dSDag-Erling Smørgrav struct permission_set local_perms; 186190cef3dSDag-Erling Smørgrav struct permission_set remote_perms; 187af12a3e7SDag-Erling Smørgrav 188af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 189af12a3e7SDag-Erling Smørgrav 190d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 1914f52dfbbSDag-Erling Smørgrav char *x11_saved_display; 192d4ecd108SDag-Erling Smørgrav 193af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 1944f52dfbbSDag-Erling Smørgrav char *x11_saved_proto; 195af12a3e7SDag-Erling Smørgrav 196af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 1974f52dfbbSDag-Erling Smørgrav char *x11_saved_data; 1984f52dfbbSDag-Erling Smørgrav u_int x11_saved_data_len; 199af12a3e7SDag-Erling Smørgrav 200557f75e5SDag-Erling Smørgrav /* Deadline after which all X11 connections are refused */ 2014d3fc8b0SEd Maste time_t x11_refuse_time; 202557f75e5SDag-Erling Smørgrav 203af12a3e7SDag-Erling Smørgrav /* 2044f52dfbbSDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be 2054f52dfbbSDag-Erling Smørgrav * sending us; we should replace any occurrences of this by the 2064f52dfbbSDag-Erling Smørgrav * real data. 207af12a3e7SDag-Erling Smørgrav */ 2084f52dfbbSDag-Erling Smørgrav u_char *x11_fake_data; 2094f52dfbbSDag-Erling Smørgrav u_int x11_fake_data_len; 210af12a3e7SDag-Erling Smørgrav 211ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 2124f52dfbbSDag-Erling Smørgrav int IPv4or6; 213f374ba41SEd Maste 214f374ba41SEd Maste /* Channel timeouts by type */ 215f374ba41SEd Maste struct ssh_channel_timeout *timeouts; 216f374ba41SEd Maste size_t ntimeouts; 217*a91a2465SEd Maste /* Global timeout for all OPEN channels */ 218*a91a2465SEd Maste int global_deadline; 219*a91a2465SEd Maste time_t lastused; 2204f52dfbbSDag-Erling Smørgrav }; 221ca3176e7SBrian Feldman 222af12a3e7SDag-Erling Smørgrav /* helper */ 2234f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); 224ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host); 225ca3176e7SBrian Feldman 226d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 227d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 228d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 2294f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); 2304f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *); 2314f52dfbbSDag-Erling Smørgrav 2324f52dfbbSDag-Erling Smørgrav /* Setup helper */ 2334f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc); 234d4af9e69SDag-Erling Smørgrav 235af12a3e7SDag-Erling Smørgrav /* -- channel core */ 236a04a10f8SKris Kennaway 2374f52dfbbSDag-Erling Smørgrav void 2384f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh) 2394f52dfbbSDag-Erling Smørgrav { 2404f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc; 2414f52dfbbSDag-Erling Smørgrav 24219261079SEd Maste if ((sc = calloc(1, sizeof(*sc))) == NULL) 24319261079SEd Maste fatal_f("allocation failed"); 2444f52dfbbSDag-Erling Smørgrav sc->channels_alloc = 10; 2454f52dfbbSDag-Erling Smørgrav sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); 2464f52dfbbSDag-Erling Smørgrav sc->IPv4or6 = AF_UNSPEC; 2474f52dfbbSDag-Erling Smørgrav channel_handler_init(sc); 2484f52dfbbSDag-Erling Smørgrav 2494f52dfbbSDag-Erling Smørgrav ssh->chanctxt = sc; 2504f52dfbbSDag-Erling Smørgrav } 2514f52dfbbSDag-Erling Smørgrav 252a04a10f8SKris Kennaway Channel * 2534f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id) 254a04a10f8SKris Kennaway { 255a04a10f8SKris Kennaway Channel *c; 256af12a3e7SDag-Erling Smørgrav 2574f52dfbbSDag-Erling Smørgrav if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { 25819261079SEd Maste logit_f("%d: bad id", id); 259a04a10f8SKris Kennaway return NULL; 260a04a10f8SKris Kennaway } 2614f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[id]; 262af12a3e7SDag-Erling Smørgrav if (c == NULL) { 26319261079SEd Maste logit_f("%d: bad id: channel free", id); 264a04a10f8SKris Kennaway return NULL; 265a04a10f8SKris Kennaway } 266a04a10f8SKris Kennaway return c; 267a04a10f8SKris Kennaway } 268a04a10f8SKris Kennaway 269ca86bcf2SDag-Erling Smørgrav Channel * 2704f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id) 271ca86bcf2SDag-Erling Smørgrav { 272ca86bcf2SDag-Erling Smørgrav Channel *c; 273ca86bcf2SDag-Erling Smørgrav u_int i; 274ca86bcf2SDag-Erling Smørgrav 2754f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 2764f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 2774f52dfbbSDag-Erling Smørgrav if (c != NULL && c->have_remote_id && c->remote_id == remote_id) 278ca86bcf2SDag-Erling Smørgrav return c; 279ca86bcf2SDag-Erling Smørgrav } 280ca86bcf2SDag-Erling Smørgrav return NULL; 281ca86bcf2SDag-Erling Smørgrav } 282ca86bcf2SDag-Erling Smørgrav 283a04a10f8SKris Kennaway /* 284b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 285b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 286b74df5b2SDag-Erling Smørgrav */ 287b74df5b2SDag-Erling Smørgrav Channel * 2884f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id) 289b74df5b2SDag-Erling Smørgrav { 290b74df5b2SDag-Erling Smørgrav Channel *c; 291b74df5b2SDag-Erling Smørgrav 2924f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 2934f52dfbbSDag-Erling Smørgrav return NULL; 294b74df5b2SDag-Erling Smørgrav 295b74df5b2SDag-Erling Smørgrav switch (c->type) { 296b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 297b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 298b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 299b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 3004f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 3014f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 302b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 303b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 304e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 305ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 3064f52dfbbSDag-Erling Smørgrav return c; 307b74df5b2SDag-Erling Smørgrav } 308b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 3094f52dfbbSDag-Erling Smørgrav return NULL; 310b74df5b2SDag-Erling Smørgrav } 311b74df5b2SDag-Erling Smørgrav 312b74df5b2SDag-Erling Smørgrav /* 313f374ba41SEd Maste * Add a timeout for open channels whose c->ctype (or c->xctype if it is set) 314f374ba41SEd Maste * match type_pattern. 315f374ba41SEd Maste */ 316f374ba41SEd Maste void 317f374ba41SEd Maste channel_add_timeout(struct ssh *ssh, const char *type_pattern, 318535af610SEd Maste int timeout_secs) 319f374ba41SEd Maste { 320f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 321f374ba41SEd Maste 322*a91a2465SEd Maste if (strcmp(type_pattern, "global") == 0) { 323*a91a2465SEd Maste debug2_f("global channel timeout %d seconds", timeout_secs); 324*a91a2465SEd Maste sc->global_deadline = timeout_secs; 325*a91a2465SEd Maste return; 326*a91a2465SEd Maste } 327535af610SEd Maste debug2_f("channel type \"%s\" timeout %d seconds", 328f374ba41SEd Maste type_pattern, timeout_secs); 329f374ba41SEd Maste sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts, 330f374ba41SEd Maste sc->ntimeouts + 1, sizeof(*sc->timeouts)); 331f374ba41SEd Maste sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern); 332f374ba41SEd Maste sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs; 333f374ba41SEd Maste sc->ntimeouts++; 334f374ba41SEd Maste } 335f374ba41SEd Maste 336f374ba41SEd Maste /* Clears all previously-added channel timeouts */ 337f374ba41SEd Maste void 338f374ba41SEd Maste channel_clear_timeouts(struct ssh *ssh) 339f374ba41SEd Maste { 340f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 341f374ba41SEd Maste size_t i; 342f374ba41SEd Maste 343f374ba41SEd Maste debug3_f("clearing"); 344f374ba41SEd Maste for (i = 0; i < sc->ntimeouts; i++) 345f374ba41SEd Maste free(sc->timeouts[i].type_pattern); 346f374ba41SEd Maste free(sc->timeouts); 347f374ba41SEd Maste sc->timeouts = NULL; 348f374ba41SEd Maste sc->ntimeouts = 0; 349f374ba41SEd Maste } 350f374ba41SEd Maste 351535af610SEd Maste static int 352f374ba41SEd Maste lookup_timeout(struct ssh *ssh, const char *type) 353f374ba41SEd Maste { 354f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 355f374ba41SEd Maste size_t i; 356f374ba41SEd Maste 357f374ba41SEd Maste for (i = 0; i < sc->ntimeouts; i++) { 358f374ba41SEd Maste if (match_pattern(type, sc->timeouts[i].type_pattern)) 359f374ba41SEd Maste return sc->timeouts[i].timeout_secs; 360f374ba41SEd Maste } 361f374ba41SEd Maste 362f374ba41SEd Maste return 0; 363f374ba41SEd Maste } 364f374ba41SEd Maste 365f374ba41SEd Maste /* 366f374ba41SEd Maste * Sets "extended type" of a channel; used by session layer to add additional 367f374ba41SEd Maste * information about channel types (e.g. shell, login, subsystem) that can then 368f374ba41SEd Maste * be used to select timeouts. 369f374ba41SEd Maste * Will reset c->inactive_deadline as a side-effect. 370f374ba41SEd Maste */ 371f374ba41SEd Maste void 372f374ba41SEd Maste channel_set_xtype(struct ssh *ssh, int id, const char *xctype) 373f374ba41SEd Maste { 374f374ba41SEd Maste Channel *c; 375f374ba41SEd Maste 376f374ba41SEd Maste if ((c = channel_by_id(ssh, id)) == NULL) 377f374ba41SEd Maste fatal_f("missing channel %d", id); 378f374ba41SEd Maste if (c->xctype != NULL) 379f374ba41SEd Maste free(c->xctype); 380f374ba41SEd Maste c->xctype = xstrdup(xctype); 381f374ba41SEd Maste /* Type has changed, so look up inactivity deadline again */ 382f374ba41SEd Maste c->inactive_deadline = lookup_timeout(ssh, c->xctype); 383f374ba41SEd Maste debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype, 384f374ba41SEd Maste c->inactive_deadline); 385f374ba41SEd Maste } 386f374ba41SEd Maste 387f374ba41SEd Maste /* 388*a91a2465SEd Maste * update "last used" time on a channel. 389*a91a2465SEd Maste * NB. nothing else should update lastused except to clear it. 390*a91a2465SEd Maste */ 391*a91a2465SEd Maste static void 392*a91a2465SEd Maste channel_set_used_time(struct ssh *ssh, Channel *c) 393*a91a2465SEd Maste { 394*a91a2465SEd Maste ssh->chanctxt->lastused = monotime(); 395*a91a2465SEd Maste if (c != NULL) 396*a91a2465SEd Maste c->lastused = ssh->chanctxt->lastused; 397*a91a2465SEd Maste } 398*a91a2465SEd Maste 399*a91a2465SEd Maste /* 400*a91a2465SEd Maste * Get the time at which a channel is due to time out for inactivity. 401*a91a2465SEd Maste * Returns 0 if the channel is not due to time out ever. 402*a91a2465SEd Maste */ 403*a91a2465SEd Maste static time_t 404*a91a2465SEd Maste channel_get_expiry(struct ssh *ssh, Channel *c) 405*a91a2465SEd Maste { 406*a91a2465SEd Maste struct ssh_channels *sc = ssh->chanctxt; 407*a91a2465SEd Maste time_t expiry = 0, channel_expiry; 408*a91a2465SEd Maste 409*a91a2465SEd Maste if (sc->lastused != 0 && sc->global_deadline != 0) 410*a91a2465SEd Maste expiry = sc->lastused + sc->global_deadline; 411*a91a2465SEd Maste if (c->lastused != 0 && c->inactive_deadline != 0) { 412*a91a2465SEd Maste channel_expiry = c->lastused + c->inactive_deadline; 413*a91a2465SEd Maste if (expiry == 0 || channel_expiry < expiry) 414*a91a2465SEd Maste expiry = channel_expiry; 415*a91a2465SEd Maste } 416*a91a2465SEd Maste return expiry; 417*a91a2465SEd Maste } 418*a91a2465SEd Maste 419*a91a2465SEd Maste /* 420a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 421a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 422a04a10f8SKris Kennaway */ 423af12a3e7SDag-Erling Smørgrav static void 4244f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, 425d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 426a04a10f8SKris Kennaway { 42738a52bd3SEd Maste int val; 42838a52bd3SEd Maste 429b15c8340SDag-Erling Smørgrav if (rfd != -1) 4304d3fc8b0SEd Maste (void)fcntl(rfd, F_SETFD, FD_CLOEXEC); 431b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 4324d3fc8b0SEd Maste (void)fcntl(wfd, F_SETFD, FD_CLOEXEC); 433b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 4344d3fc8b0SEd Maste (void)fcntl(efd, F_SETFD, FD_CLOEXEC); 435a04a10f8SKris Kennaway 436a04a10f8SKris Kennaway c->rfd = rfd; 437a04a10f8SKris Kennaway c->wfd = wfd; 438a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 439a04a10f8SKris Kennaway c->efd = efd; 440a04a10f8SKris Kennaway c->extended_usage = extusage; 4415b9b2fafSBrian Feldman 442d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 443221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 444e4a9863fSDag-Erling Smørgrav #ifdef _AIX 445e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 446d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 447e4a9863fSDag-Erling Smørgrav #endif 448e0fbb1d2SBrian Feldman 4495b9b2fafSBrian Feldman /* enable nonblocking mode */ 45019261079SEd Maste c->restore_block = 0; 45119261079SEd Maste if (nonblock == CHANNEL_NONBLOCK_STDIO) { 45219261079SEd Maste /* 45319261079SEd Maste * Special handling for stdio file descriptors: do not set 45419261079SEd Maste * non-blocking mode if they are TTYs. Otherwise prepare to 45519261079SEd Maste * restore their blocking state on exit to avoid interfering 45619261079SEd Maste * with other programs that follow. 45719261079SEd Maste */ 45838a52bd3SEd Maste if (rfd != -1 && !isatty(rfd) && 45938a52bd3SEd Maste (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 460f374ba41SEd Maste c->restore_flags[0] = val; 46119261079SEd Maste c->restore_block |= CHANNEL_RESTORE_RFD; 46219261079SEd Maste set_nonblock(rfd); 46319261079SEd Maste } 46438a52bd3SEd Maste if (wfd != -1 && !isatty(wfd) && 46538a52bd3SEd Maste (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 466f374ba41SEd Maste c->restore_flags[1] = val; 46719261079SEd Maste c->restore_block |= CHANNEL_RESTORE_WFD; 46819261079SEd Maste set_nonblock(wfd); 46919261079SEd Maste } 47038a52bd3SEd Maste if (efd != -1 && !isatty(efd) && 47138a52bd3SEd Maste (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 472f374ba41SEd Maste c->restore_flags[2] = val; 47319261079SEd Maste c->restore_block |= CHANNEL_RESTORE_EFD; 47419261079SEd Maste set_nonblock(efd); 47519261079SEd Maste } 47619261079SEd Maste } else if (nonblock) { 477a04a10f8SKris Kennaway if (rfd != -1) 478a04a10f8SKris Kennaway set_nonblock(rfd); 479a04a10f8SKris Kennaway if (wfd != -1) 480a04a10f8SKris Kennaway set_nonblock(wfd); 481a04a10f8SKris Kennaway if (efd != -1) 482a04a10f8SKris Kennaway set_nonblock(efd); 483a04a10f8SKris Kennaway } 484*a91a2465SEd Maste /* channel might be entering a larval state, so reset global timeout */ 485*a91a2465SEd Maste channel_set_used_time(ssh, NULL); 4865b9b2fafSBrian Feldman } 487a04a10f8SKris Kennaway 488511b41d2SMark Murray /* 48938a52bd3SEd Maste * Allocate a new channel object and set its type and socket. 490511b41d2SMark Murray */ 491af12a3e7SDag-Erling Smørgrav Channel * 4924f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, 49338a52bd3SEd Maste u_int window, u_int maxpack, int extusage, const char *remote_name, 49438a52bd3SEd Maste int nonblock) 495511b41d2SMark Murray { 4964f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 49738a52bd3SEd Maste u_int i, found = 0; 498511b41d2SMark Murray Channel *c; 49919261079SEd Maste int r; 500511b41d2SMark Murray 501511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 5024f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 5034f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL) { 504511b41d2SMark Murray /* Found a free slot. */ 5054f52dfbbSDag-Erling Smørgrav found = i; 506511b41d2SMark Murray break; 507511b41d2SMark Murray } 5084f52dfbbSDag-Erling Smørgrav } 5094f52dfbbSDag-Erling Smørgrav if (i >= sc->channels_alloc) { 5104f52dfbbSDag-Erling Smørgrav /* 5114f52dfbbSDag-Erling Smørgrav * There are no free slots. Take last+1 slot and expand 5124f52dfbbSDag-Erling Smørgrav * the array. 5134f52dfbbSDag-Erling Smørgrav */ 5144f52dfbbSDag-Erling Smørgrav found = sc->channels_alloc; 5154f52dfbbSDag-Erling Smørgrav if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) 51619261079SEd Maste fatal_f("internal error: channels_alloc %d too big", 51719261079SEd Maste sc->channels_alloc); 5184f52dfbbSDag-Erling Smørgrav sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, 5194f52dfbbSDag-Erling Smørgrav sc->channels_alloc + 10, sizeof(*sc->channels)); 5204f52dfbbSDag-Erling Smørgrav sc->channels_alloc += 10; 5214f52dfbbSDag-Erling Smørgrav debug2("channel: expanding %d", sc->channels_alloc); 522511b41d2SMark Murray } 523af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 5244f52dfbbSDag-Erling Smørgrav c = sc->channels[found] = xcalloc(1, sizeof(Channel)); 5254f52dfbbSDag-Erling Smørgrav if ((c->input = sshbuf_new()) == NULL || 5264f52dfbbSDag-Erling Smørgrav (c->output = sshbuf_new()) == NULL || 5274f52dfbbSDag-Erling Smørgrav (c->extended = sshbuf_new()) == NULL) 52819261079SEd Maste fatal_f("sshbuf_new failed"); 52919261079SEd Maste if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) 53019261079SEd Maste fatal_fr(r, "sshbuf_set_max_size"); 531af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 532af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 5334f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); 534511b41d2SMark Murray c->self = found; 535511b41d2SMark Murray c->type = type; 536a04a10f8SKris Kennaway c->ctype = ctype; 537a04a10f8SKris Kennaway c->local_window = window; 538a04a10f8SKris Kennaway c->local_window_max = window; 539a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 540221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 541b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 542b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 543f374ba41SEd Maste c->inactive_deadline = lookup_timeout(ssh, c->ctype); 544d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 545f374ba41SEd Maste debug("channel %d: new %s [%s] (inactive timeout: %u)", 546f374ba41SEd Maste found, c->ctype, remote_name, c->inactive_deadline); 547af12a3e7SDag-Erling Smørgrav return c; 548a04a10f8SKris Kennaway } 549511b41d2SMark Murray 550af12a3e7SDag-Erling Smørgrav int 55119261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) 552af12a3e7SDag-Erling Smørgrav { 55319261079SEd Maste int ret, fd = *fdp; 554af12a3e7SDag-Erling Smørgrav 55519261079SEd Maste if (fd == -1) 55619261079SEd Maste return 0; 55719261079SEd Maste 558f374ba41SEd Maste /* restore blocking */ 559f374ba41SEd Maste if (*fdp == c->rfd && 560f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_RFD) != 0) 561f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[0]); 562f374ba41SEd Maste else if (*fdp == c->wfd && 563f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_WFD) != 0) 564f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[1]); 565f374ba41SEd Maste else if (*fdp == c->efd && 566f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_EFD) != 0) 567f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[2]); 56819261079SEd Maste 5691323ec57SEd Maste if (*fdp == c->rfd) { 5701323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_RFD; 5711323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_RFD; 5721323ec57SEd Maste c->rfd = -1; 57387c1498dSEd Maste c->pfds[0] = -1; 5741323ec57SEd Maste } 5751323ec57SEd Maste if (*fdp == c->wfd) { 5761323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_WFD; 5771323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_WFD; 5781323ec57SEd Maste c->wfd = -1; 57987c1498dSEd Maste c->pfds[1] = -1; 5801323ec57SEd Maste } 5811323ec57SEd Maste if (*fdp == c->efd) { 5821323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_EFD; 5831323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_EFD; 5841323ec57SEd Maste c->efd = -1; 58587c1498dSEd Maste c->pfds[2] = -1; 5861323ec57SEd Maste } 5871323ec57SEd Maste if (*fdp == c->sock) { 5881323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_SOCK; 5891323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_SOCK; 5901323ec57SEd Maste c->sock = -1; 59187c1498dSEd Maste c->pfds[3] = -1; 5921323ec57SEd Maste } 5931323ec57SEd Maste 594af12a3e7SDag-Erling Smørgrav ret = close(fd); 5951323ec57SEd Maste *fdp = -1; /* probably redundant */ 596af12a3e7SDag-Erling Smørgrav return ret; 597af12a3e7SDag-Erling Smørgrav } 598a04a10f8SKris Kennaway 599a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 600af12a3e7SDag-Erling Smørgrav static void 6014f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c) 602511b41d2SMark Murray { 60347dd1d1bSDag-Erling Smørgrav int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; 60447dd1d1bSDag-Erling Smørgrav 60519261079SEd Maste channel_close_fd(ssh, c, &c->sock); 60647dd1d1bSDag-Erling Smørgrav if (rfd != sock) 60719261079SEd Maste channel_close_fd(ssh, c, &c->rfd); 60847dd1d1bSDag-Erling Smørgrav if (wfd != sock && wfd != rfd) 60919261079SEd Maste channel_close_fd(ssh, c, &c->wfd); 61047dd1d1bSDag-Erling Smørgrav if (efd != sock && efd != rfd && efd != wfd) 61119261079SEd Maste channel_close_fd(ssh, c, &c->efd); 6124f52dfbbSDag-Erling Smørgrav } 6134f52dfbbSDag-Erling Smørgrav 6144f52dfbbSDag-Erling Smørgrav static void 615190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm) 6164f52dfbbSDag-Erling Smørgrav { 617190cef3dSDag-Erling Smørgrav free(perm->host_to_connect); 618190cef3dSDag-Erling Smørgrav free(perm->listen_host); 619190cef3dSDag-Erling Smørgrav free(perm->listen_path); 62019261079SEd Maste memset(perm, 0, sizeof(*perm)); 6214f52dfbbSDag-Erling Smørgrav } 6224f52dfbbSDag-Erling Smørgrav 623190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */ 624190cef3dSDag-Erling Smørgrav static const char * 625190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where) 626190cef3dSDag-Erling Smørgrav { 627190cef3dSDag-Erling Smørgrav if (who == FORWARD_ADM) { 628190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 629190cef3dSDag-Erling Smørgrav return "admin local"; 630190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 631190cef3dSDag-Erling Smørgrav return "admin remote"; 632190cef3dSDag-Erling Smørgrav } else if (who == FORWARD_USER) { 633190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 634190cef3dSDag-Erling Smørgrav return "user local"; 635190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 636190cef3dSDag-Erling Smørgrav return "user remote"; 637190cef3dSDag-Erling Smørgrav } 638190cef3dSDag-Erling Smørgrav fatal("Unknown forward permission list %d/%d", who, where); 639190cef3dSDag-Erling Smørgrav } 6404f52dfbbSDag-Erling Smørgrav 641190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */ 642190cef3dSDag-Erling Smørgrav static struct permission_set * 643190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where) 644190cef3dSDag-Erling Smørgrav { 645190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 646190cef3dSDag-Erling Smørgrav 647190cef3dSDag-Erling Smørgrav switch (where) { 648190cef3dSDag-Erling Smørgrav case FORWARD_LOCAL: 649190cef3dSDag-Erling Smørgrav return &sc->local_perms; 650190cef3dSDag-Erling Smørgrav break; 651190cef3dSDag-Erling Smørgrav case FORWARD_REMOTE: 652190cef3dSDag-Erling Smørgrav return &sc->remote_perms; 653190cef3dSDag-Erling Smørgrav break; 654190cef3dSDag-Erling Smørgrav default: 65519261079SEd Maste fatal_f("invalid forwarding direction %d", where); 656190cef3dSDag-Erling Smørgrav } 657190cef3dSDag-Erling Smørgrav } 658190cef3dSDag-Erling Smørgrav 65919261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */ 660190cef3dSDag-Erling Smørgrav static void 661190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where, 662190cef3dSDag-Erling Smørgrav struct permission ***permpp, u_int **npermpp) 663190cef3dSDag-Erling Smørgrav { 664190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 665190cef3dSDag-Erling Smørgrav 666190cef3dSDag-Erling Smørgrav switch (who) { 667190cef3dSDag-Erling Smørgrav case FORWARD_USER: 668190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_user; 669190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_user; 670190cef3dSDag-Erling Smørgrav break; 671190cef3dSDag-Erling Smørgrav case FORWARD_ADM: 672190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_admin; 673190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_admin; 674190cef3dSDag-Erling Smørgrav break; 675190cef3dSDag-Erling Smørgrav default: 67619261079SEd Maste fatal_f("invalid forwarding client %d", who); 677190cef3dSDag-Erling Smørgrav } 678190cef3dSDag-Erling Smørgrav } 679190cef3dSDag-Erling Smørgrav 6801323ec57SEd Maste /* Adds an entry to the specified forwarding list */ 6814f52dfbbSDag-Erling Smørgrav static int 682190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where, 6834f52dfbbSDag-Erling Smørgrav const char *host_to_connect, int port_to_connect, 6844f52dfbbSDag-Erling Smørgrav const char *listen_host, const char *listen_path, int listen_port, 6854f52dfbbSDag-Erling Smørgrav Channel *downstream) 6864f52dfbbSDag-Erling Smørgrav { 687190cef3dSDag-Erling Smørgrav struct permission **permp; 688190cef3dSDag-Erling Smørgrav u_int n, *npermp; 6894f52dfbbSDag-Erling Smørgrav 690190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 6914f52dfbbSDag-Erling Smørgrav 692190cef3dSDag-Erling Smørgrav if (*npermp >= INT_MAX) 69319261079SEd Maste fatal_f("%s overflow", fwd_ident(who, where)); 6944f52dfbbSDag-Erling Smørgrav 695190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); 696190cef3dSDag-Erling Smørgrav n = (*npermp)++; 6974f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) 698190cef3dSDag-Erling Smørgrav (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); 699190cef3dSDag-Erling Smørgrav (*permp)[n].port_to_connect = port_to_connect; 700190cef3dSDag-Erling Smørgrav (*permp)[n].listen_host = MAYBE_DUP(listen_host); 701190cef3dSDag-Erling Smørgrav (*permp)[n].listen_path = MAYBE_DUP(listen_path); 702190cef3dSDag-Erling Smørgrav (*permp)[n].listen_port = listen_port; 703190cef3dSDag-Erling Smørgrav (*permp)[n].downstream = downstream; 7044f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP 7054f52dfbbSDag-Erling Smørgrav return (int)n; 7064f52dfbbSDag-Erling Smørgrav } 7074f52dfbbSDag-Erling Smørgrav 7084f52dfbbSDag-Erling Smørgrav static void 7094f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) 7104f52dfbbSDag-Erling Smørgrav { 7114f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 712190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 713190cef3dSDag-Erling Smørgrav struct permission *perm; 7144f52dfbbSDag-Erling Smørgrav int r; 7154f52dfbbSDag-Erling Smørgrav u_int i; 7164f52dfbbSDag-Erling Smørgrav 717190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 718190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 719190cef3dSDag-Erling Smørgrav if (perm->downstream != c) 7204f52dfbbSDag-Erling Smørgrav continue; 7214f52dfbbSDag-Erling Smørgrav 7224f52dfbbSDag-Erling Smørgrav /* cancel on the server, since mux client is gone */ 7234f52dfbbSDag-Erling Smørgrav debug("channel %d: cleanup remote forward for %s:%u", 724190cef3dSDag-Erling Smørgrav c->self, perm->listen_host, perm->listen_port); 7254f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 7264f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 7274f52dfbbSDag-Erling Smørgrav "cancel-tcpip-forward")) != 0 || 7284f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || 7294f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 730190cef3dSDag-Erling Smørgrav channel_rfwd_bind_host(perm->listen_host))) != 0 || 731190cef3dSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || 7324f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 73319261079SEd Maste fatal_fr(r, "channel %i", c->self); 7344f52dfbbSDag-Erling Smørgrav } 735190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 7364f52dfbbSDag-Erling Smørgrav } 737a04a10f8SKris Kennaway } 738511b41d2SMark Murray 739a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 740a04a10f8SKris Kennaway void 7414f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c) 742a04a10f8SKris Kennaway { 7434f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 744af12a3e7SDag-Erling Smørgrav char *s; 74521e764dfSDag-Erling Smørgrav u_int i, n; 746ca86bcf2SDag-Erling Smørgrav Channel *other; 747d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 748ca3176e7SBrian Feldman 7494f52dfbbSDag-Erling Smørgrav for (n = 0, i = 0; i < sc->channels_alloc; i++) { 7504f52dfbbSDag-Erling Smørgrav if ((other = sc->channels[i]) == NULL) 7514f52dfbbSDag-Erling Smørgrav continue; 752af12a3e7SDag-Erling Smørgrav n++; 753ca86bcf2SDag-Erling Smørgrav /* detach from mux client and prepare for closing */ 754ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT && 755ca86bcf2SDag-Erling Smørgrav other->type == SSH_CHANNEL_MUX_PROXY && 756ca86bcf2SDag-Erling Smørgrav other->mux_ctx == c) { 757ca86bcf2SDag-Erling Smørgrav other->mux_ctx = NULL; 758ca86bcf2SDag-Erling Smørgrav other->type = SSH_CHANNEL_OPEN; 759ca86bcf2SDag-Erling Smørgrav other->istate = CHAN_INPUT_CLOSED; 760ca86bcf2SDag-Erling Smørgrav other->ostate = CHAN_OUTPUT_CLOSED; 761ca86bcf2SDag-Erling Smørgrav } 762ca86bcf2SDag-Erling Smørgrav } 76321e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 764af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 765af12a3e7SDag-Erling Smørgrav 766e9e8876aSEd Maste if (c->type == SSH_CHANNEL_MUX_CLIENT) { 7674f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(ssh, c); 768e9e8876aSEd Maste free(c->mux_ctx); 769e9e8876aSEd Maste c->mux_ctx = NULL; 770e9e8876aSEd Maste } else if (c->type == SSH_CHANNEL_MUX_LISTENER) { 77119261079SEd Maste free(c->mux_ctx); 77219261079SEd Maste c->mux_ctx = NULL; 77319261079SEd Maste } 774ca86bcf2SDag-Erling Smørgrav 775190cef3dSDag-Erling Smørgrav if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { 7764f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 777221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 778e4a9863fSDag-Erling Smørgrav free(s); 779190cef3dSDag-Erling Smørgrav } 780ca3176e7SBrian Feldman 7814f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, c); 7824f52dfbbSDag-Erling Smørgrav sshbuf_free(c->input); 7834f52dfbbSDag-Erling Smørgrav sshbuf_free(c->output); 7844f52dfbbSDag-Erling Smørgrav sshbuf_free(c->extended); 7854f52dfbbSDag-Erling Smørgrav c->input = c->output = c->extended = NULL; 786e4a9863fSDag-Erling Smørgrav free(c->remote_name); 787a04a10f8SKris Kennaway c->remote_name = NULL; 788e4a9863fSDag-Erling Smørgrav free(c->path); 789cce7d346SDag-Erling Smørgrav c->path = NULL; 790e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 791462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 792f374ba41SEd Maste free(c->xctype); 793f374ba41SEd Maste c->xctype = NULL; 794d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 795d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 7964f52dfbbSDag-Erling Smørgrav cc->abandon_cb(ssh, c, cc->ctx); 797d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 79819261079SEd Maste freezero(cc, sizeof(*cc)); 799d4af9e69SDag-Erling Smørgrav } 800d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 8014f52dfbbSDag-Erling Smørgrav c->filter_cleanup(ssh, c->self, c->filter_ctx); 8024f52dfbbSDag-Erling Smørgrav sc->channels[c->self] = NULL; 80319261079SEd Maste freezero(c, sizeof(*c)); 804af12a3e7SDag-Erling Smørgrav } 805af12a3e7SDag-Erling Smørgrav 806af12a3e7SDag-Erling Smørgrav void 8074f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh) 808af12a3e7SDag-Erling Smørgrav { 80921e764dfSDag-Erling Smørgrav u_int i; 81019261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 811af12a3e7SDag-Erling Smørgrav 81219261079SEd Maste for (i = 0; i < sc->channels_alloc; i++) 81319261079SEd Maste if (sc->channels[i] != NULL) 81419261079SEd Maste channel_free(ssh, sc->channels[i]); 81519261079SEd Maste 81619261079SEd Maste free(sc->channels); 81719261079SEd Maste sc->channels = NULL; 81819261079SEd Maste sc->channels_alloc = 0; 81919261079SEd Maste 82019261079SEd Maste free(sc->x11_saved_display); 82119261079SEd Maste sc->x11_saved_display = NULL; 82219261079SEd Maste 82319261079SEd Maste free(sc->x11_saved_proto); 82419261079SEd Maste sc->x11_saved_proto = NULL; 82519261079SEd Maste 82619261079SEd Maste free(sc->x11_saved_data); 82719261079SEd Maste sc->x11_saved_data = NULL; 82819261079SEd Maste sc->x11_saved_data_len = 0; 82919261079SEd Maste 83019261079SEd Maste free(sc->x11_fake_data); 83119261079SEd Maste sc->x11_fake_data = NULL; 83219261079SEd Maste sc->x11_fake_data_len = 0; 833af12a3e7SDag-Erling Smørgrav } 834af12a3e7SDag-Erling Smørgrav 835af12a3e7SDag-Erling Smørgrav /* 836af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 837af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 838af12a3e7SDag-Erling Smørgrav */ 839af12a3e7SDag-Erling Smørgrav void 8404f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh) 841af12a3e7SDag-Erling Smørgrav { 84221e764dfSDag-Erling Smørgrav u_int i; 843af12a3e7SDag-Erling Smørgrav 8444f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) 8454f52dfbbSDag-Erling Smørgrav if (ssh->chanctxt->channels[i] != NULL) 8464f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, ssh->chanctxt->channels[i]); 847af12a3e7SDag-Erling Smørgrav } 848af12a3e7SDag-Erling Smørgrav 849af12a3e7SDag-Erling Smørgrav /* 850af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 851af12a3e7SDag-Erling Smørgrav */ 852af12a3e7SDag-Erling Smørgrav void 8534f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh) 854af12a3e7SDag-Erling Smørgrav { 85521e764dfSDag-Erling Smørgrav u_int i; 856af12a3e7SDag-Erling Smørgrav Channel *c; 857af12a3e7SDag-Erling Smørgrav 8584f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8594f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 860af12a3e7SDag-Erling Smørgrav if (c != NULL) { 861af12a3e7SDag-Erling Smørgrav switch (c->type) { 862af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 863af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 864af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 865af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 866a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 867a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 86819261079SEd Maste channel_close_fd(ssh, c, &c->sock); 8694f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 870af12a3e7SDag-Erling Smørgrav break; 871af12a3e7SDag-Erling Smørgrav } 872af12a3e7SDag-Erling Smørgrav } 873af12a3e7SDag-Erling Smørgrav } 874af12a3e7SDag-Erling Smørgrav } 875af12a3e7SDag-Erling Smørgrav 876af12a3e7SDag-Erling Smørgrav /* 877af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 878af12a3e7SDag-Erling Smørgrav * more channel is overfull. 879af12a3e7SDag-Erling Smørgrav */ 880af12a3e7SDag-Erling Smørgrav int 8814f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh) 882af12a3e7SDag-Erling Smørgrav { 883af12a3e7SDag-Erling Smørgrav u_int i; 8844f52dfbbSDag-Erling Smørgrav u_int maxsize = ssh_packet_get_maxsize(ssh); 885af12a3e7SDag-Erling Smørgrav Channel *c; 886af12a3e7SDag-Erling Smørgrav 8874f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8884f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 8894f52dfbbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_OPEN) 8904f52dfbbSDag-Erling Smørgrav continue; 8914f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > maxsize) { 8924f52dfbbSDag-Erling Smørgrav debug2("channel %d: big output buffer %zu > %u", 8934f52dfbbSDag-Erling Smørgrav c->self, sshbuf_len(c->output), maxsize); 894af12a3e7SDag-Erling Smørgrav return 0; 895af12a3e7SDag-Erling Smørgrav } 896af12a3e7SDag-Erling Smørgrav } 897af12a3e7SDag-Erling Smørgrav return 1; 898af12a3e7SDag-Erling Smørgrav } 899af12a3e7SDag-Erling Smørgrav 900af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 901af12a3e7SDag-Erling Smørgrav int 9024f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh) 903af12a3e7SDag-Erling Smørgrav { 90421e764dfSDag-Erling Smørgrav u_int i; 905af12a3e7SDag-Erling Smørgrav Channel *c; 906af12a3e7SDag-Erling Smørgrav 9074f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 9084f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 909af12a3e7SDag-Erling Smørgrav if (c == NULL) 910af12a3e7SDag-Erling Smørgrav continue; 911af12a3e7SDag-Erling Smørgrav switch (c->type) { 912af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 913af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 914af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 915b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 916af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 917af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 918af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 9194f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 920af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 921af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 922e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 923a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 924a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 925af12a3e7SDag-Erling Smørgrav continue; 926af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 927af12a3e7SDag-Erling Smørgrav continue; 928af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 929af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 9304f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 931af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 932b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 933ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 934af12a3e7SDag-Erling Smørgrav return 1; 935af12a3e7SDag-Erling Smørgrav default: 93619261079SEd Maste fatal_f("bad channel type %d", c->type); 937af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 938af12a3e7SDag-Erling Smørgrav } 939af12a3e7SDag-Erling Smørgrav } 940af12a3e7SDag-Erling Smørgrav return 0; 941af12a3e7SDag-Erling Smørgrav } 942af12a3e7SDag-Erling Smørgrav 943069ac184SEd Maste /* Returns true if a channel with a TTY is open. */ 944069ac184SEd Maste int 945069ac184SEd Maste channel_tty_open(struct ssh *ssh) 946069ac184SEd Maste { 947069ac184SEd Maste u_int i; 948069ac184SEd Maste Channel *c; 949069ac184SEd Maste 950069ac184SEd Maste for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 951069ac184SEd Maste c = ssh->chanctxt->channels[i]; 952069ac184SEd Maste if (c == NULL || c->type != SSH_CHANNEL_OPEN) 953069ac184SEd Maste continue; 954069ac184SEd Maste if (c->client_tty) 955069ac184SEd Maste return 1; 956069ac184SEd Maste } 957069ac184SEd Maste return 0; 958069ac184SEd Maste } 959069ac184SEd Maste 960af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 961af12a3e7SDag-Erling Smørgrav int 9624f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh) 963af12a3e7SDag-Erling Smørgrav { 96421e764dfSDag-Erling Smørgrav u_int i; 965af12a3e7SDag-Erling Smørgrav Channel *c; 966af12a3e7SDag-Erling Smørgrav 9674f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 9684f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 9694f52dfbbSDag-Erling Smørgrav if (c == NULL || !c->have_remote_id) 970af12a3e7SDag-Erling Smørgrav continue; 971af12a3e7SDag-Erling Smørgrav switch (c->type) { 972af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 973af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 9744f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 9754f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 976af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 977af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 978af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 979b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 980b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 981ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 982af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 983af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 984af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 985e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 986a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 987a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 988af12a3e7SDag-Erling Smørgrav continue; 989af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 990af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 991af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 992af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 993af12a3e7SDag-Erling Smørgrav return i; 994af12a3e7SDag-Erling Smørgrav default: 99519261079SEd Maste fatal_f("bad channel type %d", c->type); 996af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 997af12a3e7SDag-Erling Smørgrav } 998af12a3e7SDag-Erling Smørgrav } 999af12a3e7SDag-Erling Smørgrav return -1; 1000af12a3e7SDag-Erling Smørgrav } 1001af12a3e7SDag-Erling Smørgrav 10022f513db7SEd Maste /* Returns the state of the channel's extended usage flag */ 10032f513db7SEd Maste const char * 10042f513db7SEd Maste channel_format_extended_usage(const Channel *c) 10052f513db7SEd Maste { 10062f513db7SEd Maste if (c->efd == -1) 10072f513db7SEd Maste return "closed"; 10082f513db7SEd Maste 10092f513db7SEd Maste switch (c->extended_usage) { 10102f513db7SEd Maste case CHAN_EXTENDED_WRITE: 10112f513db7SEd Maste return "write"; 10122f513db7SEd Maste case CHAN_EXTENDED_READ: 10132f513db7SEd Maste return "read"; 10142f513db7SEd Maste case CHAN_EXTENDED_IGNORE: 10152f513db7SEd Maste return "ignore"; 10162f513db7SEd Maste default: 10172f513db7SEd Maste return "UNKNOWN"; 10182f513db7SEd Maste } 10192f513db7SEd Maste } 10202f513db7SEd Maste 10212f513db7SEd Maste static char * 10222f513db7SEd Maste channel_format_status(const Channel *c) 10232f513db7SEd Maste { 10242f513db7SEd Maste char *ret = NULL; 10252f513db7SEd Maste 1026f374ba41SEd Maste xasprintf(&ret, "t%d [%s] %s%u i%u/%zu o%u/%zu e[%s]/%zu " 10271323ec57SEd Maste "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", 1028f374ba41SEd Maste c->type, c->xctype != NULL ? c->xctype : c->ctype, 10292f513db7SEd Maste c->have_remote_id ? "r" : "nr", c->remote_id, 10302f513db7SEd Maste c->istate, sshbuf_len(c->input), 10312f513db7SEd Maste c->ostate, sshbuf_len(c->output), 10322f513db7SEd Maste channel_format_extended_usage(c), sshbuf_len(c->extended), 10331323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, 10341323ec57SEd Maste c->io_want, c->io_ready); 10352f513db7SEd Maste return ret; 10362f513db7SEd Maste } 10372f513db7SEd Maste 1038af12a3e7SDag-Erling Smørgrav /* 1039af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 1040af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 1041af12a3e7SDag-Erling Smørgrav * newlines. 1042af12a3e7SDag-Erling Smørgrav */ 1043af12a3e7SDag-Erling Smørgrav char * 10444f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh) 1045af12a3e7SDag-Erling Smørgrav { 10464f52dfbbSDag-Erling Smørgrav struct sshbuf *buf; 1047af12a3e7SDag-Erling Smørgrav Channel *c; 104821e764dfSDag-Erling Smørgrav u_int i; 10494f52dfbbSDag-Erling Smørgrav int r; 10502f513db7SEd Maste char *cp, *ret; 1051af12a3e7SDag-Erling Smørgrav 10524f52dfbbSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 105319261079SEd Maste fatal_f("sshbuf_new"); 10544f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(buf, 10554f52dfbbSDag-Erling Smørgrav "The following connections are open:\r\n")) != 0) 105619261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10574f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 10584f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 1059af12a3e7SDag-Erling Smørgrav if (c == NULL) 1060af12a3e7SDag-Erling Smørgrav continue; 1061af12a3e7SDag-Erling Smørgrav switch (c->type) { 1062af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 1063af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 1064af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 1065af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 1066af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 1067af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 1068e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 1069b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 1070a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 1071a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 1072af12a3e7SDag-Erling Smørgrav continue; 1073af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 1074af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 1075af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 1076af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 10774f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 10784f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 1079af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 1080af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 1081ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 1082ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 10832f513db7SEd Maste cp = channel_format_status(c); 10842f513db7SEd Maste if ((r = sshbuf_putf(buf, " #%d %.300s (%s)\r\n", 10852f513db7SEd Maste c->self, c->remote_name, cp)) != 0) { 10862f513db7SEd Maste free(cp); 108719261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10882f513db7SEd Maste } 10892f513db7SEd Maste free(cp); 1090af12a3e7SDag-Erling Smørgrav continue; 1091af12a3e7SDag-Erling Smørgrav default: 109219261079SEd Maste fatal_f("bad channel type %d", c->type); 1093af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 1094af12a3e7SDag-Erling Smørgrav } 1095af12a3e7SDag-Erling Smørgrav } 10964f52dfbbSDag-Erling Smørgrav if ((ret = sshbuf_dup_string(buf)) == NULL) 109719261079SEd Maste fatal_f("sshbuf_dup_string"); 10984f52dfbbSDag-Erling Smørgrav sshbuf_free(buf); 10994f52dfbbSDag-Erling Smørgrav return ret; 11004f52dfbbSDag-Erling Smørgrav } 11014f52dfbbSDag-Erling Smørgrav 11024f52dfbbSDag-Erling Smørgrav static void 11034f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) 11044f52dfbbSDag-Erling Smørgrav { 11054f52dfbbSDag-Erling Smørgrav int r; 11064f52dfbbSDag-Erling Smørgrav 11074f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 11084f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, type)) != 0 || 11094f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 11104f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 11114f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { 111219261079SEd Maste fatal_r(r, "%s: channel %i: open", where, c->self); 11134f52dfbbSDag-Erling Smørgrav } 1114af12a3e7SDag-Erling Smørgrav } 1115af12a3e7SDag-Erling Smørgrav 1116af12a3e7SDag-Erling Smørgrav void 11174f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id) 1118af12a3e7SDag-Erling Smørgrav { 11194f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 11204f52dfbbSDag-Erling Smørgrav int r; 1121f388f5efSDag-Erling Smørgrav 1122af12a3e7SDag-Erling Smørgrav if (c == NULL) { 1123221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 1124af12a3e7SDag-Erling Smørgrav return; 1125af12a3e7SDag-Erling Smørgrav } 1126e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 11274f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, c->ctype); 11284f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 112919261079SEd Maste fatal_fr(r, "channel %i", c->self); 1130af12a3e7SDag-Erling Smørgrav } 1131af12a3e7SDag-Erling Smørgrav 1132af12a3e7SDag-Erling Smørgrav void 11334f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) 1134af12a3e7SDag-Erling Smørgrav { 11354f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 11364f52dfbbSDag-Erling Smørgrav int r; 1137f388f5efSDag-Erling Smørgrav 1138af12a3e7SDag-Erling Smørgrav if (c == NULL) { 113919261079SEd Maste logit_f("%d: unknown channel id", id); 1140af12a3e7SDag-Erling Smørgrav return; 1141af12a3e7SDag-Erling Smørgrav } 11424f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 114319261079SEd Maste fatal_f("channel %d: no remote id", c->self); 11444f52dfbbSDag-Erling Smørgrav 114521e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 11464f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || 11474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 11484f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, service)) != 0 || 11494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { 115019261079SEd Maste fatal_fr(r, "channel %i", c->self); 11514f52dfbbSDag-Erling Smørgrav } 1152af12a3e7SDag-Erling Smørgrav } 1153333ee039SDag-Erling Smørgrav 1154af12a3e7SDag-Erling Smørgrav void 11554f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id, 11564f52dfbbSDag-Erling Smørgrav channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) 1157d4af9e69SDag-Erling Smørgrav { 1158d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 1159d4af9e69SDag-Erling Smørgrav Channel *c; 1160d4af9e69SDag-Erling Smørgrav 11614f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 116219261079SEd Maste fatal_f("%d: bad id", id); 1163d4af9e69SDag-Erling Smørgrav 11640a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 1165d4af9e69SDag-Erling Smørgrav cc->cb = cb; 1166d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 1167d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 1168d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 1169d4af9e69SDag-Erling Smørgrav } 1170d4af9e69SDag-Erling Smørgrav 1171d4af9e69SDag-Erling Smørgrav void 11724f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id, 11734f52dfbbSDag-Erling Smørgrav channel_open_fn *fn, void *ctx) 1174af12a3e7SDag-Erling Smørgrav { 11754f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1176f388f5efSDag-Erling Smørgrav 1177af12a3e7SDag-Erling Smørgrav if (c == NULL) { 117819261079SEd Maste logit_f("%d: bad id", id); 1179af12a3e7SDag-Erling Smørgrav return; 1180af12a3e7SDag-Erling Smørgrav } 1181d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 1182d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 1183af12a3e7SDag-Erling Smørgrav } 1184333ee039SDag-Erling Smørgrav 1185af12a3e7SDag-Erling Smørgrav void 11864f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id, 11874f52dfbbSDag-Erling Smørgrav channel_callback_fn *fn, int do_close) 1188af12a3e7SDag-Erling Smørgrav { 11894f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1190f388f5efSDag-Erling Smørgrav 1191af12a3e7SDag-Erling Smørgrav if (c == NULL) { 119219261079SEd Maste logit_f("%d: bad id", id); 1193af12a3e7SDag-Erling Smørgrav return; 1194af12a3e7SDag-Erling Smørgrav } 1195af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 1196b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 1197af12a3e7SDag-Erling Smørgrav } 1198333ee039SDag-Erling Smørgrav 1199af12a3e7SDag-Erling Smørgrav void 12004f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id) 1201af12a3e7SDag-Erling Smørgrav { 12024f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1203f388f5efSDag-Erling Smørgrav 1204af12a3e7SDag-Erling Smørgrav if (c == NULL) { 120519261079SEd Maste logit_f("%d: bad id", id); 1206af12a3e7SDag-Erling Smørgrav return; 1207af12a3e7SDag-Erling Smørgrav } 1208af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 1209b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 1210af12a3e7SDag-Erling Smørgrav } 1211333ee039SDag-Erling Smørgrav 1212af12a3e7SDag-Erling Smørgrav void 12134f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, 1214d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 1215af12a3e7SDag-Erling Smørgrav { 12164f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1217f388f5efSDag-Erling Smørgrav 1218af12a3e7SDag-Erling Smørgrav if (c == NULL) { 121919261079SEd Maste logit_f("%d: bad id", id); 1220af12a3e7SDag-Erling Smørgrav return; 1221af12a3e7SDag-Erling Smørgrav } 1222b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 1223b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 1224d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 1225d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 1226af12a3e7SDag-Erling Smørgrav } 1227af12a3e7SDag-Erling Smørgrav 1228af12a3e7SDag-Erling Smørgrav void 12294f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, 1230d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 1231af12a3e7SDag-Erling Smørgrav { 12324f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 12334f52dfbbSDag-Erling Smørgrav int r; 1234f388f5efSDag-Erling Smørgrav 1235af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 1236af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 12374f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 123819261079SEd Maste fatal_f("channel %d: no remote id", c->self); 12394f52dfbbSDag-Erling Smørgrav 12404f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); 1241af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1242*a91a2465SEd Maste channel_set_used_time(ssh, c); 1243af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 12444f52dfbbSDag-Erling Smørgrav 12454f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 12464f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 12474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 12484f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 124919261079SEd Maste fatal_fr(r, "channel %i", c->self); 1250511b41d2SMark Murray } 1251511b41d2SMark Murray 1252af12a3e7SDag-Erling Smørgrav static void 12531323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c) 1254511b41d2SMark Murray { 12551323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_R; 1256a04a10f8SKris Kennaway } 1257a04a10f8SKris Kennaway 1258af12a3e7SDag-Erling Smørgrav static void 12591323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c) 1260ca3176e7SBrian Feldman { 1261ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 12621323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_W; 1263ca3176e7SBrian Feldman } 1264ca3176e7SBrian Feldman 1265af12a3e7SDag-Erling Smørgrav static void 12661323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c) 1267a04a10f8SKris Kennaway { 12681323ec57SEd Maste c->io_want = 0; 1269a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 12704f52dfbbSDag-Erling Smørgrav c->remote_window > 0 && 12714f52dfbbSDag-Erling Smørgrav sshbuf_len(c->input) < c->remote_window && 12724f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 12731323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1274a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 1275a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 12764f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) { 12771323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1278a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 127980628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 12804f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 12814f52dfbbSDag-Erling Smørgrav "obuf_empty delayed efd %d/(%zu)", c->self, 12824f52dfbbSDag-Erling Smørgrav c->efd, sshbuf_len(c->extended)); 128380628bacSDag-Erling Smørgrav else 12844f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1285a04a10f8SKris Kennaway } 1286a04a10f8SKris Kennaway } 1287a04a10f8SKris Kennaway /** XXX check close conditions, too */ 12884f52dfbbSDag-Erling Smørgrav if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && 12894f52dfbbSDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_CLOSED)) { 1290a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 12914f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) > 0) 12921323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_W; 1293e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 1294e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1295e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 12964f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) < c->remote_window) 12971323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_R; 1298a04a10f8SKris Kennaway } 129921e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 1300a04a10f8SKris Kennaway } 1301a04a10f8SKris Kennaway 1302a04a10f8SKris Kennaway /* 1303a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 1304a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 1305a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 1306a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 1307a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 1308a04a10f8SKris Kennaway * XXX All this happens at the client side. 1309af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 1310a04a10f8SKris Kennaway */ 1311af12a3e7SDag-Erling Smørgrav static int 13124f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b) 1313a04a10f8SKris Kennaway { 13144f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 1315ca3176e7SBrian Feldman u_char *ucp; 1316ca3176e7SBrian Feldman u_int proto_len, data_len; 1317511b41d2SMark Murray 1318557f75e5SDag-Erling Smørgrav /* Is this being called after the refusal deadline? */ 13194f52dfbbSDag-Erling Smørgrav if (sc->x11_refuse_time != 0 && 13204d3fc8b0SEd Maste monotime() >= sc->x11_refuse_time) { 1321557f75e5SDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1322557f75e5SDag-Erling Smørgrav "expired"); 1323557f75e5SDag-Erling Smørgrav return -1; 1324557f75e5SDag-Erling Smørgrav } 1325557f75e5SDag-Erling Smørgrav 1326511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 13274f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 12) 1328a04a10f8SKris Kennaway return 0; 1329511b41d2SMark Murray 1330511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 13314f52dfbbSDag-Erling Smørgrav ucp = sshbuf_mutable_ptr(b); 1332511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 1333511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 1334511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 1335511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 1336511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 1337511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 1338511b41d2SMark Murray } else { 1339221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 1340511b41d2SMark Murray ucp[0]); 1341a04a10f8SKris Kennaway return -1; 1342511b41d2SMark Murray } 1343511b41d2SMark Murray 1344511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 13454f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 1346511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 1347a04a10f8SKris Kennaway return 0; 1348511b41d2SMark Murray 1349511b41d2SMark Murray /* Check if authentication protocol matches. */ 13504f52dfbbSDag-Erling Smørgrav if (proto_len != strlen(sc->x11_saved_proto) || 13514f52dfbbSDag-Erling Smørgrav memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { 1352221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 1353a04a10f8SKris Kennaway return -1; 1354511b41d2SMark Murray } 1355511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 13564f52dfbbSDag-Erling Smørgrav if (data_len != sc->x11_fake_data_len || 1357e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 13584f52dfbbSDag-Erling Smørgrav sc->x11_fake_data, sc->x11_fake_data_len) != 0) { 1359221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 1360a04a10f8SKris Kennaway return -1; 1361511b41d2SMark Murray } 1362511b41d2SMark Murray /* Check fake data length */ 13634f52dfbbSDag-Erling Smørgrav if (sc->x11_fake_data_len != sc->x11_saved_data_len) { 1364511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 13654f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len, sc->x11_saved_data_len); 1366a04a10f8SKris Kennaway return -1; 1367511b41d2SMark Murray } 1368511b41d2SMark Murray /* 1369511b41d2SMark Murray * Received authentication protocol and data match 1370511b41d2SMark Murray * our fake data. Substitute the fake data with real 1371511b41d2SMark Murray * data. 1372511b41d2SMark Murray */ 1373511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 13744f52dfbbSDag-Erling Smørgrav sc->x11_saved_data, sc->x11_saved_data_len); 1375a04a10f8SKris Kennaway return 1; 1376a04a10f8SKris Kennaway } 1377511b41d2SMark Murray 1378f374ba41SEd Maste void 1379f374ba41SEd Maste channel_force_close(struct ssh *ssh, Channel *c, int abandon) 1380f374ba41SEd Maste { 1381f374ba41SEd Maste debug3_f("channel %d: forcibly closing", c->self); 1382f374ba41SEd Maste if (c->istate == CHAN_INPUT_OPEN) 1383f374ba41SEd Maste chan_read_failed(ssh, c); 1384f374ba41SEd Maste if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1385f374ba41SEd Maste sshbuf_reset(c->input); 1386f374ba41SEd Maste chan_ibuf_empty(ssh, c); 1387f374ba41SEd Maste } 1388f374ba41SEd Maste if (c->ostate == CHAN_OUTPUT_OPEN || 1389f374ba41SEd Maste c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1390f374ba41SEd Maste sshbuf_reset(c->output); 1391f374ba41SEd Maste chan_write_failed(ssh, c); 1392f374ba41SEd Maste } 1393f374ba41SEd Maste if (c->detach_user) 1394f374ba41SEd Maste c->detach_user(ssh, c->self, 1, NULL); 1395f374ba41SEd Maste if (c->efd != -1) 1396f374ba41SEd Maste channel_close_fd(ssh, c, &c->efd); 1397f374ba41SEd Maste if (abandon) 1398f374ba41SEd Maste c->type = SSH_CHANNEL_ABANDONED; 1399f374ba41SEd Maste /* exempt from inactivity timeouts */ 1400f374ba41SEd Maste c->inactive_deadline = 0; 1401f374ba41SEd Maste c->lastused = 0; 1402f374ba41SEd Maste } 1403f374ba41SEd Maste 1404af12a3e7SDag-Erling Smørgrav static void 14051323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c) 1406a04a10f8SKris Kennaway { 14074f52dfbbSDag-Erling Smørgrav int ret = x11_open_helper(ssh, c->output); 1408af12a3e7SDag-Erling Smørgrav 1409af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 1410af12a3e7SDag-Erling Smørgrav 1411a04a10f8SKris Kennaway if (ret == 1) { 1412a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1413*a91a2465SEd Maste channel_set_used_time(ssh, c); 14141323ec57SEd Maste channel_pre_open(ssh, c); 1415a04a10f8SKris Kennaway } else if (ret == -1) { 1416f374ba41SEd Maste logit("X11 connection rejected because of wrong " 1417f374ba41SEd Maste "authentication."); 14184f52dfbbSDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", 14194f52dfbbSDag-Erling Smørgrav c->self, c->istate, c->ostate); 1420f374ba41SEd Maste channel_force_close(ssh, c, 0); 1421a04a10f8SKris Kennaway } 1422a04a10f8SKris Kennaway } 1423a04a10f8SKris Kennaway 1424b15c8340SDag-Erling Smørgrav static void 14251323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c) 1426b15c8340SDag-Erling Smørgrav { 14271323ec57SEd Maste c->io_want = 0; 1428e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 14294f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 14301323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1431b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1432b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 14334f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 14344f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 1435b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 14364f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 1437b15c8340SDag-Erling Smørgrav } 1438b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1439b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 14404f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) 14411323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1442b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 14434f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1444b15c8340SDag-Erling Smørgrav } 1445b15c8340SDag-Erling Smørgrav } 1446b15c8340SDag-Erling Smørgrav 1447ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1448af12a3e7SDag-Erling Smørgrav static int 14494f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) 1450ca3176e7SBrian Feldman { 14514f52dfbbSDag-Erling Smørgrav const u_char *p; 14524f52dfbbSDag-Erling Smørgrav char *host; 1453cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1454ca3176e7SBrian Feldman char username[256]; 1455ca3176e7SBrian Feldman struct { 1456ca3176e7SBrian Feldman u_int8_t version; 1457ca3176e7SBrian Feldman u_int8_t command; 1458ca3176e7SBrian Feldman u_int16_t dest_port; 1459ca3176e7SBrian Feldman struct in_addr dest_addr; 1460ca3176e7SBrian Feldman } s4_req, s4_rsp; 14614f52dfbbSDag-Erling Smørgrav int r; 1462ca3176e7SBrian Feldman 1463ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1464ca3176e7SBrian Feldman 14654f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1466ca3176e7SBrian Feldman len = sizeof(s4_req); 1467ca3176e7SBrian Feldman if (have < len) 1468ca3176e7SBrian Feldman return 0; 14694f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1470cce7d346SDag-Erling Smørgrav 1471cce7d346SDag-Erling Smørgrav need = 1; 1472cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1473cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1474cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1475cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1476cce7d346SDag-Erling Smørgrav need = 2; 1477cce7d346SDag-Erling Smørgrav } 1478cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1479ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1480ca3176e7SBrian Feldman if (p[i] == '\0') { 1481cce7d346SDag-Erling Smørgrav found++; 1482cce7d346SDag-Erling Smørgrav if (found == need) 1483ca3176e7SBrian Feldman break; 1484ca3176e7SBrian Feldman } 1485ca3176e7SBrian Feldman if (i > 1024) { 1486ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1487ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1488ca3176e7SBrian Feldman c->self); 1489ca3176e7SBrian Feldman return -1; 1490ca3176e7SBrian Feldman } 1491ca3176e7SBrian Feldman } 1492cce7d346SDag-Erling Smørgrav if (found < need) 1493ca3176e7SBrian Feldman return 0; 14944f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || 14954f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || 14964f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || 14974f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { 149819261079SEd Maste debug_r(r, "channels %d: decode socks4", c->self); 14994f52dfbbSDag-Erling Smørgrav return -1; 15004f52dfbbSDag-Erling Smørgrav } 15014f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 15024f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 15034f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 150419261079SEd Maste error("channel %d: decode socks4: unterminated user", c->self); 15054f52dfbbSDag-Erling Smørgrav return -1; 15064f52dfbbSDag-Erling Smørgrav } 1507ca3176e7SBrian Feldman len = strlen(p); 1508ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1509cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1510ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 151119261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 151219261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1513e4a9863fSDag-Erling Smørgrav free(c->path); 1514cce7d346SDag-Erling Smørgrav c->path = NULL; 1515cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1516ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1517cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1518cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 15194f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 15204f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 15214f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 15224f52dfbbSDag-Erling Smørgrav error("channel %d: decode socks4a: host not nul " 15234f52dfbbSDag-Erling Smørgrav "terminated", c->self); 15244f52dfbbSDag-Erling Smørgrav return -1; 15254f52dfbbSDag-Erling Smørgrav } 1526cce7d346SDag-Erling Smørgrav len = strlen(p); 1527cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1528cce7d346SDag-Erling Smørgrav c->self, p, len); 1529cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1530cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1531cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1532cce7d346SDag-Erling Smørgrav c->self, p); 1533cce7d346SDag-Erling Smørgrav return -1; 1534cce7d346SDag-Erling Smørgrav } 1535cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 153619261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 153719261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1538cce7d346SDag-Erling Smørgrav } 1539ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1540ca3176e7SBrian Feldman 1541221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1542cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1543ca3176e7SBrian Feldman 1544ca3176e7SBrian Feldman if (s4_req.command != 1) { 1545cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1546cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1547ca3176e7SBrian Feldman return -1; 1548ca3176e7SBrian Feldman } 1549ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1550ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1551ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1552ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 155319261079SEd Maste if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) 155419261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1555ca3176e7SBrian Feldman return 1; 1556ca3176e7SBrian Feldman } 1557ca3176e7SBrian Feldman 1558221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1559221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1560221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1561221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1562221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1563221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1564221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1565221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1566221552e4SDag-Erling Smørgrav 1567221552e4SDag-Erling Smørgrav static int 15684f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) 1569221552e4SDag-Erling Smørgrav { 15704f52dfbbSDag-Erling Smørgrav /* XXX use get/put_u8 instead of trusting struct padding */ 1571221552e4SDag-Erling Smørgrav struct { 1572221552e4SDag-Erling Smørgrav u_int8_t version; 1573221552e4SDag-Erling Smørgrav u_int8_t command; 1574221552e4SDag-Erling Smørgrav u_int8_t reserved; 1575221552e4SDag-Erling Smørgrav u_int8_t atyp; 1576221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1577221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1578e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 15794f52dfbbSDag-Erling Smørgrav const u_char *p; 1580333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 15814f52dfbbSDag-Erling Smørgrav int r; 1582221552e4SDag-Erling Smørgrav 1583221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 15844f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1585221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1586221552e4SDag-Erling Smørgrav return -1; 15874f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1588221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1589221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1590221552e4SDag-Erling Smørgrav if (have < 2) 1591221552e4SDag-Erling Smørgrav return 0; 1592221552e4SDag-Erling Smørgrav nmethods = p[1]; 1593221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1594221552e4SDag-Erling Smørgrav return 0; 1595221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1596221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1597221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1598221552e4SDag-Erling Smørgrav found = 1; 1599221552e4SDag-Erling Smørgrav break; 1600221552e4SDag-Erling Smørgrav } 1601221552e4SDag-Erling Smørgrav } 1602221552e4SDag-Erling Smørgrav if (!found) { 1603221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1604221552e4SDag-Erling Smørgrav c->self); 1605221552e4SDag-Erling Smørgrav return -1; 1606221552e4SDag-Erling Smørgrav } 160719261079SEd Maste if ((r = sshbuf_consume(input, nmethods + 2)) != 0) 160819261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 16094f52dfbbSDag-Erling Smørgrav /* version, method */ 16104f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(output, 0x05)) != 0 || 161119261079SEd Maste (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) 161219261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1613221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1614221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1615221552e4SDag-Erling Smørgrav return 0; /* need more */ 1616221552e4SDag-Erling Smørgrav } 1617221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1618221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1619221552e4SDag-Erling Smørgrav return 0; /* need more */ 1620333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1621221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1622221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1623221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1624221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1625221552e4SDag-Erling Smørgrav return -1; 1626221552e4SDag-Erling Smørgrav } 1627221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1628221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1629221552e4SDag-Erling Smørgrav addrlen = 4; 1630221552e4SDag-Erling Smørgrav af = AF_INET; 1631221552e4SDag-Erling Smørgrav break; 1632221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1633221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1634221552e4SDag-Erling Smørgrav af = -1; 1635221552e4SDag-Erling Smørgrav break; 1636221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1637221552e4SDag-Erling Smørgrav addrlen = 16; 1638221552e4SDag-Erling Smørgrav af = AF_INET6; 1639221552e4SDag-Erling Smørgrav break; 1640221552e4SDag-Erling Smørgrav default: 1641221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1642221552e4SDag-Erling Smørgrav return -1; 1643221552e4SDag-Erling Smørgrav } 1644333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1645333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1646333ee039SDag-Erling Smørgrav need++; 1647333ee039SDag-Erling Smørgrav if (have < need) 1648221552e4SDag-Erling Smørgrav return 0; 164919261079SEd Maste if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) 165019261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 16514f52dfbbSDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 16524f52dfbbSDag-Erling Smørgrav /* host string length */ 165319261079SEd Maste if ((r = sshbuf_consume(input, 1)) != 0) 165419261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 16554f52dfbbSDag-Erling Smørgrav } 16564f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || 16574f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &dest_port, 2)) != 0) { 165819261079SEd Maste debug_r(r, "channel %d: parse addr/port", c->self); 16594f52dfbbSDag-Erling Smørgrav return -1; 16604f52dfbbSDag-Erling Smørgrav } 1661221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1662e4a9863fSDag-Erling Smørgrav free(c->path); 1663cce7d346SDag-Erling Smørgrav c->path = NULL; 1664cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1665cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1666cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1667cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1668221552e4SDag-Erling Smørgrav return -1; 1669cce7d346SDag-Erling Smørgrav } 1670cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1671cce7d346SDag-Erling Smørgrav } else { 1672cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1673cce7d346SDag-Erling Smørgrav return -1; 1674cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1675cce7d346SDag-Erling Smørgrav } 1676221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1677221552e4SDag-Erling Smørgrav 1678221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1679221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1680221552e4SDag-Erling Smørgrav 1681221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1682221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1683221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1684221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1685221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1686221552e4SDag-Erling Smørgrav 16874f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || 16884f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || 16894f52dfbbSDag-Erling Smørgrav (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) 169019261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1691221552e4SDag-Erling Smørgrav return 1; 1692221552e4SDag-Erling Smørgrav } 1693221552e4SDag-Erling Smørgrav 1694b15c8340SDag-Erling Smørgrav Channel * 16954f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh, 1696535af610SEd Maste const char *host_to_connect, int port_to_connect, 169719261079SEd Maste int in, int out, int nonblock) 1698b15c8340SDag-Erling Smørgrav { 1699b15c8340SDag-Erling Smørgrav Channel *c; 1700b15c8340SDag-Erling Smørgrav 170119261079SEd Maste debug_f("%s:%d", host_to_connect, port_to_connect); 1702b15c8340SDag-Erling Smørgrav 17034f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, 1704b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 170519261079SEd Maste 0, "stdio-forward", nonblock); 1706b15c8340SDag-Erling Smørgrav 1707b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1708b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1709b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1710b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1711b15c8340SDag-Erling Smørgrav 17124f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); 1713535af610SEd Maste port_open_helper(ssh, c, port_to_connect == PORT_STREAMLOCAL ? 1714535af610SEd Maste "direct-streamlocal@openssh.com" : "direct-tcpip"); 1715b15c8340SDag-Erling Smørgrav 1716b15c8340SDag-Erling Smørgrav return c; 1717b15c8340SDag-Erling Smørgrav } 1718b15c8340SDag-Erling Smørgrav 1719ca3176e7SBrian Feldman /* dynamic port forwarding */ 1720af12a3e7SDag-Erling Smørgrav static void 17211323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c) 1722ca3176e7SBrian Feldman { 17234f52dfbbSDag-Erling Smørgrav const u_char *p; 1724d4ecd108SDag-Erling Smørgrav u_int have; 1725d4ecd108SDag-Erling Smørgrav int ret; 1726ca3176e7SBrian Feldman 17271323ec57SEd Maste c->io_want = 0; 17284f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->input); 1729ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 17304f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->input, stderr); */ 1731ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1732221552e4SDag-Erling Smørgrav if (have < 3) { 1733ca3176e7SBrian Feldman /* need more */ 17341323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1735ca3176e7SBrian Feldman return; 1736ca3176e7SBrian Feldman } 1737ca3176e7SBrian Feldman /* try to guess the protocol */ 17384f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->input); 17394f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u8? */ 1740ca3176e7SBrian Feldman switch (p[0]) { 1741ca3176e7SBrian Feldman case 0x04: 17424f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->input, c->output); 1743ca3176e7SBrian Feldman break; 1744221552e4SDag-Erling Smørgrav case 0x05: 17454f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->input, c->output); 1746221552e4SDag-Erling Smørgrav break; 1747ca3176e7SBrian Feldman default: 1748ca3176e7SBrian Feldman ret = -1; 1749ca3176e7SBrian Feldman break; 1750ca3176e7SBrian Feldman } 1751ca3176e7SBrian Feldman if (ret < 0) { 17524f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1753ca3176e7SBrian Feldman } else if (ret == 0) { 1754ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1755ca3176e7SBrian Feldman /* need more */ 17561323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 17574f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output)) 17581323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1759ca3176e7SBrian Feldman } else { 1760ca3176e7SBrian Feldman /* switch to the next state */ 1761ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 17624f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 17634f52dfbbSDag-Erling Smørgrav } 17644f52dfbbSDag-Erling Smørgrav } 17654f52dfbbSDag-Erling Smørgrav 17664f52dfbbSDag-Erling Smørgrav /* simulate read-error */ 17674f52dfbbSDag-Erling Smørgrav static void 17684f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c) 17694f52dfbbSDag-Erling Smørgrav { 17704f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1771f374ba41SEd Maste channel_force_close(ssh, c, 0); 17724f52dfbbSDag-Erling Smørgrav } 17734f52dfbbSDag-Erling Smørgrav 17744f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */ 17754f52dfbbSDag-Erling Smørgrav static void 17761323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) 17774f52dfbbSDag-Erling Smørgrav { 17784f52dfbbSDag-Erling Smørgrav const u_char *p; 17794f52dfbbSDag-Erling Smørgrav u_int have, len; 17804f52dfbbSDag-Erling Smørgrav int r, ret; 17814f52dfbbSDag-Erling Smørgrav 17824f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->output); 17834f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: have %d", c->self, have); 17844f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->output, stderr); */ 17854f52dfbbSDag-Erling Smørgrav /* EOF received */ 17864f52dfbbSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 178719261079SEd Maste if ((r = sshbuf_consume(c->output, have)) != 0) 178819261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 17894f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 17904f52dfbbSDag-Erling Smørgrav return; 17914f52dfbbSDag-Erling Smørgrav } 17924f52dfbbSDag-Erling Smørgrav /* check if the fixed size part of the packet is in buffer. */ 17934f52dfbbSDag-Erling Smørgrav if (have < 3) 17944f52dfbbSDag-Erling Smørgrav return; 17954f52dfbbSDag-Erling Smørgrav /* try to guess the protocol */ 17964f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->output); 17974f52dfbbSDag-Erling Smørgrav switch (p[0]) { 17984f52dfbbSDag-Erling Smørgrav case 0x04: 17994f52dfbbSDag-Erling Smørgrav /* switch input/output for reverse forwarding */ 18004f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->output, c->input); 18014f52dfbbSDag-Erling Smørgrav break; 18024f52dfbbSDag-Erling Smørgrav case 0x05: 18034f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->output, c->input); 18044f52dfbbSDag-Erling Smørgrav break; 18054f52dfbbSDag-Erling Smørgrav default: 18064f52dfbbSDag-Erling Smørgrav ret = -1; 18074f52dfbbSDag-Erling Smørgrav break; 18084f52dfbbSDag-Erling Smørgrav } 18094f52dfbbSDag-Erling Smørgrav if (ret < 0) { 18104f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 18114f52dfbbSDag-Erling Smørgrav } else if (ret == 0) { 18124f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: need more", c->self); 18134f52dfbbSDag-Erling Smørgrav /* send socks request to peer */ 18144f52dfbbSDag-Erling Smørgrav len = sshbuf_len(c->input); 18154f52dfbbSDag-Erling Smørgrav if (len > 0 && len < c->remote_window) { 18164f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 18174f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 18184f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_stringb(ssh, c->input)) != 0 || 18194f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 182019261079SEd Maste fatal_fr(r, "channel %i: rdynamic", c->self); 18214f52dfbbSDag-Erling Smørgrav } 182219261079SEd Maste if ((r = sshbuf_consume(c->input, len)) != 0) 182319261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 18244f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 18254f52dfbbSDag-Erling Smørgrav } 18264f52dfbbSDag-Erling Smørgrav } else if (rdynamic_connect_finish(ssh, c) < 0) { 18274f52dfbbSDag-Erling Smørgrav /* the connect failed */ 18284f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1829ca3176e7SBrian Feldman } 1830ca3176e7SBrian Feldman } 1831ca3176e7SBrian Feldman 1832a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1833af12a3e7SDag-Erling Smørgrav static void 18341323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c) 1835511b41d2SMark Murray { 1836af12a3e7SDag-Erling Smørgrav Channel *nc; 1837d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 18384f52dfbbSDag-Erling Smørgrav int r, newsock, oerrno, remote_port; 1839511b41d2SMark Murray socklen_t addrlen; 1840ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1841511b41d2SMark Murray 18421323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 18434f52dfbbSDag-Erling Smørgrav return; 18444f52dfbbSDag-Erling Smørgrav 1845511b41d2SMark Murray debug("X11 connection requested."); 1846511b41d2SMark Murray addrlen = sizeof(addr); 1847d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1848af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1849e4a9863fSDag-Erling Smørgrav oerrno = errno; 1850221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 185119261079SEd Maste channel_close_fd(ssh, c, &c->sock); 18524f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1853e4a9863fSDag-Erling Smørgrav errno = oerrno; 1854af12a3e7SDag-Erling Smørgrav } 185519261079SEd Maste if (newsock == -1) { 1856e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1857e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1858511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1859462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1860e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1861a04a10f8SKris Kennaway return; 1862511b41d2SMark Murray } 1863af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1864ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1865a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1866511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1867ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1868a04a10f8SKris Kennaway 1869f374ba41SEd Maste nc = channel_new(ssh, "x11-connection", 1870a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1871221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 18724f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "x11"); 187347dd1d1bSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 187447dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, remote_port)) != 0) { 187519261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1876511b41d2SMark Murray } 18774f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 187819261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1879e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1880a04a10f8SKris Kennaway } 1881511b41d2SMark Murray 1882af12a3e7SDag-Erling Smørgrav static void 18834f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype) 1884ca3176e7SBrian Feldman { 1885f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1886076ad2f8SDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 1887ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1888d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 18894f52dfbbSDag-Erling Smørgrav int r; 1890ca3176e7SBrian Feldman 1891b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1892b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1893e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1894b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1895b15c8340SDag-Erling Smørgrav remote_port = 65535; 1896b15c8340SDag-Erling Smørgrav } 1897b15c8340SDag-Erling Smørgrav 18984f52dfbbSDag-Erling Smørgrav free(c->remote_name); 18994f52dfbbSDag-Erling Smørgrav xasprintf(&c->remote_name, 1900ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1901f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1902ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1903f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1904ca3176e7SBrian Feldman 19054f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, rtype); 1906a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1907ca3176e7SBrian Feldman /* target host, port */ 19084f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 190919261079SEd Maste (r = sshpkt_put_u32(ssh, c->host_port)) != 0) 191019261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1911a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1912a0ee8cc6SDag-Erling Smørgrav /* target path */ 191319261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 191419261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1915a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1916a0ee8cc6SDag-Erling Smørgrav /* listen path */ 191719261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 191819261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1919ca3176e7SBrian Feldman } else { 1920ca3176e7SBrian Feldman /* listen address, port */ 19214f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 192219261079SEd Maste (r = sshpkt_put_u32(ssh, local_port)) != 0) 192319261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1924ca3176e7SBrian Feldman } 1925a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1926a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 192719261079SEd Maste if ((r = sshpkt_put_cstring(ssh, "")) != 0) 192819261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1929a0ee8cc6SDag-Erling Smørgrav } else { 1930ca3176e7SBrian Feldman /* originator host and port */ 19314f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 193219261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) 193319261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1934ca3176e7SBrian Feldman } 19354f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 193619261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1937e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1938f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1939ca3176e7SBrian Feldman } 1940ca3176e7SBrian Feldman 1941557f75e5SDag-Erling Smørgrav void 19424d3fc8b0SEd Maste channel_set_x11_refuse_time(struct ssh *ssh, time_t refuse_time) 1943557f75e5SDag-Erling Smørgrav { 19444f52dfbbSDag-Erling Smørgrav ssh->chanctxt->x11_refuse_time = refuse_time; 1945557f75e5SDag-Erling Smørgrav } 1946557f75e5SDag-Erling Smørgrav 1947511b41d2SMark Murray /* 1948a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1949511b41d2SMark Murray */ 1950af12a3e7SDag-Erling Smørgrav static void 19511323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c) 1952a04a10f8SKris Kennaway { 1953ca3176e7SBrian Feldman Channel *nc; 1954d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1955af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1956a04a10f8SKris Kennaway socklen_t addrlen; 1957ca3176e7SBrian Feldman char *rtype; 1958a04a10f8SKris Kennaway 19591323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 19604f52dfbbSDag-Erling Smørgrav return; 19614f52dfbbSDag-Erling Smørgrav 19624f52dfbbSDag-Erling Smørgrav debug("Connection to port %d forwarding to %.100s port %d requested.", 1963a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1964ca3176e7SBrian Feldman 1965af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1966af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1967af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1968a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1969a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1970a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1971a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1972a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1973a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1974a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1975af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1976af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1977af12a3e7SDag-Erling Smørgrav } else { 1978af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1979af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1980af12a3e7SDag-Erling Smørgrav } 1981ca3176e7SBrian Feldman 1982511b41d2SMark Murray addrlen = sizeof(addr); 1983d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 198419261079SEd Maste if (newsock == -1) { 1985e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1986e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1987511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1988462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1989e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1990a04a10f8SKris Kennaway return; 1991511b41d2SMark Murray } 1992a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1993af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 19944f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, 1995221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1996ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1997ca3176e7SBrian Feldman nc->host_port = c->host_port; 1998cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1999cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 2000ca3176e7SBrian Feldman 2001b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 20024f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, nc, rtype); 2003a04a10f8SKris Kennaway } 2004511b41d2SMark Murray 2005511b41d2SMark Murray /* 2006a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 2007a04a10f8SKris Kennaway * clients. 2008511b41d2SMark Murray */ 2009af12a3e7SDag-Erling Smørgrav static void 20101323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c) 2011a04a10f8SKris Kennaway { 2012af12a3e7SDag-Erling Smørgrav Channel *nc; 20134f52dfbbSDag-Erling Smørgrav int r, newsock; 2014d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 2015a04a10f8SKris Kennaway socklen_t addrlen; 2016a04a10f8SKris Kennaway 20171323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 20184f52dfbbSDag-Erling Smørgrav return; 20194f52dfbbSDag-Erling Smørgrav 2020511b41d2SMark Murray addrlen = sizeof(addr); 2021d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 202219261079SEd Maste if (newsock == -1) { 20234f52dfbbSDag-Erling Smørgrav error("accept from auth socket: %.100s", strerror(errno)); 2024462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2025e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2026a04a10f8SKris Kennaway return; 2027511b41d2SMark Murray } 2028f374ba41SEd Maste nc = channel_new(ssh, "agent-connection", 2029ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 2030ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 2031221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 20324f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); 20334f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 203419261079SEd Maste fatal_fr(r, "channel %i", c->self); 2035a04a10f8SKris Kennaway } 2036511b41d2SMark Murray 2037af12a3e7SDag-Erling Smørgrav static void 20381323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c) 2039ca3176e7SBrian Feldman { 20404f52dfbbSDag-Erling Smørgrav int err = 0, sock, isopen, r; 2041af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 2042af12a3e7SDag-Erling Smørgrav 20431323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) 20444f52dfbbSDag-Erling Smørgrav return; 20454f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 204619261079SEd Maste fatal_f("channel %d: no remote id", c->self); 20474f52dfbbSDag-Erling Smørgrav /* for rdynamic the OPEN_CONFIRMATION has been sent already */ 20484f52dfbbSDag-Erling Smørgrav isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); 20494d3fc8b0SEd Maste 205019261079SEd Maste if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { 2051af12a3e7SDag-Erling Smørgrav err = errno; 2052af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 2053af12a3e7SDag-Erling Smørgrav } 20544d3fc8b0SEd Maste 2055ca3176e7SBrian Feldman if (err == 0) { 20564d3fc8b0SEd Maste /* Non-blocking connection completed */ 2057d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 2058d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 2059d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 2060af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 2061*a91a2465SEd Maste channel_set_used_time(ssh, c); 20624f52dfbbSDag-Erling Smørgrav if (isopen) { 20634f52dfbbSDag-Erling Smørgrav /* no message necessary */ 2064af12a3e7SDag-Erling Smørgrav } else { 20654f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 20664f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 20674f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 20684f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 20694f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 207019261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 207119261079SEd Maste (r = sshpkt_send(ssh)) != 0) 207219261079SEd Maste fatal_fr(r, "channel %i open confirm", c->self); 2073af12a3e7SDag-Erling Smørgrav } 2074d4af9e69SDag-Erling Smørgrav return; 2075d4af9e69SDag-Erling Smørgrav } 20764d3fc8b0SEd Maste if (err == EINTR || err == EAGAIN || err == EINPROGRESS) 20774d3fc8b0SEd Maste return; 20784d3fc8b0SEd Maste 20794d3fc8b0SEd Maste /* Non-blocking connection failed */ 20804d3fc8b0SEd Maste debug("channel %d: connection failed: %s", c->self, strerror(err)); 20814d3fc8b0SEd Maste 20824d3fc8b0SEd Maste /* Try next address, if any */ 20834d3fc8b0SEd Maste if ((sock = connect_next(&c->connect_ctx)) == -1) { 20844d3fc8b0SEd Maste /* Exhausted all addresses for this destination */ 2085d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 2086d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 2087d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 20884f52dfbbSDag-Erling Smørgrav if (isopen) { 20894f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 2090af12a3e7SDag-Erling Smørgrav } else { 20914f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 20924f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 20934f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 209447dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, 209547dd1d1bSDag-Erling Smørgrav SSH2_OPEN_CONNECT_FAILED)) != 0 || 209647dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || 209719261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 209819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 209919261079SEd Maste fatal_fr(r, "channel %i: failure", c->self); 21004f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2101ca3176e7SBrian Feldman } 2102ca3176e7SBrian Feldman } 21034d3fc8b0SEd Maste 21044d3fc8b0SEd Maste /* New non-blocking connection in progress */ 21054d3fc8b0SEd Maste close(c->sock); 21064d3fc8b0SEd Maste c->sock = c->rfd = c->wfd = sock; 2107ca3176e7SBrian Feldman } 2108ca3176e7SBrian Feldman 2109af12a3e7SDag-Erling Smørgrav static int 21101323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c) 2111a04a10f8SKris Kennaway { 2112aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 21134f52dfbbSDag-Erling Smørgrav ssize_t len; 21144f52dfbbSDag-Erling Smørgrav int r, force; 2115f374ba41SEd Maste size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ; 21161323ec57SEd Maste int pty_zeroread = 0; 21171323ec57SEd Maste 21181323ec57SEd Maste #ifdef PTY_ZEROREAD 21191323ec57SEd Maste /* Bug on AIX: read(1) can return 0 for a non-closed fd */ 21201323ec57SEd Maste pty_zeroread = c->isatty; 21211323ec57SEd Maste #endif 2122511b41d2SMark Murray 2123d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 21244f52dfbbSDag-Erling Smørgrav 21251323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0) 21264f52dfbbSDag-Erling Smørgrav return 1; 21271323ec57SEd Maste if ((avail = sshbuf_avail(c->input)) == 0) 21281323ec57SEd Maste return 1; /* Shouldn't happen */ 21291323ec57SEd Maste 21301323ec57SEd Maste /* 21311323ec57SEd Maste * For "simple" channels (i.e. not datagram or filtered), we can 21321323ec57SEd Maste * read directly to the channel buffer. 21331323ec57SEd Maste */ 21341323ec57SEd Maste if (!pty_zeroread && c->input_filter == NULL && !c->datagram) { 21351323ec57SEd Maste /* Only OPEN channels have valid rwin */ 21361323ec57SEd Maste if (c->type == SSH_CHANNEL_OPEN) { 21371323ec57SEd Maste if ((have = sshbuf_len(c->input)) >= c->remote_window) 21381323ec57SEd Maste return 1; /* shouldn't happen */ 21391323ec57SEd Maste if (maxlen > c->remote_window - have) 21401323ec57SEd Maste maxlen = c->remote_window - have; 21411323ec57SEd Maste } 21421323ec57SEd Maste if (maxlen > avail) 21431323ec57SEd Maste maxlen = avail; 2144f374ba41SEd Maste if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) { 21451323ec57SEd Maste if (errno == EINTR || (!force && 21461323ec57SEd Maste (errno == EAGAIN || errno == EWOULDBLOCK))) 21471323ec57SEd Maste return 1; 21481323ec57SEd Maste debug2("channel %d: read failed rfd %d maxlen %zu: %s", 21491323ec57SEd Maste c->self, c->rfd, maxlen, ssh_err(r)); 21501323ec57SEd Maste goto rfail; 21511323ec57SEd Maste } 2152f374ba41SEd Maste if (nr != 0) 2153*a91a2465SEd Maste channel_set_used_time(ssh, c); 21541323ec57SEd Maste return 1; 21551323ec57SEd Maste } 21564f52dfbbSDag-Erling Smørgrav 2157333ee039SDag-Erling Smørgrav errno = 0; 2158a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 21591323ec57SEd Maste /* fixup AIX zero-length read with errno set to look more like errors */ 21601323ec57SEd Maste if (pty_zeroread && len == 0 && errno != 0) 21611323ec57SEd Maste len = -1; 216219261079SEd Maste if (len == -1 && (errno == EINTR || 2163d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 2164a04a10f8SKris Kennaway return 1; 21651323ec57SEd Maste if (len < 0 || (!pty_zeroread && len == 0)) { 21661323ec57SEd Maste debug2("channel %d: read<=0 rfd %d len %zd: %s", 21671323ec57SEd Maste c->self, c->rfd, len, 21681323ec57SEd Maste len == 0 ? "closed" : strerror(errno)); 21691323ec57SEd Maste rfail: 2170ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2171221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 21724f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2173ca3176e7SBrian Feldman return -1; 2174a04a10f8SKris Kennaway } else { 21754f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2176a04a10f8SKris Kennaway } 2177a04a10f8SKris Kennaway return -1; 2178a04a10f8SKris Kennaway } 2179*a91a2465SEd Maste channel_set_used_time(ssh, c); 2180b66f2d16SKris Kennaway if (c->input_filter != NULL) { 21814f52dfbbSDag-Erling Smørgrav if (c->input_filter(ssh, c, buf, len) == -1) { 2182221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 21834f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2184b66f2d16SKris Kennaway } 2185b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 21864f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->input, buf, len)) != 0) 218719261079SEd Maste fatal_fr(r, "channel %i: put datagram", c->self); 218819261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 218919261079SEd Maste fatal_fr(r, "channel %i: put data", c->self); 21901323ec57SEd Maste 2191a04a10f8SKris Kennaway return 1; 2192a04a10f8SKris Kennaway } 2193333ee039SDag-Erling Smørgrav 2194af12a3e7SDag-Erling Smørgrav static int 21951323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c) 2196a04a10f8SKris Kennaway { 2197ca3176e7SBrian Feldman struct termios tio; 21984f52dfbbSDag-Erling Smørgrav u_char *data = NULL, *buf; /* XXX const; need filter API change */ 21994f52dfbbSDag-Erling Smørgrav size_t dlen, olen = 0; 22004f52dfbbSDag-Erling Smørgrav int r, len; 22014f52dfbbSDag-Erling Smørgrav 22021323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 22031323ec57SEd Maste return 1; 22041323ec57SEd Maste if (sshbuf_len(c->output) == 0) 22054f52dfbbSDag-Erling Smørgrav return 1; 2206a04a10f8SKris Kennaway 2207a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 22084f52dfbbSDag-Erling Smørgrav olen = sshbuf_len(c->output); 2209b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 22104f52dfbbSDag-Erling Smørgrav if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { 2211b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 2212b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 22134f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2214b74df5b2SDag-Erling Smørgrav else 22154f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2216b74df5b2SDag-Erling Smørgrav return -1; 2217b74df5b2SDag-Erling Smørgrav } 2218b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 22194f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) 222019261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 22214f52dfbbSDag-Erling Smørgrav buf = data; 2222b74df5b2SDag-Erling Smørgrav } else { 22234f52dfbbSDag-Erling Smørgrav buf = data = sshbuf_mutable_ptr(c->output); 22244f52dfbbSDag-Erling Smørgrav dlen = sshbuf_len(c->output); 2225b74df5b2SDag-Erling Smørgrav } 2226b74df5b2SDag-Erling Smørgrav 2227b74df5b2SDag-Erling Smørgrav if (c->datagram) { 2228b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 2229b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 2230e4a9863fSDag-Erling Smørgrav free(data); 223119261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2232d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2233b74df5b2SDag-Erling Smørgrav return 1; 22344f52dfbbSDag-Erling Smørgrav if (len <= 0) 22354f52dfbbSDag-Erling Smørgrav goto write_fail; 2236e2f6069cSDag-Erling Smørgrav goto out; 2237b74df5b2SDag-Erling Smørgrav } 22384f52dfbbSDag-Erling Smørgrav 2239f388f5efSDag-Erling Smørgrav #ifdef _AIX 2240f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 22414f52dfbbSDag-Erling Smørgrav if (c->wfd_isatty) 22421323ec57SEd Maste dlen = MINIMUM(dlen, 8*1024); 2243f388f5efSDag-Erling Smørgrav #endif 2244b74df5b2SDag-Erling Smørgrav 2245b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 224619261079SEd Maste if (len == -1 && 2247d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 2248a04a10f8SKris Kennaway return 1; 2249511b41d2SMark Murray if (len <= 0) { 22504f52dfbbSDag-Erling Smørgrav write_fail: 2251ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2252221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 22534f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2254ca3176e7SBrian Feldman return -1; 2255511b41d2SMark Murray } else { 22564f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2257511b41d2SMark Murray } 2258a04a10f8SKris Kennaway return -1; 2259511b41d2SMark Murray } 2260*a91a2465SEd Maste channel_set_used_time(ssh, c); 22617aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 22624f52dfbbSDag-Erling Smørgrav if (c->isatty && dlen >= 1 && buf[0] != '\r') { 2263e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 2264e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 2265e0fbb1d2SBrian Feldman /* 2266e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 2267ca3176e7SBrian Feldman * traffic analysis. We need to match the 2268ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 2269b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 2270e0fbb1d2SBrian Feldman */ 22714f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || 22724f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 227319261079SEd Maste fatal_fr(r, "channel %i: ignore", c->self); 2274e0fbb1d2SBrian Feldman } 2275e0fbb1d2SBrian Feldman } 22764f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */ 227719261079SEd Maste if ((r = sshbuf_consume(c->output, len)) != 0) 227819261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2279e2f6069cSDag-Erling Smørgrav out: 22804f52dfbbSDag-Erling Smørgrav c->local_consumed += olen - sshbuf_len(c->output); 22814f52dfbbSDag-Erling Smørgrav 2282a04a10f8SKris Kennaway return 1; 2283511b41d2SMark Murray } 2284333ee039SDag-Erling Smørgrav 2285af12a3e7SDag-Erling Smørgrav static int 22861323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c) 2287a04a10f8SKris Kennaway { 22884f52dfbbSDag-Erling Smørgrav int r; 22894f52dfbbSDag-Erling Smørgrav ssize_t len; 2290511b41d2SMark Murray 22911323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) 22921323ec57SEd Maste return 1; 22931323ec57SEd Maste if (sshbuf_len(c->extended) == 0) 22944f52dfbbSDag-Erling Smørgrav return 1; 22954f52dfbbSDag-Erling Smørgrav 22964f52dfbbSDag-Erling Smørgrav len = write(c->efd, sshbuf_ptr(c->extended), 22974f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended)); 22984f52dfbbSDag-Erling Smørgrav debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); 229919261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2300d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2301ca3176e7SBrian Feldman return 1; 2302ca3176e7SBrian Feldman if (len <= 0) { 23034f52dfbbSDag-Erling Smørgrav debug2("channel %d: closing write-efd %d", c->self, c->efd); 230419261079SEd Maste channel_close_fd(ssh, c, &c->efd); 2305ca3176e7SBrian Feldman } else { 230619261079SEd Maste if ((r = sshbuf_consume(c->extended, len)) != 0) 230719261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2308a04a10f8SKris Kennaway c->local_consumed += len; 2309*a91a2465SEd Maste channel_set_used_time(ssh, c); 2310a04a10f8SKris Kennaway } 23114f52dfbbSDag-Erling Smørgrav return 1; 23124f52dfbbSDag-Erling Smørgrav } 23134f52dfbbSDag-Erling Smørgrav 23144f52dfbbSDag-Erling Smørgrav static int 23151323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c) 23164f52dfbbSDag-Erling Smørgrav { 23174f52dfbbSDag-Erling Smørgrav char buf[CHAN_RBUF]; 23184f52dfbbSDag-Erling Smørgrav ssize_t len; 231919261079SEd Maste int r, force; 23204f52dfbbSDag-Erling Smørgrav 232119261079SEd Maste force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 232219261079SEd Maste 23231323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0) 23244f52dfbbSDag-Erling Smørgrav return 1; 23254f52dfbbSDag-Erling Smørgrav 2326a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 23274f52dfbbSDag-Erling Smørgrav debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); 232819261079SEd Maste if (len == -1 && (errno == EINTR || ((errno == EAGAIN || 232919261079SEd Maste errno == EWOULDBLOCK) && !force))) 2330ca3176e7SBrian Feldman return 1; 2331ca3176e7SBrian Feldman if (len <= 0) { 233219261079SEd Maste debug2("channel %d: closing read-efd %d", c->self, c->efd); 233319261079SEd Maste channel_close_fd(ssh, c, &c->efd); 2334f374ba41SEd Maste return 1; 2335f374ba41SEd Maste } 2336*a91a2465SEd Maste channel_set_used_time(ssh, c); 2337f374ba41SEd Maste if (c->extended_usage == CHAN_EXTENDED_IGNORE) 233819261079SEd Maste debug3("channel %d: discard efd", c->self); 233919261079SEd Maste else if ((r = sshbuf_put(c->extended, buf, len)) != 0) 234019261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 2341a04a10f8SKris Kennaway return 1; 2342a04a10f8SKris Kennaway } 2343333ee039SDag-Erling Smørgrav 234421e764dfSDag-Erling Smørgrav static int 23451323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c) 2346a04a10f8SKris Kennaway { 23474f52dfbbSDag-Erling Smørgrav if (c->efd == -1) 23484f52dfbbSDag-Erling Smørgrav return 1; 23494f52dfbbSDag-Erling Smørgrav 23504f52dfbbSDag-Erling Smørgrav /** XXX handle drain efd, too */ 23514f52dfbbSDag-Erling Smørgrav 23524f52dfbbSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_WRITE) 23531323ec57SEd Maste return channel_handle_efd_write(ssh, c); 23544f52dfbbSDag-Erling Smørgrav else if (c->extended_usage == CHAN_EXTENDED_READ || 23554f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) 23561323ec57SEd Maste return channel_handle_efd_read(ssh, c); 23574f52dfbbSDag-Erling Smørgrav 23584f52dfbbSDag-Erling Smørgrav return 1; 23594f52dfbbSDag-Erling Smørgrav } 23604f52dfbbSDag-Erling Smørgrav 23614f52dfbbSDag-Erling Smørgrav static int 23624f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c) 23634f52dfbbSDag-Erling Smørgrav { 23644f52dfbbSDag-Erling Smørgrav int r; 23654f52dfbbSDag-Erling Smørgrav 2366ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 2367ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 2368d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 2369d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 2370d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 2371a04a10f8SKris Kennaway c->local_consumed > 0) { 23724f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 237319261079SEd Maste fatal_f("channel %d: no remote id", c->self); 23744f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 23754f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 23764f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 23774f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || 23784f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 237919261079SEd Maste fatal_fr(r, "channel %i", c->self); 23804f52dfbbSDag-Erling Smørgrav } 238119261079SEd Maste debug2("channel %d: window %d sent adjust %d", c->self, 238219261079SEd Maste c->local_window, c->local_consumed); 238360c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 2384a04a10f8SKris Kennaway c->local_consumed = 0; 2385a04a10f8SKris Kennaway } 2386a04a10f8SKris Kennaway return 1; 2387a04a10f8SKris Kennaway } 2388a04a10f8SKris Kennaway 2389af12a3e7SDag-Erling Smørgrav static void 23901323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c) 2391a04a10f8SKris Kennaway { 23921323ec57SEd Maste channel_handle_rfd(ssh, c); 23931323ec57SEd Maste channel_handle_wfd(ssh, c); 23941323ec57SEd Maste channel_handle_efd(ssh, c); 23954f52dfbbSDag-Erling Smørgrav channel_check_window(ssh, c); 2396a04a10f8SKris Kennaway } 2397a04a10f8SKris Kennaway 2398b15c8340SDag-Erling Smørgrav static u_int 23994f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need) 2400b15c8340SDag-Erling Smørgrav { 2401b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 24024f52dfbbSDag-Erling Smørgrav ssize_t len; 2403b15c8340SDag-Erling Smørgrav u_int rlen; 24044f52dfbbSDag-Erling Smørgrav int r; 2405b15c8340SDag-Erling Smørgrav 24064f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) < need) { 24074f52dfbbSDag-Erling Smørgrav rlen = need - sshbuf_len(c->input); 2408ca86bcf2SDag-Erling Smørgrav len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); 240919261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 24104f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2411b15c8340SDag-Erling Smørgrav if (len <= 0) { 24124f52dfbbSDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %zd", 2413b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 24144f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2415b15c8340SDag-Erling Smørgrav return 0; 241619261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 241719261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 24184f52dfbbSDag-Erling Smørgrav } 24194f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2420b15c8340SDag-Erling Smørgrav } 2421b15c8340SDag-Erling Smørgrav 2422b15c8340SDag-Erling Smørgrav static void 24231323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c) 2424b15c8340SDag-Erling Smørgrav { 2425b15c8340SDag-Erling Smørgrav u_int need; 2426b15c8340SDag-Erling Smørgrav 24271323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) 24284f52dfbbSDag-Erling Smørgrav return; 24294f52dfbbSDag-Erling Smørgrav if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) 24304f52dfbbSDag-Erling Smørgrav return; 24314f52dfbbSDag-Erling Smørgrav if (c->mux_pause) 24324f52dfbbSDag-Erling Smørgrav return; 2433b15c8340SDag-Erling Smørgrav 2434b15c8340SDag-Erling Smørgrav /* 2435b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 2436b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 2437b15c8340SDag-Erling Smørgrav */ 24384f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, 4) < 4) /* read header */ 2439b15c8340SDag-Erling Smørgrav return; 24404f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u32 */ 24414f52dfbbSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(c->input)); 2442b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 2443b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 2444b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 2445b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 24464f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 2447b15c8340SDag-Erling Smørgrav return; 2448b15c8340SDag-Erling Smørgrav } 24494f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ 2450b15c8340SDag-Erling Smørgrav return; 24514f52dfbbSDag-Erling Smørgrav if (c->mux_rcb(ssh, c) != 0) { 2452b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 24534f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2454b15c8340SDag-Erling Smørgrav return; 2455b15c8340SDag-Erling Smørgrav } 2456b15c8340SDag-Erling Smørgrav } 2457b15c8340SDag-Erling Smørgrav 2458b15c8340SDag-Erling Smørgrav static void 24591323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c) 24604f52dfbbSDag-Erling Smørgrav { 24614f52dfbbSDag-Erling Smørgrav ssize_t len; 24624f52dfbbSDag-Erling Smørgrav int r; 24634f52dfbbSDag-Erling Smørgrav 24641323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 24651323ec57SEd Maste return; 24661323ec57SEd Maste if (sshbuf_len(c->output) == 0) 24674f52dfbbSDag-Erling Smørgrav return; 24684f52dfbbSDag-Erling Smørgrav 24694f52dfbbSDag-Erling Smørgrav len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); 247019261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 24714f52dfbbSDag-Erling Smørgrav return; 24724f52dfbbSDag-Erling Smørgrav if (len <= 0) { 24734f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 24744f52dfbbSDag-Erling Smørgrav return; 24754f52dfbbSDag-Erling Smørgrav } 24764f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, len)) != 0) 247719261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 24784f52dfbbSDag-Erling Smørgrav } 24794f52dfbbSDag-Erling Smørgrav 24804f52dfbbSDag-Erling Smørgrav static void 24811323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c) 24824f52dfbbSDag-Erling Smørgrav { 24831323ec57SEd Maste channel_post_mux_client_read(ssh, c); 24841323ec57SEd Maste channel_post_mux_client_write(ssh, c); 24854f52dfbbSDag-Erling Smørgrav } 24864f52dfbbSDag-Erling Smørgrav 24874f52dfbbSDag-Erling Smørgrav static void 24881323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c) 2489b15c8340SDag-Erling Smørgrav { 2490b15c8340SDag-Erling Smørgrav Channel *nc; 2491b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 2492b15c8340SDag-Erling Smørgrav socklen_t addrlen; 2493b15c8340SDag-Erling Smørgrav int newsock; 2494b15c8340SDag-Erling Smørgrav uid_t euid; 2495b15c8340SDag-Erling Smørgrav gid_t egid; 2496b15c8340SDag-Erling Smørgrav 24971323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 2498b15c8340SDag-Erling Smørgrav return; 2499b15c8340SDag-Erling Smørgrav 2500b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 2501b15c8340SDag-Erling Smørgrav 2502b15c8340SDag-Erling Smørgrav /* 2503b15c8340SDag-Erling Smørgrav * Accept connection on control socket 2504b15c8340SDag-Erling Smørgrav */ 2505b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 2506b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 2507b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 2508b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 250919261079SEd Maste error_f("accept: %s", strerror(errno)); 2510462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2511e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2512b15c8340SDag-Erling Smørgrav return; 2513b15c8340SDag-Erling Smørgrav } 2514b15c8340SDag-Erling Smørgrav 251519261079SEd Maste if (getpeereid(newsock, &euid, &egid) == -1) { 251619261079SEd Maste error_f("getpeereid failed: %s", strerror(errno)); 2517b15c8340SDag-Erling Smørgrav close(newsock); 2518b15c8340SDag-Erling Smørgrav return; 2519b15c8340SDag-Erling Smørgrav } 2520b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 2521b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 2522b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 2523b15c8340SDag-Erling Smørgrav close(newsock); 2524b15c8340SDag-Erling Smørgrav return; 2525b15c8340SDag-Erling Smørgrav } 2526f374ba41SEd Maste nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT, 2527b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 2528b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 2529b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 253019261079SEd Maste debug3_f("new mux channel %d fd %d", nc->self, nc->sock); 2531b15c8340SDag-Erling Smørgrav /* establish state */ 25324f52dfbbSDag-Erling Smørgrav nc->mux_rcb(ssh, nc); 2533b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 2534b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 2535b15c8340SDag-Erling Smørgrav } 2536b15c8340SDag-Erling Smørgrav 2537af12a3e7SDag-Erling Smørgrav static void 25384f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc) 2539a04a10f8SKris Kennaway { 25404f52dfbbSDag-Erling Smørgrav chan_fn **pre, **post; 2541f388f5efSDag-Erling Smørgrav 25424f52dfbbSDag-Erling Smørgrav if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || 25434f52dfbbSDag-Erling Smørgrav (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) 254419261079SEd Maste fatal_f("allocation failed"); 2545511b41d2SMark Murray 25464f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 25474f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 25484f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 25494f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 25504f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 25514f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 25524f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 25534f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 25544f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 25554f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 25564f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; 25574f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 25584f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2559a04a10f8SKris Kennaway 25604f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_OPEN] = &channel_post_open; 25614f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 25624f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 25634f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 25644f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 25654f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 25664f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 25674f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 25684f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 25694f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; 25704f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 25714f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2572a04a10f8SKris Kennaway 25734f52dfbbSDag-Erling Smørgrav sc->channel_pre = pre; 25744f52dfbbSDag-Erling Smørgrav sc->channel_post = post; 2575a04a10f8SKris Kennaway } 2576a04a10f8SKris Kennaway 2577af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2578af12a3e7SDag-Erling Smørgrav static void 25794f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c) 2580af12a3e7SDag-Erling Smørgrav { 2581af12a3e7SDag-Erling Smørgrav if (c == NULL) 2582af12a3e7SDag-Erling Smørgrav return; 2583af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 25844f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, c->detach_close)) 2585af12a3e7SDag-Erling Smørgrav return; 25862f513db7SEd Maste 2587221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 2588f374ba41SEd Maste c->detach_user(ssh, c->self, 0, NULL); 2589af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2590af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2591af12a3e7SDag-Erling Smørgrav return; 2592221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2593af12a3e7SDag-Erling Smørgrav } 25944f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, 1)) 2595af12a3e7SDag-Erling Smørgrav return; 2596221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 25974f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2598af12a3e7SDag-Erling Smørgrav } 2599af12a3e7SDag-Erling Smørgrav 26004f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST }; 26014f52dfbbSDag-Erling Smørgrav 2602af12a3e7SDag-Erling Smørgrav static void 2603f374ba41SEd Maste channel_handler(struct ssh *ssh, int table, struct timespec *timeout) 2604a04a10f8SKris Kennaway { 26054f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 26064f52dfbbSDag-Erling Smørgrav chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; 2607b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2608a04a10f8SKris Kennaway Channel *c; 2609462c32cbSDag-Erling Smørgrav time_t now; 2610a04a10f8SKris Kennaway 2611e4a9863fSDag-Erling Smørgrav now = monotime(); 26124f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 26134f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2614af12a3e7SDag-Erling Smørgrav if (c == NULL) 2615511b41d2SMark Murray continue; 261638a52bd3SEd Maste /* Try to keep IO going while rekeying */ 261738a52bd3SEd Maste if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN) 261838a52bd3SEd Maste continue; 2619b15c8340SDag-Erling Smørgrav if (c->delayed) { 26204f52dfbbSDag-Erling Smørgrav if (table == CHAN_PRE) 2621b15c8340SDag-Erling Smørgrav c->delayed = 0; 2622b15c8340SDag-Erling Smørgrav else 2623b15c8340SDag-Erling Smørgrav continue; 2624b15c8340SDag-Erling Smørgrav } 2625462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2626*a91a2465SEd Maste if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN && 2627*a91a2465SEd Maste channel_get_expiry(ssh, c) != 0 && 2628*a91a2465SEd Maste now >= channel_get_expiry(ssh, c)) { 2629f374ba41SEd Maste /* channel closed for inactivity */ 2630f374ba41SEd Maste verbose("channel %d: closing after %u seconds " 2631f374ba41SEd Maste "of inactivity", c->self, 2632f374ba41SEd Maste c->inactive_deadline); 2633f374ba41SEd Maste channel_force_close(ssh, c, 1); 2634f374ba41SEd Maste } else if (c->notbefore <= now) { 2635f374ba41SEd Maste /* Run handlers that are not paused. */ 26361323ec57SEd Maste (*ftab[c->type])(ssh, c); 2637f374ba41SEd Maste /* inactivity timeouts must interrupt poll() */ 2638f374ba41SEd Maste if (timeout != NULL && 2639f374ba41SEd Maste c->type == SSH_CHANNEL_OPEN && 2640*a91a2465SEd Maste channel_get_expiry(ssh, c) != 0) { 2641f374ba41SEd Maste ptimeout_deadline_monotime(timeout, 2642*a91a2465SEd Maste channel_get_expiry(ssh, c)); 2643f374ba41SEd Maste } 2644f374ba41SEd Maste } else if (timeout != NULL) { 2645462c32cbSDag-Erling Smørgrav /* 2646f374ba41SEd Maste * Arrange for poll() wakeup when channel pause 2647f374ba41SEd Maste * timer expires. 2648462c32cbSDag-Erling Smørgrav */ 2649f374ba41SEd Maste ptimeout_deadline_monotime(timeout, 2650f374ba41SEd Maste c->notbefore); 2651462c32cbSDag-Erling Smørgrav } 2652462c32cbSDag-Erling Smørgrav } 26534f52dfbbSDag-Erling Smørgrav channel_garbage_collect(ssh, c); 2654511b41d2SMark Murray } 2655511b41d2SMark Murray } 2656a04a10f8SKris Kennaway 2657af12a3e7SDag-Erling Smørgrav /* 26581323ec57SEd Maste * Create sockets before preparing IO. 26594f52dfbbSDag-Erling Smørgrav * This is necessary for things that need to happen after reading 26601323ec57SEd Maste * the network-input but need to be completed before IO event setup, e.g. 26611323ec57SEd Maste * because they may create new channels. 26624f52dfbbSDag-Erling Smørgrav */ 26634f52dfbbSDag-Erling Smørgrav static void 26641323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh) 26654f52dfbbSDag-Erling Smørgrav { 26664f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 26674f52dfbbSDag-Erling Smørgrav Channel *c; 26684f52dfbbSDag-Erling Smørgrav u_int i, oalloc; 26694f52dfbbSDag-Erling Smørgrav 26704f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 26714f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 26724f52dfbbSDag-Erling Smørgrav if (c == NULL) 26734f52dfbbSDag-Erling Smørgrav continue; 26744f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) 26751323ec57SEd Maste channel_before_prepare_io_rdynamic(ssh, c); 26764f52dfbbSDag-Erling Smørgrav } 26774f52dfbbSDag-Erling Smørgrav } 26784f52dfbbSDag-Erling Smørgrav 26791323ec57SEd Maste static void 26801323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c, 26811323ec57SEd Maste u_int pollfd_offset, struct pollfd *pfd) 2682a04a10f8SKris Kennaway { 26831323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 268487c1498dSEd Maste debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] " 268587c1498dSEd Maste "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d " 268687c1498dSEd Maste "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what, 268787c1498dSEd Maste c->rfd, c->wfd, c->efd, c->sock, 268887c1498dSEd Maste c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3], 268987c1498dSEd Maste c->io_want, c->io_ready, 269087c1498dSEd Maste pollfd_offset, pfd->fd, pfd->events, pfd->revents); 26911323ec57SEd Maste #endif 2692ca3176e7SBrian Feldman } 2693ca3176e7SBrian Feldman 26941323ec57SEd Maste /* Prepare pollfd entries for a single channel */ 26951323ec57SEd Maste static void 26961323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd, 26971323ec57SEd Maste struct pollfd *pfd, u_int npfd) 26981323ec57SEd Maste { 269987c1498dSEd Maste u_int ev, p = *next_pollfd; 27001323ec57SEd Maste 27011323ec57SEd Maste if (c == NULL) 27021323ec57SEd Maste return; 27031323ec57SEd Maste if (p + 4 > npfd) { 27041323ec57SEd Maste /* Shouldn't happen */ 27051323ec57SEd Maste fatal_f("channel %d: bad pfd offset %u (max %u)", 27061323ec57SEd Maste c->self, p, npfd); 27071323ec57SEd Maste } 270887c1498dSEd Maste c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1; 27091323ec57SEd Maste /* 27101323ec57SEd Maste * prepare c->rfd 27111323ec57SEd Maste * 27121323ec57SEd Maste * This is a special case, since c->rfd might be the same as 27131323ec57SEd Maste * c->wfd, c->efd and/or c->sock. Handle those here if they want 27141323ec57SEd Maste * IO too. 27151323ec57SEd Maste */ 27161323ec57SEd Maste if (c->rfd != -1) { 271787c1498dSEd Maste ev = 0; 27181323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_RFD) != 0) 271987c1498dSEd Maste ev |= POLLIN; 27201323ec57SEd Maste /* rfd == wfd */ 272187c1498dSEd Maste if (c->wfd == c->rfd) { 272287c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD) != 0) 272387c1498dSEd Maste ev |= POLLOUT; 272487c1498dSEd Maste } 27251323ec57SEd Maste /* rfd == efd */ 272687c1498dSEd Maste if (c->efd == c->rfd) { 272787c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 272887c1498dSEd Maste ev |= POLLIN; 272987c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 273087c1498dSEd Maste ev |= POLLOUT; 273187c1498dSEd Maste } 27321323ec57SEd Maste /* rfd == sock */ 273387c1498dSEd Maste if (c->sock == c->rfd) { 273487c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 273587c1498dSEd Maste ev |= POLLIN; 273687c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 273787c1498dSEd Maste ev |= POLLOUT; 273887c1498dSEd Maste } 273987c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 274087c1498dSEd Maste if (ev != 0) { 274187c1498dSEd Maste c->pfds[0] = p; 274287c1498dSEd Maste pfd[p].fd = c->rfd; 274387c1498dSEd Maste pfd[p].events = ev; 27441323ec57SEd Maste dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); 27451323ec57SEd Maste p++; 27461323ec57SEd Maste } 274787c1498dSEd Maste } 274887c1498dSEd Maste /* prepare c->wfd if wanting IO and not already handled above */ 27491323ec57SEd Maste if (c->wfd != -1 && c->rfd != c->wfd) { 275087c1498dSEd Maste ev = 0; 275187c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD)) 275287c1498dSEd Maste ev |= POLLOUT; 275387c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 275487c1498dSEd Maste if (ev != 0) { 275587c1498dSEd Maste c->pfds[1] = p; 27561323ec57SEd Maste pfd[p].fd = c->wfd; 275787c1498dSEd Maste pfd[p].events = ev; 27581323ec57SEd Maste dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); 27591323ec57SEd Maste p++; 27601323ec57SEd Maste } 276187c1498dSEd Maste } 276287c1498dSEd Maste /* prepare c->efd if wanting IO and not already handled above */ 27631323ec57SEd Maste if (c->efd != -1 && c->rfd != c->efd) { 276487c1498dSEd Maste ev = 0; 27651323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 276687c1498dSEd Maste ev |= POLLIN; 27671323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 276887c1498dSEd Maste ev |= POLLOUT; 276987c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 277087c1498dSEd Maste if (ev != 0) { 277187c1498dSEd Maste c->pfds[2] = p; 277287c1498dSEd Maste pfd[p].fd = c->efd; 277387c1498dSEd Maste pfd[p].events = ev; 27741323ec57SEd Maste dump_channel_poll(__func__, "efd", c, p, &pfd[p]); 27751323ec57SEd Maste p++; 27761323ec57SEd Maste } 277787c1498dSEd Maste } 277887c1498dSEd Maste /* prepare c->sock if wanting IO and not already handled above */ 27791323ec57SEd Maste if (c->sock != -1 && c->rfd != c->sock) { 278087c1498dSEd Maste ev = 0; 278187c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 278287c1498dSEd Maste ev |= POLLIN; 278387c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 278487c1498dSEd Maste ev |= POLLOUT; 278587c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 278687c1498dSEd Maste if (ev != 0) { 278787c1498dSEd Maste c->pfds[3] = p; 27881323ec57SEd Maste pfd[p].fd = c->sock; 27891323ec57SEd Maste pfd[p].events = 0; 27901323ec57SEd Maste dump_channel_poll(__func__, "sock", c, p, &pfd[p]); 27911323ec57SEd Maste p++; 27921323ec57SEd Maste } 279387c1498dSEd Maste } 27941323ec57SEd Maste *next_pollfd = p; 27951323ec57SEd Maste } 27961323ec57SEd Maste 27971323ec57SEd Maste /* * Allocate/prepare poll structure */ 27981323ec57SEd Maste void 27991323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, 2800f374ba41SEd Maste u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout) 28011323ec57SEd Maste { 28021323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 28031323ec57SEd Maste u_int i, oalloc, p, npfd = npfd_reserved; 28041323ec57SEd Maste 28051323ec57SEd Maste channel_before_prepare_io(ssh); /* might create a new channel */ 28069fce8d41SEd Maste /* clear out I/O flags from last poll */ 28079fce8d41SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 28089fce8d41SEd Maste if (sc->channels[i] == NULL) 28099fce8d41SEd Maste continue; 28109fce8d41SEd Maste sc->channels[i]->io_want = sc->channels[i]->io_ready = 0; 28119fce8d41SEd Maste } 28121323ec57SEd Maste /* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ 28131323ec57SEd Maste if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) 28141323ec57SEd Maste fatal_f("too many channels"); /* shouldn't happen */ 28151323ec57SEd Maste npfd += sc->channels_alloc * 4; 28161323ec57SEd Maste if (npfd > *npfd_allocp) { 28171323ec57SEd Maste *pfdp = xrecallocarray(*pfdp, *npfd_allocp, 28181323ec57SEd Maste npfd, sizeof(**pfdp)); 28191323ec57SEd Maste *npfd_allocp = npfd; 28201323ec57SEd Maste } 28211323ec57SEd Maste *npfd_activep = npfd_reserved; 28221323ec57SEd Maste oalloc = sc->channels_alloc; 28231323ec57SEd Maste 2824f374ba41SEd Maste channel_handler(ssh, CHAN_PRE, timeout); 28251323ec57SEd Maste 28261323ec57SEd Maste if (oalloc != sc->channels_alloc) { 28271323ec57SEd Maste /* shouldn't happen */ 28281323ec57SEd Maste fatal_f("channels_alloc changed during CHAN_PRE " 28291323ec57SEd Maste "(was %u, now %u)", oalloc, sc->channels_alloc); 28301323ec57SEd Maste } 28311323ec57SEd Maste 28321323ec57SEd Maste /* Prepare pollfd */ 28331323ec57SEd Maste p = npfd_reserved; 28341323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) 28351323ec57SEd Maste channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); 28361323ec57SEd Maste *npfd_activep = p; 28371323ec57SEd Maste } 28381323ec57SEd Maste 28391323ec57SEd Maste static void 284087c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd, 28411323ec57SEd Maste const char *what, u_int revents_mask, u_int ready) 28421323ec57SEd Maste { 28431323ec57SEd Maste struct pollfd *pfd = &pfds[p]; 28441323ec57SEd Maste 28451323ec57SEd Maste if (fd == -1) 28461323ec57SEd Maste return; 284787c1498dSEd Maste if (p == -1 || (u_int)p >= npfd) 284887c1498dSEd Maste fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd); 28491323ec57SEd Maste dump_channel_poll(__func__, what, c, p, pfd); 28501323ec57SEd Maste if (pfd->fd != fd) { 28511323ec57SEd Maste fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " 28521323ec57SEd Maste "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, 28531323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock); 28541323ec57SEd Maste } 28551323ec57SEd Maste if ((pfd->revents & POLLNVAL) != 0) { 28561323ec57SEd Maste fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", 28571323ec57SEd Maste c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); 28581323ec57SEd Maste } 28591323ec57SEd Maste if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) 28601323ec57SEd Maste c->io_ready |= ready & c->io_want; 2861a04a10f8SKris Kennaway } 2862a04a10f8SKris Kennaway 2863af12a3e7SDag-Erling Smørgrav /* 28641323ec57SEd Maste * After poll, perform any appropriate operations for channels which have 2865af12a3e7SDag-Erling Smørgrav * events pending. 2866af12a3e7SDag-Erling Smørgrav */ 2867a04a10f8SKris Kennaway void 28681323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) 2869a04a10f8SKris Kennaway { 28701323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 287187c1498dSEd Maste u_int i; 287287c1498dSEd Maste int p; 28731323ec57SEd Maste Channel *c; 28741323ec57SEd Maste 28751323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 287687c1498dSEd Maste for (p = 0; p < (int)npfd; p++) { 28771323ec57SEd Maste if (pfd[p].revents == 0) 28781323ec57SEd Maste continue; 28791323ec57SEd Maste debug_f("pfd[%u].fd %d rev 0x%04x", 28801323ec57SEd Maste p, pfd[p].fd, pfd[p].revents); 28811323ec57SEd Maste } 28821323ec57SEd Maste #endif 28831323ec57SEd Maste 28841323ec57SEd Maste /* Convert pollfd into c->io_ready */ 28851323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 28861323ec57SEd Maste c = sc->channels[i]; 288787c1498dSEd Maste if (c == NULL) 28881323ec57SEd Maste continue; 28891323ec57SEd Maste /* if rfd is shared with efd/sock then wfd should be too */ 28901323ec57SEd Maste if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && 28911323ec57SEd Maste (c->rfd == c->efd || c->rfd == c->sock)) { 28921323ec57SEd Maste /* Shouldn't happen */ 28931323ec57SEd Maste fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", 28941323ec57SEd Maste c->self, c->rfd, c->wfd, c->efd, c->sock); 28951323ec57SEd Maste } 28961323ec57SEd Maste c->io_ready = 0; 28971323ec57SEd Maste /* rfd, potentially shared with wfd, efd and sock */ 289887c1498dSEd Maste if (c->rfd != -1 && (p = c->pfds[0]) != -1) { 289987c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->rfd, 290087c1498dSEd Maste "rfd", POLLIN, SSH_CHAN_IO_RFD); 29011323ec57SEd Maste if (c->rfd == c->wfd) { 290287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 290387c1498dSEd Maste "wfd/r", POLLOUT, SSH_CHAN_IO_WFD); 29041323ec57SEd Maste } 29051323ec57SEd Maste if (c->rfd == c->efd) { 290687c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 290787c1498dSEd Maste "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R); 290887c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 290987c1498dSEd Maste "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W); 29101323ec57SEd Maste } 29111323ec57SEd Maste if (c->rfd == c->sock) { 291287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 291387c1498dSEd Maste "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R); 291487c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 291587c1498dSEd Maste "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W); 29161323ec57SEd Maste } 291787c1498dSEd Maste dump_channel_poll(__func__, "rfd", c, p, pfd); 29181323ec57SEd Maste } 29191323ec57SEd Maste /* wfd */ 292087c1498dSEd Maste if (c->wfd != -1 && c->wfd != c->rfd && 292187c1498dSEd Maste (p = c->pfds[1]) != -1) { 292287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 292387c1498dSEd Maste "wfd", POLLOUT, SSH_CHAN_IO_WFD); 292487c1498dSEd Maste dump_channel_poll(__func__, "wfd", c, p, pfd); 29251323ec57SEd Maste } 29261323ec57SEd Maste /* efd */ 292787c1498dSEd Maste if (c->efd != -1 && c->efd != c->rfd && 292887c1498dSEd Maste (p = c->pfds[2]) != -1) { 292987c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 293087c1498dSEd Maste "efdr", POLLIN, SSH_CHAN_IO_EFD_R); 293187c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 293287c1498dSEd Maste "efdw", POLLOUT, SSH_CHAN_IO_EFD_W); 293387c1498dSEd Maste dump_channel_poll(__func__, "efd", c, p, pfd); 29341323ec57SEd Maste } 29351323ec57SEd Maste /* sock */ 293687c1498dSEd Maste if (c->sock != -1 && c->sock != c->rfd && 293787c1498dSEd Maste (p = c->pfds[3]) != -1) { 293887c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 293987c1498dSEd Maste "sockr", POLLIN, SSH_CHAN_IO_SOCK_R); 294087c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 294187c1498dSEd Maste "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W); 294287c1498dSEd Maste dump_channel_poll(__func__, "sock", c, p, pfd); 29431323ec57SEd Maste } 29441323ec57SEd Maste } 29451323ec57SEd Maste channel_handler(ssh, CHAN_POST, NULL); 2946511b41d2SMark Murray } 2947511b41d2SMark Murray 29484f52dfbbSDag-Erling Smørgrav /* 29494f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open or draining c->input. 2950edf85781SEd Maste * Returns non-zero if a packet was enqueued. 29514f52dfbbSDag-Erling Smørgrav */ 2952edf85781SEd Maste static int 29534f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c) 29544f52dfbbSDag-Erling Smørgrav { 29554f52dfbbSDag-Erling Smørgrav size_t len, plen; 29564f52dfbbSDag-Erling Smørgrav const u_char *pkt; 29574f52dfbbSDag-Erling Smørgrav int r; 29584f52dfbbSDag-Erling Smørgrav 29594f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->input)) == 0) { 29604f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 29614f52dfbbSDag-Erling Smørgrav /* 29624f52dfbbSDag-Erling Smørgrav * input-buffer is empty and read-socket shutdown: 29634f52dfbbSDag-Erling Smørgrav * tell peer, that we will not send more data: 29644f52dfbbSDag-Erling Smørgrav * send IEOF. 29654f52dfbbSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still 29664f52dfbbSDag-Erling Smørgrav * in use. 29674f52dfbbSDag-Erling Smørgrav */ 29684f52dfbbSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 29694f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 29704f52dfbbSDag-Erling Smørgrav "ibuf_empty delayed efd %d/(%zu)", 29714f52dfbbSDag-Erling Smørgrav c->self, c->efd, sshbuf_len(c->extended)); 29724f52dfbbSDag-Erling Smørgrav else 29734f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 29744f52dfbbSDag-Erling Smørgrav } 2975edf85781SEd Maste return 0; 29764f52dfbbSDag-Erling Smørgrav } 29774f52dfbbSDag-Erling Smørgrav 29784f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 297919261079SEd Maste fatal_f("channel %d: no remote id", c->self); 29804f52dfbbSDag-Erling Smørgrav 29814f52dfbbSDag-Erling Smørgrav if (c->datagram) { 29824f52dfbbSDag-Erling Smørgrav /* Check datagram will fit; drop if not */ 29834f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) 298419261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 29854f52dfbbSDag-Erling Smørgrav /* 29864f52dfbbSDag-Erling Smørgrav * XXX this does tail-drop on the datagram queue which is 29874f52dfbbSDag-Erling Smørgrav * usually suboptimal compared to head-drop. Better to have 29884f52dfbbSDag-Erling Smørgrav * backpressure at read time? (i.e. read + discard) 29894f52dfbbSDag-Erling Smørgrav */ 29904f52dfbbSDag-Erling Smørgrav if (plen > c->remote_window || plen > c->remote_maxpacket) { 29914f52dfbbSDag-Erling Smørgrav debug("channel %d: datagram too big", c->self); 2992edf85781SEd Maste return 0; 29934f52dfbbSDag-Erling Smørgrav } 29944f52dfbbSDag-Erling Smørgrav /* Enqueue it */ 29954f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 29964f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 29974f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || 299819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 299919261079SEd Maste fatal_fr(r, "channel %i: send datagram", c->self); 30004f52dfbbSDag-Erling Smørgrav c->remote_window -= plen; 3001edf85781SEd Maste return 1; 30024f52dfbbSDag-Erling Smørgrav } 30034f52dfbbSDag-Erling Smørgrav 30044f52dfbbSDag-Erling Smørgrav /* Enqueue packet for buffered data. */ 30054f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 30064f52dfbbSDag-Erling Smørgrav len = c->remote_window; 30074f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 30084f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 30094f52dfbbSDag-Erling Smørgrav if (len == 0) 3010edf85781SEd Maste return 0; 30114f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 30124f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 30134f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || 301419261079SEd Maste (r = sshpkt_send(ssh)) != 0) 301519261079SEd Maste fatal_fr(r, "channel %i: send data", c->self); 30164f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->input, len)) != 0) 301719261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 30184f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 3019edf85781SEd Maste return 1; 30204f52dfbbSDag-Erling Smørgrav } 30214f52dfbbSDag-Erling Smørgrav 30224f52dfbbSDag-Erling Smørgrav /* 30234f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open c->extended in read mode. 3024edf85781SEd Maste * Returns non-zero if a packet was enqueued. 30254f52dfbbSDag-Erling Smørgrav */ 3026edf85781SEd Maste static int 30274f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c) 30284f52dfbbSDag-Erling Smørgrav { 30294f52dfbbSDag-Erling Smørgrav size_t len; 30304f52dfbbSDag-Erling Smørgrav int r; 30314f52dfbbSDag-Erling Smørgrav 30324f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->extended)) == 0) 3033edf85781SEd Maste return 0; 30344f52dfbbSDag-Erling Smørgrav 30354f52dfbbSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %zu euse %d", c->self, 30364f52dfbbSDag-Erling Smørgrav c->remote_window, sshbuf_len(c->extended), c->extended_usage); 30374f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 30384f52dfbbSDag-Erling Smørgrav len = c->remote_window; 30394f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 30404f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 30414f52dfbbSDag-Erling Smørgrav if (len == 0) 3042edf85781SEd Maste return 0; 30434f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 304419261079SEd Maste fatal_f("channel %d: no remote id", c->self); 30454f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || 30464f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 30474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || 30484f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || 304919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 305019261079SEd Maste fatal_fr(r, "channel %i: data", c->self); 30514f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->extended, len)) != 0) 305219261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 30534f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 30544f52dfbbSDag-Erling Smørgrav debug2("channel %d: sent ext data %zu", c->self, len); 3055edf85781SEd Maste return 1; 30564f52dfbbSDag-Erling Smørgrav } 3057af12a3e7SDag-Erling Smørgrav 3058edf85781SEd Maste /* 3059edf85781SEd Maste * If there is data to send to the connection, enqueue some of it now. 3060edf85781SEd Maste * Returns non-zero if data was enqueued. 3061edf85781SEd Maste */ 3062edf85781SEd Maste int 30634f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh) 3064511b41d2SMark Murray { 30654f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3066a04a10f8SKris Kennaway Channel *c; 30674f52dfbbSDag-Erling Smørgrav u_int i; 3068edf85781SEd Maste int ret = 0; 3069511b41d2SMark Murray 30704f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 30714f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 3072af12a3e7SDag-Erling Smørgrav if (c == NULL) 3073af12a3e7SDag-Erling Smørgrav continue; 3074511b41d2SMark Murray 3075af12a3e7SDag-Erling Smørgrav /* 3076af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 3077af12a3e7SDag-Erling Smørgrav * incoming data. 3078af12a3e7SDag-Erling Smørgrav */ 3079a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 3080511b41d2SMark Murray continue; 30814f52dfbbSDag-Erling Smørgrav if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 3082ca3176e7SBrian Feldman /* XXX is this true? */ 30834f52dfbbSDag-Erling Smørgrav debug3("channel %d: will not send data after close", 30844f52dfbbSDag-Erling Smørgrav c->self); 3085511b41d2SMark Murray continue; 3086511b41d2SMark Murray } 3087511b41d2SMark Murray 3088511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 30894f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN || 30904f52dfbbSDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN) 3091edf85781SEd Maste ret |= channel_output_poll_input_open(ssh, c); 3092a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 30934f52dfbbSDag-Erling Smørgrav if (!(c->flags & CHAN_EOF_SENT) && 30944f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ) 3095edf85781SEd Maste ret |= channel_output_poll_extended_read(ssh, c); 3096511b41d2SMark Murray } 3097edf85781SEd Maste return ret; 3098511b41d2SMark Murray } 3099511b41d2SMark Murray 3100ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support */ 3101ca86bcf2SDag-Erling Smørgrav 3102ca86bcf2SDag-Erling Smørgrav /* 3103ca86bcf2SDag-Erling Smørgrav * When multiplexing channel messages for mux clients we have to deal 3104ca86bcf2SDag-Erling Smørgrav * with downstream messages from the mux client and upstream messages 3105ca86bcf2SDag-Erling Smørgrav * from the ssh server: 3106ca86bcf2SDag-Erling Smørgrav * 1) Handling downstream messages is straightforward and happens 3107ca86bcf2SDag-Erling Smørgrav * in channel_proxy_downstream(): 3108ca86bcf2SDag-Erling Smørgrav * - We forward all messages (mostly) unmodified to the server. 3109ca86bcf2SDag-Erling Smørgrav * - However, in order to route messages from upstream to the correct 3110ca86bcf2SDag-Erling Smørgrav * downstream client, we have to replace the channel IDs used by the 3111ca86bcf2SDag-Erling Smørgrav * mux clients with a unique channel ID because the mux clients might 3112ca86bcf2SDag-Erling Smørgrav * use conflicting channel IDs. 3113ca86bcf2SDag-Erling Smørgrav * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and 3114ca86bcf2SDag-Erling Smørgrav * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local 3115ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID 3116ca86bcf2SDag-Erling Smørgrav * with the newly allocated channel ID. 3117ca86bcf2SDag-Erling Smørgrav * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY 3118190cef3dSDag-Erling Smørgrav * channels and processed by channel_proxy_upstream(). The local channel ID 3119ca86bcf2SDag-Erling Smørgrav * is then translated back to the original mux client ID. 3120ca86bcf2SDag-Erling Smørgrav * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE 3121ca86bcf2SDag-Erling Smørgrav * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. 3122ca86bcf2SDag-Erling Smørgrav * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the 3123ca86bcf2SDag-Erling Smørgrav * downstream mux client are removed. 3124ca86bcf2SDag-Erling Smørgrav * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server 3125ca86bcf2SDag-Erling Smørgrav * requires more work, because they are not addressed to a specific 3126ca86bcf2SDag-Erling Smørgrav * channel. E.g. client_request_forwarded_tcpip() needs to figure 3127ca86bcf2SDag-Erling Smørgrav * out whether the request is addressed to the local client or a 3128ca86bcf2SDag-Erling Smørgrav * specific downstream client based on the listen-address/port. 3129190cef3dSDag-Erling Smørgrav * 6) Agent and X11-Forwarding have a similar problem and are currently 3130ca86bcf2SDag-Erling Smørgrav * not supported as the matching session/channel cannot be identified 3131ca86bcf2SDag-Erling Smørgrav * easily. 3132ca86bcf2SDag-Erling Smørgrav */ 3133ca86bcf2SDag-Erling Smørgrav 3134ca86bcf2SDag-Erling Smørgrav /* 3135ca86bcf2SDag-Erling Smørgrav * receive packets from downstream mux clients: 3136ca86bcf2SDag-Erling Smørgrav * channel callback fired on read from mux client, creates 3137ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs 3138ca86bcf2SDag-Erling Smørgrav * on channel creation. 3139ca86bcf2SDag-Erling Smørgrav */ 3140ca86bcf2SDag-Erling Smørgrav int 31414f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream) 3142ca86bcf2SDag-Erling Smørgrav { 3143ca86bcf2SDag-Erling Smørgrav Channel *c = NULL; 3144ca86bcf2SDag-Erling Smørgrav struct sshbuf *original = NULL, *modified = NULL; 3145ca86bcf2SDag-Erling Smørgrav const u_char *cp; 3146ca86bcf2SDag-Erling Smørgrav char *ctype = NULL, *listen_host = NULL; 3147ca86bcf2SDag-Erling Smørgrav u_char type; 3148ca86bcf2SDag-Erling Smørgrav size_t have; 31494f52dfbbSDag-Erling Smørgrav int ret = -1, r; 3150ca86bcf2SDag-Erling Smørgrav u_int id, remote_id, listen_port; 3151ca86bcf2SDag-Erling Smørgrav 31524f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(downstream->input, stderr); */ 31534f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) 3154ca86bcf2SDag-Erling Smørgrav != 0) { 315519261079SEd Maste error_fr(r, "parse"); 3156ca86bcf2SDag-Erling Smørgrav return -1; 3157ca86bcf2SDag-Erling Smørgrav } 3158ca86bcf2SDag-Erling Smørgrav if (have < 2) { 315919261079SEd Maste error_f("short message"); 3160ca86bcf2SDag-Erling Smørgrav return -1; 3161ca86bcf2SDag-Erling Smørgrav } 3162ca86bcf2SDag-Erling Smørgrav type = cp[1]; 3163ca86bcf2SDag-Erling Smørgrav /* skip padlen + type */ 3164ca86bcf2SDag-Erling Smørgrav cp += 2; 3165ca86bcf2SDag-Erling Smørgrav have -= 2; 3166ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 316719261079SEd Maste debug3_f("channel %u: down->up: type %u", 3168ca86bcf2SDag-Erling Smørgrav downstream->self, type); 3169ca86bcf2SDag-Erling Smørgrav 3170ca86bcf2SDag-Erling Smørgrav switch (type) { 3171ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN: 3172ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 3173ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 317419261079SEd Maste error_f("alloc"); 3175ca86bcf2SDag-Erling Smørgrav goto out; 3176ca86bcf2SDag-Erling Smørgrav } 3177ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || 3178ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 317919261079SEd Maste error_fr(r, "parse"); 3180ca86bcf2SDag-Erling Smørgrav goto out; 3181ca86bcf2SDag-Erling Smørgrav } 3182f374ba41SEd Maste c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, 3183ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, ctype, 1); 3184ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 3185ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; /* original downstream id */ 3186ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || 3187ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 3188ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 318919261079SEd Maste error_fr(r, "compose"); 31904f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3191ca86bcf2SDag-Erling Smørgrav goto out; 3192ca86bcf2SDag-Erling Smørgrav } 3193ca86bcf2SDag-Erling Smørgrav break; 3194ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3195ca86bcf2SDag-Erling Smørgrav /* 3196ca86bcf2SDag-Erling Smørgrav * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we 3197ca86bcf2SDag-Erling Smørgrav * need to parse 'remote_id' instead of 'ctype'. 3198ca86bcf2SDag-Erling Smørgrav */ 3199ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 3200ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 320119261079SEd Maste error_f("alloc"); 3202ca86bcf2SDag-Erling Smørgrav goto out; 3203ca86bcf2SDag-Erling Smørgrav } 3204ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || 3205ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 320619261079SEd Maste error_fr(r, "parse"); 3207ca86bcf2SDag-Erling Smørgrav goto out; 3208ca86bcf2SDag-Erling Smørgrav } 3209f374ba41SEd Maste c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, 3210ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); 3211ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 3212ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; 3213ca86bcf2SDag-Erling Smørgrav c->remote_id = remote_id; 32144f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 3215ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || 3216ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 3217ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 321819261079SEd Maste error_fr(r, "compose"); 32194f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3220ca86bcf2SDag-Erling Smørgrav goto out; 3221ca86bcf2SDag-Erling Smørgrav } 3222ca86bcf2SDag-Erling Smørgrav break; 3223ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_GLOBAL_REQUEST: 3224ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL) { 322519261079SEd Maste error_f("alloc"); 3226ca86bcf2SDag-Erling Smørgrav goto out; 3227ca86bcf2SDag-Erling Smørgrav } 3228ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { 322919261079SEd Maste error_fr(r, "parse"); 3230ca86bcf2SDag-Erling Smørgrav goto out; 3231ca86bcf2SDag-Erling Smørgrav } 3232ca86bcf2SDag-Erling Smørgrav if (strcmp(ctype, "tcpip-forward") != 0) { 323319261079SEd Maste error_f("unsupported request %s", ctype); 3234ca86bcf2SDag-Erling Smørgrav goto out; 3235ca86bcf2SDag-Erling Smørgrav } 3236ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u8(original, NULL)) != 0 || 3237ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || 3238ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &listen_port)) != 0) { 323919261079SEd Maste error_fr(r, "parse"); 3240ca86bcf2SDag-Erling Smørgrav goto out; 3241ca86bcf2SDag-Erling Smørgrav } 3242ca86bcf2SDag-Erling Smørgrav if (listen_port > 65535) { 324319261079SEd Maste error_f("tcpip-forward for %s: bad port %u", 324419261079SEd Maste listen_host, listen_port); 3245ca86bcf2SDag-Erling Smørgrav goto out; 3246ca86bcf2SDag-Erling Smørgrav } 3247ca86bcf2SDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 3248*a91a2465SEd Maste permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", 3249*a91a2465SEd Maste -1, listen_host, NULL, (int)listen_port, downstream); 3250ca86bcf2SDag-Erling Smørgrav break; 3251ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3252ca86bcf2SDag-Erling Smørgrav if (have < 4) 3253ca86bcf2SDag-Erling Smørgrav break; 3254ca86bcf2SDag-Erling Smørgrav remote_id = PEEK_U32(cp); 32554f52dfbbSDag-Erling Smørgrav if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { 3256ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) 32574f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3258ca86bcf2SDag-Erling Smørgrav else 3259ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT; 3260ca86bcf2SDag-Erling Smørgrav } 3261ca86bcf2SDag-Erling Smørgrav break; 3262ca86bcf2SDag-Erling Smørgrav } 3263ca86bcf2SDag-Erling Smørgrav if (modified) { 3264ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3265ca86bcf2SDag-Erling Smørgrav (r = sshpkt_putb(ssh, modified)) != 0 || 3266ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 326719261079SEd Maste error_fr(r, "send"); 3268ca86bcf2SDag-Erling Smørgrav goto out; 3269ca86bcf2SDag-Erling Smørgrav } 3270ca86bcf2SDag-Erling Smørgrav } else { 3271ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3272ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put(ssh, cp, have)) != 0 || 3273ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 327419261079SEd Maste error_fr(r, "send"); 3275ca86bcf2SDag-Erling Smørgrav goto out; 3276ca86bcf2SDag-Erling Smørgrav } 3277ca86bcf2SDag-Erling Smørgrav } 3278ca86bcf2SDag-Erling Smørgrav ret = 0; 3279ca86bcf2SDag-Erling Smørgrav out: 3280ca86bcf2SDag-Erling Smørgrav free(ctype); 3281ca86bcf2SDag-Erling Smørgrav free(listen_host); 3282ca86bcf2SDag-Erling Smørgrav sshbuf_free(original); 3283ca86bcf2SDag-Erling Smørgrav sshbuf_free(modified); 3284ca86bcf2SDag-Erling Smørgrav return ret; 3285ca86bcf2SDag-Erling Smørgrav } 3286ca86bcf2SDag-Erling Smørgrav 3287ca86bcf2SDag-Erling Smørgrav /* 3288ca86bcf2SDag-Erling Smørgrav * receive packets from upstream server and de-multiplex packets 3289ca86bcf2SDag-Erling Smørgrav * to correct downstream: 3290ca86bcf2SDag-Erling Smørgrav * implemented as a helper for channel input handlers, 3291ca86bcf2SDag-Erling Smørgrav * replaces local (proxy) channel ID with downstream channel ID. 3292ca86bcf2SDag-Erling Smørgrav */ 3293ca86bcf2SDag-Erling Smørgrav int 32944f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) 3295ca86bcf2SDag-Erling Smørgrav { 3296ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 3297ca86bcf2SDag-Erling Smørgrav Channel *downstream; 3298ca86bcf2SDag-Erling Smørgrav const u_char *cp = NULL; 3299ca86bcf2SDag-Erling Smørgrav size_t len; 3300ca86bcf2SDag-Erling Smørgrav int r; 3301ca86bcf2SDag-Erling Smørgrav 3302ca86bcf2SDag-Erling Smørgrav /* 3303ca86bcf2SDag-Erling Smørgrav * When receiving packets from the peer we need to check whether we 3304ca86bcf2SDag-Erling Smørgrav * need to forward the packets to the mux client. In this case we 3305190cef3dSDag-Erling Smørgrav * restore the original channel id and keep track of CLOSE messages, 3306ca86bcf2SDag-Erling Smørgrav * so we can cleanup the channel. 3307ca86bcf2SDag-Erling Smørgrav */ 3308ca86bcf2SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) 3309ca86bcf2SDag-Erling Smørgrav return 0; 3310ca86bcf2SDag-Erling Smørgrav if ((downstream = c->mux_ctx) == NULL) 3311ca86bcf2SDag-Erling Smørgrav return 0; 3312ca86bcf2SDag-Erling Smørgrav switch (type) { 3313ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3314ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_DATA: 3315ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EOF: 3316ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EXTENDED_DATA: 3317ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3318ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_FAILURE: 3319ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 3320ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_SUCCESS: 3321ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_FAILURE: 3322ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_REQUEST: 3323ca86bcf2SDag-Erling Smørgrav break; 3324ca86bcf2SDag-Erling Smørgrav default: 332519261079SEd Maste debug2_f("channel %u: unsupported type %u", c->self, type); 3326ca86bcf2SDag-Erling Smørgrav return 0; 3327ca86bcf2SDag-Erling Smørgrav } 3328ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 332919261079SEd Maste error_f("alloc reply"); 3330ca86bcf2SDag-Erling Smørgrav goto out; 3331ca86bcf2SDag-Erling Smørgrav } 3332ca86bcf2SDag-Erling Smørgrav /* get remaining payload (after id) */ 3333ca86bcf2SDag-Erling Smørgrav cp = sshpkt_ptr(ssh, &len); 3334ca86bcf2SDag-Erling Smørgrav if (cp == NULL) { 333519261079SEd Maste error_f("no packet"); 3336ca86bcf2SDag-Erling Smørgrav goto out; 3337ca86bcf2SDag-Erling Smørgrav } 3338ca86bcf2SDag-Erling Smørgrav /* translate id and send to muxclient */ 3339ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 3340ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, type)) != 0 || 3341ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || 3342ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put(b, cp, len)) != 0 || 33434f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(downstream->output, b)) != 0) { 334419261079SEd Maste error_fr(r, "compose muxclient"); 3345ca86bcf2SDag-Erling Smørgrav goto out; 3346ca86bcf2SDag-Erling Smørgrav } 3347ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(b, stderr); */ 3348ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 334919261079SEd Maste debug3_f("channel %u: up->down: type %u", c->self, type); 3350ca86bcf2SDag-Erling Smørgrav out: 3351ca86bcf2SDag-Erling Smørgrav /* update state */ 3352ca86bcf2SDag-Erling Smørgrav switch (type) { 3353ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3354ca86bcf2SDag-Erling Smørgrav /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ 33554f52dfbbSDag-Erling Smørgrav if (cp && len > 4) { 3356ca86bcf2SDag-Erling Smørgrav c->remote_id = PEEK_U32(cp); 33574f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 33584f52dfbbSDag-Erling Smørgrav } 3359ca86bcf2SDag-Erling Smørgrav break; 3360ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3361ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_SENT) 33624f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3363ca86bcf2SDag-Erling Smørgrav else 3364ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_RCVD; 3365ca86bcf2SDag-Erling Smørgrav break; 3366ca86bcf2SDag-Erling Smørgrav } 3367ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 3368ca86bcf2SDag-Erling Smørgrav return 1; 3369ca86bcf2SDag-Erling Smørgrav } 3370af12a3e7SDag-Erling Smørgrav 3371af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 3372511b41d2SMark Murray 33734f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */ 33744f52dfbbSDag-Erling Smørgrav static int 33754f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what) 3376511b41d2SMark Murray { 33774f52dfbbSDag-Erling Smørgrav u_int32_t id; 33784f52dfbbSDag-Erling Smørgrav int r; 33794f52dfbbSDag-Erling Smørgrav 33804f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &id)) != 0) { 338119261079SEd Maste error_r(r, "%s: parse id", where); 33824f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s message", what); 33834f52dfbbSDag-Erling Smørgrav } 33844f52dfbbSDag-Erling Smørgrav if (id > INT_MAX) { 338519261079SEd Maste error_r(r, "%s: bad channel id %u", where, id); 33864f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s channel id", what); 33874f52dfbbSDag-Erling Smørgrav } 33884f52dfbbSDag-Erling Smørgrav return (int)id; 33894f52dfbbSDag-Erling Smørgrav } 33904f52dfbbSDag-Erling Smørgrav 33914f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */ 33924f52dfbbSDag-Erling Smørgrav static Channel * 33934f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) 33944f52dfbbSDag-Erling Smørgrav { 33954f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, where, what); 3396a04a10f8SKris Kennaway Channel *c; 3397511b41d2SMark Murray 33984f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 33994f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, 34004f52dfbbSDag-Erling Smørgrav "%s packet referred to nonexistent channel %d", what, id); 34014f52dfbbSDag-Erling Smørgrav } 34024f52dfbbSDag-Erling Smørgrav return c; 34034f52dfbbSDag-Erling Smørgrav } 34044f52dfbbSDag-Erling Smørgrav 34054f52dfbbSDag-Erling Smørgrav int 34064f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh) 34074f52dfbbSDag-Erling Smørgrav { 34084f52dfbbSDag-Erling Smørgrav const u_char *data; 34094f52dfbbSDag-Erling Smørgrav size_t data_len, win_len; 34104f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "data"); 34114f52dfbbSDag-Erling Smørgrav int r; 34124f52dfbbSDag-Erling Smørgrav 34134f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3414ca86bcf2SDag-Erling Smørgrav return 0; 3415511b41d2SMark Murray 3416511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 3417a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 34184f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_OPEN && 34194f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_FINISH && 3420a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 3421bc5531deSDag-Erling Smørgrav return 0; 3422511b41d2SMark Murray 3423511b41d2SMark Murray /* Get the data. */ 342419261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 342519261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 342619261079SEd Maste fatal_fr(r, "channel %i: get data", c->self); 34274f52dfbbSDag-Erling Smørgrav 3428e2f6069cSDag-Erling Smørgrav win_len = data_len; 3429e2f6069cSDag-Erling Smørgrav if (c->datagram) 3430e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 3431a04a10f8SKris Kennaway 3432476cd3b2SDag-Erling Smørgrav /* 34334f52dfbbSDag-Erling Smørgrav * The sending side reduces its window as it sends data, so we 34344f52dfbbSDag-Erling Smørgrav * must 'fake' consumption of the data in order to ensure that window 34354f52dfbbSDag-Erling Smørgrav * updates are sent back. Otherwise the connection might deadlock. 3436476cd3b2SDag-Erling Smørgrav */ 34374f52dfbbSDag-Erling Smørgrav if (c->ostate != CHAN_OUTPUT_OPEN) { 3438e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 3439e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 3440bc5531deSDag-Erling Smørgrav return 0; 3441476cd3b2SDag-Erling Smørgrav } 3442476cd3b2SDag-Erling Smørgrav 3443e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 34444f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd big packet %zu, maxpack %u", 3445e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 34464f52dfbbSDag-Erling Smørgrav return 0; 3447a04a10f8SKris Kennaway } 3448e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 3449069ac184SEd Maste c->local_window_exceeded += win_len - c->local_window; 3450069ac184SEd Maste logit("channel %d: rcvd too much data %zu, win %u/%u " 3451069ac184SEd Maste "(excess %u)", c->self, win_len, c->local_window, 3452069ac184SEd Maste c->local_window_max, c->local_window_exceeded); 3453069ac184SEd Maste c->local_window = 0; 3454069ac184SEd Maste /* Allow 10% grace before bringing the hammer down */ 3455069ac184SEd Maste if (c->local_window_exceeded > (c->local_window_max / 10)) { 3456069ac184SEd Maste ssh_packet_disconnect(ssh, "channel %d: peer ignored " 3457069ac184SEd Maste "channel window", c->self); 3458a04a10f8SKris Kennaway } 3459069ac184SEd Maste } else { 3460e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 3461069ac184SEd Maste c->local_window_exceeded = 0; 3462069ac184SEd Maste } 34634f52dfbbSDag-Erling Smørgrav 34644f52dfbbSDag-Erling Smørgrav if (c->datagram) { 34654f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) 346619261079SEd Maste fatal_fr(r, "channel %i: append datagram", c->self); 34674f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) 346819261079SEd Maste fatal_fr(r, "channel %i: append data", c->self); 34694f52dfbbSDag-Erling Smørgrav 3470bc5531deSDag-Erling Smørgrav return 0; 3471511b41d2SMark Murray } 3472af12a3e7SDag-Erling Smørgrav 3473bc5531deSDag-Erling Smørgrav int 34744f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) 3475a04a10f8SKris Kennaway { 34764f52dfbbSDag-Erling Smørgrav const u_char *data; 34774f52dfbbSDag-Erling Smørgrav size_t data_len; 34784f52dfbbSDag-Erling Smørgrav u_int32_t tcode; 34794f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); 34804f52dfbbSDag-Erling Smørgrav int r; 3481a04a10f8SKris Kennaway 34824f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3483ca86bcf2SDag-Erling Smørgrav return 0; 3484a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 34854f52dfbbSDag-Erling Smørgrav logit("channel %d: ext data for non open", c->self); 3486bc5531deSDag-Erling Smørgrav return 0; 3487a04a10f8SKris Kennaway } 348880628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 348919261079SEd Maste if (ssh->compat & SSH_BUG_EXTEOF) 34904f52dfbbSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", 34914f52dfbbSDag-Erling Smørgrav c->self); 349280628bacSDag-Erling Smørgrav else 34934f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Received extended_data " 34944f52dfbbSDag-Erling Smørgrav "after EOF on channel %d.", c->self); 349580628bacSDag-Erling Smørgrav } 34964f52dfbbSDag-Erling Smørgrav 34974f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { 349819261079SEd Maste error_fr(r, "parse tcode"); 34994f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 35004f52dfbbSDag-Erling Smørgrav } 3501a04a10f8SKris Kennaway if (c->efd == -1 || 3502a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 3503a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 3504221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 3505bc5531deSDag-Erling Smørgrav return 0; 3506a04a10f8SKris Kennaway } 350719261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 350819261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 350919261079SEd Maste error_fr(r, "parse data"); 35104f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 35114f52dfbbSDag-Erling Smørgrav } 35124f52dfbbSDag-Erling Smørgrav 3513a04a10f8SKris Kennaway if (data_len > c->local_window) { 35144f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %zu, win %u", 3515a04a10f8SKris Kennaway c->self, data_len, c->local_window); 3516bc5531deSDag-Erling Smørgrav return 0; 3517a04a10f8SKris Kennaway } 35184f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd ext data %zu", c->self, data_len); 35194f52dfbbSDag-Erling Smørgrav /* XXX sshpkt_getb? */ 35204f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, data, data_len)) != 0) 352119261079SEd Maste error_fr(r, "append"); 3522a04a10f8SKris Kennaway c->local_window -= data_len; 3523bc5531deSDag-Erling Smørgrav return 0; 3524a04a10f8SKris Kennaway } 3525a04a10f8SKris Kennaway 3526bc5531deSDag-Erling Smørgrav int 35274f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) 3528a04a10f8SKris Kennaway { 35294f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); 353019261079SEd Maste int r; 3531a04a10f8SKris Kennaway 353219261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 353319261079SEd Maste error_fr(r, "parse data"); 353419261079SEd Maste ssh_packet_disconnect(ssh, "Invalid ieof message"); 353519261079SEd Maste } 35364f52dfbbSDag-Erling Smørgrav 35374f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3538ca86bcf2SDag-Erling Smørgrav return 0; 35394f52dfbbSDag-Erling Smørgrav chan_rcvd_ieof(ssh, c); 3540af12a3e7SDag-Erling Smørgrav 3541af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 3542af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 3543af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 3544af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 35454f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) == 0) 35464f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 3547af12a3e7SDag-Erling Smørgrav } 3548bc5531deSDag-Erling Smørgrav return 0; 3549a04a10f8SKris Kennaway } 3550511b41d2SMark Murray 3551bc5531deSDag-Erling Smørgrav int 35524f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) 3553511b41d2SMark Murray { 35544f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); 355519261079SEd Maste int r; 3556511b41d2SMark Murray 35574f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3558ca86bcf2SDag-Erling Smørgrav return 0; 355919261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 356019261079SEd Maste error_fr(r, "parse data"); 356119261079SEd Maste ssh_packet_disconnect(ssh, "Invalid oclose message"); 356219261079SEd Maste } 35634f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 3564bc5531deSDag-Erling Smørgrav return 0; 3565511b41d2SMark Murray } 3566511b41d2SMark Murray 3567bc5531deSDag-Erling Smørgrav int 35684f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) 3569a04a10f8SKris Kennaway { 35704f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); 35714f52dfbbSDag-Erling Smørgrav u_int32_t remote_window, remote_maxpacket; 35724f52dfbbSDag-Erling Smørgrav int r; 3573af12a3e7SDag-Erling Smørgrav 35744f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3575ca86bcf2SDag-Erling Smørgrav return 0; 3576ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 357719261079SEd Maste ssh_packet_disconnect(ssh, "Received open confirmation for " 35784f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 35794f52dfbbSDag-Erling Smørgrav /* 35804f52dfbbSDag-Erling Smørgrav * Record the remote channel number and mark that the channel 35814f52dfbbSDag-Erling Smørgrav * is now open. 35824f52dfbbSDag-Erling Smørgrav */ 35834f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || 35844f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || 358519261079SEd Maste (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || 358619261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 358719261079SEd Maste error_fr(r, "window/maxpacket"); 358819261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open confirmation message"); 35894f52dfbbSDag-Erling Smørgrav } 3590a04a10f8SKris Kennaway 35914f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 35924f52dfbbSDag-Erling Smørgrav c->remote_window = remote_window; 35934f52dfbbSDag-Erling Smørgrav c->remote_maxpacket = remote_maxpacket; 35944f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 3595d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 359619261079SEd Maste debug2_f("channel %d: callback start", c->self); 35974f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); 359819261079SEd Maste debug2_f("channel %d: callback done", c->self); 3599a04a10f8SKris Kennaway } 3600*a91a2465SEd Maste channel_set_used_time(ssh, c); 3601221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 3602a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 3603bc5531deSDag-Erling Smørgrav return 0; 3604af12a3e7SDag-Erling Smørgrav } 3605af12a3e7SDag-Erling Smørgrav 3606af12a3e7SDag-Erling Smørgrav static char * 3607af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 3608af12a3e7SDag-Erling Smørgrav { 3609af12a3e7SDag-Erling Smørgrav switch (reason) { 3610af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 3611af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 3612af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 3613af12a3e7SDag-Erling Smørgrav return "connect failed"; 3614af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 3615af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 3616af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 3617af12a3e7SDag-Erling Smørgrav return "resource shortage"; 3618af12a3e7SDag-Erling Smørgrav } 3619af12a3e7SDag-Erling Smørgrav return "unknown reason"; 3620a04a10f8SKris Kennaway } 3621a04a10f8SKris Kennaway 3622bc5531deSDag-Erling Smørgrav int 36234f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) 3624a04a10f8SKris Kennaway { 36254f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); 36264f52dfbbSDag-Erling Smørgrav u_int32_t reason; 36274f52dfbbSDag-Erling Smørgrav char *msg = NULL; 36284f52dfbbSDag-Erling Smørgrav int r; 3629a04a10f8SKris Kennaway 36304f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3631ca86bcf2SDag-Erling Smørgrav return 0; 3632ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 363319261079SEd Maste ssh_packet_disconnect(ssh, "Received open failure for " 36344f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 36354f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { 363619261079SEd Maste error_fr(r, "parse reason"); 363719261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 3638ca3176e7SBrian Feldman } 36394f52dfbbSDag-Erling Smørgrav /* skip language */ 36404f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || 364119261079SEd Maste (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || 364219261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 364319261079SEd Maste error_fr(r, "parse msg/lang"); 364419261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 36454f52dfbbSDag-Erling Smørgrav } 36464f52dfbbSDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", c->self, 3647af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 3648e4a9863fSDag-Erling Smørgrav free(msg); 3649e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 365019261079SEd Maste debug2_f("channel %d: callback start", c->self); 36514f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); 365219261079SEd Maste debug2_f("channel %d: callback done", c->self); 3653e2f6069cSDag-Erling Smørgrav } 3654cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 36554f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 3656bc5531deSDag-Erling Smørgrav return 0; 3657a04a10f8SKris Kennaway } 3658a04a10f8SKris Kennaway 3659bc5531deSDag-Erling Smørgrav int 36604f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) 3661a04a10f8SKris Kennaway { 36624f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "window adjust"); 3663a04a10f8SKris Kennaway Channel *c; 36644f52dfbbSDag-Erling Smørgrav u_int32_t adjust; 36654f52dfbbSDag-Erling Smørgrav u_int new_rwin; 36664f52dfbbSDag-Erling Smørgrav int r; 3667a04a10f8SKris Kennaway 36684f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 3669b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 3670bc5531deSDag-Erling Smørgrav return 0; 3671511b41d2SMark Murray } 36724f52dfbbSDag-Erling Smørgrav 36734f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3674ca86bcf2SDag-Erling Smørgrav return 0; 367519261079SEd Maste if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || 367619261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 367719261079SEd Maste error_fr(r, "parse adjust"); 367819261079SEd Maste ssh_packet_disconnect(ssh, "Invalid window adjust message"); 36794f52dfbbSDag-Erling Smørgrav } 36804f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", c->self, adjust); 36814f52dfbbSDag-Erling Smørgrav if ((new_rwin = c->remote_window + adjust) < c->remote_window) { 3682557f75e5SDag-Erling Smørgrav fatal("channel %d: adjust %u overflows remote window %u", 36834f52dfbbSDag-Erling Smørgrav c->self, adjust, c->remote_window); 36844f52dfbbSDag-Erling Smørgrav } 36854f52dfbbSDag-Erling Smørgrav c->remote_window = new_rwin; 3686bc5531deSDag-Erling Smørgrav return 0; 3687511b41d2SMark Murray } 3688511b41d2SMark Murray 3689bc5531deSDag-Erling Smørgrav int 36904f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) 3691af12a3e7SDag-Erling Smørgrav { 36924f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "status confirm"); 3693d4af9e69SDag-Erling Smørgrav Channel *c; 3694d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 3695d4af9e69SDag-Erling Smørgrav 3696d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 369719261079SEd Maste ssh_packet_set_alive_timeouts(ssh, 0); 3698d4af9e69SDag-Erling Smørgrav 369919261079SEd Maste debug2_f("type %d id %d", type, id); 3700d4af9e69SDag-Erling Smørgrav 37014f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 370219261079SEd Maste logit_f("%d: unknown", id); 3703bc5531deSDag-Erling Smørgrav return 0; 3704d4af9e69SDag-Erling Smørgrav } 37054f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3706ca86bcf2SDag-Erling Smørgrav return 0; 370719261079SEd Maste if (sshpkt_get_end(ssh) != 0) 370819261079SEd Maste ssh_packet_disconnect(ssh, "Invalid status confirm message"); 3709d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 3710bc5531deSDag-Erling Smørgrav return 0; 37114f52dfbbSDag-Erling Smørgrav cc->cb(ssh, type, c, cc->ctx); 3712d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 371319261079SEd Maste freezero(cc, sizeof(*cc)); 3714bc5531deSDag-Erling Smørgrav return 0; 3715d4af9e69SDag-Erling Smørgrav } 3716af12a3e7SDag-Erling Smørgrav 3717af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 3718511b41d2SMark Murray 3719511b41d2SMark Murray void 37204f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af) 3721511b41d2SMark Murray { 37224f52dfbbSDag-Erling Smørgrav ssh->chanctxt->IPv4or6 = af; 3723511b41d2SMark Murray } 3724511b41d2SMark Murray 372589986192SBrooks Davis 3726462c32cbSDag-Erling Smørgrav /* 3727462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 3728462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 3729462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 3730462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 3731462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 3732462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 3733462c32cbSDag-Erling Smørgrav * 3734462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 3735462c32cbSDag-Erling Smørgrav * 3736462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 3737462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 3738462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 3739a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 3740462c32cbSDag-Erling Smørgrav */ 3741462c32cbSDag-Erling Smørgrav static const char * 374219261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, 3743a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 3744462c32cbSDag-Erling Smørgrav { 3745462c32cbSDag-Erling Smørgrav const char *addr = NULL; 3746462c32cbSDag-Erling Smørgrav int wildcard = 0; 3747462c32cbSDag-Erling Smørgrav 3748462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 3749462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 3750a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 3751462c32cbSDag-Erling Smørgrav wildcard = 1; 3752a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 375319261079SEd Maste if (((ssh->compat & SSH_OLD_FORWARD_ADDR) && 3754462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 3755462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 3756a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 3757462c32cbSDag-Erling Smørgrav wildcard = 1; 3758f7167e0eSDag-Erling Smørgrav /* 3759f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 3760f7167e0eSDag-Erling Smørgrav * address and it was overridden. 3761f7167e0eSDag-Erling Smørgrav */ 3762f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 3763f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 3764f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 376519261079SEd Maste ssh_packet_send_debug(ssh, 376619261079SEd Maste "Forwarding listen address " 3767f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 3768f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 3769f7167e0eSDag-Erling Smørgrav } 3770a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 3771a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 3772a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 377319261079SEd Maste /* 377419261079SEd Maste * Accept explicit localhost address when 377519261079SEd Maste * GatewayPorts=yes. The "localhost" hostname is 377619261079SEd Maste * deliberately skipped here so it will listen on all 377719261079SEd Maste * available local address families. 377819261079SEd Maste */ 3779a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 3780f7167e0eSDag-Erling Smørgrav } 3781a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 3782a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3783a0ee8cc6SDag-Erling Smørgrav /* 3784a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 3785a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 3786a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 3787a0ee8cc6SDag-Erling Smørgrav */ 3788462c32cbSDag-Erling Smørgrav addr = listen_addr; 3789462c32cbSDag-Erling Smørgrav } 3790462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 3791462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 3792462c32cbSDag-Erling Smørgrav return addr; 3793462c32cbSDag-Erling Smørgrav } 3794462c32cbSDag-Erling Smørgrav 3795af12a3e7SDag-Erling Smørgrav static int 37964f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, 37974f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int *allocated_listen_port, 37984f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3799511b41d2SMark Murray { 3800af12a3e7SDag-Erling Smørgrav Channel *c; 3801b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 3802511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3803aa49c926SDag-Erling Smørgrav const char *host, *addr; 3804af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 3805cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 3806511b41d2SMark Murray 3807aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 3808511b41d2SMark Murray 3809557f75e5SDag-Erling Smørgrav if (is_client && fwd->connect_path != NULL) { 3810557f75e5SDag-Erling Smørgrav host = fwd->connect_path; 3811557f75e5SDag-Erling Smørgrav } else { 3812557f75e5SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 3813557f75e5SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 3814af12a3e7SDag-Erling Smørgrav if (host == NULL) { 3815af12a3e7SDag-Erling Smørgrav error("No forward host name."); 3816d4ecd108SDag-Erling Smørgrav return 0; 3817ca3176e7SBrian Feldman } 3818cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 3819ca3176e7SBrian Feldman error("Forward host name too long."); 3820d4ecd108SDag-Erling Smørgrav return 0; 3821ca3176e7SBrian Feldman } 3822557f75e5SDag-Erling Smørgrav } 3823ca3176e7SBrian Feldman 3824462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 382519261079SEd Maste addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, 3826a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 382719261079SEd Maste debug3_f("type %d wildcard %d addr %s", type, wildcard, 382819261079SEd Maste (addr == NULL) ? "NULL" : addr); 3829aa49c926SDag-Erling Smørgrav 3830aa49c926SDag-Erling Smørgrav /* 3831511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 3832511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 3833511b41d2SMark Murray */ 3834511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 38354f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 3836aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 3837511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3838a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 3839aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 3840aa49c926SDag-Erling Smørgrav if (addr == NULL) { 3841aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 384219261079SEd Maste ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", 3843d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3844aa49c926SDag-Erling Smørgrav } else { 384519261079SEd Maste error_f("getaddrinfo(%.64s): %s", addr, 3846d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3847aa49c926SDag-Erling Smørgrav } 3848d4ecd108SDag-Erling Smørgrav return 0; 3849aa49c926SDag-Erling Smørgrav } 3850cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 3851cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 3852511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3853cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 3854cce7d346SDag-Erling Smørgrav case AF_INET: 3855cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 3856cce7d346SDag-Erling Smørgrav sin_port; 3857cce7d346SDag-Erling Smørgrav break; 3858cce7d346SDag-Erling Smørgrav case AF_INET6: 3859cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 3860cce7d346SDag-Erling Smørgrav sin6_port; 3861cce7d346SDag-Erling Smørgrav break; 3862cce7d346SDag-Erling Smørgrav default: 3863511b41d2SMark Murray continue; 3864cce7d346SDag-Erling Smørgrav } 3865cce7d346SDag-Erling Smørgrav /* 3866cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 3867cce7d346SDag-Erling Smørgrav * same port for all address families. 3868cce7d346SDag-Erling Smørgrav */ 38694f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 38704f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && allocated_listen_port != NULL && 38714f52dfbbSDag-Erling Smørgrav *allocated_listen_port > 0) 3872cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 3873cce7d346SDag-Erling Smørgrav 3874511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 38754f52dfbbSDag-Erling Smørgrav strport, sizeof(strport), 38764f52dfbbSDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 387719261079SEd Maste error_f("getnameinfo failed"); 3878511b41d2SMark Murray continue; 3879511b41d2SMark Murray } 3880511b41d2SMark Murray /* Create a port to listen for the host. */ 3881221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 388219261079SEd Maste if (sock == -1) { 3883511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 388447dd1d1bSDag-Erling Smørgrav verbose("socket [%s]:%s: %.100s", ntop, strport, 388547dd1d1bSDag-Erling Smørgrav strerror(errno)); 3886511b41d2SMark Murray continue; 3887511b41d2SMark Murray } 3888b74df5b2SDag-Erling Smørgrav 388947dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 3890b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3891b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3892f388f5efSDag-Erling Smørgrav 3893cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 3894cce7d346SDag-Erling Smørgrav ntop, strport); 3895511b41d2SMark Murray 3896511b41d2SMark Murray /* Bind the socket to the address. */ 389719261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 38984f52dfbbSDag-Erling Smørgrav /* 38994f52dfbbSDag-Erling Smørgrav * address can be in if use ipv6 address is 39004f52dfbbSDag-Erling Smørgrav * already bound 39014f52dfbbSDag-Erling Smørgrav */ 3902989dd127SDag-Erling Smørgrav if (!ai->ai_next) 390347dd1d1bSDag-Erling Smørgrav error("bind [%s]:%s: %.100s", 390447dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3905989dd127SDag-Erling Smørgrav else 390647dd1d1bSDag-Erling Smørgrav verbose("bind [%s]:%s: %.100s", 390747dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3908989dd127SDag-Erling Smørgrav 3909511b41d2SMark Murray close(sock); 3910511b41d2SMark Murray continue; 3911511b41d2SMark Murray } 3912511b41d2SMark Murray /* Start listening for connections on the socket. */ 391319261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 391447dd1d1bSDag-Erling Smørgrav error("listen [%s]:%s: %.100s", ntop, strport, 391547dd1d1bSDag-Erling Smørgrav strerror(errno)); 3916511b41d2SMark Murray close(sock); 3917511b41d2SMark Murray continue; 3918511b41d2SMark Murray } 3919cce7d346SDag-Erling Smørgrav 3920cce7d346SDag-Erling Smørgrav /* 3921a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 3922cce7d346SDag-Erling Smørgrav * record what we got. 3923cce7d346SDag-Erling Smørgrav */ 39244f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 39254f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && 3926cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 3927cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 3928076ad2f8SDag-Erling Smørgrav *allocated_listen_port = get_local_port(sock); 3929cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 3930cce7d346SDag-Erling Smørgrav *allocated_listen_port); 3931cce7d346SDag-Erling Smørgrav } 3932cce7d346SDag-Erling Smørgrav 393360c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 3934f374ba41SEd Maste c = channel_new(ssh, "port-listener", type, sock, sock, -1, 3935a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3936221552e4SDag-Erling Smørgrav 0, "port listener", 1); 3937cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 3938a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 3939462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 3940a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 394119261079SEd Maste !(ssh->compat & SSH_BUG_DYNAMIC_RPORT)) 3942462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 3943462c32cbSDag-Erling Smørgrav else 3944a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 3945511b41d2SMark Murray success = 1; 3946511b41d2SMark Murray } 3947511b41d2SMark Murray if (success == 0) 394819261079SEd Maste error_f("cannot listen to port: %d", fwd->listen_port); 3949511b41d2SMark Murray freeaddrinfo(aitop); 3950ca3176e7SBrian Feldman return success; 3951511b41d2SMark Murray } 3952511b41d2SMark Murray 3953a0ee8cc6SDag-Erling Smørgrav static int 39544f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, 39554f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 3956a0ee8cc6SDag-Erling Smørgrav { 3957a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 3958a0ee8cc6SDag-Erling Smørgrav const char *path; 3959a0ee8cc6SDag-Erling Smørgrav Channel *c; 3960a0ee8cc6SDag-Erling Smørgrav int port, sock; 3961a0ee8cc6SDag-Erling Smørgrav mode_t omask; 3962a0ee8cc6SDag-Erling Smørgrav 3963a0ee8cc6SDag-Erling Smørgrav switch (type) { 3964a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 3965a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3966a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 3967a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 3968a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 3969a0ee8cc6SDag-Erling Smørgrav return 0; 3970a0ee8cc6SDag-Erling Smørgrav } 3971a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 3972a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3973a0ee8cc6SDag-Erling Smørgrav } else { 3974a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 3975a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 3976a0ee8cc6SDag-Erling Smørgrav return 0; 3977a0ee8cc6SDag-Erling Smørgrav } 3978a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 3979a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 3980a0ee8cc6SDag-Erling Smørgrav return 0; 3981a0ee8cc6SDag-Erling Smørgrav } 3982a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 3983a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 3984a0ee8cc6SDag-Erling Smørgrav } 3985a0ee8cc6SDag-Erling Smørgrav break; 3986a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 3987a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 3988a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3989a0ee8cc6SDag-Erling Smørgrav break; 3990a0ee8cc6SDag-Erling Smørgrav default: 399119261079SEd Maste error_f("unexpected channel type %d", type); 3992a0ee8cc6SDag-Erling Smørgrav return 0; 3993a0ee8cc6SDag-Erling Smørgrav } 3994a0ee8cc6SDag-Erling Smørgrav 3995a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 3996a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 3997a0ee8cc6SDag-Erling Smørgrav return 0; 3998a0ee8cc6SDag-Erling Smørgrav } 3999a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 4000a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 4001a0ee8cc6SDag-Erling Smørgrav return 0; 4002a0ee8cc6SDag-Erling Smørgrav } 4003a0ee8cc6SDag-Erling Smørgrav 400419261079SEd Maste debug3_f("type %d path %s", type, fwd->listen_path); 4005a0ee8cc6SDag-Erling Smørgrav 4006a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 4007a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 4008a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 4009a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 4010a0ee8cc6SDag-Erling Smørgrav umask(omask); 4011a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 4012a0ee8cc6SDag-Erling Smørgrav return 0; 4013a0ee8cc6SDag-Erling Smørgrav 4014a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 4015a0ee8cc6SDag-Erling Smørgrav 4016a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 4017f374ba41SEd Maste c = channel_new(ssh, "unix-listener", type, sock, sock, -1, 4018a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 4019a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 4020a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 4021a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 4022a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 4023a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 4024a0ee8cc6SDag-Erling Smørgrav return 1; 4025a0ee8cc6SDag-Erling Smørgrav } 4026a0ee8cc6SDag-Erling Smørgrav 4027a0ee8cc6SDag-Erling Smørgrav static int 40284f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh, 40294f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 403021e764dfSDag-Erling Smørgrav { 403121e764dfSDag-Erling Smørgrav u_int i; 403221e764dfSDag-Erling Smørgrav int found = 0; 403321e764dfSDag-Erling Smørgrav 40344f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 40354f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4036462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 4037462c32cbSDag-Erling Smørgrav continue; 4038462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 403919261079SEd Maste debug2_f("close channel %d", i); 40404f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 4041462c32cbSDag-Erling Smørgrav found = 1; 4042462c32cbSDag-Erling Smørgrav } 4043462c32cbSDag-Erling Smørgrav } 404421e764dfSDag-Erling Smørgrav 40454f52dfbbSDag-Erling Smørgrav return found; 4046462c32cbSDag-Erling Smørgrav } 4047462c32cbSDag-Erling Smørgrav 4048a0ee8cc6SDag-Erling Smørgrav static int 40494f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) 4050462c32cbSDag-Erling Smørgrav { 4051462c32cbSDag-Erling Smørgrav u_int i; 4052462c32cbSDag-Erling Smørgrav int found = 0; 4053a0ee8cc6SDag-Erling Smørgrav 40544f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 40554f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4056a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 4057a0ee8cc6SDag-Erling Smørgrav continue; 4058a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 4059a0ee8cc6SDag-Erling Smørgrav continue; 4060a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 406119261079SEd Maste debug2_f("close channel %d", i); 40624f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 4063a0ee8cc6SDag-Erling Smørgrav found = 1; 4064a0ee8cc6SDag-Erling Smørgrav } 4065a0ee8cc6SDag-Erling Smørgrav } 4066a0ee8cc6SDag-Erling Smørgrav 40674f52dfbbSDag-Erling Smørgrav return found; 4068a0ee8cc6SDag-Erling Smørgrav } 4069a0ee8cc6SDag-Erling Smørgrav 4070a0ee8cc6SDag-Erling Smørgrav int 40714f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) 4072a0ee8cc6SDag-Erling Smørgrav { 40734f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 40744f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(ssh, 40754f52dfbbSDag-Erling Smørgrav fwd->listen_path); 40764f52dfbbSDag-Erling Smørgrav } else { 40774f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(ssh, 40784f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 40794f52dfbbSDag-Erling Smørgrav } 4080a0ee8cc6SDag-Erling Smørgrav } 4081a0ee8cc6SDag-Erling Smørgrav 4082a0ee8cc6SDag-Erling Smørgrav static int 40834f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh, 40844f52dfbbSDag-Erling Smørgrav const char *lhost, u_short lport, int cport, 40854f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 4086a0ee8cc6SDag-Erling Smørgrav { 4087a0ee8cc6SDag-Erling Smørgrav u_int i; 4088a0ee8cc6SDag-Erling Smørgrav int found = 0; 408919261079SEd Maste const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); 4090462c32cbSDag-Erling Smørgrav 40914f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 40924f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4093462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 4094462c32cbSDag-Erling Smørgrav continue; 4095462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 4096462c32cbSDag-Erling Smørgrav continue; 4097462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 4098462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 4099462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 4100462c32cbSDag-Erling Smørgrav continue; 4101462c32cbSDag-Erling Smørgrav } else { 4102462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 4103462c32cbSDag-Erling Smørgrav continue; 4104462c32cbSDag-Erling Smørgrav } 4105462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 4106462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 4107462c32cbSDag-Erling Smørgrav continue; 4108462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 410919261079SEd Maste debug2_f("close channel %d", i); 41104f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 411121e764dfSDag-Erling Smørgrav found = 1; 411221e764dfSDag-Erling Smørgrav } 411321e764dfSDag-Erling Smørgrav } 411421e764dfSDag-Erling Smørgrav 41154f52dfbbSDag-Erling Smørgrav return found; 411621e764dfSDag-Erling Smørgrav } 411721e764dfSDag-Erling Smørgrav 4118a0ee8cc6SDag-Erling Smørgrav static int 41194f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) 4120a0ee8cc6SDag-Erling Smørgrav { 4121a0ee8cc6SDag-Erling Smørgrav u_int i; 4122a0ee8cc6SDag-Erling Smørgrav int found = 0; 4123a0ee8cc6SDag-Erling Smørgrav 4124a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 412519261079SEd Maste error_f("no path specified."); 4126a0ee8cc6SDag-Erling Smørgrav return 0; 4127a0ee8cc6SDag-Erling Smørgrav } 4128a0ee8cc6SDag-Erling Smørgrav 41294f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 41304f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4131a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 4132a0ee8cc6SDag-Erling Smørgrav continue; 4133a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 4134a0ee8cc6SDag-Erling Smørgrav continue; 4135a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 413619261079SEd Maste debug2_f("close channel %d", i); 41374f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 4138a0ee8cc6SDag-Erling Smørgrav found = 1; 4139a0ee8cc6SDag-Erling Smørgrav } 4140a0ee8cc6SDag-Erling Smørgrav } 4141a0ee8cc6SDag-Erling Smørgrav 41424f52dfbbSDag-Erling Smørgrav return found; 4143a0ee8cc6SDag-Erling Smørgrav } 4144a0ee8cc6SDag-Erling Smørgrav 4145a0ee8cc6SDag-Erling Smørgrav int 41464f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh, 41474f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 4148af12a3e7SDag-Erling Smørgrav { 4149a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 41504f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(ssh, 41514f52dfbbSDag-Erling Smørgrav fwd->listen_path); 41524f52dfbbSDag-Erling Smørgrav } else { 41534f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(ssh, 41544f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, cport, fwd_opts); 41554f52dfbbSDag-Erling Smørgrav } 41564f52dfbbSDag-Erling Smørgrav } 41574f52dfbbSDag-Erling Smørgrav 41584f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */ 41594f52dfbbSDag-Erling Smørgrav int 41604f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh, 41614f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 41624f52dfbbSDag-Erling Smørgrav { 41634f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 41644f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 4165a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 4166a0ee8cc6SDag-Erling Smørgrav } else { 41674f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 41684f52dfbbSDag-Erling Smørgrav SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); 4169a0ee8cc6SDag-Erling Smørgrav } 4170af12a3e7SDag-Erling Smørgrav } 4171af12a3e7SDag-Erling Smørgrav 4172190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */ 4173190cef3dSDag-Erling Smørgrav static int 4174190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd) 4175190cef3dSDag-Erling Smørgrav { 4176190cef3dSDag-Erling Smørgrav int ret; 4177190cef3dSDag-Erling Smørgrav char *lhost; 4178190cef3dSDag-Erling Smørgrav 4179190cef3dSDag-Erling Smørgrav /* XXX add ACLs for streamlocal */ 4180190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 4181190cef3dSDag-Erling Smørgrav return 1; 4182190cef3dSDag-Erling Smørgrav 4183190cef3dSDag-Erling Smørgrav if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) 4184190cef3dSDag-Erling Smørgrav return 0; 4185190cef3dSDag-Erling Smørgrav 4186190cef3dSDag-Erling Smørgrav if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && 4187190cef3dSDag-Erling Smørgrav allowed_open->listen_port != fwd->listen_port) 4188190cef3dSDag-Erling Smørgrav return 0; 4189190cef3dSDag-Erling Smørgrav 4190190cef3dSDag-Erling Smørgrav /* Match hostnames case-insensitively */ 4191190cef3dSDag-Erling Smørgrav lhost = xstrdup(fwd->listen_host); 4192190cef3dSDag-Erling Smørgrav lowercase(lhost); 4193190cef3dSDag-Erling Smørgrav ret = match_pattern(lhost, allowed_open->listen_host); 4194190cef3dSDag-Erling Smørgrav free(lhost); 4195190cef3dSDag-Erling Smørgrav 4196190cef3dSDag-Erling Smørgrav return ret; 4197190cef3dSDag-Erling Smørgrav } 4198190cef3dSDag-Erling Smørgrav 4199190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */ 4200190cef3dSDag-Erling Smørgrav static int 4201190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) 4202190cef3dSDag-Erling Smørgrav { 4203190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4204190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->remote_perms; 4205190cef3dSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4206190cef3dSDag-Erling Smørgrav struct permission *perm; 4207190cef3dSDag-Erling Smørgrav 4208190cef3dSDag-Erling Smørgrav /* XXX apply GatewayPorts override before checking? */ 4209190cef3dSDag-Erling Smørgrav 4210190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4211190cef3dSDag-Erling Smørgrav if (!permit) { 4212190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4213190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4214190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 4215190cef3dSDag-Erling Smørgrav permit = 1; 4216190cef3dSDag-Erling Smørgrav break; 4217190cef3dSDag-Erling Smørgrav } 4218190cef3dSDag-Erling Smørgrav } 4219190cef3dSDag-Erling Smørgrav } 4220190cef3dSDag-Erling Smørgrav 4221190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4222190cef3dSDag-Erling Smørgrav permit_adm = 0; 4223190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4224190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4225190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 4226190cef3dSDag-Erling Smørgrav permit_adm = 1; 4227190cef3dSDag-Erling Smørgrav break; 4228190cef3dSDag-Erling Smørgrav } 4229190cef3dSDag-Erling Smørgrav } 4230190cef3dSDag-Erling Smørgrav } 4231190cef3dSDag-Erling Smørgrav 4232190cef3dSDag-Erling Smørgrav return permit && permit_adm; 4233190cef3dSDag-Erling Smørgrav } 4234190cef3dSDag-Erling Smørgrav 4235af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 4236af12a3e7SDag-Erling Smørgrav int 42374f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, 4238a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 4239af12a3e7SDag-Erling Smørgrav { 4240190cef3dSDag-Erling Smørgrav if (!check_rfwd_permission(ssh, fwd)) { 424119261079SEd Maste ssh_packet_send_debug(ssh, "port forwarding refused"); 424219261079SEd Maste if (fwd->listen_path != NULL) 424319261079SEd Maste /* XXX always allowed, see remote_open_match() */ 424419261079SEd Maste logit("Received request from %.100s port %d to " 424519261079SEd Maste "remote forward to path \"%.100s\", " 424619261079SEd Maste "but the request was denied.", 424719261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 424819261079SEd Maste fwd->listen_path); 424919261079SEd Maste else if(fwd->listen_host != NULL) 425019261079SEd Maste logit("Received request from %.100s port %d to " 425119261079SEd Maste "remote forward to host %.100s port %d, " 425219261079SEd Maste "but the request was denied.", 425319261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 425419261079SEd Maste fwd->listen_host, fwd->listen_port ); 425519261079SEd Maste else 425619261079SEd Maste logit("Received request from %.100s port %d to remote " 425719261079SEd Maste "forward, but the request was denied.", 425819261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 4259190cef3dSDag-Erling Smørgrav return 0; 4260190cef3dSDag-Erling Smørgrav } 4261a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 42624f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 4263a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 4264a0ee8cc6SDag-Erling Smørgrav } else { 42654f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 4266a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 4267a0ee8cc6SDag-Erling Smørgrav fwd_opts); 4268a0ee8cc6SDag-Erling Smørgrav } 4269af12a3e7SDag-Erling Smørgrav } 4270af12a3e7SDag-Erling Smørgrav 4271511b41d2SMark Murray /* 4272462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 4273462c32cbSDag-Erling Smørgrav * this server. 4274462c32cbSDag-Erling Smørgrav */ 4275462c32cbSDag-Erling Smørgrav static const char * 4276462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 4277462c32cbSDag-Erling Smørgrav { 4278462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 4279462c32cbSDag-Erling Smørgrav return "localhost"; 4280462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 4281462c32cbSDag-Erling Smørgrav return ""; 4282462c32cbSDag-Erling Smørgrav } else 4283462c32cbSDag-Erling Smørgrav return listen_host; 4284462c32cbSDag-Erling Smørgrav } 4285462c32cbSDag-Erling Smørgrav 4286462c32cbSDag-Erling Smørgrav /* 4287511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 4288511b41d2SMark Murray * the secure channel to host:port from local side. 4289462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 4290190cef3dSDag-Erling Smørgrav * channel_update_permission(). 4291511b41d2SMark Murray */ 4292333ee039SDag-Erling Smørgrav int 42934f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) 4294511b41d2SMark Murray { 42954f52dfbbSDag-Erling Smørgrav int r, success = 0, idx = -1; 4296f374ba41SEd Maste const char *host_to_connect, *listen_host, *listen_path; 42974f52dfbbSDag-Erling Smørgrav int port_to_connect, listen_port; 4298ca3176e7SBrian Feldman 4299511b41d2SMark Murray /* Send the forward request to the remote side. */ 4300a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 43014f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 43024f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 43034f52dfbbSDag-Erling Smørgrav "streamlocal-forward@openssh.com")) != 0 || 43044f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 43054f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || 43064f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 43074f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 430819261079SEd Maste fatal_fr(r, "request streamlocal"); 4309a0ee8cc6SDag-Erling Smørgrav } else { 43104f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 43114f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || 43124f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 43134f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 43144f52dfbbSDag-Erling Smørgrav channel_rfwd_bind_host(fwd->listen_host))) != 0 || 43154f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || 43164f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 43174f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 431819261079SEd Maste fatal_fr(r, "request tcpip-forward"); 4319a0ee8cc6SDag-Erling Smørgrav } 4320ca3176e7SBrian Feldman /* Assume that server accepts the request */ 4321ca3176e7SBrian Feldman success = 1; 4322ca3176e7SBrian Feldman if (success) { 4323e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 43244f52dfbbSDag-Erling Smørgrav host_to_connect = listen_host = listen_path = NULL; 43254f52dfbbSDag-Erling Smørgrav port_to_connect = listen_port = 0; 4326a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 4327f374ba41SEd Maste host_to_connect = fwd->connect_path; 43284f52dfbbSDag-Erling Smørgrav port_to_connect = PORT_STREAMLOCAL; 4329a0ee8cc6SDag-Erling Smørgrav } else { 4330f374ba41SEd Maste host_to_connect = fwd->connect_host; 43314f52dfbbSDag-Erling Smørgrav port_to_connect = fwd->connect_port; 4332a0ee8cc6SDag-Erling Smørgrav } 4333a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 4334f374ba41SEd Maste listen_path = fwd->listen_path; 43354f52dfbbSDag-Erling Smørgrav listen_port = PORT_STREAMLOCAL; 4336a0ee8cc6SDag-Erling Smørgrav } else { 4337f374ba41SEd Maste listen_host = fwd->listen_host; 43384f52dfbbSDag-Erling Smørgrav listen_port = fwd->listen_port; 4339a0ee8cc6SDag-Erling Smørgrav } 4340190cef3dSDag-Erling Smørgrav idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, 43414f52dfbbSDag-Erling Smørgrav host_to_connect, port_to_connect, 43424f52dfbbSDag-Erling Smørgrav listen_host, listen_path, listen_port, NULL); 4343511b41d2SMark Murray } 43444f52dfbbSDag-Erling Smørgrav return idx; 4345a04a10f8SKris Kennaway } 4346511b41d2SMark Murray 4347a0ee8cc6SDag-Erling Smørgrav static int 4348190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost, 4349a0ee8cc6SDag-Erling Smørgrav int requestedport) 4350a0ee8cc6SDag-Erling Smørgrav { 4351a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4352a0ee8cc6SDag-Erling Smørgrav return 0; 4353a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 4354a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 4355a0ee8cc6SDag-Erling Smørgrav return 0; 4356076ad2f8SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 4357076ad2f8SDag-Erling Smørgrav strcmp(allowed_open->host_to_connect, requestedhost) != 0) 4358a0ee8cc6SDag-Erling Smørgrav return 0; 4359a0ee8cc6SDag-Erling Smørgrav return 1; 4360a0ee8cc6SDag-Erling Smørgrav } 4361a0ee8cc6SDag-Erling Smørgrav 4362a0ee8cc6SDag-Erling Smørgrav /* 4363a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 4364a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 4365a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 4366a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 4367a0ee8cc6SDag-Erling Smørgrav */ 4368a0ee8cc6SDag-Erling Smørgrav static int 4369190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open, 4370a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 4371a0ee8cc6SDag-Erling Smørgrav { 4372a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 4373a0ee8cc6SDag-Erling Smørgrav 4374a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4375a0ee8cc6SDag-Erling Smørgrav return 0; 4376a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 4377a0ee8cc6SDag-Erling Smørgrav return 0; 4378a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 4379a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 4380a0ee8cc6SDag-Erling Smørgrav return 1; 4381a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 4382a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 4383a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 4384190cef3dSDag-Erling Smørgrav if (allowed_host == NULL || requestedhost == NULL || 4385a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 4386a0ee8cc6SDag-Erling Smørgrav return 0; 4387a0ee8cc6SDag-Erling Smørgrav return 1; 4388a0ee8cc6SDag-Erling Smørgrav } 4389a0ee8cc6SDag-Erling Smørgrav 4390a0ee8cc6SDag-Erling Smørgrav static int 4391190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open, 4392a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 4393a0ee8cc6SDag-Erling Smørgrav { 4394a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4395a0ee8cc6SDag-Erling Smørgrav return 0; 4396a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 4397a0ee8cc6SDag-Erling Smørgrav return 0; 4398a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 4399a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 4400a0ee8cc6SDag-Erling Smørgrav return 0; 4401a0ee8cc6SDag-Erling Smørgrav return 1; 4402a0ee8cc6SDag-Erling Smørgrav } 4403a0ee8cc6SDag-Erling Smørgrav 4404511b41d2SMark Murray /* 440521e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 440621e764dfSDag-Erling Smørgrav * local side. 440721e764dfSDag-Erling Smørgrav */ 4408a0ee8cc6SDag-Erling Smørgrav static int 44094f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh, 44104f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 441121e764dfSDag-Erling Smørgrav { 44124f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4413190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 44144f52dfbbSDag-Erling Smørgrav int r; 44154f52dfbbSDag-Erling Smørgrav u_int i; 441619261079SEd Maste struct permission *perm = NULL; 441721e764dfSDag-Erling Smørgrav 4418190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4419190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4420190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, host, port, 0)) 442121e764dfSDag-Erling Smørgrav break; 4422190cef3dSDag-Erling Smørgrav perm = NULL; 442321e764dfSDag-Erling Smørgrav } 4424190cef3dSDag-Erling Smørgrav if (perm == NULL) { 442519261079SEd Maste debug_f("requested forward not found"); 4426462c32cbSDag-Erling Smørgrav return -1; 442721e764dfSDag-Erling Smørgrav } 44284f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 44294f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || 44304f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 44314f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || 44324f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, port)) != 0 || 44334f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 443419261079SEd Maste fatal_fr(r, "send cancel"); 443521e764dfSDag-Erling Smørgrav 4436190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4437462c32cbSDag-Erling Smørgrav 4438462c32cbSDag-Erling Smørgrav return 0; 443921e764dfSDag-Erling Smørgrav } 444021e764dfSDag-Erling Smørgrav 444121e764dfSDag-Erling Smørgrav /* 4442a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 4443a0ee8cc6SDag-Erling Smørgrav * path from local side. 4444a0ee8cc6SDag-Erling Smørgrav */ 4445a0ee8cc6SDag-Erling Smørgrav static int 44464f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) 4447a0ee8cc6SDag-Erling Smørgrav { 44484f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4449190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 44504f52dfbbSDag-Erling Smørgrav int r; 44514f52dfbbSDag-Erling Smørgrav u_int i; 445219261079SEd Maste struct permission *perm = NULL; 4453a0ee8cc6SDag-Erling Smørgrav 4454190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4455190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4456190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) 4457a0ee8cc6SDag-Erling Smørgrav break; 4458190cef3dSDag-Erling Smørgrav perm = NULL; 4459a0ee8cc6SDag-Erling Smørgrav } 4460190cef3dSDag-Erling Smørgrav if (perm == NULL) { 446119261079SEd Maste debug_f("requested forward not found"); 4462a0ee8cc6SDag-Erling Smørgrav return -1; 4463a0ee8cc6SDag-Erling Smørgrav } 44644f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 44654f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 44664f52dfbbSDag-Erling Smørgrav "cancel-streamlocal-forward@openssh.com")) != 0 || 44674f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 44684f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, path)) != 0 || 44694f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 447019261079SEd Maste fatal_fr(r, "send cancel"); 4471a0ee8cc6SDag-Erling Smørgrav 4472190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4473a0ee8cc6SDag-Erling Smørgrav 4474a0ee8cc6SDag-Erling Smørgrav return 0; 4475a0ee8cc6SDag-Erling Smørgrav } 4476a0ee8cc6SDag-Erling Smørgrav 4477a0ee8cc6SDag-Erling Smørgrav /* 4478a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 4479a0ee8cc6SDag-Erling Smørgrav */ 4480a0ee8cc6SDag-Erling Smørgrav int 44814f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) 4482a0ee8cc6SDag-Erling Smørgrav { 4483a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 44844f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_streamlocal(ssh, 44854f52dfbbSDag-Erling Smørgrav fwd->listen_path); 4486a0ee8cc6SDag-Erling Smørgrav } else { 44874f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_tcpip(ssh, 44884f52dfbbSDag-Erling Smørgrav fwd->listen_host, 44894f52dfbbSDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port); 4490a0ee8cc6SDag-Erling Smørgrav } 4491a0ee8cc6SDag-Erling Smørgrav } 4492a0ee8cc6SDag-Erling Smørgrav 4493a0ee8cc6SDag-Erling Smørgrav /* 4494190cef3dSDag-Erling Smørgrav * Permits opening to any host/port if permitted_user[] is empty. This is 4495ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 4496ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 4497ca3176e7SBrian Feldman */ 4498ca3176e7SBrian Feldman void 4499190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where) 4500ca3176e7SBrian Feldman { 4501190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 4502190cef3dSDag-Erling Smørgrav 4503190cef3dSDag-Erling Smørgrav if (pset->num_permitted_user == 0) 4504190cef3dSDag-Erling Smørgrav pset->all_permitted = 1; 4505ca3176e7SBrian Feldman } 4506ca3176e7SBrian Feldman 4507190cef3dSDag-Erling Smørgrav /* 4508190cef3dSDag-Erling Smørgrav * Permit the specified host/port for forwarding. 4509190cef3dSDag-Erling Smørgrav */ 4510ca3176e7SBrian Feldman void 4511190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where, 4512190cef3dSDag-Erling Smørgrav char *host, int port) 4513ca3176e7SBrian Feldman { 4514190cef3dSDag-Erling Smørgrav int local = where == FORWARD_LOCAL; 4515190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 45164f52dfbbSDag-Erling Smørgrav 4517190cef3dSDag-Erling Smørgrav debug("allow %s forwarding to host %s port %d", 4518190cef3dSDag-Erling Smørgrav fwd_ident(who, where), host, port); 4519190cef3dSDag-Erling Smørgrav /* 4520190cef3dSDag-Erling Smørgrav * Remote forwards set listen_host/port, local forwards set 4521190cef3dSDag-Erling Smørgrav * host/port_to_connect. 4522190cef3dSDag-Erling Smørgrav */ 4523190cef3dSDag-Erling Smørgrav permission_set_add(ssh, who, where, 4524190cef3dSDag-Erling Smørgrav local ? host : 0, local ? port : 0, 4525190cef3dSDag-Erling Smørgrav local ? NULL : host, NULL, local ? 0 : port, NULL); 4526190cef3dSDag-Erling Smørgrav pset->all_permitted = 0; 4527190cef3dSDag-Erling Smørgrav } 4528190cef3dSDag-Erling Smørgrav 4529190cef3dSDag-Erling Smørgrav /* 4530190cef3dSDag-Erling Smørgrav * Administratively disable forwarding. 4531190cef3dSDag-Erling Smørgrav */ 4532190cef3dSDag-Erling Smørgrav void 4533190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where) 4534190cef3dSDag-Erling Smørgrav { 4535190cef3dSDag-Erling Smørgrav channel_clear_permission(ssh, FORWARD_ADM, where); 4536190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_ADM, where, 4537190cef3dSDag-Erling Smørgrav NULL, 0, NULL, NULL, 0, NULL); 4538190cef3dSDag-Erling Smørgrav } 4539190cef3dSDag-Erling Smørgrav 4540190cef3dSDag-Erling Smørgrav /* 4541190cef3dSDag-Erling Smørgrav * Clear a list of permitted opens. 4542190cef3dSDag-Erling Smørgrav */ 4543190cef3dSDag-Erling Smørgrav void 4544190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where) 4545190cef3dSDag-Erling Smørgrav { 4546190cef3dSDag-Erling Smørgrav struct permission **permp; 4547190cef3dSDag-Erling Smørgrav u_int *npermp; 4548190cef3dSDag-Erling Smørgrav 4549190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 4550190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); 4551190cef3dSDag-Erling Smørgrav *npermp = 0; 4552ca3176e7SBrian Feldman } 4553ca3176e7SBrian Feldman 4554462c32cbSDag-Erling Smørgrav /* 4555462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 4556462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 4557462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 4558462c32cbSDag-Erling Smørgrav */ 4559462c32cbSDag-Erling Smørgrav void 4560190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport) 4561462c32cbSDag-Erling Smørgrav { 4562190cef3dSDag-Erling Smørgrav struct permission_set *pset = &ssh->chanctxt->local_perms; 45634f52dfbbSDag-Erling Smørgrav 4564190cef3dSDag-Erling Smørgrav if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { 456519261079SEd Maste debug_f("index out of range: %d num_permitted_user %d", 456619261079SEd Maste idx, pset->num_permitted_user); 4567462c32cbSDag-Erling Smørgrav return; 4568462c32cbSDag-Erling Smørgrav } 4569462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 4570462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 4571462c32cbSDag-Erling Smørgrav newport, 4572190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].host_to_connect, 4573190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].port_to_connect); 45744f52dfbbSDag-Erling Smørgrav if (newport <= 0) 4575190cef3dSDag-Erling Smørgrav fwd_perm_clear(&pset->permitted_user[idx]); 45764f52dfbbSDag-Erling Smørgrav else { 4577190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].listen_port = 457819261079SEd Maste (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 4579462c32cbSDag-Erling Smørgrav } 4580462c32cbSDag-Erling Smørgrav } 4581462c32cbSDag-Erling Smørgrav 4582462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 4583462c32cbSDag-Erling Smørgrav int 4584462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 4585462c32cbSDag-Erling Smørgrav { 4586462c32cbSDag-Erling Smørgrav int port; 4587462c32cbSDag-Erling Smørgrav 4588462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 4589462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 4590462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 4591462c32cbSDag-Erling Smørgrav return port; 4592462c32cbSDag-Erling Smørgrav return -1; 4593462c32cbSDag-Erling Smørgrav } 4594462c32cbSDag-Erling Smørgrav 4595d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 4596d4af9e69SDag-Erling Smørgrav static int 4597d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 4598d4af9e69SDag-Erling Smørgrav { 4599d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 4600a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 46014f52dfbbSDag-Erling Smørgrav char ntop[NI_MAXHOST]; 46024f52dfbbSDag-Erling Smørgrav char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; 4603d4af9e69SDag-Erling Smørgrav 4604d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 4605a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 4606a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 4607a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 4608a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 4609a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 4610a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 4611a0ee8cc6SDag-Erling Smørgrav break; 4612a0ee8cc6SDag-Erling Smørgrav case AF_INET: 4613a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 4614d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 4615d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 4616d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 461738a52bd3SEd Maste error_f("getnameinfo failed"); 4618511b41d2SMark Murray continue; 4619511b41d2SMark Murray } 4620a0ee8cc6SDag-Erling Smørgrav break; 4621a0ee8cc6SDag-Erling Smørgrav default: 4622a0ee8cc6SDag-Erling Smørgrav continue; 4623a0ee8cc6SDag-Erling Smørgrav } 462438a52bd3SEd Maste debug_f("start for host %.100s ([%.100s]:%s)", 462538a52bd3SEd Maste cctx->host, ntop, strport); 4626d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 4627d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 4628d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 4629511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4630e73e9afaSDag-Erling Smørgrav else 4631e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 4632511b41d2SMark Murray continue; 4633511b41d2SMark Murray } 463421e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 463519261079SEd Maste fatal_f("set_nonblock(%d)", sock); 4636d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 4637d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 463838a52bd3SEd Maste debug_f("host %.100s ([%.100s]:%s): %.100s", 463938a52bd3SEd Maste cctx->host, ntop, strport, strerror(errno)); 4640d4af9e69SDag-Erling Smørgrav saved_errno = errno; 4641511b41d2SMark Murray close(sock); 4642d4af9e69SDag-Erling Smørgrav errno = saved_errno; 4643511b41d2SMark Murray continue; /* fail -- try next */ 4644511b41d2SMark Murray } 4645a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 4646a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 464738a52bd3SEd Maste debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d", 464838a52bd3SEd Maste cctx->host, ntop, strport, sock); 4649d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 4650a04a10f8SKris Kennaway return sock; 4651a04a10f8SKris Kennaway } 4652ca3176e7SBrian Feldman return -1; 4653ca3176e7SBrian Feldman } 4654ca3176e7SBrian Feldman 4655d4af9e69SDag-Erling Smørgrav static void 4656d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 4657d4af9e69SDag-Erling Smørgrav { 4658e4a9863fSDag-Erling Smørgrav free(cctx->host); 4659a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 4660a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 4661a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 4662a0ee8cc6SDag-Erling Smørgrav else 4663d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 4664a0ee8cc6SDag-Erling Smørgrav } 4665b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 4666d4af9e69SDag-Erling Smørgrav } 4667d4af9e69SDag-Erling Smørgrav 4668d93a896eSDag-Erling Smørgrav /* 46694f52dfbbSDag-Erling Smørgrav * Return connecting socket to remote host:port or local socket path, 4670d93a896eSDag-Erling Smørgrav * passing back the failure reason if appropriate. 4671d93a896eSDag-Erling Smørgrav */ 46724f52dfbbSDag-Erling Smørgrav static int 46734f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, 46744f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, struct channel_connect *cctx, 4675d93a896eSDag-Erling Smørgrav int *reason, const char **errmsg) 4676d4af9e69SDag-Erling Smørgrav { 4677d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 4678d4af9e69SDag-Erling Smørgrav int gaierr; 4679d4af9e69SDag-Erling Smørgrav int sock = -1; 4680d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 4681a0ee8cc6SDag-Erling Smørgrav 4682a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 4683a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 4684a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 4685a0ee8cc6SDag-Erling Smørgrav 4686a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 4687a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 46884f52dfbbSDag-Erling Smørgrav return -1; 4689a0ee8cc6SDag-Erling Smørgrav } 4690a0ee8cc6SDag-Erling Smørgrav 4691a0ee8cc6SDag-Erling Smørgrav /* 4692a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 4693a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 4694a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 4695a0ee8cc6SDag-Erling Smørgrav */ 4696a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 4697a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 4698a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 4699a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 4700a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 47014f52dfbbSDag-Erling Smørgrav ai->ai_socktype = socktype; 4702a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 4703a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 4704a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 4705a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 47064f52dfbbSDag-Erling Smørgrav cctx->aitop = ai; 4707a0ee8cc6SDag-Erling Smørgrav } else { 4708d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 47094f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 47104f52dfbbSDag-Erling Smørgrav hints.ai_socktype = socktype; 4711d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 47124f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) 4713d93a896eSDag-Erling Smørgrav != 0) { 4714d93a896eSDag-Erling Smørgrav if (errmsg != NULL) 4715d93a896eSDag-Erling Smørgrav *errmsg = ssh_gai_strerror(gaierr); 4716d93a896eSDag-Erling Smørgrav if (reason != NULL) 4717d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_CONNECT_FAILED; 4718a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 4719d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 47204f52dfbbSDag-Erling Smørgrav return -1; 4721d4af9e69SDag-Erling Smørgrav } 4722a0ee8cc6SDag-Erling Smørgrav } 4723d4af9e69SDag-Erling Smørgrav 47244f52dfbbSDag-Erling Smørgrav cctx->host = xstrdup(name); 47254f52dfbbSDag-Erling Smørgrav cctx->port = port; 47264f52dfbbSDag-Erling Smørgrav cctx->ai = cctx->aitop; 4727d4af9e69SDag-Erling Smørgrav 47284f52dfbbSDag-Erling Smørgrav if ((sock = connect_next(cctx)) == -1) { 4729d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 4730a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 47314f52dfbbSDag-Erling Smørgrav return -1; 4732d4af9e69SDag-Erling Smørgrav } 47334f52dfbbSDag-Erling Smørgrav 47344f52dfbbSDag-Erling Smørgrav return sock; 4735d4af9e69SDag-Erling Smørgrav } 4736d4af9e69SDag-Erling Smørgrav 4737d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 4738d93a896eSDag-Erling Smørgrav static Channel * 47394f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port, 47404f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4741d93a896eSDag-Erling Smørgrav { 47424f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 47434f52dfbbSDag-Erling Smørgrav Channel *c; 47444f52dfbbSDag-Erling Smørgrav int sock; 47454f52dfbbSDag-Erling Smørgrav 47464f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 47474f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 47484f52dfbbSDag-Erling Smørgrav &cctx, NULL, NULL); 47494f52dfbbSDag-Erling Smørgrav if (sock == -1) { 47504f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 47514f52dfbbSDag-Erling Smørgrav return NULL; 47524f52dfbbSDag-Erling Smørgrav } 47534f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 47544f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 47554f52dfbbSDag-Erling Smørgrav c->host_port = port; 47564f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 47574f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 47584f52dfbbSDag-Erling Smørgrav 47594f52dfbbSDag-Erling Smørgrav return c; 4760d93a896eSDag-Erling Smørgrav } 4761d93a896eSDag-Erling Smørgrav 4762ca86bcf2SDag-Erling Smørgrav /* 4763ca86bcf2SDag-Erling Smørgrav * returns either the newly connected channel or the downstream channel 4764ca86bcf2SDag-Erling Smørgrav * that needs to deal with this connection. 4765ca86bcf2SDag-Erling Smørgrav */ 4766d4af9e69SDag-Erling Smørgrav Channel * 47674f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, 4768a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 4769d4af9e69SDag-Erling Smørgrav { 47704f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4771190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 47724f52dfbbSDag-Erling Smørgrav u_int i; 4773190cef3dSDag-Erling Smørgrav struct permission *perm; 4774d4af9e69SDag-Erling Smørgrav 4775190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4776190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4777190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, 4778190cef3dSDag-Erling Smørgrav listen_host, listen_port, 1)) { 4779190cef3dSDag-Erling Smørgrav if (perm->downstream) 4780190cef3dSDag-Erling Smørgrav return perm->downstream; 4781190cef3dSDag-Erling Smørgrav if (perm->port_to_connect == 0) 47824f52dfbbSDag-Erling Smørgrav return rdynamic_connect_prepare(ssh, 47834f52dfbbSDag-Erling Smørgrav ctype, rname); 47844f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4785190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 47864f52dfbbSDag-Erling Smørgrav ctype, rname); 4787d4af9e69SDag-Erling Smørgrav } 4788d4af9e69SDag-Erling Smørgrav } 4789d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 4790d4af9e69SDag-Erling Smørgrav listen_port); 4791d4af9e69SDag-Erling Smørgrav return NULL; 4792d4af9e69SDag-Erling Smørgrav } 4793d4af9e69SDag-Erling Smørgrav 4794a0ee8cc6SDag-Erling Smørgrav Channel * 47954f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path, 47964f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4797a0ee8cc6SDag-Erling Smørgrav { 47984f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4799190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 48004f52dfbbSDag-Erling Smørgrav u_int i; 4801190cef3dSDag-Erling Smørgrav struct permission *perm; 4802a0ee8cc6SDag-Erling Smørgrav 4803190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4804190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4805190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) { 48064f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4807190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 48084f52dfbbSDag-Erling Smørgrav ctype, rname); 4809a0ee8cc6SDag-Erling Smørgrav } 4810a0ee8cc6SDag-Erling Smørgrav } 4811a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 4812a0ee8cc6SDag-Erling Smørgrav path); 4813a0ee8cc6SDag-Erling Smørgrav return NULL; 4814a0ee8cc6SDag-Erling Smørgrav } 4815a0ee8cc6SDag-Erling Smørgrav 4816ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 4817d4af9e69SDag-Erling Smørgrav Channel * 48184f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, 48194f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, int *reason, const char **errmsg) 4820ca3176e7SBrian Feldman { 48214f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4822190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 48234f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 48244f52dfbbSDag-Erling Smørgrav Channel *c; 48254f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 48264f52dfbbSDag-Erling Smørgrav int sock; 4827190cef3dSDag-Erling Smørgrav struct permission *perm; 4828ca3176e7SBrian Feldman 4829190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4830ca3176e7SBrian Feldman if (!permit) { 4831190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4832190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4833190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4834ca3176e7SBrian Feldman permit = 1; 4835a0ee8cc6SDag-Erling Smørgrav break; 4836a0ee8cc6SDag-Erling Smørgrav } 4837ca3176e7SBrian Feldman } 48384f52dfbbSDag-Erling Smørgrav } 4839333ee039SDag-Erling Smørgrav 4840190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4841333ee039SDag-Erling Smørgrav permit_adm = 0; 4842190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4843190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4844190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4845333ee039SDag-Erling Smørgrav permit_adm = 1; 4846a0ee8cc6SDag-Erling Smørgrav break; 4847a0ee8cc6SDag-Erling Smørgrav } 4848333ee039SDag-Erling Smørgrav } 48494f52dfbbSDag-Erling Smørgrav } 4850333ee039SDag-Erling Smørgrav 4851333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 485219261079SEd Maste logit("Received request from %.100s port %d to connect to " 485319261079SEd Maste "host %.100s port %d, but the request was denied.", 485419261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); 4855d93a896eSDag-Erling Smørgrav if (reason != NULL) 4856d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; 4857d4af9e69SDag-Erling Smørgrav return NULL; 4858ca3176e7SBrian Feldman } 48594f52dfbbSDag-Erling Smørgrav 48604f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 48614f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 48624f52dfbbSDag-Erling Smørgrav &cctx, reason, errmsg); 48634f52dfbbSDag-Erling Smørgrav if (sock == -1) { 48644f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 48654f52dfbbSDag-Erling Smørgrav return NULL; 48664f52dfbbSDag-Erling Smørgrav } 48674f52dfbbSDag-Erling Smørgrav 48684f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 48694f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 48704f52dfbbSDag-Erling Smørgrav c->host_port = port; 48714f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 48724f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 48734f52dfbbSDag-Erling Smørgrav 48744f52dfbbSDag-Erling Smørgrav return c; 4875ca3176e7SBrian Feldman } 4876ca3176e7SBrian Feldman 4877a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 4878a0ee8cc6SDag-Erling Smørgrav Channel * 48794f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path, 48804f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4881a0ee8cc6SDag-Erling Smørgrav { 48824f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4883190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 48844f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4885190cef3dSDag-Erling Smørgrav struct permission *perm; 4886a0ee8cc6SDag-Erling Smørgrav 4887190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4888a0ee8cc6SDag-Erling Smørgrav if (!permit) { 4889190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4890190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4891190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4892a0ee8cc6SDag-Erling Smørgrav permit = 1; 4893a0ee8cc6SDag-Erling Smørgrav break; 4894a0ee8cc6SDag-Erling Smørgrav } 4895a0ee8cc6SDag-Erling Smørgrav } 48964f52dfbbSDag-Erling Smørgrav } 4897a0ee8cc6SDag-Erling Smørgrav 4898190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4899a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 4900190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4901190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4902190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4903a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 4904a0ee8cc6SDag-Erling Smørgrav break; 4905a0ee8cc6SDag-Erling Smørgrav } 4906a0ee8cc6SDag-Erling Smørgrav } 49074f52dfbbSDag-Erling Smørgrav } 4908a0ee8cc6SDag-Erling Smørgrav 4909a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 4910a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 4911a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 4912a0ee8cc6SDag-Erling Smørgrav return NULL; 4913a0ee8cc6SDag-Erling Smørgrav } 49144f52dfbbSDag-Erling Smørgrav return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); 4915a0ee8cc6SDag-Erling Smørgrav } 4916a0ee8cc6SDag-Erling Smørgrav 491721e764dfSDag-Erling Smørgrav void 49184f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh) 491921e764dfSDag-Erling Smørgrav { 49204f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 492121e764dfSDag-Erling Smørgrav struct winsize ws; 49224f52dfbbSDag-Erling Smørgrav int r; 49234f52dfbbSDag-Erling Smørgrav u_int i; 492421e764dfSDag-Erling Smørgrav 49254f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 49264f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || 49274f52dfbbSDag-Erling Smørgrav sc->channels[i]->type != SSH_CHANNEL_OPEN) 492821e764dfSDag-Erling Smørgrav continue; 492919261079SEd Maste if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) 493021e764dfSDag-Erling Smørgrav continue; 49314f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, i, "window-change", 0); 49324f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 49334f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 49344f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 49354f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || 49364f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 493719261079SEd Maste fatal_fr(r, "channel %u; send window-change", i); 493821e764dfSDag-Erling Smørgrav } 493921e764dfSDag-Erling Smørgrav } 494021e764dfSDag-Erling Smørgrav 49414f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ 49424f52dfbbSDag-Erling Smørgrav static Channel * 49434f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) 49444f52dfbbSDag-Erling Smørgrav { 49454f52dfbbSDag-Erling Smørgrav Channel *c; 49464f52dfbbSDag-Erling Smørgrav int r; 49474f52dfbbSDag-Erling Smørgrav 49484f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, 49494f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 49504f52dfbbSDag-Erling Smørgrav c->host_port = 0; 49514f52dfbbSDag-Erling Smørgrav c->path = NULL; 49524f52dfbbSDag-Erling Smørgrav 49534f52dfbbSDag-Erling Smørgrav /* 49544f52dfbbSDag-Erling Smørgrav * We need to open the channel before we have a FD, 49554f52dfbbSDag-Erling Smørgrav * so that we can get SOCKS header from peer. 49564f52dfbbSDag-Erling Smørgrav */ 49574f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 49584f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 49594f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 49604f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 496119261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) 496219261079SEd Maste fatal_fr(r, "channel %i; confirm", c->self); 49634f52dfbbSDag-Erling Smørgrav return c; 49644f52dfbbSDag-Erling Smørgrav } 49654f52dfbbSDag-Erling Smørgrav 49664f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */ 49674f52dfbbSDag-Erling Smørgrav static int 49684f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c) 49694f52dfbbSDag-Erling Smørgrav { 497019261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 497119261079SEd Maste struct permission_set *pset = &sc->local_perms; 497219261079SEd Maste struct permission *perm; 49734f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 497419261079SEd Maste u_int i, permit_adm = 1; 49754f52dfbbSDag-Erling Smørgrav int sock; 49764f52dfbbSDag-Erling Smørgrav 497719261079SEd Maste if (pset->num_permitted_admin > 0) { 497819261079SEd Maste permit_adm = 0; 497919261079SEd Maste for (i = 0; i < pset->num_permitted_admin; i++) { 498019261079SEd Maste perm = &pset->permitted_admin[i]; 498119261079SEd Maste if (open_match(perm, c->path, c->host_port)) { 498219261079SEd Maste permit_adm = 1; 498319261079SEd Maste break; 498419261079SEd Maste } 498519261079SEd Maste } 498619261079SEd Maste } 498719261079SEd Maste if (!permit_adm) { 498819261079SEd Maste debug_f("requested forward not permitted"); 498919261079SEd Maste return -1; 499019261079SEd Maste } 499119261079SEd Maste 49924f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 49934f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, 49944f52dfbbSDag-Erling Smørgrav NULL, &cctx, NULL, NULL); 49954f52dfbbSDag-Erling Smørgrav if (sock == -1) 49964f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 49974f52dfbbSDag-Erling Smørgrav else { 49984f52dfbbSDag-Erling Smørgrav /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ 49994f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_RDYNAMIC_FINISH; 50004f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 50014f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); 50024f52dfbbSDag-Erling Smørgrav } 50034f52dfbbSDag-Erling Smørgrav return sock; 50044f52dfbbSDag-Erling Smørgrav } 50054f52dfbbSDag-Erling Smørgrav 5006af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 5007511b41d2SMark Murray 5008511b41d2SMark Murray /* 5009511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 5010a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 5011a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 5012511b41d2SMark Murray */ 5013af12a3e7SDag-Erling Smørgrav int 50144f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset, 50154f52dfbbSDag-Erling Smørgrav int x11_use_localhost, int single_connection, 50164f52dfbbSDag-Erling Smørgrav u_int *display_numberp, int **chanids) 5017511b41d2SMark Murray { 5018af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 5019511b41d2SMark Murray int display_number, sock; 5020511b41d2SMark Murray u_short port; 5021511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 5022511b41d2SMark Murray char strport[NI_MAXSERV]; 5023511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 5024511b41d2SMark Murray 5025b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 5026b74df5b2SDag-Erling Smørgrav return -1; 5027b74df5b2SDag-Erling Smørgrav 5028511b41d2SMark Murray for (display_number = x11_display_offset; 5029511b41d2SMark Murray display_number < MAX_DISPLAYS; 5030511b41d2SMark Murray display_number++) { 5031511b41d2SMark Murray port = 6000 + display_number; 5032511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 50334f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 5034af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 5035511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 5036511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 50374f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(NULL, strport, 50384f52dfbbSDag-Erling Smørgrav &hints, &aitop)) != 0) { 5039d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 5040af12a3e7SDag-Erling Smørgrav return -1; 5041511b41d2SMark Murray } 5042511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 50434f52dfbbSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 50444f52dfbbSDag-Erling Smørgrav ai->ai_family != AF_INET6) 5045511b41d2SMark Murray continue; 5046221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 5047221552e4SDag-Erling Smørgrav ai->ai_protocol); 504819261079SEd Maste if (sock == -1) { 50498ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 50508ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 50518ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 50528ad9b54aSDag-Erling Smørgrav #endif 50538ad9b54aSDag-Erling Smørgrav ) { 5054511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 505521e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 5056af12a3e7SDag-Erling Smørgrav return -1; 5057989dd127SDag-Erling Smørgrav } else { 5058989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 5059989dd127SDag-Erling Smørgrav ai->ai_family); 5060989dd127SDag-Erling Smørgrav continue; 5061511b41d2SMark Murray } 5062989dd127SDag-Erling Smørgrav } 5063b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 5064b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 5065d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 506647dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 506719261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 506819261079SEd Maste debug2_f("bind port %d: %.100s", port, 506919261079SEd Maste strerror(errno)); 5070511b41d2SMark Murray close(sock); 50714f52dfbbSDag-Erling Smørgrav for (n = 0; n < num_socks; n++) 5072511b41d2SMark Murray close(socks[n]); 5073511b41d2SMark Murray num_socks = 0; 5074511b41d2SMark Murray break; 5075511b41d2SMark Murray } 5076511b41d2SMark Murray socks[num_socks++] = sock; 5077511b41d2SMark Murray if (num_socks == NUM_SOCKS) 5078511b41d2SMark Murray break; 5079511b41d2SMark Murray } 5080ca3176e7SBrian Feldman freeaddrinfo(aitop); 5081511b41d2SMark Murray if (num_socks > 0) 5082511b41d2SMark Murray break; 5083511b41d2SMark Murray } 5084511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 5085511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 5086af12a3e7SDag-Erling Smørgrav return -1; 5087511b41d2SMark Murray } 5088511b41d2SMark Murray /* Start listening for connections on the socket. */ 5089511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 5090511b41d2SMark Murray sock = socks[n]; 509119261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 5092511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 5093511b41d2SMark Murray close(sock); 5094af12a3e7SDag-Erling Smørgrav return -1; 5095511b41d2SMark Murray } 5096511b41d2SMark Murray } 5097511b41d2SMark Murray 5098511b41d2SMark Murray /* Allocate a channel for each socket. */ 5099333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 5100511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 5101511b41d2SMark Murray sock = socks[n]; 5102f374ba41SEd Maste nc = channel_new(ssh, "x11-listener", 5103a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 5104a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 5105221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 5106af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 5107d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 5108511b41d2SMark Murray } 5109d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 5110511b41d2SMark Murray 5111af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 5112a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 51134f52dfbbSDag-Erling Smørgrav return 0; 5114511b41d2SMark Murray } 5115511b41d2SMark Murray 5116af12a3e7SDag-Erling Smørgrav static int 5117cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 5118511b41d2SMark Murray { 5119511b41d2SMark Murray int sock; 5120511b41d2SMark Murray struct sockaddr_un addr; 5121511b41d2SMark Murray 5122511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 5123535af610SEd Maste if (sock == -1) { 5124511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 5125535af610SEd Maste return -1; 5126535af610SEd Maste } 5127511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 5128511b41d2SMark Murray addr.sun_family = AF_UNIX; 5129cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 5130511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 5131511b41d2SMark Murray return sock; 5132511b41d2SMark Murray close(sock); 5133511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 5134511b41d2SMark Murray return -1; 5135511b41d2SMark Murray } 5136511b41d2SMark Murray 5137cce7d346SDag-Erling Smørgrav static int 5138cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 5139cce7d346SDag-Erling Smørgrav { 5140cce7d346SDag-Erling Smørgrav char buf[1024]; 5141cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 5142cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 5143cce7d346SDag-Erling Smørgrav } 5144cce7d346SDag-Erling Smørgrav 5145d93a896eSDag-Erling Smørgrav #ifdef __APPLE__ 5146d93a896eSDag-Erling Smørgrav static int 5147d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen) 5148d93a896eSDag-Erling Smørgrav { 5149d93a896eSDag-Erling Smørgrav struct stat sbuf; 5150d93a896eSDag-Erling Smørgrav 5151d93a896eSDag-Erling Smørgrav if (strlcpy(path, display, pathlen) >= pathlen) { 5152d93a896eSDag-Erling Smørgrav error("%s: display path too long", __func__); 5153d93a896eSDag-Erling Smørgrav return 0; 5154d93a896eSDag-Erling Smørgrav } 5155d93a896eSDag-Erling Smørgrav if (display[0] != '/') 5156d93a896eSDag-Erling Smørgrav return 0; 5157d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 5158d93a896eSDag-Erling Smørgrav return 1; 5159d93a896eSDag-Erling Smørgrav } else { 5160d93a896eSDag-Erling Smørgrav char *dot = strrchr(path, '.'); 5161d93a896eSDag-Erling Smørgrav if (dot != NULL) { 5162d93a896eSDag-Erling Smørgrav *dot = '\0'; 5163d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 5164d93a896eSDag-Erling Smørgrav return 1; 5165d93a896eSDag-Erling Smørgrav } 5166d93a896eSDag-Erling Smørgrav } 5167d93a896eSDag-Erling Smørgrav } 5168d93a896eSDag-Erling Smørgrav return 0; 5169d93a896eSDag-Erling Smørgrav } 5170d93a896eSDag-Erling Smørgrav #endif 5171d93a896eSDag-Erling Smørgrav 5172a04a10f8SKris Kennaway int 51734f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh) 5174511b41d2SMark Murray { 5175333ee039SDag-Erling Smørgrav u_int display_number; 5176511b41d2SMark Murray const char *display; 5177a04a10f8SKris Kennaway char buf[1024], *cp; 5178511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 5179511b41d2SMark Murray char strport[NI_MAXSERV]; 5180333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 5181511b41d2SMark Murray 5182511b41d2SMark Murray /* Try to open a socket for the local X server. */ 5183511b41d2SMark Murray display = getenv("DISPLAY"); 5184511b41d2SMark Murray if (!display) { 5185511b41d2SMark Murray error("DISPLAY not set."); 5186a04a10f8SKris Kennaway return -1; 5187511b41d2SMark Murray } 5188511b41d2SMark Murray /* 5189511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 5190511b41d2SMark Murray * connection to the real X server. 5191511b41d2SMark Murray */ 5192511b41d2SMark Murray 5193cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 5194d93a896eSDag-Erling Smørgrav /* Check if display is a path to a socket (as set by launchd). */ 5195d93a896eSDag-Erling Smørgrav { 5196d93a896eSDag-Erling Smørgrav char path[PATH_MAX]; 5197d93a896eSDag-Erling Smørgrav 5198d93a896eSDag-Erling Smørgrav if (is_path_to_xsocket(display, path, sizeof(path))) { 5199d93a896eSDag-Erling Smørgrav debug("x11_connect_display: $DISPLAY is launchd"); 5200d93a896eSDag-Erling Smørgrav 5201d93a896eSDag-Erling Smørgrav /* Create a socket. */ 5202d93a896eSDag-Erling Smørgrav sock = connect_local_xsocket_path(path); 5203cce7d346SDag-Erling Smørgrav if (sock < 0) 5204cce7d346SDag-Erling Smørgrav return -1; 5205cce7d346SDag-Erling Smørgrav 5206cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 5207cce7d346SDag-Erling Smørgrav return sock; 5208cce7d346SDag-Erling Smørgrav } 5209d93a896eSDag-Erling Smørgrav } 5210cce7d346SDag-Erling Smørgrav #endif 5211511b41d2SMark Murray /* 5212511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 5213511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 5214511b41d2SMark Murray */ 5215511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 5216511b41d2SMark Murray display[0] == ':') { 5217511b41d2SMark Murray /* Connect to the unix domain socket. */ 52184f52dfbbSDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", 52194f52dfbbSDag-Erling Smørgrav &display_number) != 1) { 52204f52dfbbSDag-Erling Smørgrav error("Could not parse display number from DISPLAY: " 52214f52dfbbSDag-Erling Smørgrav "%.100s", display); 5222a04a10f8SKris Kennaway return -1; 5223511b41d2SMark Murray } 5224511b41d2SMark Murray /* Create a socket. */ 5225511b41d2SMark Murray sock = connect_local_xsocket(display_number); 5226511b41d2SMark Murray if (sock < 0) 5227a04a10f8SKris Kennaway return -1; 5228511b41d2SMark Murray 5229511b41d2SMark Murray /* OK, we now have a connection to the display. */ 5230a04a10f8SKris Kennaway return sock; 5231511b41d2SMark Murray } 5232511b41d2SMark Murray /* 5233511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 5234511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 5235511b41d2SMark Murray */ 5236af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 5237511b41d2SMark Murray cp = strchr(buf, ':'); 5238511b41d2SMark Murray if (!cp) { 5239511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 5240a04a10f8SKris Kennaway return -1; 5241511b41d2SMark Murray } 5242511b41d2SMark Murray *cp = 0; 52434f52dfbbSDag-Erling Smørgrav /* 52444f52dfbbSDag-Erling Smørgrav * buf now contains the host name. But first we parse the 52454f52dfbbSDag-Erling Smørgrav * display number. 52464f52dfbbSDag-Erling Smørgrav */ 5247333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 5248511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 5249511b41d2SMark Murray display); 5250a04a10f8SKris Kennaway return -1; 5251511b41d2SMark Murray } 5252511b41d2SMark Murray 5253511b41d2SMark Murray /* Look up the host address */ 5254511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 52554f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 5256511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 5257333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 5258511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 5259d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 5260d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 5261a04a10f8SKris Kennaway return -1; 5262511b41d2SMark Murray } 5263511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 5264511b41d2SMark Murray /* Create a socket. */ 5265221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 526619261079SEd Maste if (sock == -1) { 5267221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 5268511b41d2SMark Murray continue; 5269511b41d2SMark Murray } 5270511b41d2SMark Murray /* Connect it to the display. */ 527119261079SEd Maste if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 5272333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 5273a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 5274511b41d2SMark Murray close(sock); 5275511b41d2SMark Murray continue; 5276511b41d2SMark Murray } 5277511b41d2SMark Murray /* Success */ 5278511b41d2SMark Murray break; 5279a04a10f8SKris Kennaway } 5280511b41d2SMark Murray freeaddrinfo(aitop); 5281511b41d2SMark Murray if (!ai) { 52824f52dfbbSDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 52834f52dfbbSDag-Erling Smørgrav 6000 + display_number, strerror(errno)); 5284a04a10f8SKris Kennaway return -1; 5285511b41d2SMark Murray } 5286af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 5287a04a10f8SKris Kennaway return sock; 5288a04a10f8SKris Kennaway } 5289511b41d2SMark Murray 5290a04a10f8SKris Kennaway /* 5291511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 5292511b41d2SMark Murray * data, and enables authentication spoofing. 5293af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 5294511b41d2SMark Murray */ 5295511b41d2SMark Murray void 52964f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, 52974f52dfbbSDag-Erling Smørgrav const char *disp, const char *proto, const char *data, int want_reply) 5298511b41d2SMark Murray { 52994f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 5300ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 5301d4ecd108SDag-Erling Smørgrav u_int i, value; 5302511b41d2SMark Murray const char *cp; 53034f52dfbbSDag-Erling Smørgrav char *new_data; 53044f52dfbbSDag-Erling Smørgrav int r, screen_number; 5305511b41d2SMark Murray 53064f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_display == NULL) 53074f52dfbbSDag-Erling Smørgrav sc->x11_saved_display = xstrdup(disp); 53084f52dfbbSDag-Erling Smørgrav else if (strcmp(disp, sc->x11_saved_display) != 0) { 5309d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 5310d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 5311d4ecd108SDag-Erling Smørgrav return; 5312d4ecd108SDag-Erling Smørgrav } 5313d4ecd108SDag-Erling Smørgrav 5314d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 5315511b41d2SMark Murray if (cp) 5316511b41d2SMark Murray cp = strchr(cp, '.'); 5317511b41d2SMark Murray if (cp) 5318333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 5319511b41d2SMark Murray else 5320511b41d2SMark Murray screen_number = 0; 5321511b41d2SMark Murray 53224f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_proto == NULL) { 5323511b41d2SMark Murray /* Save protocol name. */ 53244f52dfbbSDag-Erling Smørgrav sc->x11_saved_proto = xstrdup(proto); 5325ca86bcf2SDag-Erling Smørgrav 5326ca86bcf2SDag-Erling Smørgrav /* Extract real authentication data. */ 53274f52dfbbSDag-Erling Smørgrav sc->x11_saved_data = xmalloc(data_len); 5328511b41d2SMark Murray for (i = 0; i < data_len; i++) { 532919261079SEd Maste if (sscanf(data + 2 * i, "%2x", &value) != 1) { 5330d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 5331d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 533219261079SEd Maste } 53334f52dfbbSDag-Erling Smørgrav sc->x11_saved_data[i] = value; 5334511b41d2SMark Murray } 53354f52dfbbSDag-Erling Smørgrav sc->x11_saved_data_len = data_len; 5336ca86bcf2SDag-Erling Smørgrav 5337ca86bcf2SDag-Erling Smørgrav /* Generate fake data of the same length. */ 53384f52dfbbSDag-Erling Smørgrav sc->x11_fake_data = xmalloc(data_len); 53394f52dfbbSDag-Erling Smørgrav arc4random_buf(sc->x11_fake_data, data_len); 53404f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len = data_len; 5341d4ecd108SDag-Erling Smørgrav } 5342511b41d2SMark Murray 5343511b41d2SMark Murray /* Convert the fake data into hex. */ 53444f52dfbbSDag-Erling Smørgrav new_data = tohex(sc->x11_fake_data, data_len); 5345511b41d2SMark Murray 5346511b41d2SMark Murray /* Send the request packet. */ 53474f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, client_session_id, "x11-req", want_reply); 53484f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ 53494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, proto)) != 0 || 53504f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, new_data)) != 0 || 53514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, screen_number)) != 0 || 53524f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 53534f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 535419261079SEd Maste fatal_fr(r, "send x11-req"); 5355e4a9863fSDag-Erling Smørgrav free(new_data); 5356511b41d2SMark Murray } 5357