1*f374ba41SEd Maste /* $OpenBSD: channels.c,v 1.427 2023/01/18 02:00:10 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 154*f374ba41SEd Maste /* Used to record timeouts per channel type */ 155*f374ba41SEd Maste struct ssh_channel_timeout { 156*f374ba41SEd Maste char *type_pattern; 157*f374ba41SEd Maste u_int timeout_secs; 158*f374ba41SEd Maste }; 159*f374ba41SEd 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 */ 2014f52dfbbSDag-Erling Smørgrav u_int 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; 213*f374ba41SEd Maste 214*f374ba41SEd Maste /* Channel timeouts by type */ 215*f374ba41SEd Maste struct ssh_channel_timeout *timeouts; 216*f374ba41SEd Maste size_t ntimeouts; 2174f52dfbbSDag-Erling Smørgrav }; 218ca3176e7SBrian Feldman 219af12a3e7SDag-Erling Smørgrav /* helper */ 2204f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); 221ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host); 222ca3176e7SBrian Feldman 223d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 224d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 225d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 2264f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); 2274f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *); 2284f52dfbbSDag-Erling Smørgrav 2294f52dfbbSDag-Erling Smørgrav /* Setup helper */ 2304f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc); 231d4af9e69SDag-Erling Smørgrav 232af12a3e7SDag-Erling Smørgrav /* -- channel core */ 233a04a10f8SKris Kennaway 2344f52dfbbSDag-Erling Smørgrav void 2354f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh) 2364f52dfbbSDag-Erling Smørgrav { 2374f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc; 2384f52dfbbSDag-Erling Smørgrav 23919261079SEd Maste if ((sc = calloc(1, sizeof(*sc))) == NULL) 24019261079SEd Maste fatal_f("allocation failed"); 2414f52dfbbSDag-Erling Smørgrav sc->channels_alloc = 10; 2424f52dfbbSDag-Erling Smørgrav sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); 2434f52dfbbSDag-Erling Smørgrav sc->IPv4or6 = AF_UNSPEC; 2444f52dfbbSDag-Erling Smørgrav channel_handler_init(sc); 2454f52dfbbSDag-Erling Smørgrav 2464f52dfbbSDag-Erling Smørgrav ssh->chanctxt = sc; 2474f52dfbbSDag-Erling Smørgrav } 2484f52dfbbSDag-Erling Smørgrav 249a04a10f8SKris Kennaway Channel * 2504f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id) 251a04a10f8SKris Kennaway { 252a04a10f8SKris Kennaway Channel *c; 253af12a3e7SDag-Erling Smørgrav 2544f52dfbbSDag-Erling Smørgrav if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { 25519261079SEd Maste logit_f("%d: bad id", id); 256a04a10f8SKris Kennaway return NULL; 257a04a10f8SKris Kennaway } 2584f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[id]; 259af12a3e7SDag-Erling Smørgrav if (c == NULL) { 26019261079SEd Maste logit_f("%d: bad id: channel free", id); 261a04a10f8SKris Kennaway return NULL; 262a04a10f8SKris Kennaway } 263a04a10f8SKris Kennaway return c; 264a04a10f8SKris Kennaway } 265a04a10f8SKris Kennaway 266ca86bcf2SDag-Erling Smørgrav Channel * 2674f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id) 268ca86bcf2SDag-Erling Smørgrav { 269ca86bcf2SDag-Erling Smørgrav Channel *c; 270ca86bcf2SDag-Erling Smørgrav u_int i; 271ca86bcf2SDag-Erling Smørgrav 2724f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 2734f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 2744f52dfbbSDag-Erling Smørgrav if (c != NULL && c->have_remote_id && c->remote_id == remote_id) 275ca86bcf2SDag-Erling Smørgrav return c; 276ca86bcf2SDag-Erling Smørgrav } 277ca86bcf2SDag-Erling Smørgrav return NULL; 278ca86bcf2SDag-Erling Smørgrav } 279ca86bcf2SDag-Erling Smørgrav 280a04a10f8SKris Kennaway /* 281b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 282b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 283b74df5b2SDag-Erling Smørgrav */ 284b74df5b2SDag-Erling Smørgrav Channel * 2854f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id) 286b74df5b2SDag-Erling Smørgrav { 287b74df5b2SDag-Erling Smørgrav Channel *c; 288b74df5b2SDag-Erling Smørgrav 2894f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 2904f52dfbbSDag-Erling Smørgrav return NULL; 291b74df5b2SDag-Erling Smørgrav 292b74df5b2SDag-Erling Smørgrav switch (c->type) { 293b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 294b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 295b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 296b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 2974f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 2984f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 299b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 300b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 301e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 302ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 3034f52dfbbSDag-Erling Smørgrav return c; 304b74df5b2SDag-Erling Smørgrav } 305b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 3064f52dfbbSDag-Erling Smørgrav return NULL; 307b74df5b2SDag-Erling Smørgrav } 308b74df5b2SDag-Erling Smørgrav 309b74df5b2SDag-Erling Smørgrav /* 310*f374ba41SEd Maste * Add a timeout for open channels whose c->ctype (or c->xctype if it is set) 311*f374ba41SEd Maste * match type_pattern. 312*f374ba41SEd Maste */ 313*f374ba41SEd Maste void 314*f374ba41SEd Maste channel_add_timeout(struct ssh *ssh, const char *type_pattern, 315*f374ba41SEd Maste u_int timeout_secs) 316*f374ba41SEd Maste { 317*f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 318*f374ba41SEd Maste 319*f374ba41SEd Maste debug2_f("channel type \"%s\" timeout %u seconds", 320*f374ba41SEd Maste type_pattern, timeout_secs); 321*f374ba41SEd Maste sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts, 322*f374ba41SEd Maste sc->ntimeouts + 1, sizeof(*sc->timeouts)); 323*f374ba41SEd Maste sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern); 324*f374ba41SEd Maste sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs; 325*f374ba41SEd Maste sc->ntimeouts++; 326*f374ba41SEd Maste } 327*f374ba41SEd Maste 328*f374ba41SEd Maste /* Clears all previously-added channel timeouts */ 329*f374ba41SEd Maste void 330*f374ba41SEd Maste channel_clear_timeouts(struct ssh *ssh) 331*f374ba41SEd Maste { 332*f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 333*f374ba41SEd Maste size_t i; 334*f374ba41SEd Maste 335*f374ba41SEd Maste debug3_f("clearing"); 336*f374ba41SEd Maste for (i = 0; i < sc->ntimeouts; i++) 337*f374ba41SEd Maste free(sc->timeouts[i].type_pattern); 338*f374ba41SEd Maste free(sc->timeouts); 339*f374ba41SEd Maste sc->timeouts = NULL; 340*f374ba41SEd Maste sc->ntimeouts = 0; 341*f374ba41SEd Maste } 342*f374ba41SEd Maste 343*f374ba41SEd Maste static u_int 344*f374ba41SEd Maste lookup_timeout(struct ssh *ssh, const char *type) 345*f374ba41SEd Maste { 346*f374ba41SEd Maste struct ssh_channels *sc = ssh->chanctxt; 347*f374ba41SEd Maste size_t i; 348*f374ba41SEd Maste 349*f374ba41SEd Maste for (i = 0; i < sc->ntimeouts; i++) { 350*f374ba41SEd Maste if (match_pattern(type, sc->timeouts[i].type_pattern)) 351*f374ba41SEd Maste return sc->timeouts[i].timeout_secs; 352*f374ba41SEd Maste } 353*f374ba41SEd Maste 354*f374ba41SEd Maste return 0; 355*f374ba41SEd Maste } 356*f374ba41SEd Maste 357*f374ba41SEd Maste /* 358*f374ba41SEd Maste * Sets "extended type" of a channel; used by session layer to add additional 359*f374ba41SEd Maste * information about channel types (e.g. shell, login, subsystem) that can then 360*f374ba41SEd Maste * be used to select timeouts. 361*f374ba41SEd Maste * Will reset c->inactive_deadline as a side-effect. 362*f374ba41SEd Maste */ 363*f374ba41SEd Maste void 364*f374ba41SEd Maste channel_set_xtype(struct ssh *ssh, int id, const char *xctype) 365*f374ba41SEd Maste { 366*f374ba41SEd Maste Channel *c; 367*f374ba41SEd Maste 368*f374ba41SEd Maste if ((c = channel_by_id(ssh, id)) == NULL) 369*f374ba41SEd Maste fatal_f("missing channel %d", id); 370*f374ba41SEd Maste if (c->xctype != NULL) 371*f374ba41SEd Maste free(c->xctype); 372*f374ba41SEd Maste c->xctype = xstrdup(xctype); 373*f374ba41SEd Maste /* Type has changed, so look up inactivity deadline again */ 374*f374ba41SEd Maste c->inactive_deadline = lookup_timeout(ssh, c->xctype); 375*f374ba41SEd Maste debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype, 376*f374ba41SEd Maste c->inactive_deadline); 377*f374ba41SEd Maste } 378*f374ba41SEd Maste 379*f374ba41SEd Maste /* 380a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 381a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 382a04a10f8SKris Kennaway */ 383af12a3e7SDag-Erling Smørgrav static void 3844f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, 385d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 386a04a10f8SKris Kennaway { 38738a52bd3SEd Maste int val; 38838a52bd3SEd Maste 389b15c8340SDag-Erling Smørgrav if (rfd != -1) 390b15c8340SDag-Erling Smørgrav fcntl(rfd, F_SETFD, FD_CLOEXEC); 391b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 392b15c8340SDag-Erling Smørgrav fcntl(wfd, F_SETFD, FD_CLOEXEC); 393b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 394b15c8340SDag-Erling Smørgrav fcntl(efd, F_SETFD, FD_CLOEXEC); 395a04a10f8SKris Kennaway 396a04a10f8SKris Kennaway c->rfd = rfd; 397a04a10f8SKris Kennaway c->wfd = wfd; 398a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 399a04a10f8SKris Kennaway c->efd = efd; 400a04a10f8SKris Kennaway c->extended_usage = extusage; 4015b9b2fafSBrian Feldman 402d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 403221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 404e4a9863fSDag-Erling Smørgrav #ifdef _AIX 405e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 406d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 407e4a9863fSDag-Erling Smørgrav #endif 408e0fbb1d2SBrian Feldman 4095b9b2fafSBrian Feldman /* enable nonblocking mode */ 41019261079SEd Maste c->restore_block = 0; 41119261079SEd Maste if (nonblock == CHANNEL_NONBLOCK_STDIO) { 41219261079SEd Maste /* 41319261079SEd Maste * Special handling for stdio file descriptors: do not set 41419261079SEd Maste * non-blocking mode if they are TTYs. Otherwise prepare to 41519261079SEd Maste * restore their blocking state on exit to avoid interfering 41619261079SEd Maste * with other programs that follow. 41719261079SEd Maste */ 41838a52bd3SEd Maste if (rfd != -1 && !isatty(rfd) && 41938a52bd3SEd Maste (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 420*f374ba41SEd Maste c->restore_flags[0] = val; 42119261079SEd Maste c->restore_block |= CHANNEL_RESTORE_RFD; 42219261079SEd Maste set_nonblock(rfd); 42319261079SEd Maste } 42438a52bd3SEd Maste if (wfd != -1 && !isatty(wfd) && 42538a52bd3SEd Maste (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 426*f374ba41SEd Maste c->restore_flags[1] = val; 42719261079SEd Maste c->restore_block |= CHANNEL_RESTORE_WFD; 42819261079SEd Maste set_nonblock(wfd); 42919261079SEd Maste } 43038a52bd3SEd Maste if (efd != -1 && !isatty(efd) && 43138a52bd3SEd Maste (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { 432*f374ba41SEd Maste c->restore_flags[2] = val; 43319261079SEd Maste c->restore_block |= CHANNEL_RESTORE_EFD; 43419261079SEd Maste set_nonblock(efd); 43519261079SEd Maste } 43619261079SEd Maste } else if (nonblock) { 437a04a10f8SKris Kennaway if (rfd != -1) 438a04a10f8SKris Kennaway set_nonblock(rfd); 439a04a10f8SKris Kennaway if (wfd != -1) 440a04a10f8SKris Kennaway set_nonblock(wfd); 441a04a10f8SKris Kennaway if (efd != -1) 442a04a10f8SKris Kennaway set_nonblock(efd); 443a04a10f8SKris Kennaway } 4445b9b2fafSBrian Feldman } 445a04a10f8SKris Kennaway 446511b41d2SMark Murray /* 44738a52bd3SEd Maste * Allocate a new channel object and set its type and socket. 448511b41d2SMark Murray */ 449af12a3e7SDag-Erling Smørgrav Channel * 4504f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, 45138a52bd3SEd Maste u_int window, u_int maxpack, int extusage, const char *remote_name, 45238a52bd3SEd Maste int nonblock) 453511b41d2SMark Murray { 4544f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 45538a52bd3SEd Maste u_int i, found = 0; 456511b41d2SMark Murray Channel *c; 45719261079SEd Maste int r; 458511b41d2SMark Murray 459511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 4604f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 4614f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL) { 462511b41d2SMark Murray /* Found a free slot. */ 4634f52dfbbSDag-Erling Smørgrav found = i; 464511b41d2SMark Murray break; 465511b41d2SMark Murray } 4664f52dfbbSDag-Erling Smørgrav } 4674f52dfbbSDag-Erling Smørgrav if (i >= sc->channels_alloc) { 4684f52dfbbSDag-Erling Smørgrav /* 4694f52dfbbSDag-Erling Smørgrav * There are no free slots. Take last+1 slot and expand 4704f52dfbbSDag-Erling Smørgrav * the array. 4714f52dfbbSDag-Erling Smørgrav */ 4724f52dfbbSDag-Erling Smørgrav found = sc->channels_alloc; 4734f52dfbbSDag-Erling Smørgrav if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) 47419261079SEd Maste fatal_f("internal error: channels_alloc %d too big", 47519261079SEd Maste sc->channels_alloc); 4764f52dfbbSDag-Erling Smørgrav sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, 4774f52dfbbSDag-Erling Smørgrav sc->channels_alloc + 10, sizeof(*sc->channels)); 4784f52dfbbSDag-Erling Smørgrav sc->channels_alloc += 10; 4794f52dfbbSDag-Erling Smørgrav debug2("channel: expanding %d", sc->channels_alloc); 480511b41d2SMark Murray } 481af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 4824f52dfbbSDag-Erling Smørgrav c = sc->channels[found] = xcalloc(1, sizeof(Channel)); 4834f52dfbbSDag-Erling Smørgrav if ((c->input = sshbuf_new()) == NULL || 4844f52dfbbSDag-Erling Smørgrav (c->output = sshbuf_new()) == NULL || 4854f52dfbbSDag-Erling Smørgrav (c->extended = sshbuf_new()) == NULL) 48619261079SEd Maste fatal_f("sshbuf_new failed"); 48719261079SEd Maste if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) 48819261079SEd Maste fatal_fr(r, "sshbuf_set_max_size"); 489af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 490af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 4914f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); 492511b41d2SMark Murray c->self = found; 493511b41d2SMark Murray c->type = type; 494a04a10f8SKris Kennaway c->ctype = ctype; 495a04a10f8SKris Kennaway c->local_window = window; 496a04a10f8SKris Kennaway c->local_window_max = window; 497a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 498221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 499b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 500b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 501*f374ba41SEd Maste c->inactive_deadline = lookup_timeout(ssh, c->ctype); 502d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 503*f374ba41SEd Maste debug("channel %d: new %s [%s] (inactive timeout: %u)", 504*f374ba41SEd Maste found, c->ctype, remote_name, c->inactive_deadline); 505af12a3e7SDag-Erling Smørgrav return c; 506a04a10f8SKris Kennaway } 507511b41d2SMark Murray 508af12a3e7SDag-Erling Smørgrav int 50919261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) 510af12a3e7SDag-Erling Smørgrav { 51119261079SEd Maste int ret, fd = *fdp; 512af12a3e7SDag-Erling Smørgrav 51319261079SEd Maste if (fd == -1) 51419261079SEd Maste return 0; 51519261079SEd Maste 516*f374ba41SEd Maste /* restore blocking */ 517*f374ba41SEd Maste if (*fdp == c->rfd && 518*f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_RFD) != 0) 519*f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[0]); 520*f374ba41SEd Maste else if (*fdp == c->wfd && 521*f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_WFD) != 0) 522*f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[1]); 523*f374ba41SEd Maste else if (*fdp == c->efd && 524*f374ba41SEd Maste (c->restore_block & CHANNEL_RESTORE_EFD) != 0) 525*f374ba41SEd Maste (void)fcntl(*fdp, F_SETFL, c->restore_flags[2]); 52619261079SEd Maste 5271323ec57SEd Maste if (*fdp == c->rfd) { 5281323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_RFD; 5291323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_RFD; 5301323ec57SEd Maste c->rfd = -1; 53187c1498dSEd Maste c->pfds[0] = -1; 5321323ec57SEd Maste } 5331323ec57SEd Maste if (*fdp == c->wfd) { 5341323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_WFD; 5351323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_WFD; 5361323ec57SEd Maste c->wfd = -1; 53787c1498dSEd Maste c->pfds[1] = -1; 5381323ec57SEd Maste } 5391323ec57SEd Maste if (*fdp == c->efd) { 5401323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_EFD; 5411323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_EFD; 5421323ec57SEd Maste c->efd = -1; 54387c1498dSEd Maste c->pfds[2] = -1; 5441323ec57SEd Maste } 5451323ec57SEd Maste if (*fdp == c->sock) { 5461323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_SOCK; 5471323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_SOCK; 5481323ec57SEd Maste c->sock = -1; 54987c1498dSEd Maste c->pfds[3] = -1; 5501323ec57SEd Maste } 5511323ec57SEd Maste 552af12a3e7SDag-Erling Smørgrav ret = close(fd); 5531323ec57SEd Maste *fdp = -1; /* probably redundant */ 554af12a3e7SDag-Erling Smørgrav return ret; 555af12a3e7SDag-Erling Smørgrav } 556a04a10f8SKris Kennaway 557a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 558af12a3e7SDag-Erling Smørgrav static void 5594f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c) 560511b41d2SMark Murray { 56147dd1d1bSDag-Erling Smørgrav int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; 56247dd1d1bSDag-Erling Smørgrav 56319261079SEd Maste channel_close_fd(ssh, c, &c->sock); 56447dd1d1bSDag-Erling Smørgrav if (rfd != sock) 56519261079SEd Maste channel_close_fd(ssh, c, &c->rfd); 56647dd1d1bSDag-Erling Smørgrav if (wfd != sock && wfd != rfd) 56719261079SEd Maste channel_close_fd(ssh, c, &c->wfd); 56847dd1d1bSDag-Erling Smørgrav if (efd != sock && efd != rfd && efd != wfd) 56919261079SEd Maste channel_close_fd(ssh, c, &c->efd); 5704f52dfbbSDag-Erling Smørgrav } 5714f52dfbbSDag-Erling Smørgrav 5724f52dfbbSDag-Erling Smørgrav static void 573190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm) 5744f52dfbbSDag-Erling Smørgrav { 575190cef3dSDag-Erling Smørgrav free(perm->host_to_connect); 576190cef3dSDag-Erling Smørgrav free(perm->listen_host); 577190cef3dSDag-Erling Smørgrav free(perm->listen_path); 57819261079SEd Maste memset(perm, 0, sizeof(*perm)); 5794f52dfbbSDag-Erling Smørgrav } 5804f52dfbbSDag-Erling Smørgrav 581190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */ 582190cef3dSDag-Erling Smørgrav static const char * 583190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where) 584190cef3dSDag-Erling Smørgrav { 585190cef3dSDag-Erling Smørgrav if (who == FORWARD_ADM) { 586190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 587190cef3dSDag-Erling Smørgrav return "admin local"; 588190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 589190cef3dSDag-Erling Smørgrav return "admin remote"; 590190cef3dSDag-Erling Smørgrav } else if (who == FORWARD_USER) { 591190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 592190cef3dSDag-Erling Smørgrav return "user local"; 593190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 594190cef3dSDag-Erling Smørgrav return "user remote"; 595190cef3dSDag-Erling Smørgrav } 596190cef3dSDag-Erling Smørgrav fatal("Unknown forward permission list %d/%d", who, where); 597190cef3dSDag-Erling Smørgrav } 5984f52dfbbSDag-Erling Smørgrav 599190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */ 600190cef3dSDag-Erling Smørgrav static struct permission_set * 601190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where) 602190cef3dSDag-Erling Smørgrav { 603190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 604190cef3dSDag-Erling Smørgrav 605190cef3dSDag-Erling Smørgrav switch (where) { 606190cef3dSDag-Erling Smørgrav case FORWARD_LOCAL: 607190cef3dSDag-Erling Smørgrav return &sc->local_perms; 608190cef3dSDag-Erling Smørgrav break; 609190cef3dSDag-Erling Smørgrav case FORWARD_REMOTE: 610190cef3dSDag-Erling Smørgrav return &sc->remote_perms; 611190cef3dSDag-Erling Smørgrav break; 612190cef3dSDag-Erling Smørgrav default: 61319261079SEd Maste fatal_f("invalid forwarding direction %d", where); 614190cef3dSDag-Erling Smørgrav } 615190cef3dSDag-Erling Smørgrav } 616190cef3dSDag-Erling Smørgrav 61719261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */ 618190cef3dSDag-Erling Smørgrav static void 619190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where, 620190cef3dSDag-Erling Smørgrav struct permission ***permpp, u_int **npermpp) 621190cef3dSDag-Erling Smørgrav { 622190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 623190cef3dSDag-Erling Smørgrav 624190cef3dSDag-Erling Smørgrav switch (who) { 625190cef3dSDag-Erling Smørgrav case FORWARD_USER: 626190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_user; 627190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_user; 628190cef3dSDag-Erling Smørgrav break; 629190cef3dSDag-Erling Smørgrav case FORWARD_ADM: 630190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_admin; 631190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_admin; 632190cef3dSDag-Erling Smørgrav break; 633190cef3dSDag-Erling Smørgrav default: 63419261079SEd Maste fatal_f("invalid forwarding client %d", who); 635190cef3dSDag-Erling Smørgrav } 636190cef3dSDag-Erling Smørgrav } 637190cef3dSDag-Erling Smørgrav 6381323ec57SEd Maste /* Adds an entry to the specified forwarding list */ 6394f52dfbbSDag-Erling Smørgrav static int 640190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where, 6414f52dfbbSDag-Erling Smørgrav const char *host_to_connect, int port_to_connect, 6424f52dfbbSDag-Erling Smørgrav const char *listen_host, const char *listen_path, int listen_port, 6434f52dfbbSDag-Erling Smørgrav Channel *downstream) 6444f52dfbbSDag-Erling Smørgrav { 645190cef3dSDag-Erling Smørgrav struct permission **permp; 646190cef3dSDag-Erling Smørgrav u_int n, *npermp; 6474f52dfbbSDag-Erling Smørgrav 648190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 6494f52dfbbSDag-Erling Smørgrav 650190cef3dSDag-Erling Smørgrav if (*npermp >= INT_MAX) 65119261079SEd Maste fatal_f("%s overflow", fwd_ident(who, where)); 6524f52dfbbSDag-Erling Smørgrav 653190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); 654190cef3dSDag-Erling Smørgrav n = (*npermp)++; 6554f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) 656190cef3dSDag-Erling Smørgrav (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); 657190cef3dSDag-Erling Smørgrav (*permp)[n].port_to_connect = port_to_connect; 658190cef3dSDag-Erling Smørgrav (*permp)[n].listen_host = MAYBE_DUP(listen_host); 659190cef3dSDag-Erling Smørgrav (*permp)[n].listen_path = MAYBE_DUP(listen_path); 660190cef3dSDag-Erling Smørgrav (*permp)[n].listen_port = listen_port; 661190cef3dSDag-Erling Smørgrav (*permp)[n].downstream = downstream; 6624f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP 6634f52dfbbSDag-Erling Smørgrav return (int)n; 6644f52dfbbSDag-Erling Smørgrav } 6654f52dfbbSDag-Erling Smørgrav 6664f52dfbbSDag-Erling Smørgrav static void 6674f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) 6684f52dfbbSDag-Erling Smørgrav { 6694f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 670190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 671190cef3dSDag-Erling Smørgrav struct permission *perm; 6724f52dfbbSDag-Erling Smørgrav int r; 6734f52dfbbSDag-Erling Smørgrav u_int i; 6744f52dfbbSDag-Erling Smørgrav 675190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 676190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 677190cef3dSDag-Erling Smørgrav if (perm->downstream != c) 6784f52dfbbSDag-Erling Smørgrav continue; 6794f52dfbbSDag-Erling Smørgrav 6804f52dfbbSDag-Erling Smørgrav /* cancel on the server, since mux client is gone */ 6814f52dfbbSDag-Erling Smørgrav debug("channel %d: cleanup remote forward for %s:%u", 682190cef3dSDag-Erling Smørgrav c->self, perm->listen_host, perm->listen_port); 6834f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 6844f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 6854f52dfbbSDag-Erling Smørgrav "cancel-tcpip-forward")) != 0 || 6864f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || 6874f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 688190cef3dSDag-Erling Smørgrav channel_rfwd_bind_host(perm->listen_host))) != 0 || 689190cef3dSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || 6904f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 69119261079SEd Maste fatal_fr(r, "channel %i", c->self); 6924f52dfbbSDag-Erling Smørgrav } 693190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 6944f52dfbbSDag-Erling Smørgrav } 695a04a10f8SKris Kennaway } 696511b41d2SMark Murray 697a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 698a04a10f8SKris Kennaway void 6994f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c) 700a04a10f8SKris Kennaway { 7014f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 702af12a3e7SDag-Erling Smørgrav char *s; 70321e764dfSDag-Erling Smørgrav u_int i, n; 704ca86bcf2SDag-Erling Smørgrav Channel *other; 705d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 706ca3176e7SBrian Feldman 7074f52dfbbSDag-Erling Smørgrav for (n = 0, i = 0; i < sc->channels_alloc; i++) { 7084f52dfbbSDag-Erling Smørgrav if ((other = sc->channels[i]) == NULL) 7094f52dfbbSDag-Erling Smørgrav continue; 710af12a3e7SDag-Erling Smørgrav n++; 711ca86bcf2SDag-Erling Smørgrav /* detach from mux client and prepare for closing */ 712ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT && 713ca86bcf2SDag-Erling Smørgrav other->type == SSH_CHANNEL_MUX_PROXY && 714ca86bcf2SDag-Erling Smørgrav other->mux_ctx == c) { 715ca86bcf2SDag-Erling Smørgrav other->mux_ctx = NULL; 716ca86bcf2SDag-Erling Smørgrav other->type = SSH_CHANNEL_OPEN; 717ca86bcf2SDag-Erling Smørgrav other->istate = CHAN_INPUT_CLOSED; 718ca86bcf2SDag-Erling Smørgrav other->ostate = CHAN_OUTPUT_CLOSED; 719ca86bcf2SDag-Erling Smørgrav } 720ca86bcf2SDag-Erling Smørgrav } 72121e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 722af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 723af12a3e7SDag-Erling Smørgrav 724e9e8876aSEd Maste if (c->type == SSH_CHANNEL_MUX_CLIENT) { 7254f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(ssh, c); 726e9e8876aSEd Maste free(c->mux_ctx); 727e9e8876aSEd Maste c->mux_ctx = NULL; 728e9e8876aSEd Maste } else if (c->type == SSH_CHANNEL_MUX_LISTENER) { 72919261079SEd Maste free(c->mux_ctx); 73019261079SEd Maste c->mux_ctx = NULL; 73119261079SEd Maste } 732ca86bcf2SDag-Erling Smørgrav 733190cef3dSDag-Erling Smørgrav if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { 7344f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 735221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 736e4a9863fSDag-Erling Smørgrav free(s); 737190cef3dSDag-Erling Smørgrav } 738ca3176e7SBrian Feldman 7394f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, c); 7404f52dfbbSDag-Erling Smørgrav sshbuf_free(c->input); 7414f52dfbbSDag-Erling Smørgrav sshbuf_free(c->output); 7424f52dfbbSDag-Erling Smørgrav sshbuf_free(c->extended); 7434f52dfbbSDag-Erling Smørgrav c->input = c->output = c->extended = NULL; 744e4a9863fSDag-Erling Smørgrav free(c->remote_name); 745a04a10f8SKris Kennaway c->remote_name = NULL; 746e4a9863fSDag-Erling Smørgrav free(c->path); 747cce7d346SDag-Erling Smørgrav c->path = NULL; 748e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 749462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 750*f374ba41SEd Maste free(c->xctype); 751*f374ba41SEd Maste c->xctype = NULL; 752d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 753d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 7544f52dfbbSDag-Erling Smørgrav cc->abandon_cb(ssh, c, cc->ctx); 755d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 75619261079SEd Maste freezero(cc, sizeof(*cc)); 757d4af9e69SDag-Erling Smørgrav } 758d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 7594f52dfbbSDag-Erling Smørgrav c->filter_cleanup(ssh, c->self, c->filter_ctx); 7604f52dfbbSDag-Erling Smørgrav sc->channels[c->self] = NULL; 76119261079SEd Maste freezero(c, sizeof(*c)); 762af12a3e7SDag-Erling Smørgrav } 763af12a3e7SDag-Erling Smørgrav 764af12a3e7SDag-Erling Smørgrav void 7654f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh) 766af12a3e7SDag-Erling Smørgrav { 76721e764dfSDag-Erling Smørgrav u_int i; 76819261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 769af12a3e7SDag-Erling Smørgrav 77019261079SEd Maste for (i = 0; i < sc->channels_alloc; i++) 77119261079SEd Maste if (sc->channels[i] != NULL) 77219261079SEd Maste channel_free(ssh, sc->channels[i]); 77319261079SEd Maste 77419261079SEd Maste free(sc->channels); 77519261079SEd Maste sc->channels = NULL; 77619261079SEd Maste sc->channels_alloc = 0; 77719261079SEd Maste 77819261079SEd Maste free(sc->x11_saved_display); 77919261079SEd Maste sc->x11_saved_display = NULL; 78019261079SEd Maste 78119261079SEd Maste free(sc->x11_saved_proto); 78219261079SEd Maste sc->x11_saved_proto = NULL; 78319261079SEd Maste 78419261079SEd Maste free(sc->x11_saved_data); 78519261079SEd Maste sc->x11_saved_data = NULL; 78619261079SEd Maste sc->x11_saved_data_len = 0; 78719261079SEd Maste 78819261079SEd Maste free(sc->x11_fake_data); 78919261079SEd Maste sc->x11_fake_data = NULL; 79019261079SEd Maste sc->x11_fake_data_len = 0; 791af12a3e7SDag-Erling Smørgrav } 792af12a3e7SDag-Erling Smørgrav 793af12a3e7SDag-Erling Smørgrav /* 794af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 795af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 796af12a3e7SDag-Erling Smørgrav */ 797af12a3e7SDag-Erling Smørgrav void 7984f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh) 799af12a3e7SDag-Erling Smørgrav { 80021e764dfSDag-Erling Smørgrav u_int i; 801af12a3e7SDag-Erling Smørgrav 8024f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) 8034f52dfbbSDag-Erling Smørgrav if (ssh->chanctxt->channels[i] != NULL) 8044f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, ssh->chanctxt->channels[i]); 805af12a3e7SDag-Erling Smørgrav } 806af12a3e7SDag-Erling Smørgrav 807af12a3e7SDag-Erling Smørgrav /* 808af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 809af12a3e7SDag-Erling Smørgrav */ 810af12a3e7SDag-Erling Smørgrav void 8114f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh) 812af12a3e7SDag-Erling Smørgrav { 81321e764dfSDag-Erling Smørgrav u_int i; 814af12a3e7SDag-Erling Smørgrav Channel *c; 815af12a3e7SDag-Erling Smørgrav 8164f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8174f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 818af12a3e7SDag-Erling Smørgrav if (c != NULL) { 819af12a3e7SDag-Erling Smørgrav switch (c->type) { 820af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 821af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 822af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 823af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 824a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 825a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 82619261079SEd Maste channel_close_fd(ssh, c, &c->sock); 8274f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 828af12a3e7SDag-Erling Smørgrav break; 829af12a3e7SDag-Erling Smørgrav } 830af12a3e7SDag-Erling Smørgrav } 831af12a3e7SDag-Erling Smørgrav } 832af12a3e7SDag-Erling Smørgrav } 833af12a3e7SDag-Erling Smørgrav 834af12a3e7SDag-Erling Smørgrav /* 835af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 836af12a3e7SDag-Erling Smørgrav * more channel is overfull. 837af12a3e7SDag-Erling Smørgrav */ 838af12a3e7SDag-Erling Smørgrav int 8394f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh) 840af12a3e7SDag-Erling Smørgrav { 841af12a3e7SDag-Erling Smørgrav u_int i; 8424f52dfbbSDag-Erling Smørgrav u_int maxsize = ssh_packet_get_maxsize(ssh); 843af12a3e7SDag-Erling Smørgrav Channel *c; 844af12a3e7SDag-Erling Smørgrav 8454f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8464f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 8474f52dfbbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_OPEN) 8484f52dfbbSDag-Erling Smørgrav continue; 8494f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > maxsize) { 8504f52dfbbSDag-Erling Smørgrav debug2("channel %d: big output buffer %zu > %u", 8514f52dfbbSDag-Erling Smørgrav c->self, sshbuf_len(c->output), maxsize); 852af12a3e7SDag-Erling Smørgrav return 0; 853af12a3e7SDag-Erling Smørgrav } 854af12a3e7SDag-Erling Smørgrav } 855af12a3e7SDag-Erling Smørgrav return 1; 856af12a3e7SDag-Erling Smørgrav } 857af12a3e7SDag-Erling Smørgrav 858af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 859af12a3e7SDag-Erling Smørgrav int 8604f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh) 861af12a3e7SDag-Erling Smørgrav { 86221e764dfSDag-Erling Smørgrav u_int i; 863af12a3e7SDag-Erling Smørgrav Channel *c; 864af12a3e7SDag-Erling Smørgrav 8654f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8664f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 867af12a3e7SDag-Erling Smørgrav if (c == NULL) 868af12a3e7SDag-Erling Smørgrav continue; 869af12a3e7SDag-Erling Smørgrav switch (c->type) { 870af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 871af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 872af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 873b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 874af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 875af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 876af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 8774f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 878af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 879af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 880e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 881a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 882a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 883af12a3e7SDag-Erling Smørgrav continue; 884af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 885af12a3e7SDag-Erling Smørgrav continue; 886af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 887af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 8884f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 889af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 890b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 891ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 892af12a3e7SDag-Erling Smørgrav return 1; 893af12a3e7SDag-Erling Smørgrav default: 89419261079SEd Maste fatal_f("bad channel type %d", c->type); 895af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 896af12a3e7SDag-Erling Smørgrav } 897af12a3e7SDag-Erling Smørgrav } 898af12a3e7SDag-Erling Smørgrav return 0; 899af12a3e7SDag-Erling Smørgrav } 900af12a3e7SDag-Erling Smørgrav 901af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 902af12a3e7SDag-Erling Smørgrav int 9034f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh) 904af12a3e7SDag-Erling Smørgrav { 90521e764dfSDag-Erling Smørgrav u_int i; 906af12a3e7SDag-Erling Smørgrav Channel *c; 907af12a3e7SDag-Erling Smørgrav 9084f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 9094f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 9104f52dfbbSDag-Erling Smørgrav if (c == NULL || !c->have_remote_id) 911af12a3e7SDag-Erling Smørgrav continue; 912af12a3e7SDag-Erling Smørgrav switch (c->type) { 913af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 914af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 9154f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 9164f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 917af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 918af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 919af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 920b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 921b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 922ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 923af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 924af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 925af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 926e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 927a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 928a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 929af12a3e7SDag-Erling Smørgrav continue; 930af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 931af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 932af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 933af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 934af12a3e7SDag-Erling Smørgrav return i; 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 -1; 941af12a3e7SDag-Erling Smørgrav } 942af12a3e7SDag-Erling Smørgrav 9432f513db7SEd Maste /* Returns the state of the channel's extended usage flag */ 9442f513db7SEd Maste const char * 9452f513db7SEd Maste channel_format_extended_usage(const Channel *c) 9462f513db7SEd Maste { 9472f513db7SEd Maste if (c->efd == -1) 9482f513db7SEd Maste return "closed"; 9492f513db7SEd Maste 9502f513db7SEd Maste switch (c->extended_usage) { 9512f513db7SEd Maste case CHAN_EXTENDED_WRITE: 9522f513db7SEd Maste return "write"; 9532f513db7SEd Maste case CHAN_EXTENDED_READ: 9542f513db7SEd Maste return "read"; 9552f513db7SEd Maste case CHAN_EXTENDED_IGNORE: 9562f513db7SEd Maste return "ignore"; 9572f513db7SEd Maste default: 9582f513db7SEd Maste return "UNKNOWN"; 9592f513db7SEd Maste } 9602f513db7SEd Maste } 9612f513db7SEd Maste 9622f513db7SEd Maste static char * 9632f513db7SEd Maste channel_format_status(const Channel *c) 9642f513db7SEd Maste { 9652f513db7SEd Maste char *ret = NULL; 9662f513db7SEd Maste 967*f374ba41SEd Maste xasprintf(&ret, "t%d [%s] %s%u i%u/%zu o%u/%zu e[%s]/%zu " 9681323ec57SEd Maste "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", 969*f374ba41SEd Maste c->type, c->xctype != NULL ? c->xctype : c->ctype, 9702f513db7SEd Maste c->have_remote_id ? "r" : "nr", c->remote_id, 9712f513db7SEd Maste c->istate, sshbuf_len(c->input), 9722f513db7SEd Maste c->ostate, sshbuf_len(c->output), 9732f513db7SEd Maste channel_format_extended_usage(c), sshbuf_len(c->extended), 9741323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, 9751323ec57SEd Maste c->io_want, c->io_ready); 9762f513db7SEd Maste return ret; 9772f513db7SEd Maste } 9782f513db7SEd Maste 979af12a3e7SDag-Erling Smørgrav /* 980af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 981af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 982af12a3e7SDag-Erling Smørgrav * newlines. 983af12a3e7SDag-Erling Smørgrav */ 984af12a3e7SDag-Erling Smørgrav char * 9854f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh) 986af12a3e7SDag-Erling Smørgrav { 9874f52dfbbSDag-Erling Smørgrav struct sshbuf *buf; 988af12a3e7SDag-Erling Smørgrav Channel *c; 98921e764dfSDag-Erling Smørgrav u_int i; 9904f52dfbbSDag-Erling Smørgrav int r; 9912f513db7SEd Maste char *cp, *ret; 992af12a3e7SDag-Erling Smørgrav 9934f52dfbbSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 99419261079SEd Maste fatal_f("sshbuf_new"); 9954f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(buf, 9964f52dfbbSDag-Erling Smørgrav "The following connections are open:\r\n")) != 0) 99719261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9984f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 9994f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 1000af12a3e7SDag-Erling Smørgrav if (c == NULL) 1001af12a3e7SDag-Erling Smørgrav continue; 1002af12a3e7SDag-Erling Smørgrav switch (c->type) { 1003af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 1004af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 1005af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 1006af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 1007af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 1008af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 1009e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 1010b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 1011a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 1012a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 1013af12a3e7SDag-Erling Smørgrav continue; 1014af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 1015af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 1016af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 1017af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 10184f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 10194f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 1020af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 1021af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 1022ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 1023ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 10242f513db7SEd Maste cp = channel_format_status(c); 10252f513db7SEd Maste if ((r = sshbuf_putf(buf, " #%d %.300s (%s)\r\n", 10262f513db7SEd Maste c->self, c->remote_name, cp)) != 0) { 10272f513db7SEd Maste free(cp); 102819261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10292f513db7SEd Maste } 10302f513db7SEd Maste free(cp); 1031af12a3e7SDag-Erling Smørgrav continue; 1032af12a3e7SDag-Erling Smørgrav default: 103319261079SEd Maste fatal_f("bad channel type %d", c->type); 1034af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 1035af12a3e7SDag-Erling Smørgrav } 1036af12a3e7SDag-Erling Smørgrav } 10374f52dfbbSDag-Erling Smørgrav if ((ret = sshbuf_dup_string(buf)) == NULL) 103819261079SEd Maste fatal_f("sshbuf_dup_string"); 10394f52dfbbSDag-Erling Smørgrav sshbuf_free(buf); 10404f52dfbbSDag-Erling Smørgrav return ret; 10414f52dfbbSDag-Erling Smørgrav } 10424f52dfbbSDag-Erling Smørgrav 10434f52dfbbSDag-Erling Smørgrav static void 10444f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) 10454f52dfbbSDag-Erling Smørgrav { 10464f52dfbbSDag-Erling Smørgrav int r; 10474f52dfbbSDag-Erling Smørgrav 10484f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 10494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, type)) != 0 || 10504f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 10514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 10524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { 105319261079SEd Maste fatal_r(r, "%s: channel %i: open", where, c->self); 10544f52dfbbSDag-Erling Smørgrav } 1055af12a3e7SDag-Erling Smørgrav } 1056af12a3e7SDag-Erling Smørgrav 1057af12a3e7SDag-Erling Smørgrav void 10584f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id) 1059af12a3e7SDag-Erling Smørgrav { 10604f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 10614f52dfbbSDag-Erling Smørgrav int r; 1062f388f5efSDag-Erling Smørgrav 1063af12a3e7SDag-Erling Smørgrav if (c == NULL) { 1064221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 1065af12a3e7SDag-Erling Smørgrav return; 1066af12a3e7SDag-Erling Smørgrav } 1067e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 10684f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, c->ctype); 10694f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 107019261079SEd Maste fatal_fr(r, "channel %i", c->self); 1071af12a3e7SDag-Erling Smørgrav } 1072af12a3e7SDag-Erling Smørgrav 1073af12a3e7SDag-Erling Smørgrav void 10744f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) 1075af12a3e7SDag-Erling Smørgrav { 10764f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 10774f52dfbbSDag-Erling Smørgrav int r; 1078f388f5efSDag-Erling Smørgrav 1079af12a3e7SDag-Erling Smørgrav if (c == NULL) { 108019261079SEd Maste logit_f("%d: unknown channel id", id); 1081af12a3e7SDag-Erling Smørgrav return; 1082af12a3e7SDag-Erling Smørgrav } 10834f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 108419261079SEd Maste fatal_f("channel %d: no remote id", c->self); 10854f52dfbbSDag-Erling Smørgrav 108621e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 10874f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || 10884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 10894f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, service)) != 0 || 10904f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { 109119261079SEd Maste fatal_fr(r, "channel %i", c->self); 10924f52dfbbSDag-Erling Smørgrav } 1093af12a3e7SDag-Erling Smørgrav } 1094333ee039SDag-Erling Smørgrav 1095af12a3e7SDag-Erling Smørgrav void 10964f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id, 10974f52dfbbSDag-Erling Smørgrav channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) 1098d4af9e69SDag-Erling Smørgrav { 1099d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 1100d4af9e69SDag-Erling Smørgrav Channel *c; 1101d4af9e69SDag-Erling Smørgrav 11024f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 110319261079SEd Maste fatal_f("%d: bad id", id); 1104d4af9e69SDag-Erling Smørgrav 11050a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 1106d4af9e69SDag-Erling Smørgrav cc->cb = cb; 1107d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 1108d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 1109d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 1110d4af9e69SDag-Erling Smørgrav } 1111d4af9e69SDag-Erling Smørgrav 1112d4af9e69SDag-Erling Smørgrav void 11134f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id, 11144f52dfbbSDag-Erling Smørgrav channel_open_fn *fn, void *ctx) 1115af12a3e7SDag-Erling Smørgrav { 11164f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1117f388f5efSDag-Erling Smørgrav 1118af12a3e7SDag-Erling Smørgrav if (c == NULL) { 111919261079SEd Maste logit_f("%d: bad id", id); 1120af12a3e7SDag-Erling Smørgrav return; 1121af12a3e7SDag-Erling Smørgrav } 1122d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 1123d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 1124af12a3e7SDag-Erling Smørgrav } 1125333ee039SDag-Erling Smørgrav 1126af12a3e7SDag-Erling Smørgrav void 11274f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id, 11284f52dfbbSDag-Erling Smørgrav channel_callback_fn *fn, int do_close) 1129af12a3e7SDag-Erling Smørgrav { 11304f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1131f388f5efSDag-Erling Smørgrav 1132af12a3e7SDag-Erling Smørgrav if (c == NULL) { 113319261079SEd Maste logit_f("%d: bad id", id); 1134af12a3e7SDag-Erling Smørgrav return; 1135af12a3e7SDag-Erling Smørgrav } 1136af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 1137b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 1138af12a3e7SDag-Erling Smørgrav } 1139333ee039SDag-Erling Smørgrav 1140af12a3e7SDag-Erling Smørgrav void 11414f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id) 1142af12a3e7SDag-Erling Smørgrav { 11434f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1144f388f5efSDag-Erling Smørgrav 1145af12a3e7SDag-Erling Smørgrav if (c == NULL) { 114619261079SEd Maste logit_f("%d: bad id", id); 1147af12a3e7SDag-Erling Smørgrav return; 1148af12a3e7SDag-Erling Smørgrav } 1149af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 1150b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 1151af12a3e7SDag-Erling Smørgrav } 1152333ee039SDag-Erling Smørgrav 1153af12a3e7SDag-Erling Smørgrav void 11544f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, 1155d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 1156af12a3e7SDag-Erling Smørgrav { 11574f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1158f388f5efSDag-Erling Smørgrav 1159af12a3e7SDag-Erling Smørgrav if (c == NULL) { 116019261079SEd Maste logit_f("%d: bad id", id); 1161af12a3e7SDag-Erling Smørgrav return; 1162af12a3e7SDag-Erling Smørgrav } 1163b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 1164b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 1165d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 1166d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 1167af12a3e7SDag-Erling Smørgrav } 1168af12a3e7SDag-Erling Smørgrav 1169af12a3e7SDag-Erling Smørgrav void 11704f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, 1171d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 1172af12a3e7SDag-Erling Smørgrav { 11734f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 11744f52dfbbSDag-Erling Smørgrav int r; 1175f388f5efSDag-Erling Smørgrav 1176af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 1177af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 11784f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 117919261079SEd Maste fatal_f("channel %d: no remote id", c->self); 11804f52dfbbSDag-Erling Smørgrav 11814f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); 1182af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1183*f374ba41SEd Maste c->lastused = monotime(); 1184af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 11854f52dfbbSDag-Erling Smørgrav 11864f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 11874f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 11884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 11894f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 119019261079SEd Maste fatal_fr(r, "channel %i", c->self); 1191511b41d2SMark Murray } 1192511b41d2SMark Murray 1193af12a3e7SDag-Erling Smørgrav static void 11941323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c) 1195511b41d2SMark Murray { 11961323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_R; 1197a04a10f8SKris Kennaway } 1198a04a10f8SKris Kennaway 1199af12a3e7SDag-Erling Smørgrav static void 12001323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c) 1201ca3176e7SBrian Feldman { 1202ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 12031323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_W; 1204ca3176e7SBrian Feldman } 1205ca3176e7SBrian Feldman 1206af12a3e7SDag-Erling Smørgrav static void 12071323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c) 1208a04a10f8SKris Kennaway { 12091323ec57SEd Maste c->io_want = 0; 1210a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 12114f52dfbbSDag-Erling Smørgrav c->remote_window > 0 && 12124f52dfbbSDag-Erling Smørgrav sshbuf_len(c->input) < c->remote_window && 12134f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 12141323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1215a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 1216a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 12174f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) { 12181323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1219a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 122080628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 12214f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 12224f52dfbbSDag-Erling Smørgrav "obuf_empty delayed efd %d/(%zu)", c->self, 12234f52dfbbSDag-Erling Smørgrav c->efd, sshbuf_len(c->extended)); 122480628bacSDag-Erling Smørgrav else 12254f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1226a04a10f8SKris Kennaway } 1227a04a10f8SKris Kennaway } 1228a04a10f8SKris Kennaway /** XXX check close conditions, too */ 12294f52dfbbSDag-Erling Smørgrav if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && 12304f52dfbbSDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_CLOSED)) { 1231a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 12324f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) > 0) 12331323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_W; 1234e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 1235e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1236e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 12374f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) < c->remote_window) 12381323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_R; 1239a04a10f8SKris Kennaway } 124021e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 1241a04a10f8SKris Kennaway } 1242a04a10f8SKris Kennaway 1243a04a10f8SKris Kennaway /* 1244a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 1245a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 1246a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 1247a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 1248a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 1249a04a10f8SKris Kennaway * XXX All this happens at the client side. 1250af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 1251a04a10f8SKris Kennaway */ 1252af12a3e7SDag-Erling Smørgrav static int 12534f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b) 1254a04a10f8SKris Kennaway { 12554f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 1256ca3176e7SBrian Feldman u_char *ucp; 1257ca3176e7SBrian Feldman u_int proto_len, data_len; 1258511b41d2SMark Murray 1259557f75e5SDag-Erling Smørgrav /* Is this being called after the refusal deadline? */ 12604f52dfbbSDag-Erling Smørgrav if (sc->x11_refuse_time != 0 && 12614f52dfbbSDag-Erling Smørgrav (u_int)monotime() >= sc->x11_refuse_time) { 1262557f75e5SDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1263557f75e5SDag-Erling Smørgrav "expired"); 1264557f75e5SDag-Erling Smørgrav return -1; 1265557f75e5SDag-Erling Smørgrav } 1266557f75e5SDag-Erling Smørgrav 1267511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 12684f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 12) 1269a04a10f8SKris Kennaway return 0; 1270511b41d2SMark Murray 1271511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 12724f52dfbbSDag-Erling Smørgrav ucp = sshbuf_mutable_ptr(b); 1273511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 1274511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 1275511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 1276511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 1277511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 1278511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 1279511b41d2SMark Murray } else { 1280221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 1281511b41d2SMark Murray ucp[0]); 1282a04a10f8SKris Kennaway return -1; 1283511b41d2SMark Murray } 1284511b41d2SMark Murray 1285511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 12864f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 1287511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 1288a04a10f8SKris Kennaway return 0; 1289511b41d2SMark Murray 1290511b41d2SMark Murray /* Check if authentication protocol matches. */ 12914f52dfbbSDag-Erling Smørgrav if (proto_len != strlen(sc->x11_saved_proto) || 12924f52dfbbSDag-Erling Smørgrav memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { 1293221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 1294a04a10f8SKris Kennaway return -1; 1295511b41d2SMark Murray } 1296511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 12974f52dfbbSDag-Erling Smørgrav if (data_len != sc->x11_fake_data_len || 1298e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 12994f52dfbbSDag-Erling Smørgrav sc->x11_fake_data, sc->x11_fake_data_len) != 0) { 1300221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 1301a04a10f8SKris Kennaway return -1; 1302511b41d2SMark Murray } 1303511b41d2SMark Murray /* Check fake data length */ 13044f52dfbbSDag-Erling Smørgrav if (sc->x11_fake_data_len != sc->x11_saved_data_len) { 1305511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 13064f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len, sc->x11_saved_data_len); 1307a04a10f8SKris Kennaway return -1; 1308511b41d2SMark Murray } 1309511b41d2SMark Murray /* 1310511b41d2SMark Murray * Received authentication protocol and data match 1311511b41d2SMark Murray * our fake data. Substitute the fake data with real 1312511b41d2SMark Murray * data. 1313511b41d2SMark Murray */ 1314511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 13154f52dfbbSDag-Erling Smørgrav sc->x11_saved_data, sc->x11_saved_data_len); 1316a04a10f8SKris Kennaway return 1; 1317a04a10f8SKris Kennaway } 1318511b41d2SMark Murray 1319*f374ba41SEd Maste void 1320*f374ba41SEd Maste channel_force_close(struct ssh *ssh, Channel *c, int abandon) 1321*f374ba41SEd Maste { 1322*f374ba41SEd Maste debug3_f("channel %d: forcibly closing", c->self); 1323*f374ba41SEd Maste if (c->istate == CHAN_INPUT_OPEN) 1324*f374ba41SEd Maste chan_read_failed(ssh, c); 1325*f374ba41SEd Maste if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1326*f374ba41SEd Maste sshbuf_reset(c->input); 1327*f374ba41SEd Maste chan_ibuf_empty(ssh, c); 1328*f374ba41SEd Maste } 1329*f374ba41SEd Maste if (c->ostate == CHAN_OUTPUT_OPEN || 1330*f374ba41SEd Maste c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 1331*f374ba41SEd Maste sshbuf_reset(c->output); 1332*f374ba41SEd Maste chan_write_failed(ssh, c); 1333*f374ba41SEd Maste } 1334*f374ba41SEd Maste if (c->detach_user) 1335*f374ba41SEd Maste c->detach_user(ssh, c->self, 1, NULL); 1336*f374ba41SEd Maste if (c->efd != -1) 1337*f374ba41SEd Maste channel_close_fd(ssh, c, &c->efd); 1338*f374ba41SEd Maste if (abandon) 1339*f374ba41SEd Maste c->type = SSH_CHANNEL_ABANDONED; 1340*f374ba41SEd Maste /* exempt from inactivity timeouts */ 1341*f374ba41SEd Maste c->inactive_deadline = 0; 1342*f374ba41SEd Maste c->lastused = 0; 1343*f374ba41SEd Maste } 1344*f374ba41SEd Maste 1345af12a3e7SDag-Erling Smørgrav static void 13461323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c) 1347a04a10f8SKris Kennaway { 13484f52dfbbSDag-Erling Smørgrav int ret = x11_open_helper(ssh, c->output); 1349af12a3e7SDag-Erling Smørgrav 1350af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 1351af12a3e7SDag-Erling Smørgrav 1352a04a10f8SKris Kennaway if (ret == 1) { 1353a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 1354*f374ba41SEd Maste c->lastused = monotime(); 13551323ec57SEd Maste channel_pre_open(ssh, c); 1356a04a10f8SKris Kennaway } else if (ret == -1) { 1357*f374ba41SEd Maste logit("X11 connection rejected because of wrong " 1358*f374ba41SEd Maste "authentication."); 13594f52dfbbSDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", 13604f52dfbbSDag-Erling Smørgrav c->self, c->istate, c->ostate); 1361*f374ba41SEd Maste channel_force_close(ssh, c, 0); 1362a04a10f8SKris Kennaway } 1363a04a10f8SKris Kennaway } 1364a04a10f8SKris Kennaway 1365b15c8340SDag-Erling Smørgrav static void 13661323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c) 1367b15c8340SDag-Erling Smørgrav { 13681323ec57SEd Maste c->io_want = 0; 1369e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 13704f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 13711323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1372b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1373b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 13744f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 13754f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 1376b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 13774f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 1378b15c8340SDag-Erling Smørgrav } 1379b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1380b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 13814f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) 13821323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1383b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 13844f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1385b15c8340SDag-Erling Smørgrav } 1386b15c8340SDag-Erling Smørgrav } 1387b15c8340SDag-Erling Smørgrav 1388ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1389af12a3e7SDag-Erling Smørgrav static int 13904f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) 1391ca3176e7SBrian Feldman { 13924f52dfbbSDag-Erling Smørgrav const u_char *p; 13934f52dfbbSDag-Erling Smørgrav char *host; 1394cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1395ca3176e7SBrian Feldman char username[256]; 1396ca3176e7SBrian Feldman struct { 1397ca3176e7SBrian Feldman u_int8_t version; 1398ca3176e7SBrian Feldman u_int8_t command; 1399ca3176e7SBrian Feldman u_int16_t dest_port; 1400ca3176e7SBrian Feldman struct in_addr dest_addr; 1401ca3176e7SBrian Feldman } s4_req, s4_rsp; 14024f52dfbbSDag-Erling Smørgrav int r; 1403ca3176e7SBrian Feldman 1404ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1405ca3176e7SBrian Feldman 14064f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1407ca3176e7SBrian Feldman len = sizeof(s4_req); 1408ca3176e7SBrian Feldman if (have < len) 1409ca3176e7SBrian Feldman return 0; 14104f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1411cce7d346SDag-Erling Smørgrav 1412cce7d346SDag-Erling Smørgrav need = 1; 1413cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1414cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1415cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1416cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1417cce7d346SDag-Erling Smørgrav need = 2; 1418cce7d346SDag-Erling Smørgrav } 1419cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1420ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1421ca3176e7SBrian Feldman if (p[i] == '\0') { 1422cce7d346SDag-Erling Smørgrav found++; 1423cce7d346SDag-Erling Smørgrav if (found == need) 1424ca3176e7SBrian Feldman break; 1425ca3176e7SBrian Feldman } 1426ca3176e7SBrian Feldman if (i > 1024) { 1427ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1428ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1429ca3176e7SBrian Feldman c->self); 1430ca3176e7SBrian Feldman return -1; 1431ca3176e7SBrian Feldman } 1432ca3176e7SBrian Feldman } 1433cce7d346SDag-Erling Smørgrav if (found < need) 1434ca3176e7SBrian Feldman return 0; 14354f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || 14364f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || 14374f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || 14384f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { 143919261079SEd Maste debug_r(r, "channels %d: decode socks4", c->self); 14404f52dfbbSDag-Erling Smørgrav return -1; 14414f52dfbbSDag-Erling Smørgrav } 14424f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 14434f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 14444f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 144519261079SEd Maste error("channel %d: decode socks4: unterminated user", c->self); 14464f52dfbbSDag-Erling Smørgrav return -1; 14474f52dfbbSDag-Erling Smørgrav } 1448ca3176e7SBrian Feldman len = strlen(p); 1449ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1450cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1451ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 145219261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 145319261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1454e4a9863fSDag-Erling Smørgrav free(c->path); 1455cce7d346SDag-Erling Smørgrav c->path = NULL; 1456cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1457ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1458cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1459cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 14604f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 14614f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 14624f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 14634f52dfbbSDag-Erling Smørgrav error("channel %d: decode socks4a: host not nul " 14644f52dfbbSDag-Erling Smørgrav "terminated", c->self); 14654f52dfbbSDag-Erling Smørgrav return -1; 14664f52dfbbSDag-Erling Smørgrav } 1467cce7d346SDag-Erling Smørgrav len = strlen(p); 1468cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1469cce7d346SDag-Erling Smørgrav c->self, p, len); 1470cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1471cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1472cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1473cce7d346SDag-Erling Smørgrav c->self, p); 1474cce7d346SDag-Erling Smørgrav return -1; 1475cce7d346SDag-Erling Smørgrav } 1476cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 147719261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 147819261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1479cce7d346SDag-Erling Smørgrav } 1480ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1481ca3176e7SBrian Feldman 1482221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1483cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1484ca3176e7SBrian Feldman 1485ca3176e7SBrian Feldman if (s4_req.command != 1) { 1486cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1487cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1488ca3176e7SBrian Feldman return -1; 1489ca3176e7SBrian Feldman } 1490ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1491ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1492ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1493ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 149419261079SEd Maste if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) 149519261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1496ca3176e7SBrian Feldman return 1; 1497ca3176e7SBrian Feldman } 1498ca3176e7SBrian Feldman 1499221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1500221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1501221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1502221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1503221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1504221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1505221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1506221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1507221552e4SDag-Erling Smørgrav 1508221552e4SDag-Erling Smørgrav static int 15094f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) 1510221552e4SDag-Erling Smørgrav { 15114f52dfbbSDag-Erling Smørgrav /* XXX use get/put_u8 instead of trusting struct padding */ 1512221552e4SDag-Erling Smørgrav struct { 1513221552e4SDag-Erling Smørgrav u_int8_t version; 1514221552e4SDag-Erling Smørgrav u_int8_t command; 1515221552e4SDag-Erling Smørgrav u_int8_t reserved; 1516221552e4SDag-Erling Smørgrav u_int8_t atyp; 1517221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1518221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1519e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 15204f52dfbbSDag-Erling Smørgrav const u_char *p; 1521333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 15224f52dfbbSDag-Erling Smørgrav int r; 1523221552e4SDag-Erling Smørgrav 1524221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 15254f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1526221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1527221552e4SDag-Erling Smørgrav return -1; 15284f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1529221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1530221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1531221552e4SDag-Erling Smørgrav if (have < 2) 1532221552e4SDag-Erling Smørgrav return 0; 1533221552e4SDag-Erling Smørgrav nmethods = p[1]; 1534221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1535221552e4SDag-Erling Smørgrav return 0; 1536221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1537221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1538221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1539221552e4SDag-Erling Smørgrav found = 1; 1540221552e4SDag-Erling Smørgrav break; 1541221552e4SDag-Erling Smørgrav } 1542221552e4SDag-Erling Smørgrav } 1543221552e4SDag-Erling Smørgrav if (!found) { 1544221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1545221552e4SDag-Erling Smørgrav c->self); 1546221552e4SDag-Erling Smørgrav return -1; 1547221552e4SDag-Erling Smørgrav } 154819261079SEd Maste if ((r = sshbuf_consume(input, nmethods + 2)) != 0) 154919261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 15504f52dfbbSDag-Erling Smørgrav /* version, method */ 15514f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(output, 0x05)) != 0 || 155219261079SEd Maste (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) 155319261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1554221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1555221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1556221552e4SDag-Erling Smørgrav return 0; /* need more */ 1557221552e4SDag-Erling Smørgrav } 1558221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1559221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1560221552e4SDag-Erling Smørgrav return 0; /* need more */ 1561333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1562221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1563221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1564221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1565221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1566221552e4SDag-Erling Smørgrav return -1; 1567221552e4SDag-Erling Smørgrav } 1568221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1569221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1570221552e4SDag-Erling Smørgrav addrlen = 4; 1571221552e4SDag-Erling Smørgrav af = AF_INET; 1572221552e4SDag-Erling Smørgrav break; 1573221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1574221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1575221552e4SDag-Erling Smørgrav af = -1; 1576221552e4SDag-Erling Smørgrav break; 1577221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1578221552e4SDag-Erling Smørgrav addrlen = 16; 1579221552e4SDag-Erling Smørgrav af = AF_INET6; 1580221552e4SDag-Erling Smørgrav break; 1581221552e4SDag-Erling Smørgrav default: 1582221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1583221552e4SDag-Erling Smørgrav return -1; 1584221552e4SDag-Erling Smørgrav } 1585333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1586333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1587333ee039SDag-Erling Smørgrav need++; 1588333ee039SDag-Erling Smørgrav if (have < need) 1589221552e4SDag-Erling Smørgrav return 0; 159019261079SEd Maste if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) 159119261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 15924f52dfbbSDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 15934f52dfbbSDag-Erling Smørgrav /* host string length */ 159419261079SEd Maste if ((r = sshbuf_consume(input, 1)) != 0) 159519261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 15964f52dfbbSDag-Erling Smørgrav } 15974f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || 15984f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &dest_port, 2)) != 0) { 159919261079SEd Maste debug_r(r, "channel %d: parse addr/port", c->self); 16004f52dfbbSDag-Erling Smørgrav return -1; 16014f52dfbbSDag-Erling Smørgrav } 1602221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1603e4a9863fSDag-Erling Smørgrav free(c->path); 1604cce7d346SDag-Erling Smørgrav c->path = NULL; 1605cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1606cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1607cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1608cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1609221552e4SDag-Erling Smørgrav return -1; 1610cce7d346SDag-Erling Smørgrav } 1611cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1612cce7d346SDag-Erling Smørgrav } else { 1613cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1614cce7d346SDag-Erling Smørgrav return -1; 1615cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1616cce7d346SDag-Erling Smørgrav } 1617221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1618221552e4SDag-Erling Smørgrav 1619221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1620221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1621221552e4SDag-Erling Smørgrav 1622221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1623221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1624221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1625221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1626221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1627221552e4SDag-Erling Smørgrav 16284f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || 16294f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || 16304f52dfbbSDag-Erling Smørgrav (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) 163119261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1632221552e4SDag-Erling Smørgrav return 1; 1633221552e4SDag-Erling Smørgrav } 1634221552e4SDag-Erling Smørgrav 1635b15c8340SDag-Erling Smørgrav Channel * 16364f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh, 163719261079SEd Maste const char *host_to_connect, u_short port_to_connect, 163819261079SEd Maste int in, int out, int nonblock) 1639b15c8340SDag-Erling Smørgrav { 1640b15c8340SDag-Erling Smørgrav Channel *c; 1641b15c8340SDag-Erling Smørgrav 164219261079SEd Maste debug_f("%s:%d", host_to_connect, port_to_connect); 1643b15c8340SDag-Erling Smørgrav 16444f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, 1645b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 164619261079SEd Maste 0, "stdio-forward", nonblock); 1647b15c8340SDag-Erling Smørgrav 1648b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1649b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1650b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1651b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1652b15c8340SDag-Erling Smørgrav 16534f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); 16544f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 1655b15c8340SDag-Erling Smørgrav 1656b15c8340SDag-Erling Smørgrav return c; 1657b15c8340SDag-Erling Smørgrav } 1658b15c8340SDag-Erling Smørgrav 1659ca3176e7SBrian Feldman /* dynamic port forwarding */ 1660af12a3e7SDag-Erling Smørgrav static void 16611323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c) 1662ca3176e7SBrian Feldman { 16634f52dfbbSDag-Erling Smørgrav const u_char *p; 1664d4ecd108SDag-Erling Smørgrav u_int have; 1665d4ecd108SDag-Erling Smørgrav int ret; 1666ca3176e7SBrian Feldman 16671323ec57SEd Maste c->io_want = 0; 16684f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->input); 1669ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 16704f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->input, stderr); */ 1671ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1672221552e4SDag-Erling Smørgrav if (have < 3) { 1673ca3176e7SBrian Feldman /* need more */ 16741323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1675ca3176e7SBrian Feldman return; 1676ca3176e7SBrian Feldman } 1677ca3176e7SBrian Feldman /* try to guess the protocol */ 16784f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->input); 16794f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u8? */ 1680ca3176e7SBrian Feldman switch (p[0]) { 1681ca3176e7SBrian Feldman case 0x04: 16824f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->input, c->output); 1683ca3176e7SBrian Feldman break; 1684221552e4SDag-Erling Smørgrav case 0x05: 16854f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->input, c->output); 1686221552e4SDag-Erling Smørgrav break; 1687ca3176e7SBrian Feldman default: 1688ca3176e7SBrian Feldman ret = -1; 1689ca3176e7SBrian Feldman break; 1690ca3176e7SBrian Feldman } 1691ca3176e7SBrian Feldman if (ret < 0) { 16924f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1693ca3176e7SBrian Feldman } else if (ret == 0) { 1694ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1695ca3176e7SBrian Feldman /* need more */ 16961323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 16974f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output)) 16981323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1699ca3176e7SBrian Feldman } else { 1700ca3176e7SBrian Feldman /* switch to the next state */ 1701ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 17024f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 17034f52dfbbSDag-Erling Smørgrav } 17044f52dfbbSDag-Erling Smørgrav } 17054f52dfbbSDag-Erling Smørgrav 17064f52dfbbSDag-Erling Smørgrav /* simulate read-error */ 17074f52dfbbSDag-Erling Smørgrav static void 17084f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c) 17094f52dfbbSDag-Erling Smørgrav { 17104f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1711*f374ba41SEd Maste channel_force_close(ssh, c, 0); 17124f52dfbbSDag-Erling Smørgrav } 17134f52dfbbSDag-Erling Smørgrav 17144f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */ 17154f52dfbbSDag-Erling Smørgrav static void 17161323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) 17174f52dfbbSDag-Erling Smørgrav { 17184f52dfbbSDag-Erling Smørgrav const u_char *p; 17194f52dfbbSDag-Erling Smørgrav u_int have, len; 17204f52dfbbSDag-Erling Smørgrav int r, ret; 17214f52dfbbSDag-Erling Smørgrav 17224f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->output); 17234f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: have %d", c->self, have); 17244f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->output, stderr); */ 17254f52dfbbSDag-Erling Smørgrav /* EOF received */ 17264f52dfbbSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 172719261079SEd Maste if ((r = sshbuf_consume(c->output, have)) != 0) 172819261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 17294f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 17304f52dfbbSDag-Erling Smørgrav return; 17314f52dfbbSDag-Erling Smørgrav } 17324f52dfbbSDag-Erling Smørgrav /* check if the fixed size part of the packet is in buffer. */ 17334f52dfbbSDag-Erling Smørgrav if (have < 3) 17344f52dfbbSDag-Erling Smørgrav return; 17354f52dfbbSDag-Erling Smørgrav /* try to guess the protocol */ 17364f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->output); 17374f52dfbbSDag-Erling Smørgrav switch (p[0]) { 17384f52dfbbSDag-Erling Smørgrav case 0x04: 17394f52dfbbSDag-Erling Smørgrav /* switch input/output for reverse forwarding */ 17404f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->output, c->input); 17414f52dfbbSDag-Erling Smørgrav break; 17424f52dfbbSDag-Erling Smørgrav case 0x05: 17434f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->output, c->input); 17444f52dfbbSDag-Erling Smørgrav break; 17454f52dfbbSDag-Erling Smørgrav default: 17464f52dfbbSDag-Erling Smørgrav ret = -1; 17474f52dfbbSDag-Erling Smørgrav break; 17484f52dfbbSDag-Erling Smørgrav } 17494f52dfbbSDag-Erling Smørgrav if (ret < 0) { 17504f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 17514f52dfbbSDag-Erling Smørgrav } else if (ret == 0) { 17524f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: need more", c->self); 17534f52dfbbSDag-Erling Smørgrav /* send socks request to peer */ 17544f52dfbbSDag-Erling Smørgrav len = sshbuf_len(c->input); 17554f52dfbbSDag-Erling Smørgrav if (len > 0 && len < c->remote_window) { 17564f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 17574f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 17584f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_stringb(ssh, c->input)) != 0 || 17594f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 176019261079SEd Maste fatal_fr(r, "channel %i: rdynamic", c->self); 17614f52dfbbSDag-Erling Smørgrav } 176219261079SEd Maste if ((r = sshbuf_consume(c->input, len)) != 0) 176319261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 17644f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 17654f52dfbbSDag-Erling Smørgrav } 17664f52dfbbSDag-Erling Smørgrav } else if (rdynamic_connect_finish(ssh, c) < 0) { 17674f52dfbbSDag-Erling Smørgrav /* the connect failed */ 17684f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1769ca3176e7SBrian Feldman } 1770ca3176e7SBrian Feldman } 1771ca3176e7SBrian Feldman 1772a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1773af12a3e7SDag-Erling Smørgrav static void 17741323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c) 1775511b41d2SMark Murray { 1776af12a3e7SDag-Erling Smørgrav Channel *nc; 1777d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 17784f52dfbbSDag-Erling Smørgrav int r, newsock, oerrno, remote_port; 1779511b41d2SMark Murray socklen_t addrlen; 1780ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1781511b41d2SMark Murray 17821323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 17834f52dfbbSDag-Erling Smørgrav return; 17844f52dfbbSDag-Erling Smørgrav 1785511b41d2SMark Murray debug("X11 connection requested."); 1786511b41d2SMark Murray addrlen = sizeof(addr); 1787d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1788af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1789e4a9863fSDag-Erling Smørgrav oerrno = errno; 1790221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 179119261079SEd Maste channel_close_fd(ssh, c, &c->sock); 17924f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1793e4a9863fSDag-Erling Smørgrav errno = oerrno; 1794af12a3e7SDag-Erling Smørgrav } 179519261079SEd Maste if (newsock == -1) { 1796e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1797e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1798511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1799462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1800e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1801a04a10f8SKris Kennaway return; 1802511b41d2SMark Murray } 1803af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1804ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1805a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1806511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1807ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1808a04a10f8SKris Kennaway 1809*f374ba41SEd Maste nc = channel_new(ssh, "x11-connection", 1810a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1811221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 18124f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "x11"); 181347dd1d1bSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 181447dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, remote_port)) != 0) { 181519261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1816511b41d2SMark Murray } 18174f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 181819261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1819e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1820a04a10f8SKris Kennaway } 1821511b41d2SMark Murray 1822af12a3e7SDag-Erling Smørgrav static void 18234f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype) 1824ca3176e7SBrian Feldman { 1825f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1826076ad2f8SDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 1827ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1828d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 18294f52dfbbSDag-Erling Smørgrav int r; 1830ca3176e7SBrian Feldman 1831b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1832b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1833e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1834b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1835b15c8340SDag-Erling Smørgrav remote_port = 65535; 1836b15c8340SDag-Erling Smørgrav } 1837b15c8340SDag-Erling Smørgrav 18384f52dfbbSDag-Erling Smørgrav free(c->remote_name); 18394f52dfbbSDag-Erling Smørgrav xasprintf(&c->remote_name, 1840ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1841f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1842ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1843f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1844ca3176e7SBrian Feldman 18454f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, rtype); 1846a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1847ca3176e7SBrian Feldman /* target host, port */ 18484f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 184919261079SEd Maste (r = sshpkt_put_u32(ssh, c->host_port)) != 0) 185019261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1851a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1852a0ee8cc6SDag-Erling Smørgrav /* target path */ 185319261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 185419261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1855a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1856a0ee8cc6SDag-Erling Smørgrav /* listen path */ 185719261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 185819261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1859ca3176e7SBrian Feldman } else { 1860ca3176e7SBrian Feldman /* listen address, port */ 18614f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 186219261079SEd Maste (r = sshpkt_put_u32(ssh, local_port)) != 0) 186319261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1864ca3176e7SBrian Feldman } 1865a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1866a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 186719261079SEd Maste if ((r = sshpkt_put_cstring(ssh, "")) != 0) 186819261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1869a0ee8cc6SDag-Erling Smørgrav } else { 1870ca3176e7SBrian Feldman /* originator host and port */ 18714f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 187219261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) 187319261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1874ca3176e7SBrian Feldman } 18754f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 187619261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1877e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1878f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1879ca3176e7SBrian Feldman } 1880ca3176e7SBrian Feldman 1881557f75e5SDag-Erling Smørgrav void 18824f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) 1883557f75e5SDag-Erling Smørgrav { 18844f52dfbbSDag-Erling Smørgrav ssh->chanctxt->x11_refuse_time = refuse_time; 1885557f75e5SDag-Erling Smørgrav } 1886557f75e5SDag-Erling Smørgrav 1887511b41d2SMark Murray /* 1888a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1889511b41d2SMark Murray */ 1890af12a3e7SDag-Erling Smørgrav static void 18911323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c) 1892a04a10f8SKris Kennaway { 1893ca3176e7SBrian Feldman Channel *nc; 1894d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1895af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1896a04a10f8SKris Kennaway socklen_t addrlen; 1897ca3176e7SBrian Feldman char *rtype; 1898a04a10f8SKris Kennaway 18991323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 19004f52dfbbSDag-Erling Smørgrav return; 19014f52dfbbSDag-Erling Smørgrav 19024f52dfbbSDag-Erling Smørgrav debug("Connection to port %d forwarding to %.100s port %d requested.", 1903a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1904ca3176e7SBrian Feldman 1905af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1906af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1907af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1908a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1909a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1910a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1911a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1912a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1913a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1914a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1915af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1916af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1917af12a3e7SDag-Erling Smørgrav } else { 1918af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1919af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1920af12a3e7SDag-Erling Smørgrav } 1921ca3176e7SBrian Feldman 1922511b41d2SMark Murray addrlen = sizeof(addr); 1923d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 192419261079SEd Maste if (newsock == -1) { 1925e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1926e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1927511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1928462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1929e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1930a04a10f8SKris Kennaway return; 1931511b41d2SMark Murray } 1932a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1933af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 19344f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, 1935221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1936ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1937ca3176e7SBrian Feldman nc->host_port = c->host_port; 1938cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1939cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 1940ca3176e7SBrian Feldman 1941b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 19424f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, nc, rtype); 1943a04a10f8SKris Kennaway } 1944511b41d2SMark Murray 1945511b41d2SMark Murray /* 1946a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1947a04a10f8SKris Kennaway * clients. 1948511b41d2SMark Murray */ 1949af12a3e7SDag-Erling Smørgrav static void 19501323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c) 1951a04a10f8SKris Kennaway { 1952af12a3e7SDag-Erling Smørgrav Channel *nc; 19534f52dfbbSDag-Erling Smørgrav int r, newsock; 1954d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1955a04a10f8SKris Kennaway socklen_t addrlen; 1956a04a10f8SKris Kennaway 19571323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 19584f52dfbbSDag-Erling Smørgrav return; 19594f52dfbbSDag-Erling Smørgrav 1960511b41d2SMark Murray addrlen = sizeof(addr); 1961d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 196219261079SEd Maste if (newsock == -1) { 19634f52dfbbSDag-Erling Smørgrav error("accept from auth socket: %.100s", strerror(errno)); 1964462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1965e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1966a04a10f8SKris Kennaway return; 1967511b41d2SMark Murray } 1968*f374ba41SEd Maste nc = channel_new(ssh, "agent-connection", 1969ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1970ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1971221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 19724f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); 19734f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 197419261079SEd Maste fatal_fr(r, "channel %i", c->self); 1975a04a10f8SKris Kennaway } 1976511b41d2SMark Murray 1977af12a3e7SDag-Erling Smørgrav static void 19781323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c) 1979ca3176e7SBrian Feldman { 19804f52dfbbSDag-Erling Smørgrav int err = 0, sock, isopen, r; 1981af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1982af12a3e7SDag-Erling Smørgrav 19831323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) 19844f52dfbbSDag-Erling Smørgrav return; 19854f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 198619261079SEd Maste fatal_f("channel %d: no remote id", c->self); 19874f52dfbbSDag-Erling Smørgrav /* for rdynamic the OPEN_CONFIRMATION has been sent already */ 19884f52dfbbSDag-Erling Smørgrav isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); 198919261079SEd Maste if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { 1990af12a3e7SDag-Erling Smørgrav err = errno; 1991af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1992af12a3e7SDag-Erling Smørgrav } 1993ca3176e7SBrian Feldman if (err == 0) { 1994d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 1995d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 1996d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1997af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1998*f374ba41SEd Maste c->lastused = monotime(); 19994f52dfbbSDag-Erling Smørgrav if (isopen) { 20004f52dfbbSDag-Erling Smørgrav /* no message necessary */ 2001af12a3e7SDag-Erling Smørgrav } else { 20024f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 20034f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 20044f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 20054f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 20064f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 200719261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 200819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 200919261079SEd Maste fatal_fr(r, "channel %i open confirm", c->self); 2010af12a3e7SDag-Erling Smørgrav } 2011ca3176e7SBrian Feldman } else { 2012d4af9e69SDag-Erling Smørgrav debug("channel %d: connection failed: %s", 2013ca3176e7SBrian Feldman c->self, strerror(err)); 2014d4af9e69SDag-Erling Smørgrav /* Try next address, if any */ 2015d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&c->connect_ctx)) > 0) { 2016d4af9e69SDag-Erling Smørgrav close(c->sock); 2017d4af9e69SDag-Erling Smørgrav c->sock = c->rfd = c->wfd = sock; 2018d4af9e69SDag-Erling Smørgrav return; 2019d4af9e69SDag-Erling Smørgrav } 2020d4af9e69SDag-Erling Smørgrav /* Exhausted all addresses */ 2021d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 2022d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 2023d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 20244f52dfbbSDag-Erling Smørgrav if (isopen) { 20254f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 2026af12a3e7SDag-Erling Smørgrav } else { 20274f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 20284f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 20294f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 203047dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, 203147dd1d1bSDag-Erling Smørgrav SSH2_OPEN_CONNECT_FAILED)) != 0 || 203247dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || 203319261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 203419261079SEd Maste (r = sshpkt_send(ssh)) != 0) 203519261079SEd Maste fatal_fr(r, "channel %i: failure", c->self); 20364f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2037ca3176e7SBrian Feldman } 2038ca3176e7SBrian Feldman } 2039ca3176e7SBrian Feldman } 2040ca3176e7SBrian Feldman 2041af12a3e7SDag-Erling Smørgrav static int 20421323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c) 2043a04a10f8SKris Kennaway { 2044aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 20454f52dfbbSDag-Erling Smørgrav ssize_t len; 20464f52dfbbSDag-Erling Smørgrav int r, force; 2047*f374ba41SEd Maste size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ; 20481323ec57SEd Maste int pty_zeroread = 0; 20491323ec57SEd Maste 20501323ec57SEd Maste #ifdef PTY_ZEROREAD 20511323ec57SEd Maste /* Bug on AIX: read(1) can return 0 for a non-closed fd */ 20521323ec57SEd Maste pty_zeroread = c->isatty; 20531323ec57SEd Maste #endif 2054511b41d2SMark Murray 2055d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 20564f52dfbbSDag-Erling Smørgrav 20571323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0) 20584f52dfbbSDag-Erling Smørgrav return 1; 20591323ec57SEd Maste if ((avail = sshbuf_avail(c->input)) == 0) 20601323ec57SEd Maste return 1; /* Shouldn't happen */ 20611323ec57SEd Maste 20621323ec57SEd Maste /* 20631323ec57SEd Maste * For "simple" channels (i.e. not datagram or filtered), we can 20641323ec57SEd Maste * read directly to the channel buffer. 20651323ec57SEd Maste */ 20661323ec57SEd Maste if (!pty_zeroread && c->input_filter == NULL && !c->datagram) { 20671323ec57SEd Maste /* Only OPEN channels have valid rwin */ 20681323ec57SEd Maste if (c->type == SSH_CHANNEL_OPEN) { 20691323ec57SEd Maste if ((have = sshbuf_len(c->input)) >= c->remote_window) 20701323ec57SEd Maste return 1; /* shouldn't happen */ 20711323ec57SEd Maste if (maxlen > c->remote_window - have) 20721323ec57SEd Maste maxlen = c->remote_window - have; 20731323ec57SEd Maste } 20741323ec57SEd Maste if (maxlen > avail) 20751323ec57SEd Maste maxlen = avail; 2076*f374ba41SEd Maste if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) { 20771323ec57SEd Maste if (errno == EINTR || (!force && 20781323ec57SEd Maste (errno == EAGAIN || errno == EWOULDBLOCK))) 20791323ec57SEd Maste return 1; 20801323ec57SEd Maste debug2("channel %d: read failed rfd %d maxlen %zu: %s", 20811323ec57SEd Maste c->self, c->rfd, maxlen, ssh_err(r)); 20821323ec57SEd Maste goto rfail; 20831323ec57SEd Maste } 2084*f374ba41SEd Maste if (nr != 0) 2085*f374ba41SEd Maste c->lastused = monotime(); 20861323ec57SEd Maste return 1; 20871323ec57SEd Maste } 20884f52dfbbSDag-Erling Smørgrav 2089333ee039SDag-Erling Smørgrav errno = 0; 2090a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 20911323ec57SEd Maste /* fixup AIX zero-length read with errno set to look more like errors */ 20921323ec57SEd Maste if (pty_zeroread && len == 0 && errno != 0) 20931323ec57SEd Maste len = -1; 209419261079SEd Maste if (len == -1 && (errno == EINTR || 2095d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 2096a04a10f8SKris Kennaway return 1; 20971323ec57SEd Maste if (len < 0 || (!pty_zeroread && len == 0)) { 20981323ec57SEd Maste debug2("channel %d: read<=0 rfd %d len %zd: %s", 20991323ec57SEd Maste c->self, c->rfd, len, 21001323ec57SEd Maste len == 0 ? "closed" : strerror(errno)); 21011323ec57SEd Maste rfail: 2102ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2103221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 21044f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2105ca3176e7SBrian Feldman return -1; 2106a04a10f8SKris Kennaway } else { 21074f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2108a04a10f8SKris Kennaway } 2109a04a10f8SKris Kennaway return -1; 2110a04a10f8SKris Kennaway } 2111*f374ba41SEd Maste c->lastused = monotime(); 2112b66f2d16SKris Kennaway if (c->input_filter != NULL) { 21134f52dfbbSDag-Erling Smørgrav if (c->input_filter(ssh, c, buf, len) == -1) { 2114221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 21154f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2116b66f2d16SKris Kennaway } 2117b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 21184f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->input, buf, len)) != 0) 211919261079SEd Maste fatal_fr(r, "channel %i: put datagram", c->self); 212019261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 212119261079SEd Maste fatal_fr(r, "channel %i: put data", c->self); 21221323ec57SEd Maste 2123a04a10f8SKris Kennaway return 1; 2124a04a10f8SKris Kennaway } 2125333ee039SDag-Erling Smørgrav 2126af12a3e7SDag-Erling Smørgrav static int 21271323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c) 2128a04a10f8SKris Kennaway { 2129ca3176e7SBrian Feldman struct termios tio; 21304f52dfbbSDag-Erling Smørgrav u_char *data = NULL, *buf; /* XXX const; need filter API change */ 21314f52dfbbSDag-Erling Smørgrav size_t dlen, olen = 0; 21324f52dfbbSDag-Erling Smørgrav int r, len; 21334f52dfbbSDag-Erling Smørgrav 21341323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 21351323ec57SEd Maste return 1; 21361323ec57SEd Maste if (sshbuf_len(c->output) == 0) 21374f52dfbbSDag-Erling Smørgrav return 1; 2138a04a10f8SKris Kennaway 2139a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 21404f52dfbbSDag-Erling Smørgrav olen = sshbuf_len(c->output); 2141b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 21424f52dfbbSDag-Erling Smørgrav if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { 2143b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 2144b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 21454f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2146b74df5b2SDag-Erling Smørgrav else 21474f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2148b74df5b2SDag-Erling Smørgrav return -1; 2149b74df5b2SDag-Erling Smørgrav } 2150b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 21514f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) 215219261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 21534f52dfbbSDag-Erling Smørgrav buf = data; 2154b74df5b2SDag-Erling Smørgrav } else { 21554f52dfbbSDag-Erling Smørgrav buf = data = sshbuf_mutable_ptr(c->output); 21564f52dfbbSDag-Erling Smørgrav dlen = sshbuf_len(c->output); 2157b74df5b2SDag-Erling Smørgrav } 2158b74df5b2SDag-Erling Smørgrav 2159b74df5b2SDag-Erling Smørgrav if (c->datagram) { 2160b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 2161b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 2162e4a9863fSDag-Erling Smørgrav free(data); 216319261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2164d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2165b74df5b2SDag-Erling Smørgrav return 1; 21664f52dfbbSDag-Erling Smørgrav if (len <= 0) 21674f52dfbbSDag-Erling Smørgrav goto write_fail; 2168e2f6069cSDag-Erling Smørgrav goto out; 2169b74df5b2SDag-Erling Smørgrav } 21704f52dfbbSDag-Erling Smørgrav 2171f388f5efSDag-Erling Smørgrav #ifdef _AIX 2172f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 21734f52dfbbSDag-Erling Smørgrav if (c->wfd_isatty) 21741323ec57SEd Maste dlen = MINIMUM(dlen, 8*1024); 2175f388f5efSDag-Erling Smørgrav #endif 2176b74df5b2SDag-Erling Smørgrav 2177b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 217819261079SEd Maste if (len == -1 && 2179d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 2180a04a10f8SKris Kennaway return 1; 2181511b41d2SMark Murray if (len <= 0) { 21824f52dfbbSDag-Erling Smørgrav write_fail: 2183ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2184221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 21854f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2186ca3176e7SBrian Feldman return -1; 2187511b41d2SMark Murray } else { 21884f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2189511b41d2SMark Murray } 2190a04a10f8SKris Kennaway return -1; 2191511b41d2SMark Murray } 2192*f374ba41SEd Maste c->lastused = monotime(); 21937aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 21944f52dfbbSDag-Erling Smørgrav if (c->isatty && dlen >= 1 && buf[0] != '\r') { 2195e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 2196e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 2197e0fbb1d2SBrian Feldman /* 2198e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 2199ca3176e7SBrian Feldman * traffic analysis. We need to match the 2200ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 2201b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 2202e0fbb1d2SBrian Feldman */ 22034f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || 22044f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 220519261079SEd Maste fatal_fr(r, "channel %i: ignore", c->self); 2206e0fbb1d2SBrian Feldman } 2207e0fbb1d2SBrian Feldman } 22084f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */ 220919261079SEd Maste if ((r = sshbuf_consume(c->output, len)) != 0) 221019261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2211e2f6069cSDag-Erling Smørgrav out: 22124f52dfbbSDag-Erling Smørgrav c->local_consumed += olen - sshbuf_len(c->output); 22134f52dfbbSDag-Erling Smørgrav 2214a04a10f8SKris Kennaway return 1; 2215511b41d2SMark Murray } 2216333ee039SDag-Erling Smørgrav 2217af12a3e7SDag-Erling Smørgrav static int 22181323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c) 2219a04a10f8SKris Kennaway { 22204f52dfbbSDag-Erling Smørgrav int r; 22214f52dfbbSDag-Erling Smørgrav ssize_t len; 2222511b41d2SMark Murray 22231323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) 22241323ec57SEd Maste return 1; 22251323ec57SEd Maste if (sshbuf_len(c->extended) == 0) 22264f52dfbbSDag-Erling Smørgrav return 1; 22274f52dfbbSDag-Erling Smørgrav 22284f52dfbbSDag-Erling Smørgrav len = write(c->efd, sshbuf_ptr(c->extended), 22294f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended)); 22304f52dfbbSDag-Erling Smørgrav debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); 223119261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2232d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2233ca3176e7SBrian Feldman return 1; 2234ca3176e7SBrian Feldman if (len <= 0) { 22354f52dfbbSDag-Erling Smørgrav debug2("channel %d: closing write-efd %d", c->self, c->efd); 223619261079SEd Maste channel_close_fd(ssh, c, &c->efd); 2237ca3176e7SBrian Feldman } else { 223819261079SEd Maste if ((r = sshbuf_consume(c->extended, len)) != 0) 223919261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2240a04a10f8SKris Kennaway c->local_consumed += len; 2241*f374ba41SEd Maste c->lastused = monotime(); 2242a04a10f8SKris Kennaway } 22434f52dfbbSDag-Erling Smørgrav return 1; 22444f52dfbbSDag-Erling Smørgrav } 22454f52dfbbSDag-Erling Smørgrav 22464f52dfbbSDag-Erling Smørgrav static int 22471323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c) 22484f52dfbbSDag-Erling Smørgrav { 22494f52dfbbSDag-Erling Smørgrav char buf[CHAN_RBUF]; 22504f52dfbbSDag-Erling Smørgrav ssize_t len; 225119261079SEd Maste int r, force; 22524f52dfbbSDag-Erling Smørgrav 225319261079SEd Maste force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 225419261079SEd Maste 22551323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0) 22564f52dfbbSDag-Erling Smørgrav return 1; 22574f52dfbbSDag-Erling Smørgrav 2258a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 22594f52dfbbSDag-Erling Smørgrav debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); 226019261079SEd Maste if (len == -1 && (errno == EINTR || ((errno == EAGAIN || 226119261079SEd Maste errno == EWOULDBLOCK) && !force))) 2262ca3176e7SBrian Feldman return 1; 2263ca3176e7SBrian Feldman if (len <= 0) { 226419261079SEd Maste debug2("channel %d: closing read-efd %d", c->self, c->efd); 226519261079SEd Maste channel_close_fd(ssh, c, &c->efd); 2266*f374ba41SEd Maste return 1; 2267*f374ba41SEd Maste } 2268*f374ba41SEd Maste c->lastused = monotime(); 2269*f374ba41SEd Maste if (c->extended_usage == CHAN_EXTENDED_IGNORE) 227019261079SEd Maste debug3("channel %d: discard efd", c->self); 227119261079SEd Maste else if ((r = sshbuf_put(c->extended, buf, len)) != 0) 227219261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 2273a04a10f8SKris Kennaway return 1; 2274a04a10f8SKris Kennaway } 2275333ee039SDag-Erling Smørgrav 227621e764dfSDag-Erling Smørgrav static int 22771323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c) 2278a04a10f8SKris Kennaway { 22794f52dfbbSDag-Erling Smørgrav if (c->efd == -1) 22804f52dfbbSDag-Erling Smørgrav return 1; 22814f52dfbbSDag-Erling Smørgrav 22824f52dfbbSDag-Erling Smørgrav /** XXX handle drain efd, too */ 22834f52dfbbSDag-Erling Smørgrav 22844f52dfbbSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_WRITE) 22851323ec57SEd Maste return channel_handle_efd_write(ssh, c); 22864f52dfbbSDag-Erling Smørgrav else if (c->extended_usage == CHAN_EXTENDED_READ || 22874f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) 22881323ec57SEd Maste return channel_handle_efd_read(ssh, c); 22894f52dfbbSDag-Erling Smørgrav 22904f52dfbbSDag-Erling Smørgrav return 1; 22914f52dfbbSDag-Erling Smørgrav } 22924f52dfbbSDag-Erling Smørgrav 22934f52dfbbSDag-Erling Smørgrav static int 22944f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c) 22954f52dfbbSDag-Erling Smørgrav { 22964f52dfbbSDag-Erling Smørgrav int r; 22974f52dfbbSDag-Erling Smørgrav 2298ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 2299ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 2300d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 2301d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 2302d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 2303a04a10f8SKris Kennaway c->local_consumed > 0) { 23044f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 230519261079SEd Maste fatal_f("channel %d: no remote id", c->self); 23064f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 23074f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 23084f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 23094f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || 23104f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 231119261079SEd Maste fatal_fr(r, "channel %i", c->self); 23124f52dfbbSDag-Erling Smørgrav } 231319261079SEd Maste debug2("channel %d: window %d sent adjust %d", c->self, 231419261079SEd Maste c->local_window, c->local_consumed); 231560c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 2316a04a10f8SKris Kennaway c->local_consumed = 0; 2317a04a10f8SKris Kennaway } 2318a04a10f8SKris Kennaway return 1; 2319a04a10f8SKris Kennaway } 2320a04a10f8SKris Kennaway 2321af12a3e7SDag-Erling Smørgrav static void 23221323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c) 2323a04a10f8SKris Kennaway { 23241323ec57SEd Maste channel_handle_rfd(ssh, c); 23251323ec57SEd Maste channel_handle_wfd(ssh, c); 23261323ec57SEd Maste channel_handle_efd(ssh, c); 23274f52dfbbSDag-Erling Smørgrav channel_check_window(ssh, c); 2328a04a10f8SKris Kennaway } 2329a04a10f8SKris Kennaway 2330b15c8340SDag-Erling Smørgrav static u_int 23314f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need) 2332b15c8340SDag-Erling Smørgrav { 2333b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 23344f52dfbbSDag-Erling Smørgrav ssize_t len; 2335b15c8340SDag-Erling Smørgrav u_int rlen; 23364f52dfbbSDag-Erling Smørgrav int r; 2337b15c8340SDag-Erling Smørgrav 23384f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) < need) { 23394f52dfbbSDag-Erling Smørgrav rlen = need - sshbuf_len(c->input); 2340ca86bcf2SDag-Erling Smørgrav len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); 234119261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 23424f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2343b15c8340SDag-Erling Smørgrav if (len <= 0) { 23444f52dfbbSDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %zd", 2345b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 23464f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2347b15c8340SDag-Erling Smørgrav return 0; 234819261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 234919261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 23504f52dfbbSDag-Erling Smørgrav } 23514f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2352b15c8340SDag-Erling Smørgrav } 2353b15c8340SDag-Erling Smørgrav 2354b15c8340SDag-Erling Smørgrav static void 23551323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c) 2356b15c8340SDag-Erling Smørgrav { 2357b15c8340SDag-Erling Smørgrav u_int need; 2358b15c8340SDag-Erling Smørgrav 23591323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) 23604f52dfbbSDag-Erling Smørgrav return; 23614f52dfbbSDag-Erling Smørgrav if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) 23624f52dfbbSDag-Erling Smørgrav return; 23634f52dfbbSDag-Erling Smørgrav if (c->mux_pause) 23644f52dfbbSDag-Erling Smørgrav return; 2365b15c8340SDag-Erling Smørgrav 2366b15c8340SDag-Erling Smørgrav /* 2367b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 2368b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 2369b15c8340SDag-Erling Smørgrav */ 23704f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, 4) < 4) /* read header */ 2371b15c8340SDag-Erling Smørgrav return; 23724f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u32 */ 23734f52dfbbSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(c->input)); 2374b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 2375b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 2376b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 2377b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 23784f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 2379b15c8340SDag-Erling Smørgrav return; 2380b15c8340SDag-Erling Smørgrav } 23814f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ 2382b15c8340SDag-Erling Smørgrav return; 23834f52dfbbSDag-Erling Smørgrav if (c->mux_rcb(ssh, c) != 0) { 2384b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 23854f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2386b15c8340SDag-Erling Smørgrav return; 2387b15c8340SDag-Erling Smørgrav } 2388b15c8340SDag-Erling Smørgrav } 2389b15c8340SDag-Erling Smørgrav 2390b15c8340SDag-Erling Smørgrav static void 23911323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c) 23924f52dfbbSDag-Erling Smørgrav { 23934f52dfbbSDag-Erling Smørgrav ssize_t len; 23944f52dfbbSDag-Erling Smørgrav int r; 23954f52dfbbSDag-Erling Smørgrav 23961323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 23971323ec57SEd Maste return; 23981323ec57SEd Maste if (sshbuf_len(c->output) == 0) 23994f52dfbbSDag-Erling Smørgrav return; 24004f52dfbbSDag-Erling Smørgrav 24014f52dfbbSDag-Erling Smørgrav len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); 240219261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 24034f52dfbbSDag-Erling Smørgrav return; 24044f52dfbbSDag-Erling Smørgrav if (len <= 0) { 24054f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 24064f52dfbbSDag-Erling Smørgrav return; 24074f52dfbbSDag-Erling Smørgrav } 24084f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, len)) != 0) 240919261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 24104f52dfbbSDag-Erling Smørgrav } 24114f52dfbbSDag-Erling Smørgrav 24124f52dfbbSDag-Erling Smørgrav static void 24131323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c) 24144f52dfbbSDag-Erling Smørgrav { 24151323ec57SEd Maste channel_post_mux_client_read(ssh, c); 24161323ec57SEd Maste channel_post_mux_client_write(ssh, c); 24174f52dfbbSDag-Erling Smørgrav } 24184f52dfbbSDag-Erling Smørgrav 24194f52dfbbSDag-Erling Smørgrav static void 24201323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c) 2421b15c8340SDag-Erling Smørgrav { 2422b15c8340SDag-Erling Smørgrav Channel *nc; 2423b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 2424b15c8340SDag-Erling Smørgrav socklen_t addrlen; 2425b15c8340SDag-Erling Smørgrav int newsock; 2426b15c8340SDag-Erling Smørgrav uid_t euid; 2427b15c8340SDag-Erling Smørgrav gid_t egid; 2428b15c8340SDag-Erling Smørgrav 24291323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 2430b15c8340SDag-Erling Smørgrav return; 2431b15c8340SDag-Erling Smørgrav 2432b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 2433b15c8340SDag-Erling Smørgrav 2434b15c8340SDag-Erling Smørgrav /* 2435b15c8340SDag-Erling Smørgrav * Accept connection on control socket 2436b15c8340SDag-Erling Smørgrav */ 2437b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 2438b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 2439b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 2440b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 244119261079SEd Maste error_f("accept: %s", strerror(errno)); 2442462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2443e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2444b15c8340SDag-Erling Smørgrav return; 2445b15c8340SDag-Erling Smørgrav } 2446b15c8340SDag-Erling Smørgrav 244719261079SEd Maste if (getpeereid(newsock, &euid, &egid) == -1) { 244819261079SEd Maste error_f("getpeereid failed: %s", strerror(errno)); 2449b15c8340SDag-Erling Smørgrav close(newsock); 2450b15c8340SDag-Erling Smørgrav return; 2451b15c8340SDag-Erling Smørgrav } 2452b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 2453b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 2454b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 2455b15c8340SDag-Erling Smørgrav close(newsock); 2456b15c8340SDag-Erling Smørgrav return; 2457b15c8340SDag-Erling Smørgrav } 2458*f374ba41SEd Maste nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT, 2459b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 2460b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 2461b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 246219261079SEd Maste debug3_f("new mux channel %d fd %d", nc->self, nc->sock); 2463b15c8340SDag-Erling Smørgrav /* establish state */ 24644f52dfbbSDag-Erling Smørgrav nc->mux_rcb(ssh, nc); 2465b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 2466b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 2467b15c8340SDag-Erling Smørgrav } 2468b15c8340SDag-Erling Smørgrav 2469af12a3e7SDag-Erling Smørgrav static void 24704f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc) 2471a04a10f8SKris Kennaway { 24724f52dfbbSDag-Erling Smørgrav chan_fn **pre, **post; 2473f388f5efSDag-Erling Smørgrav 24744f52dfbbSDag-Erling Smørgrav if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || 24754f52dfbbSDag-Erling Smørgrav (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) 247619261079SEd Maste fatal_f("allocation failed"); 2477511b41d2SMark Murray 24784f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 24794f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 24804f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 24814f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 24824f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 24834f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 24844f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 24854f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 24864f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 24874f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 24884f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; 24894f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 24904f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2491a04a10f8SKris Kennaway 24924f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_OPEN] = &channel_post_open; 24934f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 24944f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 24954f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 24964f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 24974f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 24984f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 24994f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 25004f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 25014f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; 25024f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 25034f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2504a04a10f8SKris Kennaway 25054f52dfbbSDag-Erling Smørgrav sc->channel_pre = pre; 25064f52dfbbSDag-Erling Smørgrav sc->channel_post = post; 2507a04a10f8SKris Kennaway } 2508a04a10f8SKris Kennaway 2509af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2510af12a3e7SDag-Erling Smørgrav static void 25114f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c) 2512af12a3e7SDag-Erling Smørgrav { 2513af12a3e7SDag-Erling Smørgrav if (c == NULL) 2514af12a3e7SDag-Erling Smørgrav return; 2515af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 25164f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, c->detach_close)) 2517af12a3e7SDag-Erling Smørgrav return; 25182f513db7SEd Maste 2519221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 2520*f374ba41SEd Maste c->detach_user(ssh, c->self, 0, NULL); 2521af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2522af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2523af12a3e7SDag-Erling Smørgrav return; 2524221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2525af12a3e7SDag-Erling Smørgrav } 25264f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, 1)) 2527af12a3e7SDag-Erling Smørgrav return; 2528221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 25294f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2530af12a3e7SDag-Erling Smørgrav } 2531af12a3e7SDag-Erling Smørgrav 25324f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST }; 25334f52dfbbSDag-Erling Smørgrav 2534af12a3e7SDag-Erling Smørgrav static void 2535*f374ba41SEd Maste channel_handler(struct ssh *ssh, int table, struct timespec *timeout) 2536a04a10f8SKris Kennaway { 25374f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 25384f52dfbbSDag-Erling Smørgrav chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; 2539b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2540a04a10f8SKris Kennaway Channel *c; 2541462c32cbSDag-Erling Smørgrav time_t now; 2542a04a10f8SKris Kennaway 2543e4a9863fSDag-Erling Smørgrav now = monotime(); 25444f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 25454f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2546af12a3e7SDag-Erling Smørgrav if (c == NULL) 2547511b41d2SMark Murray continue; 254838a52bd3SEd Maste /* Try to keep IO going while rekeying */ 254938a52bd3SEd Maste if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN) 255038a52bd3SEd Maste continue; 2551b15c8340SDag-Erling Smørgrav if (c->delayed) { 25524f52dfbbSDag-Erling Smørgrav if (table == CHAN_PRE) 2553b15c8340SDag-Erling Smørgrav c->delayed = 0; 2554b15c8340SDag-Erling Smørgrav else 2555b15c8340SDag-Erling Smørgrav continue; 2556b15c8340SDag-Erling Smørgrav } 2557462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2558*f374ba41SEd Maste if (table == CHAN_PRE && 2559*f374ba41SEd Maste c->type == SSH_CHANNEL_OPEN && 2560*f374ba41SEd Maste c->inactive_deadline != 0 && c->lastused != 0 && 2561*f374ba41SEd Maste now >= c->lastused + c->inactive_deadline) { 2562*f374ba41SEd Maste /* channel closed for inactivity */ 2563*f374ba41SEd Maste verbose("channel %d: closing after %u seconds " 2564*f374ba41SEd Maste "of inactivity", c->self, 2565*f374ba41SEd Maste c->inactive_deadline); 2566*f374ba41SEd Maste channel_force_close(ssh, c, 1); 2567*f374ba41SEd Maste } else if (c->notbefore <= now) { 2568*f374ba41SEd Maste /* Run handlers that are not paused. */ 25691323ec57SEd Maste (*ftab[c->type])(ssh, c); 2570*f374ba41SEd Maste /* inactivity timeouts must interrupt poll() */ 2571*f374ba41SEd Maste if (timeout != NULL && 2572*f374ba41SEd Maste c->type == SSH_CHANNEL_OPEN && 2573*f374ba41SEd Maste c->lastused != 0 && 2574*f374ba41SEd Maste c->inactive_deadline != 0) { 2575*f374ba41SEd Maste ptimeout_deadline_monotime(timeout, 2576*f374ba41SEd Maste c->lastused + c->inactive_deadline); 2577*f374ba41SEd Maste } 2578*f374ba41SEd Maste } else if (timeout != NULL) { 2579462c32cbSDag-Erling Smørgrav /* 2580*f374ba41SEd Maste * Arrange for poll() wakeup when channel pause 2581*f374ba41SEd Maste * timer expires. 2582462c32cbSDag-Erling Smørgrav */ 2583*f374ba41SEd Maste ptimeout_deadline_monotime(timeout, 2584*f374ba41SEd Maste c->notbefore); 2585462c32cbSDag-Erling Smørgrav } 2586462c32cbSDag-Erling Smørgrav } 25874f52dfbbSDag-Erling Smørgrav channel_garbage_collect(ssh, c); 2588511b41d2SMark Murray } 2589511b41d2SMark Murray } 2590a04a10f8SKris Kennaway 2591af12a3e7SDag-Erling Smørgrav /* 25921323ec57SEd Maste * Create sockets before preparing IO. 25934f52dfbbSDag-Erling Smørgrav * This is necessary for things that need to happen after reading 25941323ec57SEd Maste * the network-input but need to be completed before IO event setup, e.g. 25951323ec57SEd Maste * because they may create new channels. 25964f52dfbbSDag-Erling Smørgrav */ 25974f52dfbbSDag-Erling Smørgrav static void 25981323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh) 25994f52dfbbSDag-Erling Smørgrav { 26004f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 26014f52dfbbSDag-Erling Smørgrav Channel *c; 26024f52dfbbSDag-Erling Smørgrav u_int i, oalloc; 26034f52dfbbSDag-Erling Smørgrav 26044f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 26054f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 26064f52dfbbSDag-Erling Smørgrav if (c == NULL) 26074f52dfbbSDag-Erling Smørgrav continue; 26084f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) 26091323ec57SEd Maste channel_before_prepare_io_rdynamic(ssh, c); 26104f52dfbbSDag-Erling Smørgrav } 26114f52dfbbSDag-Erling Smørgrav } 26124f52dfbbSDag-Erling Smørgrav 26131323ec57SEd Maste static void 26141323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c, 26151323ec57SEd Maste u_int pollfd_offset, struct pollfd *pfd) 2616a04a10f8SKris Kennaway { 26171323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 261887c1498dSEd Maste debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] " 261987c1498dSEd Maste "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d " 262087c1498dSEd Maste "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what, 262187c1498dSEd Maste c->rfd, c->wfd, c->efd, c->sock, 262287c1498dSEd Maste c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3], 262387c1498dSEd Maste c->io_want, c->io_ready, 262487c1498dSEd Maste pollfd_offset, pfd->fd, pfd->events, pfd->revents); 26251323ec57SEd Maste #endif 2626ca3176e7SBrian Feldman } 2627ca3176e7SBrian Feldman 26281323ec57SEd Maste /* Prepare pollfd entries for a single channel */ 26291323ec57SEd Maste static void 26301323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd, 26311323ec57SEd Maste struct pollfd *pfd, u_int npfd) 26321323ec57SEd Maste { 263387c1498dSEd Maste u_int ev, p = *next_pollfd; 26341323ec57SEd Maste 26351323ec57SEd Maste if (c == NULL) 26361323ec57SEd Maste return; 26371323ec57SEd Maste if (p + 4 > npfd) { 26381323ec57SEd Maste /* Shouldn't happen */ 26391323ec57SEd Maste fatal_f("channel %d: bad pfd offset %u (max %u)", 26401323ec57SEd Maste c->self, p, npfd); 26411323ec57SEd Maste } 264287c1498dSEd Maste c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1; 26431323ec57SEd Maste /* 26441323ec57SEd Maste * prepare c->rfd 26451323ec57SEd Maste * 26461323ec57SEd Maste * This is a special case, since c->rfd might be the same as 26471323ec57SEd Maste * c->wfd, c->efd and/or c->sock. Handle those here if they want 26481323ec57SEd Maste * IO too. 26491323ec57SEd Maste */ 26501323ec57SEd Maste if (c->rfd != -1) { 265187c1498dSEd Maste ev = 0; 26521323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_RFD) != 0) 265387c1498dSEd Maste ev |= POLLIN; 26541323ec57SEd Maste /* rfd == wfd */ 265587c1498dSEd Maste if (c->wfd == c->rfd) { 265687c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD) != 0) 265787c1498dSEd Maste ev |= POLLOUT; 265887c1498dSEd Maste } 26591323ec57SEd Maste /* rfd == efd */ 266087c1498dSEd Maste if (c->efd == c->rfd) { 266187c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 266287c1498dSEd Maste ev |= POLLIN; 266387c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 266487c1498dSEd Maste ev |= POLLOUT; 266587c1498dSEd Maste } 26661323ec57SEd Maste /* rfd == sock */ 266787c1498dSEd Maste if (c->sock == c->rfd) { 266887c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 266987c1498dSEd Maste ev |= POLLIN; 267087c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 267187c1498dSEd Maste ev |= POLLOUT; 267287c1498dSEd Maste } 267387c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 267487c1498dSEd Maste if (ev != 0) { 267587c1498dSEd Maste c->pfds[0] = p; 267687c1498dSEd Maste pfd[p].fd = c->rfd; 267787c1498dSEd Maste pfd[p].events = ev; 26781323ec57SEd Maste dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); 26791323ec57SEd Maste p++; 26801323ec57SEd Maste } 268187c1498dSEd Maste } 268287c1498dSEd Maste /* prepare c->wfd if wanting IO and not already handled above */ 26831323ec57SEd Maste if (c->wfd != -1 && c->rfd != c->wfd) { 268487c1498dSEd Maste ev = 0; 268587c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD)) 268687c1498dSEd Maste ev |= POLLOUT; 268787c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 268887c1498dSEd Maste if (ev != 0) { 268987c1498dSEd Maste c->pfds[1] = p; 26901323ec57SEd Maste pfd[p].fd = c->wfd; 269187c1498dSEd Maste pfd[p].events = ev; 26921323ec57SEd Maste dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); 26931323ec57SEd Maste p++; 26941323ec57SEd Maste } 269587c1498dSEd Maste } 269687c1498dSEd Maste /* prepare c->efd if wanting IO and not already handled above */ 26971323ec57SEd Maste if (c->efd != -1 && c->rfd != c->efd) { 269887c1498dSEd Maste ev = 0; 26991323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 270087c1498dSEd Maste ev |= POLLIN; 27011323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 270287c1498dSEd Maste ev |= POLLOUT; 270387c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 270487c1498dSEd Maste if (ev != 0) { 270587c1498dSEd Maste c->pfds[2] = p; 270687c1498dSEd Maste pfd[p].fd = c->efd; 270787c1498dSEd Maste pfd[p].events = ev; 27081323ec57SEd Maste dump_channel_poll(__func__, "efd", c, p, &pfd[p]); 27091323ec57SEd Maste p++; 27101323ec57SEd Maste } 271187c1498dSEd Maste } 271287c1498dSEd Maste /* prepare c->sock if wanting IO and not already handled above */ 27131323ec57SEd Maste if (c->sock != -1 && c->rfd != c->sock) { 271487c1498dSEd Maste ev = 0; 271587c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 271687c1498dSEd Maste ev |= POLLIN; 271787c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 271887c1498dSEd Maste ev |= POLLOUT; 271987c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 272087c1498dSEd Maste if (ev != 0) { 272187c1498dSEd Maste c->pfds[3] = p; 27221323ec57SEd Maste pfd[p].fd = c->sock; 27231323ec57SEd Maste pfd[p].events = 0; 27241323ec57SEd Maste dump_channel_poll(__func__, "sock", c, p, &pfd[p]); 27251323ec57SEd Maste p++; 27261323ec57SEd Maste } 272787c1498dSEd Maste } 27281323ec57SEd Maste *next_pollfd = p; 27291323ec57SEd Maste } 27301323ec57SEd Maste 27311323ec57SEd Maste /* * Allocate/prepare poll structure */ 27321323ec57SEd Maste void 27331323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, 2734*f374ba41SEd Maste u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout) 27351323ec57SEd Maste { 27361323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 27371323ec57SEd Maste u_int i, oalloc, p, npfd = npfd_reserved; 27381323ec57SEd Maste 27391323ec57SEd Maste channel_before_prepare_io(ssh); /* might create a new channel */ 27409fce8d41SEd Maste /* clear out I/O flags from last poll */ 27419fce8d41SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 27429fce8d41SEd Maste if (sc->channels[i] == NULL) 27439fce8d41SEd Maste continue; 27449fce8d41SEd Maste sc->channels[i]->io_want = sc->channels[i]->io_ready = 0; 27459fce8d41SEd Maste } 27461323ec57SEd Maste /* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ 27471323ec57SEd Maste if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) 27481323ec57SEd Maste fatal_f("too many channels"); /* shouldn't happen */ 27491323ec57SEd Maste npfd += sc->channels_alloc * 4; 27501323ec57SEd Maste if (npfd > *npfd_allocp) { 27511323ec57SEd Maste *pfdp = xrecallocarray(*pfdp, *npfd_allocp, 27521323ec57SEd Maste npfd, sizeof(**pfdp)); 27531323ec57SEd Maste *npfd_allocp = npfd; 27541323ec57SEd Maste } 27551323ec57SEd Maste *npfd_activep = npfd_reserved; 27561323ec57SEd Maste oalloc = sc->channels_alloc; 27571323ec57SEd Maste 2758*f374ba41SEd Maste channel_handler(ssh, CHAN_PRE, timeout); 27591323ec57SEd Maste 27601323ec57SEd Maste if (oalloc != sc->channels_alloc) { 27611323ec57SEd Maste /* shouldn't happen */ 27621323ec57SEd Maste fatal_f("channels_alloc changed during CHAN_PRE " 27631323ec57SEd Maste "(was %u, now %u)", oalloc, sc->channels_alloc); 27641323ec57SEd Maste } 27651323ec57SEd Maste 27661323ec57SEd Maste /* Prepare pollfd */ 27671323ec57SEd Maste p = npfd_reserved; 27681323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) 27691323ec57SEd Maste channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); 27701323ec57SEd Maste *npfd_activep = p; 27711323ec57SEd Maste } 27721323ec57SEd Maste 27731323ec57SEd Maste static void 277487c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd, 27751323ec57SEd Maste const char *what, u_int revents_mask, u_int ready) 27761323ec57SEd Maste { 27771323ec57SEd Maste struct pollfd *pfd = &pfds[p]; 27781323ec57SEd Maste 27791323ec57SEd Maste if (fd == -1) 27801323ec57SEd Maste return; 278187c1498dSEd Maste if (p == -1 || (u_int)p >= npfd) 278287c1498dSEd Maste fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd); 27831323ec57SEd Maste dump_channel_poll(__func__, what, c, p, pfd); 27841323ec57SEd Maste if (pfd->fd != fd) { 27851323ec57SEd Maste fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " 27861323ec57SEd Maste "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, 27871323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock); 27881323ec57SEd Maste } 27891323ec57SEd Maste if ((pfd->revents & POLLNVAL) != 0) { 27901323ec57SEd Maste fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", 27911323ec57SEd Maste c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); 27921323ec57SEd Maste } 27931323ec57SEd Maste if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) 27941323ec57SEd Maste c->io_ready |= ready & c->io_want; 2795a04a10f8SKris Kennaway } 2796a04a10f8SKris Kennaway 2797af12a3e7SDag-Erling Smørgrav /* 27981323ec57SEd Maste * After poll, perform any appropriate operations for channels which have 2799af12a3e7SDag-Erling Smørgrav * events pending. 2800af12a3e7SDag-Erling Smørgrav */ 2801a04a10f8SKris Kennaway void 28021323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) 2803a04a10f8SKris Kennaway { 28041323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 280587c1498dSEd Maste u_int i; 280687c1498dSEd Maste int p; 28071323ec57SEd Maste Channel *c; 28081323ec57SEd Maste 28091323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 281087c1498dSEd Maste for (p = 0; p < (int)npfd; p++) { 28111323ec57SEd Maste if (pfd[p].revents == 0) 28121323ec57SEd Maste continue; 28131323ec57SEd Maste debug_f("pfd[%u].fd %d rev 0x%04x", 28141323ec57SEd Maste p, pfd[p].fd, pfd[p].revents); 28151323ec57SEd Maste } 28161323ec57SEd Maste #endif 28171323ec57SEd Maste 28181323ec57SEd Maste /* Convert pollfd into c->io_ready */ 28191323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 28201323ec57SEd Maste c = sc->channels[i]; 282187c1498dSEd Maste if (c == NULL) 28221323ec57SEd Maste continue; 28231323ec57SEd Maste /* if rfd is shared with efd/sock then wfd should be too */ 28241323ec57SEd Maste if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && 28251323ec57SEd Maste (c->rfd == c->efd || c->rfd == c->sock)) { 28261323ec57SEd Maste /* Shouldn't happen */ 28271323ec57SEd Maste fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", 28281323ec57SEd Maste c->self, c->rfd, c->wfd, c->efd, c->sock); 28291323ec57SEd Maste } 28301323ec57SEd Maste c->io_ready = 0; 28311323ec57SEd Maste /* rfd, potentially shared with wfd, efd and sock */ 283287c1498dSEd Maste if (c->rfd != -1 && (p = c->pfds[0]) != -1) { 283387c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->rfd, 283487c1498dSEd Maste "rfd", POLLIN, SSH_CHAN_IO_RFD); 28351323ec57SEd Maste if (c->rfd == c->wfd) { 283687c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 283787c1498dSEd Maste "wfd/r", POLLOUT, SSH_CHAN_IO_WFD); 28381323ec57SEd Maste } 28391323ec57SEd Maste if (c->rfd == c->efd) { 284087c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 284187c1498dSEd Maste "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R); 284287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 284387c1498dSEd Maste "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W); 28441323ec57SEd Maste } 28451323ec57SEd Maste if (c->rfd == c->sock) { 284687c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 284787c1498dSEd Maste "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R); 284887c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 284987c1498dSEd Maste "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W); 28501323ec57SEd Maste } 285187c1498dSEd Maste dump_channel_poll(__func__, "rfd", c, p, pfd); 28521323ec57SEd Maste } 28531323ec57SEd Maste /* wfd */ 285487c1498dSEd Maste if (c->wfd != -1 && c->wfd != c->rfd && 285587c1498dSEd Maste (p = c->pfds[1]) != -1) { 285687c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 285787c1498dSEd Maste "wfd", POLLOUT, SSH_CHAN_IO_WFD); 285887c1498dSEd Maste dump_channel_poll(__func__, "wfd", c, p, pfd); 28591323ec57SEd Maste } 28601323ec57SEd Maste /* efd */ 286187c1498dSEd Maste if (c->efd != -1 && c->efd != c->rfd && 286287c1498dSEd Maste (p = c->pfds[2]) != -1) { 286387c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 286487c1498dSEd Maste "efdr", POLLIN, SSH_CHAN_IO_EFD_R); 286587c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 286687c1498dSEd Maste "efdw", POLLOUT, SSH_CHAN_IO_EFD_W); 286787c1498dSEd Maste dump_channel_poll(__func__, "efd", c, p, pfd); 28681323ec57SEd Maste } 28691323ec57SEd Maste /* sock */ 287087c1498dSEd Maste if (c->sock != -1 && c->sock != c->rfd && 287187c1498dSEd Maste (p = c->pfds[3]) != -1) { 287287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 287387c1498dSEd Maste "sockr", POLLIN, SSH_CHAN_IO_SOCK_R); 287487c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 287587c1498dSEd Maste "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W); 287687c1498dSEd Maste dump_channel_poll(__func__, "sock", c, p, pfd); 28771323ec57SEd Maste } 28781323ec57SEd Maste } 28791323ec57SEd Maste channel_handler(ssh, CHAN_POST, NULL); 2880511b41d2SMark Murray } 2881511b41d2SMark Murray 28824f52dfbbSDag-Erling Smørgrav /* 28834f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open or draining c->input. 28844f52dfbbSDag-Erling Smørgrav */ 28854f52dfbbSDag-Erling Smørgrav static void 28864f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c) 28874f52dfbbSDag-Erling Smørgrav { 28884f52dfbbSDag-Erling Smørgrav size_t len, plen; 28894f52dfbbSDag-Erling Smørgrav const u_char *pkt; 28904f52dfbbSDag-Erling Smørgrav int r; 28914f52dfbbSDag-Erling Smørgrav 28924f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->input)) == 0) { 28934f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 28944f52dfbbSDag-Erling Smørgrav /* 28954f52dfbbSDag-Erling Smørgrav * input-buffer is empty and read-socket shutdown: 28964f52dfbbSDag-Erling Smørgrav * tell peer, that we will not send more data: 28974f52dfbbSDag-Erling Smørgrav * send IEOF. 28984f52dfbbSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still 28994f52dfbbSDag-Erling Smørgrav * in use. 29004f52dfbbSDag-Erling Smørgrav */ 29014f52dfbbSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 29024f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 29034f52dfbbSDag-Erling Smørgrav "ibuf_empty delayed efd %d/(%zu)", 29044f52dfbbSDag-Erling Smørgrav c->self, c->efd, sshbuf_len(c->extended)); 29054f52dfbbSDag-Erling Smørgrav else 29064f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 29074f52dfbbSDag-Erling Smørgrav } 29084f52dfbbSDag-Erling Smørgrav return; 29094f52dfbbSDag-Erling Smørgrav } 29104f52dfbbSDag-Erling Smørgrav 29114f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 291219261079SEd Maste fatal_f("channel %d: no remote id", c->self); 29134f52dfbbSDag-Erling Smørgrav 29144f52dfbbSDag-Erling Smørgrav if (c->datagram) { 29154f52dfbbSDag-Erling Smørgrav /* Check datagram will fit; drop if not */ 29164f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) 291719261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 29184f52dfbbSDag-Erling Smørgrav /* 29194f52dfbbSDag-Erling Smørgrav * XXX this does tail-drop on the datagram queue which is 29204f52dfbbSDag-Erling Smørgrav * usually suboptimal compared to head-drop. Better to have 29214f52dfbbSDag-Erling Smørgrav * backpressure at read time? (i.e. read + discard) 29224f52dfbbSDag-Erling Smørgrav */ 29234f52dfbbSDag-Erling Smørgrav if (plen > c->remote_window || plen > c->remote_maxpacket) { 29244f52dfbbSDag-Erling Smørgrav debug("channel %d: datagram too big", c->self); 29254f52dfbbSDag-Erling Smørgrav return; 29264f52dfbbSDag-Erling Smørgrav } 29274f52dfbbSDag-Erling Smørgrav /* Enqueue it */ 29284f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 29294f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 29304f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || 293119261079SEd Maste (r = sshpkt_send(ssh)) != 0) 293219261079SEd Maste fatal_fr(r, "channel %i: send datagram", c->self); 29334f52dfbbSDag-Erling Smørgrav c->remote_window -= plen; 29344f52dfbbSDag-Erling Smørgrav return; 29354f52dfbbSDag-Erling Smørgrav } 29364f52dfbbSDag-Erling Smørgrav 29374f52dfbbSDag-Erling Smørgrav /* Enqueue packet for buffered data. */ 29384f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 29394f52dfbbSDag-Erling Smørgrav len = c->remote_window; 29404f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 29414f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 29424f52dfbbSDag-Erling Smørgrav if (len == 0) 29434f52dfbbSDag-Erling Smørgrav return; 29444f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 29454f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 29464f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || 294719261079SEd Maste (r = sshpkt_send(ssh)) != 0) 294819261079SEd Maste fatal_fr(r, "channel %i: send data", c->self); 29494f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->input, len)) != 0) 295019261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 29514f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 29524f52dfbbSDag-Erling Smørgrav } 29534f52dfbbSDag-Erling Smørgrav 29544f52dfbbSDag-Erling Smørgrav /* 29554f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open c->extended in read mode. 29564f52dfbbSDag-Erling Smørgrav */ 29574f52dfbbSDag-Erling Smørgrav static void 29584f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c) 29594f52dfbbSDag-Erling Smørgrav { 29604f52dfbbSDag-Erling Smørgrav size_t len; 29614f52dfbbSDag-Erling Smørgrav int r; 29624f52dfbbSDag-Erling Smørgrav 29634f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->extended)) == 0) 29644f52dfbbSDag-Erling Smørgrav return; 29654f52dfbbSDag-Erling Smørgrav 29664f52dfbbSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %zu euse %d", c->self, 29674f52dfbbSDag-Erling Smørgrav c->remote_window, sshbuf_len(c->extended), c->extended_usage); 29684f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 29694f52dfbbSDag-Erling Smørgrav len = c->remote_window; 29704f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 29714f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 29724f52dfbbSDag-Erling Smørgrav if (len == 0) 29734f52dfbbSDag-Erling Smørgrav return; 29744f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 297519261079SEd Maste fatal_f("channel %d: no remote id", c->self); 29764f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || 29774f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 29784f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || 29794f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || 298019261079SEd Maste (r = sshpkt_send(ssh)) != 0) 298119261079SEd Maste fatal_fr(r, "channel %i: data", c->self); 29824f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->extended, len)) != 0) 298319261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 29844f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 29854f52dfbbSDag-Erling Smørgrav debug2("channel %d: sent ext data %zu", c->self, len); 29864f52dfbbSDag-Erling Smørgrav } 2987af12a3e7SDag-Erling Smørgrav 2988ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 2989511b41d2SMark Murray void 29904f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh) 2991511b41d2SMark Murray { 29924f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 2993a04a10f8SKris Kennaway Channel *c; 29944f52dfbbSDag-Erling Smørgrav u_int i; 2995511b41d2SMark Murray 29964f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 29974f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2998af12a3e7SDag-Erling Smørgrav if (c == NULL) 2999af12a3e7SDag-Erling Smørgrav continue; 3000511b41d2SMark Murray 3001af12a3e7SDag-Erling Smørgrav /* 3002af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 3003af12a3e7SDag-Erling Smørgrav * incoming data. 3004af12a3e7SDag-Erling Smørgrav */ 3005a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 3006511b41d2SMark Murray continue; 30074f52dfbbSDag-Erling Smørgrav if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 3008ca3176e7SBrian Feldman /* XXX is this true? */ 30094f52dfbbSDag-Erling Smørgrav debug3("channel %d: will not send data after close", 30104f52dfbbSDag-Erling Smørgrav c->self); 3011511b41d2SMark Murray continue; 3012511b41d2SMark Murray } 3013511b41d2SMark Murray 3014511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 30154f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN || 30164f52dfbbSDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN) 30174f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(ssh, c); 3018a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 30194f52dfbbSDag-Erling Smørgrav if (!(c->flags & CHAN_EOF_SENT) && 30204f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ) 30214f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(ssh, c); 3022511b41d2SMark Murray } 3023511b41d2SMark Murray } 3024511b41d2SMark Murray 3025ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support */ 3026ca86bcf2SDag-Erling Smørgrav 3027ca86bcf2SDag-Erling Smørgrav /* 3028ca86bcf2SDag-Erling Smørgrav * When multiplexing channel messages for mux clients we have to deal 3029ca86bcf2SDag-Erling Smørgrav * with downstream messages from the mux client and upstream messages 3030ca86bcf2SDag-Erling Smørgrav * from the ssh server: 3031ca86bcf2SDag-Erling Smørgrav * 1) Handling downstream messages is straightforward and happens 3032ca86bcf2SDag-Erling Smørgrav * in channel_proxy_downstream(): 3033ca86bcf2SDag-Erling Smørgrav * - We forward all messages (mostly) unmodified to the server. 3034ca86bcf2SDag-Erling Smørgrav * - However, in order to route messages from upstream to the correct 3035ca86bcf2SDag-Erling Smørgrav * downstream client, we have to replace the channel IDs used by the 3036ca86bcf2SDag-Erling Smørgrav * mux clients with a unique channel ID because the mux clients might 3037ca86bcf2SDag-Erling Smørgrav * use conflicting channel IDs. 3038ca86bcf2SDag-Erling Smørgrav * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and 3039ca86bcf2SDag-Erling Smørgrav * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local 3040ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID 3041ca86bcf2SDag-Erling Smørgrav * with the newly allocated channel ID. 3042ca86bcf2SDag-Erling Smørgrav * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY 3043190cef3dSDag-Erling Smørgrav * channels and processed by channel_proxy_upstream(). The local channel ID 3044ca86bcf2SDag-Erling Smørgrav * is then translated back to the original mux client ID. 3045ca86bcf2SDag-Erling Smørgrav * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE 3046ca86bcf2SDag-Erling Smørgrav * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. 3047ca86bcf2SDag-Erling Smørgrav * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the 3048ca86bcf2SDag-Erling Smørgrav * downstream mux client are removed. 3049ca86bcf2SDag-Erling Smørgrav * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server 3050ca86bcf2SDag-Erling Smørgrav * requires more work, because they are not addressed to a specific 3051ca86bcf2SDag-Erling Smørgrav * channel. E.g. client_request_forwarded_tcpip() needs to figure 3052ca86bcf2SDag-Erling Smørgrav * out whether the request is addressed to the local client or a 3053ca86bcf2SDag-Erling Smørgrav * specific downstream client based on the listen-address/port. 3054190cef3dSDag-Erling Smørgrav * 6) Agent and X11-Forwarding have a similar problem and are currently 3055ca86bcf2SDag-Erling Smørgrav * not supported as the matching session/channel cannot be identified 3056ca86bcf2SDag-Erling Smørgrav * easily. 3057ca86bcf2SDag-Erling Smørgrav */ 3058ca86bcf2SDag-Erling Smørgrav 3059ca86bcf2SDag-Erling Smørgrav /* 3060ca86bcf2SDag-Erling Smørgrav * receive packets from downstream mux clients: 3061ca86bcf2SDag-Erling Smørgrav * channel callback fired on read from mux client, creates 3062ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs 3063ca86bcf2SDag-Erling Smørgrav * on channel creation. 3064ca86bcf2SDag-Erling Smørgrav */ 3065ca86bcf2SDag-Erling Smørgrav int 30664f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream) 3067ca86bcf2SDag-Erling Smørgrav { 3068ca86bcf2SDag-Erling Smørgrav Channel *c = NULL; 3069ca86bcf2SDag-Erling Smørgrav struct sshbuf *original = NULL, *modified = NULL; 3070ca86bcf2SDag-Erling Smørgrav const u_char *cp; 3071ca86bcf2SDag-Erling Smørgrav char *ctype = NULL, *listen_host = NULL; 3072ca86bcf2SDag-Erling Smørgrav u_char type; 3073ca86bcf2SDag-Erling Smørgrav size_t have; 30744f52dfbbSDag-Erling Smørgrav int ret = -1, r; 3075ca86bcf2SDag-Erling Smørgrav u_int id, remote_id, listen_port; 3076ca86bcf2SDag-Erling Smørgrav 30774f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(downstream->input, stderr); */ 30784f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) 3079ca86bcf2SDag-Erling Smørgrav != 0) { 308019261079SEd Maste error_fr(r, "parse"); 3081ca86bcf2SDag-Erling Smørgrav return -1; 3082ca86bcf2SDag-Erling Smørgrav } 3083ca86bcf2SDag-Erling Smørgrav if (have < 2) { 308419261079SEd Maste error_f("short message"); 3085ca86bcf2SDag-Erling Smørgrav return -1; 3086ca86bcf2SDag-Erling Smørgrav } 3087ca86bcf2SDag-Erling Smørgrav type = cp[1]; 3088ca86bcf2SDag-Erling Smørgrav /* skip padlen + type */ 3089ca86bcf2SDag-Erling Smørgrav cp += 2; 3090ca86bcf2SDag-Erling Smørgrav have -= 2; 3091ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 309219261079SEd Maste debug3_f("channel %u: down->up: type %u", 3093ca86bcf2SDag-Erling Smørgrav downstream->self, type); 3094ca86bcf2SDag-Erling Smørgrav 3095ca86bcf2SDag-Erling Smørgrav switch (type) { 3096ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN: 3097ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 3098ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 309919261079SEd Maste error_f("alloc"); 3100ca86bcf2SDag-Erling Smørgrav goto out; 3101ca86bcf2SDag-Erling Smørgrav } 3102ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || 3103ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 310419261079SEd Maste error_fr(r, "parse"); 3105ca86bcf2SDag-Erling Smørgrav goto out; 3106ca86bcf2SDag-Erling Smørgrav } 3107*f374ba41SEd Maste c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, 3108ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, ctype, 1); 3109ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 3110ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; /* original downstream id */ 3111ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || 3112ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 3113ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 311419261079SEd Maste error_fr(r, "compose"); 31154f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3116ca86bcf2SDag-Erling Smørgrav goto out; 3117ca86bcf2SDag-Erling Smørgrav } 3118ca86bcf2SDag-Erling Smørgrav break; 3119ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3120ca86bcf2SDag-Erling Smørgrav /* 3121ca86bcf2SDag-Erling Smørgrav * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we 3122ca86bcf2SDag-Erling Smørgrav * need to parse 'remote_id' instead of 'ctype'. 3123ca86bcf2SDag-Erling Smørgrav */ 3124ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 3125ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 312619261079SEd Maste error_f("alloc"); 3127ca86bcf2SDag-Erling Smørgrav goto out; 3128ca86bcf2SDag-Erling Smørgrav } 3129ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || 3130ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 313119261079SEd Maste error_fr(r, "parse"); 3132ca86bcf2SDag-Erling Smørgrav goto out; 3133ca86bcf2SDag-Erling Smørgrav } 3134*f374ba41SEd Maste c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, 3135ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); 3136ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 3137ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; 3138ca86bcf2SDag-Erling Smørgrav c->remote_id = remote_id; 31394f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 3140ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || 3141ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 3142ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 314319261079SEd Maste error_fr(r, "compose"); 31444f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3145ca86bcf2SDag-Erling Smørgrav goto out; 3146ca86bcf2SDag-Erling Smørgrav } 3147ca86bcf2SDag-Erling Smørgrav break; 3148ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_GLOBAL_REQUEST: 3149ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL) { 315019261079SEd Maste error_f("alloc"); 3151ca86bcf2SDag-Erling Smørgrav goto out; 3152ca86bcf2SDag-Erling Smørgrav } 3153ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { 315419261079SEd Maste error_fr(r, "parse"); 3155ca86bcf2SDag-Erling Smørgrav goto out; 3156ca86bcf2SDag-Erling Smørgrav } 3157ca86bcf2SDag-Erling Smørgrav if (strcmp(ctype, "tcpip-forward") != 0) { 315819261079SEd Maste error_f("unsupported request %s", ctype); 3159ca86bcf2SDag-Erling Smørgrav goto out; 3160ca86bcf2SDag-Erling Smørgrav } 3161ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u8(original, NULL)) != 0 || 3162ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || 3163ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &listen_port)) != 0) { 316419261079SEd Maste error_fr(r, "parse"); 3165ca86bcf2SDag-Erling Smørgrav goto out; 3166ca86bcf2SDag-Erling Smørgrav } 3167ca86bcf2SDag-Erling Smørgrav if (listen_port > 65535) { 316819261079SEd Maste error_f("tcpip-forward for %s: bad port %u", 316919261079SEd Maste listen_host, listen_port); 3170ca86bcf2SDag-Erling Smørgrav goto out; 3171ca86bcf2SDag-Erling Smørgrav } 3172ca86bcf2SDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 3173190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1, 31744f52dfbbSDag-Erling Smørgrav listen_host, NULL, (int)listen_port, downstream); 3175ca86bcf2SDag-Erling Smørgrav listen_host = NULL; 3176ca86bcf2SDag-Erling Smørgrav break; 3177ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3178ca86bcf2SDag-Erling Smørgrav if (have < 4) 3179ca86bcf2SDag-Erling Smørgrav break; 3180ca86bcf2SDag-Erling Smørgrav remote_id = PEEK_U32(cp); 31814f52dfbbSDag-Erling Smørgrav if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { 3182ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) 31834f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3184ca86bcf2SDag-Erling Smørgrav else 3185ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT; 3186ca86bcf2SDag-Erling Smørgrav } 3187ca86bcf2SDag-Erling Smørgrav break; 3188ca86bcf2SDag-Erling Smørgrav } 3189ca86bcf2SDag-Erling Smørgrav if (modified) { 3190ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3191ca86bcf2SDag-Erling Smørgrav (r = sshpkt_putb(ssh, modified)) != 0 || 3192ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 319319261079SEd Maste error_fr(r, "send"); 3194ca86bcf2SDag-Erling Smørgrav goto out; 3195ca86bcf2SDag-Erling Smørgrav } 3196ca86bcf2SDag-Erling Smørgrav } else { 3197ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3198ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put(ssh, cp, have)) != 0 || 3199ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 320019261079SEd Maste error_fr(r, "send"); 3201ca86bcf2SDag-Erling Smørgrav goto out; 3202ca86bcf2SDag-Erling Smørgrav } 3203ca86bcf2SDag-Erling Smørgrav } 3204ca86bcf2SDag-Erling Smørgrav ret = 0; 3205ca86bcf2SDag-Erling Smørgrav out: 3206ca86bcf2SDag-Erling Smørgrav free(ctype); 3207ca86bcf2SDag-Erling Smørgrav free(listen_host); 3208ca86bcf2SDag-Erling Smørgrav sshbuf_free(original); 3209ca86bcf2SDag-Erling Smørgrav sshbuf_free(modified); 3210ca86bcf2SDag-Erling Smørgrav return ret; 3211ca86bcf2SDag-Erling Smørgrav } 3212ca86bcf2SDag-Erling Smørgrav 3213ca86bcf2SDag-Erling Smørgrav /* 3214ca86bcf2SDag-Erling Smørgrav * receive packets from upstream server and de-multiplex packets 3215ca86bcf2SDag-Erling Smørgrav * to correct downstream: 3216ca86bcf2SDag-Erling Smørgrav * implemented as a helper for channel input handlers, 3217ca86bcf2SDag-Erling Smørgrav * replaces local (proxy) channel ID with downstream channel ID. 3218ca86bcf2SDag-Erling Smørgrav */ 3219ca86bcf2SDag-Erling Smørgrav int 32204f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) 3221ca86bcf2SDag-Erling Smørgrav { 3222ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 3223ca86bcf2SDag-Erling Smørgrav Channel *downstream; 3224ca86bcf2SDag-Erling Smørgrav const u_char *cp = NULL; 3225ca86bcf2SDag-Erling Smørgrav size_t len; 3226ca86bcf2SDag-Erling Smørgrav int r; 3227ca86bcf2SDag-Erling Smørgrav 3228ca86bcf2SDag-Erling Smørgrav /* 3229ca86bcf2SDag-Erling Smørgrav * When receiving packets from the peer we need to check whether we 3230ca86bcf2SDag-Erling Smørgrav * need to forward the packets to the mux client. In this case we 3231190cef3dSDag-Erling Smørgrav * restore the original channel id and keep track of CLOSE messages, 3232ca86bcf2SDag-Erling Smørgrav * so we can cleanup the channel. 3233ca86bcf2SDag-Erling Smørgrav */ 3234ca86bcf2SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) 3235ca86bcf2SDag-Erling Smørgrav return 0; 3236ca86bcf2SDag-Erling Smørgrav if ((downstream = c->mux_ctx) == NULL) 3237ca86bcf2SDag-Erling Smørgrav return 0; 3238ca86bcf2SDag-Erling Smørgrav switch (type) { 3239ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3240ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_DATA: 3241ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EOF: 3242ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EXTENDED_DATA: 3243ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3244ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_FAILURE: 3245ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 3246ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_SUCCESS: 3247ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_FAILURE: 3248ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_REQUEST: 3249ca86bcf2SDag-Erling Smørgrav break; 3250ca86bcf2SDag-Erling Smørgrav default: 325119261079SEd Maste debug2_f("channel %u: unsupported type %u", c->self, type); 3252ca86bcf2SDag-Erling Smørgrav return 0; 3253ca86bcf2SDag-Erling Smørgrav } 3254ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 325519261079SEd Maste error_f("alloc reply"); 3256ca86bcf2SDag-Erling Smørgrav goto out; 3257ca86bcf2SDag-Erling Smørgrav } 3258ca86bcf2SDag-Erling Smørgrav /* get remaining payload (after id) */ 3259ca86bcf2SDag-Erling Smørgrav cp = sshpkt_ptr(ssh, &len); 3260ca86bcf2SDag-Erling Smørgrav if (cp == NULL) { 326119261079SEd Maste error_f("no packet"); 3262ca86bcf2SDag-Erling Smørgrav goto out; 3263ca86bcf2SDag-Erling Smørgrav } 3264ca86bcf2SDag-Erling Smørgrav /* translate id and send to muxclient */ 3265ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 3266ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, type)) != 0 || 3267ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || 3268ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put(b, cp, len)) != 0 || 32694f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(downstream->output, b)) != 0) { 327019261079SEd Maste error_fr(r, "compose muxclient"); 3271ca86bcf2SDag-Erling Smørgrav goto out; 3272ca86bcf2SDag-Erling Smørgrav } 3273ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(b, stderr); */ 3274ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 327519261079SEd Maste debug3_f("channel %u: up->down: type %u", c->self, type); 3276ca86bcf2SDag-Erling Smørgrav out: 3277ca86bcf2SDag-Erling Smørgrav /* update state */ 3278ca86bcf2SDag-Erling Smørgrav switch (type) { 3279ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3280ca86bcf2SDag-Erling Smørgrav /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ 32814f52dfbbSDag-Erling Smørgrav if (cp && len > 4) { 3282ca86bcf2SDag-Erling Smørgrav c->remote_id = PEEK_U32(cp); 32834f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 32844f52dfbbSDag-Erling Smørgrav } 3285ca86bcf2SDag-Erling Smørgrav break; 3286ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3287ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_SENT) 32884f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3289ca86bcf2SDag-Erling Smørgrav else 3290ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_RCVD; 3291ca86bcf2SDag-Erling Smørgrav break; 3292ca86bcf2SDag-Erling Smørgrav } 3293ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 3294ca86bcf2SDag-Erling Smørgrav return 1; 3295ca86bcf2SDag-Erling Smørgrav } 3296af12a3e7SDag-Erling Smørgrav 3297af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 3298511b41d2SMark Murray 32994f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */ 33004f52dfbbSDag-Erling Smørgrav static int 33014f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what) 3302511b41d2SMark Murray { 33034f52dfbbSDag-Erling Smørgrav u_int32_t id; 33044f52dfbbSDag-Erling Smørgrav int r; 33054f52dfbbSDag-Erling Smørgrav 33064f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &id)) != 0) { 330719261079SEd Maste error_r(r, "%s: parse id", where); 33084f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s message", what); 33094f52dfbbSDag-Erling Smørgrav } 33104f52dfbbSDag-Erling Smørgrav if (id > INT_MAX) { 331119261079SEd Maste error_r(r, "%s: bad channel id %u", where, id); 33124f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s channel id", what); 33134f52dfbbSDag-Erling Smørgrav } 33144f52dfbbSDag-Erling Smørgrav return (int)id; 33154f52dfbbSDag-Erling Smørgrav } 33164f52dfbbSDag-Erling Smørgrav 33174f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */ 33184f52dfbbSDag-Erling Smørgrav static Channel * 33194f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) 33204f52dfbbSDag-Erling Smørgrav { 33214f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, where, what); 3322a04a10f8SKris Kennaway Channel *c; 3323511b41d2SMark Murray 33244f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 33254f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, 33264f52dfbbSDag-Erling Smørgrav "%s packet referred to nonexistent channel %d", what, id); 33274f52dfbbSDag-Erling Smørgrav } 33284f52dfbbSDag-Erling Smørgrav return c; 33294f52dfbbSDag-Erling Smørgrav } 33304f52dfbbSDag-Erling Smørgrav 33314f52dfbbSDag-Erling Smørgrav int 33324f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh) 33334f52dfbbSDag-Erling Smørgrav { 33344f52dfbbSDag-Erling Smørgrav const u_char *data; 33354f52dfbbSDag-Erling Smørgrav size_t data_len, win_len; 33364f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "data"); 33374f52dfbbSDag-Erling Smørgrav int r; 33384f52dfbbSDag-Erling Smørgrav 33394f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3340ca86bcf2SDag-Erling Smørgrav return 0; 3341511b41d2SMark Murray 3342511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 3343a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 33444f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_OPEN && 33454f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_FINISH && 3346a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 3347bc5531deSDag-Erling Smørgrav return 0; 3348511b41d2SMark Murray 3349511b41d2SMark Murray /* Get the data. */ 335019261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 335119261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 335219261079SEd Maste fatal_fr(r, "channel %i: get data", c->self); 33534f52dfbbSDag-Erling Smørgrav 3354e2f6069cSDag-Erling Smørgrav win_len = data_len; 3355e2f6069cSDag-Erling Smørgrav if (c->datagram) 3356e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 3357a04a10f8SKris Kennaway 3358476cd3b2SDag-Erling Smørgrav /* 33594f52dfbbSDag-Erling Smørgrav * The sending side reduces its window as it sends data, so we 33604f52dfbbSDag-Erling Smørgrav * must 'fake' consumption of the data in order to ensure that window 33614f52dfbbSDag-Erling Smørgrav * updates are sent back. Otherwise the connection might deadlock. 3362476cd3b2SDag-Erling Smørgrav */ 33634f52dfbbSDag-Erling Smørgrav if (c->ostate != CHAN_OUTPUT_OPEN) { 3364e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 3365e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 3366bc5531deSDag-Erling Smørgrav return 0; 3367476cd3b2SDag-Erling Smørgrav } 3368476cd3b2SDag-Erling Smørgrav 3369e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 33704f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd big packet %zu, maxpack %u", 3371e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 33724f52dfbbSDag-Erling Smørgrav return 0; 3373a04a10f8SKris Kennaway } 3374e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 33754f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much data %zu, win %u", 3376e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_window); 3377bc5531deSDag-Erling Smørgrav return 0; 3378a04a10f8SKris Kennaway } 3379e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 33804f52dfbbSDag-Erling Smørgrav 33814f52dfbbSDag-Erling Smørgrav if (c->datagram) { 33824f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) 338319261079SEd Maste fatal_fr(r, "channel %i: append datagram", c->self); 33844f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) 338519261079SEd Maste fatal_fr(r, "channel %i: append data", c->self); 33864f52dfbbSDag-Erling Smørgrav 3387bc5531deSDag-Erling Smørgrav return 0; 3388511b41d2SMark Murray } 3389af12a3e7SDag-Erling Smørgrav 3390bc5531deSDag-Erling Smørgrav int 33914f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) 3392a04a10f8SKris Kennaway { 33934f52dfbbSDag-Erling Smørgrav const u_char *data; 33944f52dfbbSDag-Erling Smørgrav size_t data_len; 33954f52dfbbSDag-Erling Smørgrav u_int32_t tcode; 33964f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); 33974f52dfbbSDag-Erling Smørgrav int r; 3398a04a10f8SKris Kennaway 33994f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3400ca86bcf2SDag-Erling Smørgrav return 0; 3401a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 34024f52dfbbSDag-Erling Smørgrav logit("channel %d: ext data for non open", c->self); 3403bc5531deSDag-Erling Smørgrav return 0; 3404a04a10f8SKris Kennaway } 340580628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 340619261079SEd Maste if (ssh->compat & SSH_BUG_EXTEOF) 34074f52dfbbSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", 34084f52dfbbSDag-Erling Smørgrav c->self); 340980628bacSDag-Erling Smørgrav else 34104f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Received extended_data " 34114f52dfbbSDag-Erling Smørgrav "after EOF on channel %d.", c->self); 341280628bacSDag-Erling Smørgrav } 34134f52dfbbSDag-Erling Smørgrav 34144f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { 341519261079SEd Maste error_fr(r, "parse tcode"); 34164f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 34174f52dfbbSDag-Erling Smørgrav } 3418a04a10f8SKris Kennaway if (c->efd == -1 || 3419a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 3420a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 3421221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 3422bc5531deSDag-Erling Smørgrav return 0; 3423a04a10f8SKris Kennaway } 342419261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 342519261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 342619261079SEd Maste error_fr(r, "parse data"); 34274f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 34284f52dfbbSDag-Erling Smørgrav } 34294f52dfbbSDag-Erling Smørgrav 3430a04a10f8SKris Kennaway if (data_len > c->local_window) { 34314f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %zu, win %u", 3432a04a10f8SKris Kennaway c->self, data_len, c->local_window); 3433bc5531deSDag-Erling Smørgrav return 0; 3434a04a10f8SKris Kennaway } 34354f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd ext data %zu", c->self, data_len); 34364f52dfbbSDag-Erling Smørgrav /* XXX sshpkt_getb? */ 34374f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, data, data_len)) != 0) 343819261079SEd Maste error_fr(r, "append"); 3439a04a10f8SKris Kennaway c->local_window -= data_len; 3440bc5531deSDag-Erling Smørgrav return 0; 3441a04a10f8SKris Kennaway } 3442a04a10f8SKris Kennaway 3443bc5531deSDag-Erling Smørgrav int 34444f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) 3445a04a10f8SKris Kennaway { 34464f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); 344719261079SEd Maste int r; 3448a04a10f8SKris Kennaway 344919261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 345019261079SEd Maste error_fr(r, "parse data"); 345119261079SEd Maste ssh_packet_disconnect(ssh, "Invalid ieof message"); 345219261079SEd Maste } 34534f52dfbbSDag-Erling Smørgrav 34544f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3455ca86bcf2SDag-Erling Smørgrav return 0; 34564f52dfbbSDag-Erling Smørgrav chan_rcvd_ieof(ssh, c); 3457af12a3e7SDag-Erling Smørgrav 3458af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 3459af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 3460af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 3461af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 34624f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) == 0) 34634f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 3464af12a3e7SDag-Erling Smørgrav } 3465bc5531deSDag-Erling Smørgrav return 0; 3466a04a10f8SKris Kennaway } 3467511b41d2SMark Murray 3468bc5531deSDag-Erling Smørgrav int 34694f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) 3470511b41d2SMark Murray { 34714f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); 347219261079SEd Maste int r; 3473511b41d2SMark Murray 34744f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3475ca86bcf2SDag-Erling Smørgrav return 0; 347619261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 347719261079SEd Maste error_fr(r, "parse data"); 347819261079SEd Maste ssh_packet_disconnect(ssh, "Invalid oclose message"); 347919261079SEd Maste } 34804f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 3481bc5531deSDag-Erling Smørgrav return 0; 3482511b41d2SMark Murray } 3483511b41d2SMark Murray 3484bc5531deSDag-Erling Smørgrav int 34854f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) 3486a04a10f8SKris Kennaway { 34874f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); 34884f52dfbbSDag-Erling Smørgrav u_int32_t remote_window, remote_maxpacket; 34894f52dfbbSDag-Erling Smørgrav int r; 3490af12a3e7SDag-Erling Smørgrav 34914f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3492ca86bcf2SDag-Erling Smørgrav return 0; 3493ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 349419261079SEd Maste ssh_packet_disconnect(ssh, "Received open confirmation for " 34954f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 34964f52dfbbSDag-Erling Smørgrav /* 34974f52dfbbSDag-Erling Smørgrav * Record the remote channel number and mark that the channel 34984f52dfbbSDag-Erling Smørgrav * is now open. 34994f52dfbbSDag-Erling Smørgrav */ 35004f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || 35014f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || 350219261079SEd Maste (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || 350319261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 350419261079SEd Maste error_fr(r, "window/maxpacket"); 350519261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open confirmation message"); 35064f52dfbbSDag-Erling Smørgrav } 3507a04a10f8SKris Kennaway 35084f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 35094f52dfbbSDag-Erling Smørgrav c->remote_window = remote_window; 35104f52dfbbSDag-Erling Smørgrav c->remote_maxpacket = remote_maxpacket; 35114f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 3512d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 351319261079SEd Maste debug2_f("channel %d: callback start", c->self); 35144f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); 351519261079SEd Maste debug2_f("channel %d: callback done", c->self); 3516a04a10f8SKris Kennaway } 3517*f374ba41SEd Maste c->lastused = monotime(); 3518221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 3519a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 3520bc5531deSDag-Erling Smørgrav return 0; 3521af12a3e7SDag-Erling Smørgrav } 3522af12a3e7SDag-Erling Smørgrav 3523af12a3e7SDag-Erling Smørgrav static char * 3524af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 3525af12a3e7SDag-Erling Smørgrav { 3526af12a3e7SDag-Erling Smørgrav switch (reason) { 3527af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 3528af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 3529af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 3530af12a3e7SDag-Erling Smørgrav return "connect failed"; 3531af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 3532af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 3533af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 3534af12a3e7SDag-Erling Smørgrav return "resource shortage"; 3535af12a3e7SDag-Erling Smørgrav } 3536af12a3e7SDag-Erling Smørgrav return "unknown reason"; 3537a04a10f8SKris Kennaway } 3538a04a10f8SKris Kennaway 3539bc5531deSDag-Erling Smørgrav int 35404f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) 3541a04a10f8SKris Kennaway { 35424f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); 35434f52dfbbSDag-Erling Smørgrav u_int32_t reason; 35444f52dfbbSDag-Erling Smørgrav char *msg = NULL; 35454f52dfbbSDag-Erling Smørgrav int r; 3546a04a10f8SKris Kennaway 35474f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3548ca86bcf2SDag-Erling Smørgrav return 0; 3549ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 355019261079SEd Maste ssh_packet_disconnect(ssh, "Received open failure for " 35514f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 35524f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { 355319261079SEd Maste error_fr(r, "parse reason"); 355419261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 3555ca3176e7SBrian Feldman } 35564f52dfbbSDag-Erling Smørgrav /* skip language */ 35574f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || 355819261079SEd Maste (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || 355919261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 356019261079SEd Maste error_fr(r, "parse msg/lang"); 356119261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 35624f52dfbbSDag-Erling Smørgrav } 35634f52dfbbSDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", c->self, 3564af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 3565e4a9863fSDag-Erling Smørgrav free(msg); 3566e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 356719261079SEd Maste debug2_f("channel %d: callback start", c->self); 35684f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); 356919261079SEd Maste debug2_f("channel %d: callback done", c->self); 3570e2f6069cSDag-Erling Smørgrav } 3571cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 35724f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 3573bc5531deSDag-Erling Smørgrav return 0; 3574a04a10f8SKris Kennaway } 3575a04a10f8SKris Kennaway 3576bc5531deSDag-Erling Smørgrav int 35774f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) 3578a04a10f8SKris Kennaway { 35794f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "window adjust"); 3580a04a10f8SKris Kennaway Channel *c; 35814f52dfbbSDag-Erling Smørgrav u_int32_t adjust; 35824f52dfbbSDag-Erling Smørgrav u_int new_rwin; 35834f52dfbbSDag-Erling Smørgrav int r; 3584a04a10f8SKris Kennaway 35854f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 3586b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 3587bc5531deSDag-Erling Smørgrav return 0; 3588511b41d2SMark Murray } 35894f52dfbbSDag-Erling Smørgrav 35904f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3591ca86bcf2SDag-Erling Smørgrav return 0; 359219261079SEd Maste if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || 359319261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 359419261079SEd Maste error_fr(r, "parse adjust"); 359519261079SEd Maste ssh_packet_disconnect(ssh, "Invalid window adjust message"); 35964f52dfbbSDag-Erling Smørgrav } 35974f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", c->self, adjust); 35984f52dfbbSDag-Erling Smørgrav if ((new_rwin = c->remote_window + adjust) < c->remote_window) { 3599557f75e5SDag-Erling Smørgrav fatal("channel %d: adjust %u overflows remote window %u", 36004f52dfbbSDag-Erling Smørgrav c->self, adjust, c->remote_window); 36014f52dfbbSDag-Erling Smørgrav } 36024f52dfbbSDag-Erling Smørgrav c->remote_window = new_rwin; 3603bc5531deSDag-Erling Smørgrav return 0; 3604511b41d2SMark Murray } 3605511b41d2SMark Murray 3606bc5531deSDag-Erling Smørgrav int 36074f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) 3608af12a3e7SDag-Erling Smørgrav { 36094f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "status confirm"); 3610d4af9e69SDag-Erling Smørgrav Channel *c; 3611d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 3612d4af9e69SDag-Erling Smørgrav 3613d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 361419261079SEd Maste ssh_packet_set_alive_timeouts(ssh, 0); 3615d4af9e69SDag-Erling Smørgrav 361619261079SEd Maste debug2_f("type %d id %d", type, id); 3617d4af9e69SDag-Erling Smørgrav 36184f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 361919261079SEd Maste logit_f("%d: unknown", id); 3620bc5531deSDag-Erling Smørgrav return 0; 3621d4af9e69SDag-Erling Smørgrav } 36224f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3623ca86bcf2SDag-Erling Smørgrav return 0; 362419261079SEd Maste if (sshpkt_get_end(ssh) != 0) 362519261079SEd Maste ssh_packet_disconnect(ssh, "Invalid status confirm message"); 3626d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 3627bc5531deSDag-Erling Smørgrav return 0; 36284f52dfbbSDag-Erling Smørgrav cc->cb(ssh, type, c, cc->ctx); 3629d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 363019261079SEd Maste freezero(cc, sizeof(*cc)); 3631bc5531deSDag-Erling Smørgrav return 0; 3632d4af9e69SDag-Erling Smørgrav } 3633af12a3e7SDag-Erling Smørgrav 3634af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 3635511b41d2SMark Murray 3636511b41d2SMark Murray void 36374f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af) 3638511b41d2SMark Murray { 36394f52dfbbSDag-Erling Smørgrav ssh->chanctxt->IPv4or6 = af; 3640511b41d2SMark Murray } 3641511b41d2SMark Murray 364289986192SBrooks Davis 3643462c32cbSDag-Erling Smørgrav /* 3644462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 3645462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 3646462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 3647462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 3648462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 3649462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 3650462c32cbSDag-Erling Smørgrav * 3651462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 3652462c32cbSDag-Erling Smørgrav * 3653462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 3654462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 3655462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 3656a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 3657462c32cbSDag-Erling Smørgrav */ 3658462c32cbSDag-Erling Smørgrav static const char * 365919261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, 3660a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 3661462c32cbSDag-Erling Smørgrav { 3662462c32cbSDag-Erling Smørgrav const char *addr = NULL; 3663462c32cbSDag-Erling Smørgrav int wildcard = 0; 3664462c32cbSDag-Erling Smørgrav 3665462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 3666462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 3667a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 3668462c32cbSDag-Erling Smørgrav wildcard = 1; 3669a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 367019261079SEd Maste if (((ssh->compat & SSH_OLD_FORWARD_ADDR) && 3671462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 3672462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 3673a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 3674462c32cbSDag-Erling Smørgrav wildcard = 1; 3675f7167e0eSDag-Erling Smørgrav /* 3676f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 3677f7167e0eSDag-Erling Smørgrav * address and it was overridden. 3678f7167e0eSDag-Erling Smørgrav */ 3679f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 3680f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 3681f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 368219261079SEd Maste ssh_packet_send_debug(ssh, 368319261079SEd Maste "Forwarding listen address " 3684f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 3685f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 3686f7167e0eSDag-Erling Smørgrav } 3687a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 3688a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 3689a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 369019261079SEd Maste /* 369119261079SEd Maste * Accept explicit localhost address when 369219261079SEd Maste * GatewayPorts=yes. The "localhost" hostname is 369319261079SEd Maste * deliberately skipped here so it will listen on all 369419261079SEd Maste * available local address families. 369519261079SEd Maste */ 3696a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 3697f7167e0eSDag-Erling Smørgrav } 3698a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 3699a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3700a0ee8cc6SDag-Erling Smørgrav /* 3701a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 3702a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 3703a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 3704a0ee8cc6SDag-Erling Smørgrav */ 3705462c32cbSDag-Erling Smørgrav addr = listen_addr; 3706462c32cbSDag-Erling Smørgrav } 3707462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 3708462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 3709462c32cbSDag-Erling Smørgrav return addr; 3710462c32cbSDag-Erling Smørgrav } 3711462c32cbSDag-Erling Smørgrav 3712af12a3e7SDag-Erling Smørgrav static int 37134f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, 37144f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int *allocated_listen_port, 37154f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3716511b41d2SMark Murray { 3717af12a3e7SDag-Erling Smørgrav Channel *c; 3718b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 3719511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3720aa49c926SDag-Erling Smørgrav const char *host, *addr; 3721af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 3722cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 3723511b41d2SMark Murray 3724aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 3725511b41d2SMark Murray 3726557f75e5SDag-Erling Smørgrav if (is_client && fwd->connect_path != NULL) { 3727557f75e5SDag-Erling Smørgrav host = fwd->connect_path; 3728557f75e5SDag-Erling Smørgrav } else { 3729557f75e5SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 3730557f75e5SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 3731af12a3e7SDag-Erling Smørgrav if (host == NULL) { 3732af12a3e7SDag-Erling Smørgrav error("No forward host name."); 3733d4ecd108SDag-Erling Smørgrav return 0; 3734ca3176e7SBrian Feldman } 3735cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 3736ca3176e7SBrian Feldman error("Forward host name too long."); 3737d4ecd108SDag-Erling Smørgrav return 0; 3738ca3176e7SBrian Feldman } 3739557f75e5SDag-Erling Smørgrav } 3740ca3176e7SBrian Feldman 3741462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 374219261079SEd Maste addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, 3743a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 374419261079SEd Maste debug3_f("type %d wildcard %d addr %s", type, wildcard, 374519261079SEd Maste (addr == NULL) ? "NULL" : addr); 3746aa49c926SDag-Erling Smørgrav 3747aa49c926SDag-Erling Smørgrav /* 3748511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 3749511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 3750511b41d2SMark Murray */ 3751511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 37524f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 3753aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 3754511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3755a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 3756aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 3757aa49c926SDag-Erling Smørgrav if (addr == NULL) { 3758aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 375919261079SEd Maste ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", 3760d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3761aa49c926SDag-Erling Smørgrav } else { 376219261079SEd Maste error_f("getaddrinfo(%.64s): %s", addr, 3763d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3764aa49c926SDag-Erling Smørgrav } 3765d4ecd108SDag-Erling Smørgrav return 0; 3766aa49c926SDag-Erling Smørgrav } 3767cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 3768cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 3769511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3770cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 3771cce7d346SDag-Erling Smørgrav case AF_INET: 3772cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 3773cce7d346SDag-Erling Smørgrav sin_port; 3774cce7d346SDag-Erling Smørgrav break; 3775cce7d346SDag-Erling Smørgrav case AF_INET6: 3776cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 3777cce7d346SDag-Erling Smørgrav sin6_port; 3778cce7d346SDag-Erling Smørgrav break; 3779cce7d346SDag-Erling Smørgrav default: 3780511b41d2SMark Murray continue; 3781cce7d346SDag-Erling Smørgrav } 3782cce7d346SDag-Erling Smørgrav /* 3783cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 3784cce7d346SDag-Erling Smørgrav * same port for all address families. 3785cce7d346SDag-Erling Smørgrav */ 37864f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 37874f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && allocated_listen_port != NULL && 37884f52dfbbSDag-Erling Smørgrav *allocated_listen_port > 0) 3789cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 3790cce7d346SDag-Erling Smørgrav 3791511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 37924f52dfbbSDag-Erling Smørgrav strport, sizeof(strport), 37934f52dfbbSDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 379419261079SEd Maste error_f("getnameinfo failed"); 3795511b41d2SMark Murray continue; 3796511b41d2SMark Murray } 3797511b41d2SMark Murray /* Create a port to listen for the host. */ 3798221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 379919261079SEd Maste if (sock == -1) { 3800511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 380147dd1d1bSDag-Erling Smørgrav verbose("socket [%s]:%s: %.100s", ntop, strport, 380247dd1d1bSDag-Erling Smørgrav strerror(errno)); 3803511b41d2SMark Murray continue; 3804511b41d2SMark Murray } 3805b74df5b2SDag-Erling Smørgrav 380647dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 3807b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3808b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3809f388f5efSDag-Erling Smørgrav 3810cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 3811cce7d346SDag-Erling Smørgrav ntop, strport); 3812511b41d2SMark Murray 3813511b41d2SMark Murray /* Bind the socket to the address. */ 381419261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 38154f52dfbbSDag-Erling Smørgrav /* 38164f52dfbbSDag-Erling Smørgrav * address can be in if use ipv6 address is 38174f52dfbbSDag-Erling Smørgrav * already bound 38184f52dfbbSDag-Erling Smørgrav */ 3819989dd127SDag-Erling Smørgrav if (!ai->ai_next) 382047dd1d1bSDag-Erling Smørgrav error("bind [%s]:%s: %.100s", 382147dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3822989dd127SDag-Erling Smørgrav else 382347dd1d1bSDag-Erling Smørgrav verbose("bind [%s]:%s: %.100s", 382447dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3825989dd127SDag-Erling Smørgrav 3826511b41d2SMark Murray close(sock); 3827511b41d2SMark Murray continue; 3828511b41d2SMark Murray } 3829511b41d2SMark Murray /* Start listening for connections on the socket. */ 383019261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 383147dd1d1bSDag-Erling Smørgrav error("listen [%s]:%s: %.100s", ntop, strport, 383247dd1d1bSDag-Erling Smørgrav strerror(errno)); 3833511b41d2SMark Murray close(sock); 3834511b41d2SMark Murray continue; 3835511b41d2SMark Murray } 3836cce7d346SDag-Erling Smørgrav 3837cce7d346SDag-Erling Smørgrav /* 3838a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 3839cce7d346SDag-Erling Smørgrav * record what we got. 3840cce7d346SDag-Erling Smørgrav */ 38414f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 38424f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && 3843cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 3844cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 3845076ad2f8SDag-Erling Smørgrav *allocated_listen_port = get_local_port(sock); 3846cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 3847cce7d346SDag-Erling Smørgrav *allocated_listen_port); 3848cce7d346SDag-Erling Smørgrav } 3849cce7d346SDag-Erling Smørgrav 385060c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 3851*f374ba41SEd Maste c = channel_new(ssh, "port-listener", type, sock, sock, -1, 3852a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3853221552e4SDag-Erling Smørgrav 0, "port listener", 1); 3854cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 3855a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 3856462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 3857a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 385819261079SEd Maste !(ssh->compat & SSH_BUG_DYNAMIC_RPORT)) 3859462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 3860462c32cbSDag-Erling Smørgrav else 3861a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 3862511b41d2SMark Murray success = 1; 3863511b41d2SMark Murray } 3864511b41d2SMark Murray if (success == 0) 386519261079SEd Maste error_f("cannot listen to port: %d", fwd->listen_port); 3866511b41d2SMark Murray freeaddrinfo(aitop); 3867ca3176e7SBrian Feldman return success; 3868511b41d2SMark Murray } 3869511b41d2SMark Murray 3870a0ee8cc6SDag-Erling Smørgrav static int 38714f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, 38724f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 3873a0ee8cc6SDag-Erling Smørgrav { 3874a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 3875a0ee8cc6SDag-Erling Smørgrav const char *path; 3876a0ee8cc6SDag-Erling Smørgrav Channel *c; 3877a0ee8cc6SDag-Erling Smørgrav int port, sock; 3878a0ee8cc6SDag-Erling Smørgrav mode_t omask; 3879a0ee8cc6SDag-Erling Smørgrav 3880a0ee8cc6SDag-Erling Smørgrav switch (type) { 3881a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 3882a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3883a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 3884a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 3885a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 3886a0ee8cc6SDag-Erling Smørgrav return 0; 3887a0ee8cc6SDag-Erling Smørgrav } 3888a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 3889a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3890a0ee8cc6SDag-Erling Smørgrav } else { 3891a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 3892a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 3893a0ee8cc6SDag-Erling Smørgrav return 0; 3894a0ee8cc6SDag-Erling Smørgrav } 3895a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 3896a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 3897a0ee8cc6SDag-Erling Smørgrav return 0; 3898a0ee8cc6SDag-Erling Smørgrav } 3899a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 3900a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 3901a0ee8cc6SDag-Erling Smørgrav } 3902a0ee8cc6SDag-Erling Smørgrav break; 3903a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 3904a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 3905a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3906a0ee8cc6SDag-Erling Smørgrav break; 3907a0ee8cc6SDag-Erling Smørgrav default: 390819261079SEd Maste error_f("unexpected channel type %d", type); 3909a0ee8cc6SDag-Erling Smørgrav return 0; 3910a0ee8cc6SDag-Erling Smørgrav } 3911a0ee8cc6SDag-Erling Smørgrav 3912a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 3913a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 3914a0ee8cc6SDag-Erling Smørgrav return 0; 3915a0ee8cc6SDag-Erling Smørgrav } 3916a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 3917a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 3918a0ee8cc6SDag-Erling Smørgrav return 0; 3919a0ee8cc6SDag-Erling Smørgrav } 3920a0ee8cc6SDag-Erling Smørgrav 392119261079SEd Maste debug3_f("type %d path %s", type, fwd->listen_path); 3922a0ee8cc6SDag-Erling Smørgrav 3923a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 3924a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 3925a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 3926a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 3927a0ee8cc6SDag-Erling Smørgrav umask(omask); 3928a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 3929a0ee8cc6SDag-Erling Smørgrav return 0; 3930a0ee8cc6SDag-Erling Smørgrav 3931a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 3932a0ee8cc6SDag-Erling Smørgrav 3933a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 3934*f374ba41SEd Maste c = channel_new(ssh, "unix-listener", type, sock, sock, -1, 3935a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3936a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 3937a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 3938a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 3939a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 3940a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 3941a0ee8cc6SDag-Erling Smørgrav return 1; 3942a0ee8cc6SDag-Erling Smørgrav } 3943a0ee8cc6SDag-Erling Smørgrav 3944a0ee8cc6SDag-Erling Smørgrav static int 39454f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh, 39464f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 394721e764dfSDag-Erling Smørgrav { 394821e764dfSDag-Erling Smørgrav u_int i; 394921e764dfSDag-Erling Smørgrav int found = 0; 395021e764dfSDag-Erling Smørgrav 39514f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 39524f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3953462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3954462c32cbSDag-Erling Smørgrav continue; 3955462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 395619261079SEd Maste debug2_f("close channel %d", i); 39574f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3958462c32cbSDag-Erling Smørgrav found = 1; 3959462c32cbSDag-Erling Smørgrav } 3960462c32cbSDag-Erling Smørgrav } 396121e764dfSDag-Erling Smørgrav 39624f52dfbbSDag-Erling Smørgrav return found; 3963462c32cbSDag-Erling Smørgrav } 3964462c32cbSDag-Erling Smørgrav 3965a0ee8cc6SDag-Erling Smørgrav static int 39664f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) 3967462c32cbSDag-Erling Smørgrav { 3968462c32cbSDag-Erling Smørgrav u_int i; 3969462c32cbSDag-Erling Smørgrav int found = 0; 3970a0ee8cc6SDag-Erling Smørgrav 39714f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 39724f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3973a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3974a0ee8cc6SDag-Erling Smørgrav continue; 3975a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 3976a0ee8cc6SDag-Erling Smørgrav continue; 3977a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 397819261079SEd Maste debug2_f("close channel %d", i); 39794f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3980a0ee8cc6SDag-Erling Smørgrav found = 1; 3981a0ee8cc6SDag-Erling Smørgrav } 3982a0ee8cc6SDag-Erling Smørgrav } 3983a0ee8cc6SDag-Erling Smørgrav 39844f52dfbbSDag-Erling Smørgrav return found; 3985a0ee8cc6SDag-Erling Smørgrav } 3986a0ee8cc6SDag-Erling Smørgrav 3987a0ee8cc6SDag-Erling Smørgrav int 39884f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) 3989a0ee8cc6SDag-Erling Smørgrav { 39904f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 39914f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(ssh, 39924f52dfbbSDag-Erling Smørgrav fwd->listen_path); 39934f52dfbbSDag-Erling Smørgrav } else { 39944f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(ssh, 39954f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 39964f52dfbbSDag-Erling Smørgrav } 3997a0ee8cc6SDag-Erling Smørgrav } 3998a0ee8cc6SDag-Erling Smørgrav 3999a0ee8cc6SDag-Erling Smørgrav static int 40004f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh, 40014f52dfbbSDag-Erling Smørgrav const char *lhost, u_short lport, int cport, 40024f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 4003a0ee8cc6SDag-Erling Smørgrav { 4004a0ee8cc6SDag-Erling Smørgrav u_int i; 4005a0ee8cc6SDag-Erling Smørgrav int found = 0; 400619261079SEd Maste const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); 4007462c32cbSDag-Erling Smørgrav 40084f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 40094f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4010462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 4011462c32cbSDag-Erling Smørgrav continue; 4012462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 4013462c32cbSDag-Erling Smørgrav continue; 4014462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 4015462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 4016462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 4017462c32cbSDag-Erling Smørgrav continue; 4018462c32cbSDag-Erling Smørgrav } else { 4019462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 4020462c32cbSDag-Erling Smørgrav continue; 4021462c32cbSDag-Erling Smørgrav } 4022462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 4023462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 4024462c32cbSDag-Erling Smørgrav continue; 4025462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 402619261079SEd Maste debug2_f("close channel %d", i); 40274f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 402821e764dfSDag-Erling Smørgrav found = 1; 402921e764dfSDag-Erling Smørgrav } 403021e764dfSDag-Erling Smørgrav } 403121e764dfSDag-Erling Smørgrav 40324f52dfbbSDag-Erling Smørgrav return found; 403321e764dfSDag-Erling Smørgrav } 403421e764dfSDag-Erling Smørgrav 4035a0ee8cc6SDag-Erling Smørgrav static int 40364f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) 4037a0ee8cc6SDag-Erling Smørgrav { 4038a0ee8cc6SDag-Erling Smørgrav u_int i; 4039a0ee8cc6SDag-Erling Smørgrav int found = 0; 4040a0ee8cc6SDag-Erling Smørgrav 4041a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 404219261079SEd Maste error_f("no path specified."); 4043a0ee8cc6SDag-Erling Smørgrav return 0; 4044a0ee8cc6SDag-Erling Smørgrav } 4045a0ee8cc6SDag-Erling Smørgrav 40464f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 40474f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 4048a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 4049a0ee8cc6SDag-Erling Smørgrav continue; 4050a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 4051a0ee8cc6SDag-Erling Smørgrav continue; 4052a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 405319261079SEd Maste debug2_f("close channel %d", i); 40544f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 4055a0ee8cc6SDag-Erling Smørgrav found = 1; 4056a0ee8cc6SDag-Erling Smørgrav } 4057a0ee8cc6SDag-Erling Smørgrav } 4058a0ee8cc6SDag-Erling Smørgrav 40594f52dfbbSDag-Erling Smørgrav return found; 4060a0ee8cc6SDag-Erling Smørgrav } 4061a0ee8cc6SDag-Erling Smørgrav 4062a0ee8cc6SDag-Erling Smørgrav int 40634f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh, 40644f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 4065af12a3e7SDag-Erling Smørgrav { 4066a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 40674f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(ssh, 40684f52dfbbSDag-Erling Smørgrav fwd->listen_path); 40694f52dfbbSDag-Erling Smørgrav } else { 40704f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(ssh, 40714f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, cport, fwd_opts); 40724f52dfbbSDag-Erling Smørgrav } 40734f52dfbbSDag-Erling Smørgrav } 40744f52dfbbSDag-Erling Smørgrav 40754f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */ 40764f52dfbbSDag-Erling Smørgrav int 40774f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh, 40784f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 40794f52dfbbSDag-Erling Smørgrav { 40804f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 40814f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 4082a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 4083a0ee8cc6SDag-Erling Smørgrav } else { 40844f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 40854f52dfbbSDag-Erling Smørgrav SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); 4086a0ee8cc6SDag-Erling Smørgrav } 4087af12a3e7SDag-Erling Smørgrav } 4088af12a3e7SDag-Erling Smørgrav 4089190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */ 4090190cef3dSDag-Erling Smørgrav static int 4091190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd) 4092190cef3dSDag-Erling Smørgrav { 4093190cef3dSDag-Erling Smørgrav int ret; 4094190cef3dSDag-Erling Smørgrav char *lhost; 4095190cef3dSDag-Erling Smørgrav 4096190cef3dSDag-Erling Smørgrav /* XXX add ACLs for streamlocal */ 4097190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 4098190cef3dSDag-Erling Smørgrav return 1; 4099190cef3dSDag-Erling Smørgrav 4100190cef3dSDag-Erling Smørgrav if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) 4101190cef3dSDag-Erling Smørgrav return 0; 4102190cef3dSDag-Erling Smørgrav 4103190cef3dSDag-Erling Smørgrav if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && 4104190cef3dSDag-Erling Smørgrav allowed_open->listen_port != fwd->listen_port) 4105190cef3dSDag-Erling Smørgrav return 0; 4106190cef3dSDag-Erling Smørgrav 4107190cef3dSDag-Erling Smørgrav /* Match hostnames case-insensitively */ 4108190cef3dSDag-Erling Smørgrav lhost = xstrdup(fwd->listen_host); 4109190cef3dSDag-Erling Smørgrav lowercase(lhost); 4110190cef3dSDag-Erling Smørgrav ret = match_pattern(lhost, allowed_open->listen_host); 4111190cef3dSDag-Erling Smørgrav free(lhost); 4112190cef3dSDag-Erling Smørgrav 4113190cef3dSDag-Erling Smørgrav return ret; 4114190cef3dSDag-Erling Smørgrav } 4115190cef3dSDag-Erling Smørgrav 4116190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */ 4117190cef3dSDag-Erling Smørgrav static int 4118190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) 4119190cef3dSDag-Erling Smørgrav { 4120190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4121190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->remote_perms; 4122190cef3dSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4123190cef3dSDag-Erling Smørgrav struct permission *perm; 4124190cef3dSDag-Erling Smørgrav 4125190cef3dSDag-Erling Smørgrav /* XXX apply GatewayPorts override before checking? */ 4126190cef3dSDag-Erling Smørgrav 4127190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4128190cef3dSDag-Erling Smørgrav if (!permit) { 4129190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4130190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4131190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 4132190cef3dSDag-Erling Smørgrav permit = 1; 4133190cef3dSDag-Erling Smørgrav break; 4134190cef3dSDag-Erling Smørgrav } 4135190cef3dSDag-Erling Smørgrav } 4136190cef3dSDag-Erling Smørgrav } 4137190cef3dSDag-Erling Smørgrav 4138190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4139190cef3dSDag-Erling Smørgrav permit_adm = 0; 4140190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4141190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4142190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 4143190cef3dSDag-Erling Smørgrav permit_adm = 1; 4144190cef3dSDag-Erling Smørgrav break; 4145190cef3dSDag-Erling Smørgrav } 4146190cef3dSDag-Erling Smørgrav } 4147190cef3dSDag-Erling Smørgrav } 4148190cef3dSDag-Erling Smørgrav 4149190cef3dSDag-Erling Smørgrav return permit && permit_adm; 4150190cef3dSDag-Erling Smørgrav } 4151190cef3dSDag-Erling Smørgrav 4152af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 4153af12a3e7SDag-Erling Smørgrav int 41544f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, 4155a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 4156af12a3e7SDag-Erling Smørgrav { 4157190cef3dSDag-Erling Smørgrav if (!check_rfwd_permission(ssh, fwd)) { 415819261079SEd Maste ssh_packet_send_debug(ssh, "port forwarding refused"); 415919261079SEd Maste if (fwd->listen_path != NULL) 416019261079SEd Maste /* XXX always allowed, see remote_open_match() */ 416119261079SEd Maste logit("Received request from %.100s port %d to " 416219261079SEd Maste "remote forward to path \"%.100s\", " 416319261079SEd Maste "but the request was denied.", 416419261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 416519261079SEd Maste fwd->listen_path); 416619261079SEd Maste else if(fwd->listen_host != NULL) 416719261079SEd Maste logit("Received request from %.100s port %d to " 416819261079SEd Maste "remote forward to host %.100s port %d, " 416919261079SEd Maste "but the request was denied.", 417019261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 417119261079SEd Maste fwd->listen_host, fwd->listen_port ); 417219261079SEd Maste else 417319261079SEd Maste logit("Received request from %.100s port %d to remote " 417419261079SEd Maste "forward, but the request was denied.", 417519261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 4176190cef3dSDag-Erling Smørgrav return 0; 4177190cef3dSDag-Erling Smørgrav } 4178a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 41794f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 4180a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 4181a0ee8cc6SDag-Erling Smørgrav } else { 41824f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 4183a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 4184a0ee8cc6SDag-Erling Smørgrav fwd_opts); 4185a0ee8cc6SDag-Erling Smørgrav } 4186af12a3e7SDag-Erling Smørgrav } 4187af12a3e7SDag-Erling Smørgrav 4188511b41d2SMark Murray /* 4189462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 4190462c32cbSDag-Erling Smørgrav * this server. 4191462c32cbSDag-Erling Smørgrav */ 4192462c32cbSDag-Erling Smørgrav static const char * 4193462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 4194462c32cbSDag-Erling Smørgrav { 4195462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 4196462c32cbSDag-Erling Smørgrav return "localhost"; 4197462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 4198462c32cbSDag-Erling Smørgrav return ""; 4199462c32cbSDag-Erling Smørgrav } else 4200462c32cbSDag-Erling Smørgrav return listen_host; 4201462c32cbSDag-Erling Smørgrav } 4202462c32cbSDag-Erling Smørgrav 4203462c32cbSDag-Erling Smørgrav /* 4204511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 4205511b41d2SMark Murray * the secure channel to host:port from local side. 4206462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 4207190cef3dSDag-Erling Smørgrav * channel_update_permission(). 4208511b41d2SMark Murray */ 4209333ee039SDag-Erling Smørgrav int 42104f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) 4211511b41d2SMark Murray { 42124f52dfbbSDag-Erling Smørgrav int r, success = 0, idx = -1; 4213*f374ba41SEd Maste const char *host_to_connect, *listen_host, *listen_path; 42144f52dfbbSDag-Erling Smørgrav int port_to_connect, listen_port; 4215ca3176e7SBrian Feldman 4216511b41d2SMark Murray /* Send the forward request to the remote side. */ 4217a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 42184f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 42194f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 42204f52dfbbSDag-Erling Smørgrav "streamlocal-forward@openssh.com")) != 0 || 42214f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 42224f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || 42234f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 42244f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 422519261079SEd Maste fatal_fr(r, "request streamlocal"); 4226a0ee8cc6SDag-Erling Smørgrav } else { 42274f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 42284f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || 42294f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 42304f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 42314f52dfbbSDag-Erling Smørgrav channel_rfwd_bind_host(fwd->listen_host))) != 0 || 42324f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || 42334f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 42344f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 423519261079SEd Maste fatal_fr(r, "request tcpip-forward"); 4236a0ee8cc6SDag-Erling Smørgrav } 4237ca3176e7SBrian Feldman /* Assume that server accepts the request */ 4238ca3176e7SBrian Feldman success = 1; 4239ca3176e7SBrian Feldman if (success) { 4240e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 42414f52dfbbSDag-Erling Smørgrav host_to_connect = listen_host = listen_path = NULL; 42424f52dfbbSDag-Erling Smørgrav port_to_connect = listen_port = 0; 4243a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 4244*f374ba41SEd Maste host_to_connect = fwd->connect_path; 42454f52dfbbSDag-Erling Smørgrav port_to_connect = PORT_STREAMLOCAL; 4246a0ee8cc6SDag-Erling Smørgrav } else { 4247*f374ba41SEd Maste host_to_connect = fwd->connect_host; 42484f52dfbbSDag-Erling Smørgrav port_to_connect = fwd->connect_port; 4249a0ee8cc6SDag-Erling Smørgrav } 4250a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 4251*f374ba41SEd Maste listen_path = fwd->listen_path; 42524f52dfbbSDag-Erling Smørgrav listen_port = PORT_STREAMLOCAL; 4253a0ee8cc6SDag-Erling Smørgrav } else { 4254*f374ba41SEd Maste listen_host = fwd->listen_host; 42554f52dfbbSDag-Erling Smørgrav listen_port = fwd->listen_port; 4256a0ee8cc6SDag-Erling Smørgrav } 4257190cef3dSDag-Erling Smørgrav idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, 42584f52dfbbSDag-Erling Smørgrav host_to_connect, port_to_connect, 42594f52dfbbSDag-Erling Smørgrav listen_host, listen_path, listen_port, NULL); 4260511b41d2SMark Murray } 42614f52dfbbSDag-Erling Smørgrav return idx; 4262a04a10f8SKris Kennaway } 4263511b41d2SMark Murray 4264a0ee8cc6SDag-Erling Smørgrav static int 4265190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost, 4266a0ee8cc6SDag-Erling Smørgrav int requestedport) 4267a0ee8cc6SDag-Erling Smørgrav { 4268a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4269a0ee8cc6SDag-Erling Smørgrav return 0; 4270a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 4271a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 4272a0ee8cc6SDag-Erling Smørgrav return 0; 4273076ad2f8SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 4274076ad2f8SDag-Erling Smørgrav strcmp(allowed_open->host_to_connect, requestedhost) != 0) 4275a0ee8cc6SDag-Erling Smørgrav return 0; 4276a0ee8cc6SDag-Erling Smørgrav return 1; 4277a0ee8cc6SDag-Erling Smørgrav } 4278a0ee8cc6SDag-Erling Smørgrav 4279a0ee8cc6SDag-Erling Smørgrav /* 4280a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 4281a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 4282a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 4283a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 4284a0ee8cc6SDag-Erling Smørgrav */ 4285a0ee8cc6SDag-Erling Smørgrav static int 4286190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open, 4287a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 4288a0ee8cc6SDag-Erling Smørgrav { 4289a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 4290a0ee8cc6SDag-Erling Smørgrav 4291a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4292a0ee8cc6SDag-Erling Smørgrav return 0; 4293a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 4294a0ee8cc6SDag-Erling Smørgrav return 0; 4295a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 4296a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 4297a0ee8cc6SDag-Erling Smørgrav return 1; 4298a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 4299a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 4300a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 4301190cef3dSDag-Erling Smørgrav if (allowed_host == NULL || requestedhost == NULL || 4302a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 4303a0ee8cc6SDag-Erling Smørgrav return 0; 4304a0ee8cc6SDag-Erling Smørgrav return 1; 4305a0ee8cc6SDag-Erling Smørgrav } 4306a0ee8cc6SDag-Erling Smørgrav 4307a0ee8cc6SDag-Erling Smørgrav static int 4308190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open, 4309a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 4310a0ee8cc6SDag-Erling Smørgrav { 4311a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4312a0ee8cc6SDag-Erling Smørgrav return 0; 4313a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 4314a0ee8cc6SDag-Erling Smørgrav return 0; 4315a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 4316a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 4317a0ee8cc6SDag-Erling Smørgrav return 0; 4318a0ee8cc6SDag-Erling Smørgrav return 1; 4319a0ee8cc6SDag-Erling Smørgrav } 4320a0ee8cc6SDag-Erling Smørgrav 4321511b41d2SMark Murray /* 432221e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 432321e764dfSDag-Erling Smørgrav * local side. 432421e764dfSDag-Erling Smørgrav */ 4325a0ee8cc6SDag-Erling Smørgrav static int 43264f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh, 43274f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 432821e764dfSDag-Erling Smørgrav { 43294f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4330190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 43314f52dfbbSDag-Erling Smørgrav int r; 43324f52dfbbSDag-Erling Smørgrav u_int i; 433319261079SEd Maste struct permission *perm = NULL; 433421e764dfSDag-Erling Smørgrav 4335190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4336190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4337190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, host, port, 0)) 433821e764dfSDag-Erling Smørgrav break; 4339190cef3dSDag-Erling Smørgrav perm = NULL; 434021e764dfSDag-Erling Smørgrav } 4341190cef3dSDag-Erling Smørgrav if (perm == NULL) { 434219261079SEd Maste debug_f("requested forward not found"); 4343462c32cbSDag-Erling Smørgrav return -1; 434421e764dfSDag-Erling Smørgrav } 43454f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 43464f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || 43474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 43484f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || 43494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, port)) != 0 || 43504f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 435119261079SEd Maste fatal_fr(r, "send cancel"); 435221e764dfSDag-Erling Smørgrav 4353190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4354462c32cbSDag-Erling Smørgrav 4355462c32cbSDag-Erling Smørgrav return 0; 435621e764dfSDag-Erling Smørgrav } 435721e764dfSDag-Erling Smørgrav 435821e764dfSDag-Erling Smørgrav /* 4359a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 4360a0ee8cc6SDag-Erling Smørgrav * path from local side. 4361a0ee8cc6SDag-Erling Smørgrav */ 4362a0ee8cc6SDag-Erling Smørgrav static int 43634f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) 4364a0ee8cc6SDag-Erling Smørgrav { 43654f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4366190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 43674f52dfbbSDag-Erling Smørgrav int r; 43684f52dfbbSDag-Erling Smørgrav u_int i; 436919261079SEd Maste struct permission *perm = NULL; 4370a0ee8cc6SDag-Erling Smørgrav 4371190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4372190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4373190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) 4374a0ee8cc6SDag-Erling Smørgrav break; 4375190cef3dSDag-Erling Smørgrav perm = NULL; 4376a0ee8cc6SDag-Erling Smørgrav } 4377190cef3dSDag-Erling Smørgrav if (perm == NULL) { 437819261079SEd Maste debug_f("requested forward not found"); 4379a0ee8cc6SDag-Erling Smørgrav return -1; 4380a0ee8cc6SDag-Erling Smørgrav } 43814f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 43824f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 43834f52dfbbSDag-Erling Smørgrav "cancel-streamlocal-forward@openssh.com")) != 0 || 43844f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 43854f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, path)) != 0 || 43864f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 438719261079SEd Maste fatal_fr(r, "send cancel"); 4388a0ee8cc6SDag-Erling Smørgrav 4389190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4390a0ee8cc6SDag-Erling Smørgrav 4391a0ee8cc6SDag-Erling Smørgrav return 0; 4392a0ee8cc6SDag-Erling Smørgrav } 4393a0ee8cc6SDag-Erling Smørgrav 4394a0ee8cc6SDag-Erling Smørgrav /* 4395a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 4396a0ee8cc6SDag-Erling Smørgrav */ 4397a0ee8cc6SDag-Erling Smørgrav int 43984f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) 4399a0ee8cc6SDag-Erling Smørgrav { 4400a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 44014f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_streamlocal(ssh, 44024f52dfbbSDag-Erling Smørgrav fwd->listen_path); 4403a0ee8cc6SDag-Erling Smørgrav } else { 44044f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_tcpip(ssh, 44054f52dfbbSDag-Erling Smørgrav fwd->listen_host, 44064f52dfbbSDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port); 4407a0ee8cc6SDag-Erling Smørgrav } 4408a0ee8cc6SDag-Erling Smørgrav } 4409a0ee8cc6SDag-Erling Smørgrav 4410a0ee8cc6SDag-Erling Smørgrav /* 4411190cef3dSDag-Erling Smørgrav * Permits opening to any host/port if permitted_user[] is empty. This is 4412ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 4413ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 4414ca3176e7SBrian Feldman */ 4415ca3176e7SBrian Feldman void 4416190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where) 4417ca3176e7SBrian Feldman { 4418190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 4419190cef3dSDag-Erling Smørgrav 4420190cef3dSDag-Erling Smørgrav if (pset->num_permitted_user == 0) 4421190cef3dSDag-Erling Smørgrav pset->all_permitted = 1; 4422ca3176e7SBrian Feldman } 4423ca3176e7SBrian Feldman 4424190cef3dSDag-Erling Smørgrav /* 4425190cef3dSDag-Erling Smørgrav * Permit the specified host/port for forwarding. 4426190cef3dSDag-Erling Smørgrav */ 4427ca3176e7SBrian Feldman void 4428190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where, 4429190cef3dSDag-Erling Smørgrav char *host, int port) 4430ca3176e7SBrian Feldman { 4431190cef3dSDag-Erling Smørgrav int local = where == FORWARD_LOCAL; 4432190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 44334f52dfbbSDag-Erling Smørgrav 4434190cef3dSDag-Erling Smørgrav debug("allow %s forwarding to host %s port %d", 4435190cef3dSDag-Erling Smørgrav fwd_ident(who, where), host, port); 4436190cef3dSDag-Erling Smørgrav /* 4437190cef3dSDag-Erling Smørgrav * Remote forwards set listen_host/port, local forwards set 4438190cef3dSDag-Erling Smørgrav * host/port_to_connect. 4439190cef3dSDag-Erling Smørgrav */ 4440190cef3dSDag-Erling Smørgrav permission_set_add(ssh, who, where, 4441190cef3dSDag-Erling Smørgrav local ? host : 0, local ? port : 0, 4442190cef3dSDag-Erling Smørgrav local ? NULL : host, NULL, local ? 0 : port, NULL); 4443190cef3dSDag-Erling Smørgrav pset->all_permitted = 0; 4444190cef3dSDag-Erling Smørgrav } 4445190cef3dSDag-Erling Smørgrav 4446190cef3dSDag-Erling Smørgrav /* 4447190cef3dSDag-Erling Smørgrav * Administratively disable forwarding. 4448190cef3dSDag-Erling Smørgrav */ 4449190cef3dSDag-Erling Smørgrav void 4450190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where) 4451190cef3dSDag-Erling Smørgrav { 4452190cef3dSDag-Erling Smørgrav channel_clear_permission(ssh, FORWARD_ADM, where); 4453190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_ADM, where, 4454190cef3dSDag-Erling Smørgrav NULL, 0, NULL, NULL, 0, NULL); 4455190cef3dSDag-Erling Smørgrav } 4456190cef3dSDag-Erling Smørgrav 4457190cef3dSDag-Erling Smørgrav /* 4458190cef3dSDag-Erling Smørgrav * Clear a list of permitted opens. 4459190cef3dSDag-Erling Smørgrav */ 4460190cef3dSDag-Erling Smørgrav void 4461190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where) 4462190cef3dSDag-Erling Smørgrav { 4463190cef3dSDag-Erling Smørgrav struct permission **permp; 4464190cef3dSDag-Erling Smørgrav u_int *npermp; 4465190cef3dSDag-Erling Smørgrav 4466190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 4467190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); 4468190cef3dSDag-Erling Smørgrav *npermp = 0; 4469ca3176e7SBrian Feldman } 4470ca3176e7SBrian Feldman 4471462c32cbSDag-Erling Smørgrav /* 4472462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 4473462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 4474462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 4475462c32cbSDag-Erling Smørgrav */ 4476462c32cbSDag-Erling Smørgrav void 4477190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport) 4478462c32cbSDag-Erling Smørgrav { 4479190cef3dSDag-Erling Smørgrav struct permission_set *pset = &ssh->chanctxt->local_perms; 44804f52dfbbSDag-Erling Smørgrav 4481190cef3dSDag-Erling Smørgrav if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { 448219261079SEd Maste debug_f("index out of range: %d num_permitted_user %d", 448319261079SEd Maste idx, pset->num_permitted_user); 4484462c32cbSDag-Erling Smørgrav return; 4485462c32cbSDag-Erling Smørgrav } 4486462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 4487462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 4488462c32cbSDag-Erling Smørgrav newport, 4489190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].host_to_connect, 4490190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].port_to_connect); 44914f52dfbbSDag-Erling Smørgrav if (newport <= 0) 4492190cef3dSDag-Erling Smørgrav fwd_perm_clear(&pset->permitted_user[idx]); 44934f52dfbbSDag-Erling Smørgrav else { 4494190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].listen_port = 449519261079SEd Maste (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 4496462c32cbSDag-Erling Smørgrav } 4497462c32cbSDag-Erling Smørgrav } 4498462c32cbSDag-Erling Smørgrav 4499462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 4500462c32cbSDag-Erling Smørgrav int 4501462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 4502462c32cbSDag-Erling Smørgrav { 4503462c32cbSDag-Erling Smørgrav int port; 4504462c32cbSDag-Erling Smørgrav 4505462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 4506462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 4507462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 4508462c32cbSDag-Erling Smørgrav return port; 4509462c32cbSDag-Erling Smørgrav return -1; 4510462c32cbSDag-Erling Smørgrav } 4511462c32cbSDag-Erling Smørgrav 4512d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 4513d4af9e69SDag-Erling Smørgrav static int 4514d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 4515d4af9e69SDag-Erling Smørgrav { 4516d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 4517a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 45184f52dfbbSDag-Erling Smørgrav char ntop[NI_MAXHOST]; 45194f52dfbbSDag-Erling Smørgrav char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; 4520d4af9e69SDag-Erling Smørgrav 4521d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 4522a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 4523a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 4524a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 4525a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 4526a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 4527a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 4528a0ee8cc6SDag-Erling Smørgrav break; 4529a0ee8cc6SDag-Erling Smørgrav case AF_INET: 4530a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 4531d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 4532d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 4533d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 453438a52bd3SEd Maste error_f("getnameinfo failed"); 4535511b41d2SMark Murray continue; 4536511b41d2SMark Murray } 4537a0ee8cc6SDag-Erling Smørgrav break; 4538a0ee8cc6SDag-Erling Smørgrav default: 4539a0ee8cc6SDag-Erling Smørgrav continue; 4540a0ee8cc6SDag-Erling Smørgrav } 454138a52bd3SEd Maste debug_f("start for host %.100s ([%.100s]:%s)", 454238a52bd3SEd Maste cctx->host, ntop, strport); 4543d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 4544d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 4545d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 4546511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4547e73e9afaSDag-Erling Smørgrav else 4548e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 4549511b41d2SMark Murray continue; 4550511b41d2SMark Murray } 455121e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 455219261079SEd Maste fatal_f("set_nonblock(%d)", sock); 4553d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 4554d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 455538a52bd3SEd Maste debug_f("host %.100s ([%.100s]:%s): %.100s", 455638a52bd3SEd Maste cctx->host, ntop, strport, strerror(errno)); 4557d4af9e69SDag-Erling Smørgrav saved_errno = errno; 4558511b41d2SMark Murray close(sock); 4559d4af9e69SDag-Erling Smørgrav errno = saved_errno; 4560511b41d2SMark Murray continue; /* fail -- try next */ 4561511b41d2SMark Murray } 4562a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 4563a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 456438a52bd3SEd Maste debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d", 456538a52bd3SEd Maste cctx->host, ntop, strport, sock); 4566d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 4567a04a10f8SKris Kennaway return sock; 4568a04a10f8SKris Kennaway } 4569ca3176e7SBrian Feldman return -1; 4570ca3176e7SBrian Feldman } 4571ca3176e7SBrian Feldman 4572d4af9e69SDag-Erling Smørgrav static void 4573d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 4574d4af9e69SDag-Erling Smørgrav { 4575e4a9863fSDag-Erling Smørgrav free(cctx->host); 4576a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 4577a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 4578a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 4579a0ee8cc6SDag-Erling Smørgrav else 4580d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 4581a0ee8cc6SDag-Erling Smørgrav } 4582b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 4583d4af9e69SDag-Erling Smørgrav } 4584d4af9e69SDag-Erling Smørgrav 4585d93a896eSDag-Erling Smørgrav /* 45864f52dfbbSDag-Erling Smørgrav * Return connecting socket to remote host:port or local socket path, 4587d93a896eSDag-Erling Smørgrav * passing back the failure reason if appropriate. 4588d93a896eSDag-Erling Smørgrav */ 45894f52dfbbSDag-Erling Smørgrav static int 45904f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, 45914f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, struct channel_connect *cctx, 4592d93a896eSDag-Erling Smørgrav int *reason, const char **errmsg) 4593d4af9e69SDag-Erling Smørgrav { 4594d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 4595d4af9e69SDag-Erling Smørgrav int gaierr; 4596d4af9e69SDag-Erling Smørgrav int sock = -1; 4597d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 4598a0ee8cc6SDag-Erling Smørgrav 4599a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 4600a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 4601a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 4602a0ee8cc6SDag-Erling Smørgrav 4603a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 4604a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 46054f52dfbbSDag-Erling Smørgrav return -1; 4606a0ee8cc6SDag-Erling Smørgrav } 4607a0ee8cc6SDag-Erling Smørgrav 4608a0ee8cc6SDag-Erling Smørgrav /* 4609a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 4610a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 4611a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 4612a0ee8cc6SDag-Erling Smørgrav */ 4613a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 4614a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 4615a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 4616a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 4617a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 46184f52dfbbSDag-Erling Smørgrav ai->ai_socktype = socktype; 4619a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 4620a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 4621a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 4622a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 46234f52dfbbSDag-Erling Smørgrav cctx->aitop = ai; 4624a0ee8cc6SDag-Erling Smørgrav } else { 4625d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 46264f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 46274f52dfbbSDag-Erling Smørgrav hints.ai_socktype = socktype; 4628d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 46294f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) 4630d93a896eSDag-Erling Smørgrav != 0) { 4631d93a896eSDag-Erling Smørgrav if (errmsg != NULL) 4632d93a896eSDag-Erling Smørgrav *errmsg = ssh_gai_strerror(gaierr); 4633d93a896eSDag-Erling Smørgrav if (reason != NULL) 4634d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_CONNECT_FAILED; 4635a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 4636d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 46374f52dfbbSDag-Erling Smørgrav return -1; 4638d4af9e69SDag-Erling Smørgrav } 4639a0ee8cc6SDag-Erling Smørgrav } 4640d4af9e69SDag-Erling Smørgrav 46414f52dfbbSDag-Erling Smørgrav cctx->host = xstrdup(name); 46424f52dfbbSDag-Erling Smørgrav cctx->port = port; 46434f52dfbbSDag-Erling Smørgrav cctx->ai = cctx->aitop; 4644d4af9e69SDag-Erling Smørgrav 46454f52dfbbSDag-Erling Smørgrav if ((sock = connect_next(cctx)) == -1) { 4646d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 4647a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 46484f52dfbbSDag-Erling Smørgrav return -1; 4649d4af9e69SDag-Erling Smørgrav } 46504f52dfbbSDag-Erling Smørgrav 46514f52dfbbSDag-Erling Smørgrav return sock; 4652d4af9e69SDag-Erling Smørgrav } 4653d4af9e69SDag-Erling Smørgrav 4654d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 4655d93a896eSDag-Erling Smørgrav static Channel * 46564f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port, 46574f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4658d93a896eSDag-Erling Smørgrav { 46594f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 46604f52dfbbSDag-Erling Smørgrav Channel *c; 46614f52dfbbSDag-Erling Smørgrav int sock; 46624f52dfbbSDag-Erling Smørgrav 46634f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 46644f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 46654f52dfbbSDag-Erling Smørgrav &cctx, NULL, NULL); 46664f52dfbbSDag-Erling Smørgrav if (sock == -1) { 46674f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 46684f52dfbbSDag-Erling Smørgrav return NULL; 46694f52dfbbSDag-Erling Smørgrav } 46704f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 46714f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 46724f52dfbbSDag-Erling Smørgrav c->host_port = port; 46734f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 46744f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 46754f52dfbbSDag-Erling Smørgrav 46764f52dfbbSDag-Erling Smørgrav return c; 4677d93a896eSDag-Erling Smørgrav } 4678d93a896eSDag-Erling Smørgrav 4679ca86bcf2SDag-Erling Smørgrav /* 4680ca86bcf2SDag-Erling Smørgrav * returns either the newly connected channel or the downstream channel 4681ca86bcf2SDag-Erling Smørgrav * that needs to deal with this connection. 4682ca86bcf2SDag-Erling Smørgrav */ 4683d4af9e69SDag-Erling Smørgrav Channel * 46844f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, 4685a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 4686d4af9e69SDag-Erling Smørgrav { 46874f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4688190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 46894f52dfbbSDag-Erling Smørgrav u_int i; 4690190cef3dSDag-Erling Smørgrav struct permission *perm; 4691d4af9e69SDag-Erling Smørgrav 4692190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4693190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4694190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, 4695190cef3dSDag-Erling Smørgrav listen_host, listen_port, 1)) { 4696190cef3dSDag-Erling Smørgrav if (perm->downstream) 4697190cef3dSDag-Erling Smørgrav return perm->downstream; 4698190cef3dSDag-Erling Smørgrav if (perm->port_to_connect == 0) 46994f52dfbbSDag-Erling Smørgrav return rdynamic_connect_prepare(ssh, 47004f52dfbbSDag-Erling Smørgrav ctype, rname); 47014f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4702190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 47034f52dfbbSDag-Erling Smørgrav ctype, rname); 4704d4af9e69SDag-Erling Smørgrav } 4705d4af9e69SDag-Erling Smørgrav } 4706d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 4707d4af9e69SDag-Erling Smørgrav listen_port); 4708d4af9e69SDag-Erling Smørgrav return NULL; 4709d4af9e69SDag-Erling Smørgrav } 4710d4af9e69SDag-Erling Smørgrav 4711a0ee8cc6SDag-Erling Smørgrav Channel * 47124f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path, 47134f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4714a0ee8cc6SDag-Erling Smørgrav { 47154f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4716190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 47174f52dfbbSDag-Erling Smørgrav u_int i; 4718190cef3dSDag-Erling Smørgrav struct permission *perm; 4719a0ee8cc6SDag-Erling Smørgrav 4720190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4721190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4722190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) { 47234f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4724190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 47254f52dfbbSDag-Erling Smørgrav ctype, rname); 4726a0ee8cc6SDag-Erling Smørgrav } 4727a0ee8cc6SDag-Erling Smørgrav } 4728a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 4729a0ee8cc6SDag-Erling Smørgrav path); 4730a0ee8cc6SDag-Erling Smørgrav return NULL; 4731a0ee8cc6SDag-Erling Smørgrav } 4732a0ee8cc6SDag-Erling Smørgrav 4733ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 4734d4af9e69SDag-Erling Smørgrav Channel * 47354f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, 47364f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, int *reason, const char **errmsg) 4737ca3176e7SBrian Feldman { 47384f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4739190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 47404f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 47414f52dfbbSDag-Erling Smørgrav Channel *c; 47424f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 47434f52dfbbSDag-Erling Smørgrav int sock; 4744190cef3dSDag-Erling Smørgrav struct permission *perm; 4745ca3176e7SBrian Feldman 4746190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4747ca3176e7SBrian Feldman if (!permit) { 4748190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4749190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4750190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4751ca3176e7SBrian Feldman permit = 1; 4752a0ee8cc6SDag-Erling Smørgrav break; 4753a0ee8cc6SDag-Erling Smørgrav } 4754ca3176e7SBrian Feldman } 47554f52dfbbSDag-Erling Smørgrav } 4756333ee039SDag-Erling Smørgrav 4757190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4758333ee039SDag-Erling Smørgrav permit_adm = 0; 4759190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4760190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4761190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4762333ee039SDag-Erling Smørgrav permit_adm = 1; 4763a0ee8cc6SDag-Erling Smørgrav break; 4764a0ee8cc6SDag-Erling Smørgrav } 4765333ee039SDag-Erling Smørgrav } 47664f52dfbbSDag-Erling Smørgrav } 4767333ee039SDag-Erling Smørgrav 4768333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 476919261079SEd Maste logit("Received request from %.100s port %d to connect to " 477019261079SEd Maste "host %.100s port %d, but the request was denied.", 477119261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); 4772d93a896eSDag-Erling Smørgrav if (reason != NULL) 4773d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; 4774d4af9e69SDag-Erling Smørgrav return NULL; 4775ca3176e7SBrian Feldman } 47764f52dfbbSDag-Erling Smørgrav 47774f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 47784f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 47794f52dfbbSDag-Erling Smørgrav &cctx, reason, errmsg); 47804f52dfbbSDag-Erling Smørgrav if (sock == -1) { 47814f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 47824f52dfbbSDag-Erling Smørgrav return NULL; 47834f52dfbbSDag-Erling Smørgrav } 47844f52dfbbSDag-Erling Smørgrav 47854f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 47864f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 47874f52dfbbSDag-Erling Smørgrav c->host_port = port; 47884f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 47894f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 47904f52dfbbSDag-Erling Smørgrav 47914f52dfbbSDag-Erling Smørgrav return c; 4792ca3176e7SBrian Feldman } 4793ca3176e7SBrian Feldman 4794a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 4795a0ee8cc6SDag-Erling Smørgrav Channel * 47964f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path, 47974f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4798a0ee8cc6SDag-Erling Smørgrav { 47994f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4800190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 48014f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4802190cef3dSDag-Erling Smørgrav struct permission *perm; 4803a0ee8cc6SDag-Erling Smørgrav 4804190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4805a0ee8cc6SDag-Erling Smørgrav if (!permit) { 4806190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4807190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4808190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4809a0ee8cc6SDag-Erling Smørgrav permit = 1; 4810a0ee8cc6SDag-Erling Smørgrav break; 4811a0ee8cc6SDag-Erling Smørgrav } 4812a0ee8cc6SDag-Erling Smørgrav } 48134f52dfbbSDag-Erling Smørgrav } 4814a0ee8cc6SDag-Erling Smørgrav 4815190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4816a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 4817190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4818190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4819190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4820a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 4821a0ee8cc6SDag-Erling Smørgrav break; 4822a0ee8cc6SDag-Erling Smørgrav } 4823a0ee8cc6SDag-Erling Smørgrav } 48244f52dfbbSDag-Erling Smørgrav } 4825a0ee8cc6SDag-Erling Smørgrav 4826a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 4827a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 4828a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 4829a0ee8cc6SDag-Erling Smørgrav return NULL; 4830a0ee8cc6SDag-Erling Smørgrav } 48314f52dfbbSDag-Erling Smørgrav return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); 4832a0ee8cc6SDag-Erling Smørgrav } 4833a0ee8cc6SDag-Erling Smørgrav 483421e764dfSDag-Erling Smørgrav void 48354f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh) 483621e764dfSDag-Erling Smørgrav { 48374f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 483821e764dfSDag-Erling Smørgrav struct winsize ws; 48394f52dfbbSDag-Erling Smørgrav int r; 48404f52dfbbSDag-Erling Smørgrav u_int i; 484121e764dfSDag-Erling Smørgrav 48424f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 48434f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || 48444f52dfbbSDag-Erling Smørgrav sc->channels[i]->type != SSH_CHANNEL_OPEN) 484521e764dfSDag-Erling Smørgrav continue; 484619261079SEd Maste if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) 484721e764dfSDag-Erling Smørgrav continue; 48484f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, i, "window-change", 0); 48494f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 48504f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 48514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 48524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || 48534f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 485419261079SEd Maste fatal_fr(r, "channel %u; send window-change", i); 485521e764dfSDag-Erling Smørgrav } 485621e764dfSDag-Erling Smørgrav } 485721e764dfSDag-Erling Smørgrav 48584f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ 48594f52dfbbSDag-Erling Smørgrav static Channel * 48604f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) 48614f52dfbbSDag-Erling Smørgrav { 48624f52dfbbSDag-Erling Smørgrav Channel *c; 48634f52dfbbSDag-Erling Smørgrav int r; 48644f52dfbbSDag-Erling Smørgrav 48654f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, 48664f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 48674f52dfbbSDag-Erling Smørgrav c->host_port = 0; 48684f52dfbbSDag-Erling Smørgrav c->path = NULL; 48694f52dfbbSDag-Erling Smørgrav 48704f52dfbbSDag-Erling Smørgrav /* 48714f52dfbbSDag-Erling Smørgrav * We need to open the channel before we have a FD, 48724f52dfbbSDag-Erling Smørgrav * so that we can get SOCKS header from peer. 48734f52dfbbSDag-Erling Smørgrav */ 48744f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 48754f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 48764f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 48774f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 487819261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) 487919261079SEd Maste fatal_fr(r, "channel %i; confirm", c->self); 48804f52dfbbSDag-Erling Smørgrav return c; 48814f52dfbbSDag-Erling Smørgrav } 48824f52dfbbSDag-Erling Smørgrav 48834f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */ 48844f52dfbbSDag-Erling Smørgrav static int 48854f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c) 48864f52dfbbSDag-Erling Smørgrav { 488719261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 488819261079SEd Maste struct permission_set *pset = &sc->local_perms; 488919261079SEd Maste struct permission *perm; 48904f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 489119261079SEd Maste u_int i, permit_adm = 1; 48924f52dfbbSDag-Erling Smørgrav int sock; 48934f52dfbbSDag-Erling Smørgrav 489419261079SEd Maste if (pset->num_permitted_admin > 0) { 489519261079SEd Maste permit_adm = 0; 489619261079SEd Maste for (i = 0; i < pset->num_permitted_admin; i++) { 489719261079SEd Maste perm = &pset->permitted_admin[i]; 489819261079SEd Maste if (open_match(perm, c->path, c->host_port)) { 489919261079SEd Maste permit_adm = 1; 490019261079SEd Maste break; 490119261079SEd Maste } 490219261079SEd Maste } 490319261079SEd Maste } 490419261079SEd Maste if (!permit_adm) { 490519261079SEd Maste debug_f("requested forward not permitted"); 490619261079SEd Maste return -1; 490719261079SEd Maste } 490819261079SEd Maste 49094f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 49104f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, 49114f52dfbbSDag-Erling Smørgrav NULL, &cctx, NULL, NULL); 49124f52dfbbSDag-Erling Smørgrav if (sock == -1) 49134f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 49144f52dfbbSDag-Erling Smørgrav else { 49154f52dfbbSDag-Erling Smørgrav /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ 49164f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_RDYNAMIC_FINISH; 49174f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 49184f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); 49194f52dfbbSDag-Erling Smørgrav } 49204f52dfbbSDag-Erling Smørgrav return sock; 49214f52dfbbSDag-Erling Smørgrav } 49224f52dfbbSDag-Erling Smørgrav 4923af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 4924511b41d2SMark Murray 4925511b41d2SMark Murray /* 4926511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 4927a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 4928a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 4929511b41d2SMark Murray */ 4930af12a3e7SDag-Erling Smørgrav int 49314f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset, 49324f52dfbbSDag-Erling Smørgrav int x11_use_localhost, int single_connection, 49334f52dfbbSDag-Erling Smørgrav u_int *display_numberp, int **chanids) 4934511b41d2SMark Murray { 4935af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 4936511b41d2SMark Murray int display_number, sock; 4937511b41d2SMark Murray u_short port; 4938511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4939511b41d2SMark Murray char strport[NI_MAXSERV]; 4940511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 4941511b41d2SMark Murray 4942b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 4943b74df5b2SDag-Erling Smørgrav return -1; 4944b74df5b2SDag-Erling Smørgrav 4945511b41d2SMark Murray for (display_number = x11_display_offset; 4946511b41d2SMark Murray display_number < MAX_DISPLAYS; 4947511b41d2SMark Murray display_number++) { 4948511b41d2SMark Murray port = 6000 + display_number; 4949511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 49504f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 4951af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 4952511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4953511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 49544f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(NULL, strport, 49554f52dfbbSDag-Erling Smørgrav &hints, &aitop)) != 0) { 4956d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 4957af12a3e7SDag-Erling Smørgrav return -1; 4958511b41d2SMark Murray } 4959511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 49604f52dfbbSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 49614f52dfbbSDag-Erling Smørgrav ai->ai_family != AF_INET6) 4962511b41d2SMark Murray continue; 4963221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 4964221552e4SDag-Erling Smørgrav ai->ai_protocol); 496519261079SEd Maste if (sock == -1) { 49668ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 49678ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 49688ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 49698ad9b54aSDag-Erling Smørgrav #endif 49708ad9b54aSDag-Erling Smørgrav ) { 4971511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 497221e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 4973af12a3e7SDag-Erling Smørgrav return -1; 4974989dd127SDag-Erling Smørgrav } else { 4975989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 4976989dd127SDag-Erling Smørgrav ai->ai_family); 4977989dd127SDag-Erling Smørgrav continue; 4978511b41d2SMark Murray } 4979989dd127SDag-Erling Smørgrav } 4980b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 4981b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 4982d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 498347dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 498419261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 498519261079SEd Maste debug2_f("bind port %d: %.100s", port, 498619261079SEd Maste strerror(errno)); 4987511b41d2SMark Murray close(sock); 49884f52dfbbSDag-Erling Smørgrav for (n = 0; n < num_socks; n++) 4989511b41d2SMark Murray close(socks[n]); 4990511b41d2SMark Murray num_socks = 0; 4991511b41d2SMark Murray break; 4992511b41d2SMark Murray } 4993511b41d2SMark Murray socks[num_socks++] = sock; 4994511b41d2SMark Murray if (num_socks == NUM_SOCKS) 4995511b41d2SMark Murray break; 4996511b41d2SMark Murray } 4997ca3176e7SBrian Feldman freeaddrinfo(aitop); 4998511b41d2SMark Murray if (num_socks > 0) 4999511b41d2SMark Murray break; 5000511b41d2SMark Murray } 5001511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 5002511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 5003af12a3e7SDag-Erling Smørgrav return -1; 5004511b41d2SMark Murray } 5005511b41d2SMark Murray /* Start listening for connections on the socket. */ 5006511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 5007511b41d2SMark Murray sock = socks[n]; 500819261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 5009511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 5010511b41d2SMark Murray close(sock); 5011af12a3e7SDag-Erling Smørgrav return -1; 5012511b41d2SMark Murray } 5013511b41d2SMark Murray } 5014511b41d2SMark Murray 5015511b41d2SMark Murray /* Allocate a channel for each socket. */ 5016333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 5017511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 5018511b41d2SMark Murray sock = socks[n]; 5019*f374ba41SEd Maste nc = channel_new(ssh, "x11-listener", 5020a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 5021a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 5022221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 5023af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 5024d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 5025511b41d2SMark Murray } 5026d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 5027511b41d2SMark Murray 5028af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 5029a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 50304f52dfbbSDag-Erling Smørgrav return 0; 5031511b41d2SMark Murray } 5032511b41d2SMark Murray 5033af12a3e7SDag-Erling Smørgrav static int 5034cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 5035511b41d2SMark Murray { 5036511b41d2SMark Murray int sock; 5037511b41d2SMark Murray struct sockaddr_un addr; 5038511b41d2SMark Murray 5039511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 504019261079SEd Maste if (sock == -1) 5041511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 5042511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 5043511b41d2SMark Murray addr.sun_family = AF_UNIX; 5044cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 5045511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 5046511b41d2SMark Murray return sock; 5047511b41d2SMark Murray close(sock); 5048511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 5049511b41d2SMark Murray return -1; 5050511b41d2SMark Murray } 5051511b41d2SMark Murray 5052cce7d346SDag-Erling Smørgrav static int 5053cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 5054cce7d346SDag-Erling Smørgrav { 5055cce7d346SDag-Erling Smørgrav char buf[1024]; 5056cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 5057cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 5058cce7d346SDag-Erling Smørgrav } 5059cce7d346SDag-Erling Smørgrav 5060d93a896eSDag-Erling Smørgrav #ifdef __APPLE__ 5061d93a896eSDag-Erling Smørgrav static int 5062d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen) 5063d93a896eSDag-Erling Smørgrav { 5064d93a896eSDag-Erling Smørgrav struct stat sbuf; 5065d93a896eSDag-Erling Smørgrav 5066d93a896eSDag-Erling Smørgrav if (strlcpy(path, display, pathlen) >= pathlen) { 5067d93a896eSDag-Erling Smørgrav error("%s: display path too long", __func__); 5068d93a896eSDag-Erling Smørgrav return 0; 5069d93a896eSDag-Erling Smørgrav } 5070d93a896eSDag-Erling Smørgrav if (display[0] != '/') 5071d93a896eSDag-Erling Smørgrav return 0; 5072d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 5073d93a896eSDag-Erling Smørgrav return 1; 5074d93a896eSDag-Erling Smørgrav } else { 5075d93a896eSDag-Erling Smørgrav char *dot = strrchr(path, '.'); 5076d93a896eSDag-Erling Smørgrav if (dot != NULL) { 5077d93a896eSDag-Erling Smørgrav *dot = '\0'; 5078d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 5079d93a896eSDag-Erling Smørgrav return 1; 5080d93a896eSDag-Erling Smørgrav } 5081d93a896eSDag-Erling Smørgrav } 5082d93a896eSDag-Erling Smørgrav } 5083d93a896eSDag-Erling Smørgrav return 0; 5084d93a896eSDag-Erling Smørgrav } 5085d93a896eSDag-Erling Smørgrav #endif 5086d93a896eSDag-Erling Smørgrav 5087a04a10f8SKris Kennaway int 50884f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh) 5089511b41d2SMark Murray { 5090333ee039SDag-Erling Smørgrav u_int display_number; 5091511b41d2SMark Murray const char *display; 5092a04a10f8SKris Kennaway char buf[1024], *cp; 5093511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 5094511b41d2SMark Murray char strport[NI_MAXSERV]; 5095333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 5096511b41d2SMark Murray 5097511b41d2SMark Murray /* Try to open a socket for the local X server. */ 5098511b41d2SMark Murray display = getenv("DISPLAY"); 5099511b41d2SMark Murray if (!display) { 5100511b41d2SMark Murray error("DISPLAY not set."); 5101a04a10f8SKris Kennaway return -1; 5102511b41d2SMark Murray } 5103511b41d2SMark Murray /* 5104511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 5105511b41d2SMark Murray * connection to the real X server. 5106511b41d2SMark Murray */ 5107511b41d2SMark Murray 5108cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 5109d93a896eSDag-Erling Smørgrav /* Check if display is a path to a socket (as set by launchd). */ 5110d93a896eSDag-Erling Smørgrav { 5111d93a896eSDag-Erling Smørgrav char path[PATH_MAX]; 5112d93a896eSDag-Erling Smørgrav 5113d93a896eSDag-Erling Smørgrav if (is_path_to_xsocket(display, path, sizeof(path))) { 5114d93a896eSDag-Erling Smørgrav debug("x11_connect_display: $DISPLAY is launchd"); 5115d93a896eSDag-Erling Smørgrav 5116d93a896eSDag-Erling Smørgrav /* Create a socket. */ 5117d93a896eSDag-Erling Smørgrav sock = connect_local_xsocket_path(path); 5118cce7d346SDag-Erling Smørgrav if (sock < 0) 5119cce7d346SDag-Erling Smørgrav return -1; 5120cce7d346SDag-Erling Smørgrav 5121cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 5122cce7d346SDag-Erling Smørgrav return sock; 5123cce7d346SDag-Erling Smørgrav } 5124d93a896eSDag-Erling Smørgrav } 5125cce7d346SDag-Erling Smørgrav #endif 5126511b41d2SMark Murray /* 5127511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 5128511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 5129511b41d2SMark Murray */ 5130511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 5131511b41d2SMark Murray display[0] == ':') { 5132511b41d2SMark Murray /* Connect to the unix domain socket. */ 51334f52dfbbSDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", 51344f52dfbbSDag-Erling Smørgrav &display_number) != 1) { 51354f52dfbbSDag-Erling Smørgrav error("Could not parse display number from DISPLAY: " 51364f52dfbbSDag-Erling Smørgrav "%.100s", display); 5137a04a10f8SKris Kennaway return -1; 5138511b41d2SMark Murray } 5139511b41d2SMark Murray /* Create a socket. */ 5140511b41d2SMark Murray sock = connect_local_xsocket(display_number); 5141511b41d2SMark Murray if (sock < 0) 5142a04a10f8SKris Kennaway return -1; 5143511b41d2SMark Murray 5144511b41d2SMark Murray /* OK, we now have a connection to the display. */ 5145a04a10f8SKris Kennaway return sock; 5146511b41d2SMark Murray } 5147511b41d2SMark Murray /* 5148511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 5149511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 5150511b41d2SMark Murray */ 5151af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 5152511b41d2SMark Murray cp = strchr(buf, ':'); 5153511b41d2SMark Murray if (!cp) { 5154511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 5155a04a10f8SKris Kennaway return -1; 5156511b41d2SMark Murray } 5157511b41d2SMark Murray *cp = 0; 51584f52dfbbSDag-Erling Smørgrav /* 51594f52dfbbSDag-Erling Smørgrav * buf now contains the host name. But first we parse the 51604f52dfbbSDag-Erling Smørgrav * display number. 51614f52dfbbSDag-Erling Smørgrav */ 5162333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 5163511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 5164511b41d2SMark Murray display); 5165a04a10f8SKris Kennaway return -1; 5166511b41d2SMark Murray } 5167511b41d2SMark Murray 5168511b41d2SMark Murray /* Look up the host address */ 5169511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 51704f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 5171511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 5172333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 5173511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 5174d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 5175d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 5176a04a10f8SKris Kennaway return -1; 5177511b41d2SMark Murray } 5178511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 5179511b41d2SMark Murray /* Create a socket. */ 5180221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 518119261079SEd Maste if (sock == -1) { 5182221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 5183511b41d2SMark Murray continue; 5184511b41d2SMark Murray } 5185511b41d2SMark Murray /* Connect it to the display. */ 518619261079SEd Maste if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 5187333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 5188a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 5189511b41d2SMark Murray close(sock); 5190511b41d2SMark Murray continue; 5191511b41d2SMark Murray } 5192511b41d2SMark Murray /* Success */ 5193511b41d2SMark Murray break; 5194a04a10f8SKris Kennaway } 5195511b41d2SMark Murray freeaddrinfo(aitop); 5196511b41d2SMark Murray if (!ai) { 51974f52dfbbSDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 51984f52dfbbSDag-Erling Smørgrav 6000 + display_number, strerror(errno)); 5199a04a10f8SKris Kennaway return -1; 5200511b41d2SMark Murray } 5201af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 5202a04a10f8SKris Kennaway return sock; 5203a04a10f8SKris Kennaway } 5204511b41d2SMark Murray 5205a04a10f8SKris Kennaway /* 5206511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 5207511b41d2SMark Murray * data, and enables authentication spoofing. 5208af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 5209511b41d2SMark Murray */ 5210511b41d2SMark Murray void 52114f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, 52124f52dfbbSDag-Erling Smørgrav const char *disp, const char *proto, const char *data, int want_reply) 5213511b41d2SMark Murray { 52144f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 5215ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 5216d4ecd108SDag-Erling Smørgrav u_int i, value; 5217511b41d2SMark Murray const char *cp; 52184f52dfbbSDag-Erling Smørgrav char *new_data; 52194f52dfbbSDag-Erling Smørgrav int r, screen_number; 5220511b41d2SMark Murray 52214f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_display == NULL) 52224f52dfbbSDag-Erling Smørgrav sc->x11_saved_display = xstrdup(disp); 52234f52dfbbSDag-Erling Smørgrav else if (strcmp(disp, sc->x11_saved_display) != 0) { 5224d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 5225d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 5226d4ecd108SDag-Erling Smørgrav return; 5227d4ecd108SDag-Erling Smørgrav } 5228d4ecd108SDag-Erling Smørgrav 5229d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 5230511b41d2SMark Murray if (cp) 5231511b41d2SMark Murray cp = strchr(cp, '.'); 5232511b41d2SMark Murray if (cp) 5233333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 5234511b41d2SMark Murray else 5235511b41d2SMark Murray screen_number = 0; 5236511b41d2SMark Murray 52374f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_proto == NULL) { 5238511b41d2SMark Murray /* Save protocol name. */ 52394f52dfbbSDag-Erling Smørgrav sc->x11_saved_proto = xstrdup(proto); 5240ca86bcf2SDag-Erling Smørgrav 5241ca86bcf2SDag-Erling Smørgrav /* Extract real authentication data. */ 52424f52dfbbSDag-Erling Smørgrav sc->x11_saved_data = xmalloc(data_len); 5243511b41d2SMark Murray for (i = 0; i < data_len; i++) { 524419261079SEd Maste if (sscanf(data + 2 * i, "%2x", &value) != 1) { 5245d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 5246d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 524719261079SEd Maste } 52484f52dfbbSDag-Erling Smørgrav sc->x11_saved_data[i] = value; 5249511b41d2SMark Murray } 52504f52dfbbSDag-Erling Smørgrav sc->x11_saved_data_len = data_len; 5251ca86bcf2SDag-Erling Smørgrav 5252ca86bcf2SDag-Erling Smørgrav /* Generate fake data of the same length. */ 52534f52dfbbSDag-Erling Smørgrav sc->x11_fake_data = xmalloc(data_len); 52544f52dfbbSDag-Erling Smørgrav arc4random_buf(sc->x11_fake_data, data_len); 52554f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len = data_len; 5256d4ecd108SDag-Erling Smørgrav } 5257511b41d2SMark Murray 5258511b41d2SMark Murray /* Convert the fake data into hex. */ 52594f52dfbbSDag-Erling Smørgrav new_data = tohex(sc->x11_fake_data, data_len); 5260511b41d2SMark Murray 5261511b41d2SMark Murray /* Send the request packet. */ 52624f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, client_session_id, "x11-req", want_reply); 52634f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ 52644f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, proto)) != 0 || 52654f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, new_data)) != 0 || 52664f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, screen_number)) != 0 || 52674f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 52684f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 526919261079SEd Maste fatal_fr(r, "send x11-req"); 5270e4a9863fSDag-Erling Smørgrav free(new_data); 5271511b41d2SMark Murray } 5272