1*9fce8d41SEd Maste /* $OpenBSD: channels.c,v 1.416 2022/04/11 22:52:08 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 1544f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */ 1554f52dfbbSDag-Erling Smørgrav struct ssh_channels { 1564f52dfbbSDag-Erling Smørgrav /* 1574f52dfbbSDag-Erling Smørgrav * Pointer to an array containing all allocated channels. The array 1584f52dfbbSDag-Erling Smørgrav * is dynamically extended as needed. 1594f52dfbbSDag-Erling Smørgrav */ 1604f52dfbbSDag-Erling Smørgrav Channel **channels; 161076ad2f8SDag-Erling Smørgrav 162511b41d2SMark Murray /* 1634f52dfbbSDag-Erling Smørgrav * Size of the channel array. All slots of the array must always be 1644f52dfbbSDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 165511b41d2SMark Murray */ 1664f52dfbbSDag-Erling Smørgrav u_int channels_alloc; 167511b41d2SMark Murray 1684f52dfbbSDag-Erling Smørgrav /* 1691323ec57SEd Maste * 'channel_pre*' are called just before IO to add any bits 1701323ec57SEd Maste * relevant to channels in the c->io_want bitmasks. 1714f52dfbbSDag-Erling Smørgrav * 1724f52dfbbSDag-Erling Smørgrav * 'channel_post*': perform any appropriate operations for 1731323ec57SEd Maste * channels which have c->io_ready events pending. 1744f52dfbbSDag-Erling Smørgrav */ 1754f52dfbbSDag-Erling Smørgrav chan_fn **channel_pre; 1764f52dfbbSDag-Erling Smørgrav chan_fn **channel_post; 1774f52dfbbSDag-Erling Smørgrav 1784f52dfbbSDag-Erling Smørgrav /* -- tcp forwarding */ 179190cef3dSDag-Erling Smørgrav struct permission_set local_perms; 180190cef3dSDag-Erling Smørgrav struct permission_set remote_perms; 181af12a3e7SDag-Erling Smørgrav 182af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 183af12a3e7SDag-Erling Smørgrav 184d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 1854f52dfbbSDag-Erling Smørgrav char *x11_saved_display; 186d4ecd108SDag-Erling Smørgrav 187af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 1884f52dfbbSDag-Erling Smørgrav char *x11_saved_proto; 189af12a3e7SDag-Erling Smørgrav 190af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 1914f52dfbbSDag-Erling Smørgrav char *x11_saved_data; 1924f52dfbbSDag-Erling Smørgrav u_int x11_saved_data_len; 193af12a3e7SDag-Erling Smørgrav 194557f75e5SDag-Erling Smørgrav /* Deadline after which all X11 connections are refused */ 1954f52dfbbSDag-Erling Smørgrav u_int x11_refuse_time; 196557f75e5SDag-Erling Smørgrav 197af12a3e7SDag-Erling Smørgrav /* 1984f52dfbbSDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be 1994f52dfbbSDag-Erling Smørgrav * sending us; we should replace any occurrences of this by the 2004f52dfbbSDag-Erling Smørgrav * real data. 201af12a3e7SDag-Erling Smørgrav */ 2024f52dfbbSDag-Erling Smørgrav u_char *x11_fake_data; 2034f52dfbbSDag-Erling Smørgrav u_int x11_fake_data_len; 204af12a3e7SDag-Erling Smørgrav 205ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 2064f52dfbbSDag-Erling Smørgrav int IPv4or6; 2074f52dfbbSDag-Erling Smørgrav }; 208ca3176e7SBrian Feldman 209af12a3e7SDag-Erling Smørgrav /* helper */ 2104f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); 211ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host); 212ca3176e7SBrian Feldman 213d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 214d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 215d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 2164f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); 2174f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *); 2184f52dfbbSDag-Erling Smørgrav 2194f52dfbbSDag-Erling Smørgrav /* Setup helper */ 2204f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc); 221d4af9e69SDag-Erling Smørgrav 222af12a3e7SDag-Erling Smørgrav /* -- channel core */ 223a04a10f8SKris Kennaway 2244f52dfbbSDag-Erling Smørgrav void 2254f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh) 2264f52dfbbSDag-Erling Smørgrav { 2274f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc; 2284f52dfbbSDag-Erling Smørgrav 22919261079SEd Maste if ((sc = calloc(1, sizeof(*sc))) == NULL) 23019261079SEd Maste fatal_f("allocation failed"); 2314f52dfbbSDag-Erling Smørgrav sc->channels_alloc = 10; 2324f52dfbbSDag-Erling Smørgrav sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); 2334f52dfbbSDag-Erling Smørgrav sc->IPv4or6 = AF_UNSPEC; 2344f52dfbbSDag-Erling Smørgrav channel_handler_init(sc); 2354f52dfbbSDag-Erling Smørgrav 2364f52dfbbSDag-Erling Smørgrav ssh->chanctxt = sc; 2374f52dfbbSDag-Erling Smørgrav } 2384f52dfbbSDag-Erling Smørgrav 239a04a10f8SKris Kennaway Channel * 2404f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id) 241a04a10f8SKris Kennaway { 242a04a10f8SKris Kennaway Channel *c; 243af12a3e7SDag-Erling Smørgrav 2444f52dfbbSDag-Erling Smørgrav if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { 24519261079SEd Maste logit_f("%d: bad id", id); 246a04a10f8SKris Kennaway return NULL; 247a04a10f8SKris Kennaway } 2484f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[id]; 249af12a3e7SDag-Erling Smørgrav if (c == NULL) { 25019261079SEd Maste logit_f("%d: bad id: channel free", id); 251a04a10f8SKris Kennaway return NULL; 252a04a10f8SKris Kennaway } 253a04a10f8SKris Kennaway return c; 254a04a10f8SKris Kennaway } 255a04a10f8SKris Kennaway 256ca86bcf2SDag-Erling Smørgrav Channel * 2574f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id) 258ca86bcf2SDag-Erling Smørgrav { 259ca86bcf2SDag-Erling Smørgrav Channel *c; 260ca86bcf2SDag-Erling Smørgrav u_int i; 261ca86bcf2SDag-Erling Smørgrav 2624f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 2634f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 2644f52dfbbSDag-Erling Smørgrav if (c != NULL && c->have_remote_id && c->remote_id == remote_id) 265ca86bcf2SDag-Erling Smørgrav return c; 266ca86bcf2SDag-Erling Smørgrav } 267ca86bcf2SDag-Erling Smørgrav return NULL; 268ca86bcf2SDag-Erling Smørgrav } 269ca86bcf2SDag-Erling Smørgrav 270a04a10f8SKris Kennaway /* 271b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 272b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 273b74df5b2SDag-Erling Smørgrav */ 274b74df5b2SDag-Erling Smørgrav Channel * 2754f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id) 276b74df5b2SDag-Erling Smørgrav { 277b74df5b2SDag-Erling Smørgrav Channel *c; 278b74df5b2SDag-Erling Smørgrav 2794f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 2804f52dfbbSDag-Erling Smørgrav return NULL; 281b74df5b2SDag-Erling Smørgrav 282b74df5b2SDag-Erling Smørgrav switch (c->type) { 283b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 284b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 285b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 286b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 2874f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 2884f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 289b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 290b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 291e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 292ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 2934f52dfbbSDag-Erling Smørgrav return c; 294b74df5b2SDag-Erling Smørgrav } 295b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 2964f52dfbbSDag-Erling Smørgrav return NULL; 297b74df5b2SDag-Erling Smørgrav } 298b74df5b2SDag-Erling Smørgrav 299b74df5b2SDag-Erling Smørgrav /* 300a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 301a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 302a04a10f8SKris Kennaway */ 303af12a3e7SDag-Erling Smørgrav static void 3044f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, 305d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 306a04a10f8SKris Kennaway { 307b15c8340SDag-Erling Smørgrav if (rfd != -1) 308b15c8340SDag-Erling Smørgrav fcntl(rfd, F_SETFD, FD_CLOEXEC); 309b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 310b15c8340SDag-Erling Smørgrav fcntl(wfd, F_SETFD, FD_CLOEXEC); 311b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 312b15c8340SDag-Erling Smørgrav fcntl(efd, F_SETFD, FD_CLOEXEC); 313a04a10f8SKris Kennaway 314a04a10f8SKris Kennaway c->rfd = rfd; 315a04a10f8SKris Kennaway c->wfd = wfd; 316a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 317a04a10f8SKris Kennaway c->efd = efd; 318a04a10f8SKris Kennaway c->extended_usage = extusage; 3195b9b2fafSBrian Feldman 320d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 321221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 322e4a9863fSDag-Erling Smørgrav #ifdef _AIX 323e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 324d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 325e4a9863fSDag-Erling Smørgrav #endif 326e0fbb1d2SBrian Feldman 3275b9b2fafSBrian Feldman /* enable nonblocking mode */ 32819261079SEd Maste c->restore_block = 0; 32919261079SEd Maste if (nonblock == CHANNEL_NONBLOCK_STDIO) { 33019261079SEd Maste /* 33119261079SEd Maste * Special handling for stdio file descriptors: do not set 33219261079SEd Maste * non-blocking mode if they are TTYs. Otherwise prepare to 33319261079SEd Maste * restore their blocking state on exit to avoid interfering 33419261079SEd Maste * with other programs that follow. 33519261079SEd Maste */ 33619261079SEd Maste if (rfd != -1 && !isatty(rfd) && fcntl(rfd, F_GETFL) == 0) { 33719261079SEd Maste c->restore_block |= CHANNEL_RESTORE_RFD; 33819261079SEd Maste set_nonblock(rfd); 33919261079SEd Maste } 34019261079SEd Maste if (wfd != -1 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) { 34119261079SEd Maste c->restore_block |= CHANNEL_RESTORE_WFD; 34219261079SEd Maste set_nonblock(wfd); 34319261079SEd Maste } 34419261079SEd Maste if (efd != -1 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) { 34519261079SEd Maste c->restore_block |= CHANNEL_RESTORE_EFD; 34619261079SEd Maste set_nonblock(efd); 34719261079SEd Maste } 34819261079SEd Maste } else if (nonblock) { 349a04a10f8SKris Kennaway if (rfd != -1) 350a04a10f8SKris Kennaway set_nonblock(rfd); 351a04a10f8SKris Kennaway if (wfd != -1) 352a04a10f8SKris Kennaway set_nonblock(wfd); 353a04a10f8SKris Kennaway if (efd != -1) 354a04a10f8SKris Kennaway set_nonblock(efd); 355a04a10f8SKris Kennaway } 3565b9b2fafSBrian Feldman } 357a04a10f8SKris Kennaway 358511b41d2SMark Murray /* 359511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 360511b41d2SMark Murray * remote_name to be freed. 361511b41d2SMark Murray */ 362af12a3e7SDag-Erling Smørgrav Channel * 3634f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, 364a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 365511b41d2SMark Murray { 3664f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3674f52dfbbSDag-Erling Smørgrav u_int i, found; 368511b41d2SMark Murray Channel *c; 36919261079SEd Maste int r; 370511b41d2SMark Murray 371511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 3724f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 3734f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL) { 374511b41d2SMark Murray /* Found a free slot. */ 3754f52dfbbSDag-Erling Smørgrav found = i; 376511b41d2SMark Murray break; 377511b41d2SMark Murray } 3784f52dfbbSDag-Erling Smørgrav } 3794f52dfbbSDag-Erling Smørgrav if (i >= sc->channels_alloc) { 3804f52dfbbSDag-Erling Smørgrav /* 3814f52dfbbSDag-Erling Smørgrav * There are no free slots. Take last+1 slot and expand 3824f52dfbbSDag-Erling Smørgrav * the array. 3834f52dfbbSDag-Erling Smørgrav */ 3844f52dfbbSDag-Erling Smørgrav found = sc->channels_alloc; 3854f52dfbbSDag-Erling Smørgrav if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) 38619261079SEd Maste fatal_f("internal error: channels_alloc %d too big", 38719261079SEd Maste sc->channels_alloc); 3884f52dfbbSDag-Erling Smørgrav sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, 3894f52dfbbSDag-Erling Smørgrav sc->channels_alloc + 10, sizeof(*sc->channels)); 3904f52dfbbSDag-Erling Smørgrav sc->channels_alloc += 10; 3914f52dfbbSDag-Erling Smørgrav debug2("channel: expanding %d", sc->channels_alloc); 392511b41d2SMark Murray } 393af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 3944f52dfbbSDag-Erling Smørgrav c = sc->channels[found] = xcalloc(1, sizeof(Channel)); 3954f52dfbbSDag-Erling Smørgrav if ((c->input = sshbuf_new()) == NULL || 3964f52dfbbSDag-Erling Smørgrav (c->output = sshbuf_new()) == NULL || 3974f52dfbbSDag-Erling Smørgrav (c->extended = sshbuf_new()) == NULL) 39819261079SEd Maste fatal_f("sshbuf_new failed"); 39919261079SEd Maste if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) 40019261079SEd Maste fatal_fr(r, "sshbuf_set_max_size"); 401af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 402af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 4034f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); 404511b41d2SMark Murray c->self = found; 405511b41d2SMark Murray c->type = type; 406a04a10f8SKris Kennaway c->ctype = ctype; 407a04a10f8SKris Kennaway c->local_window = window; 408a04a10f8SKris Kennaway c->local_window_max = window; 409a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 410221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 411b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 412b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 413d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 414511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 415af12a3e7SDag-Erling Smørgrav return c; 416a04a10f8SKris Kennaway } 417511b41d2SMark Murray 418af12a3e7SDag-Erling Smørgrav int 41919261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) 420af12a3e7SDag-Erling Smørgrav { 42119261079SEd Maste int ret, fd = *fdp; 422af12a3e7SDag-Erling Smørgrav 42319261079SEd Maste if (fd == -1) 42419261079SEd Maste return 0; 42519261079SEd Maste 42619261079SEd Maste if ((*fdp == c->rfd && (c->restore_block & CHANNEL_RESTORE_RFD) != 0) || 42719261079SEd Maste (*fdp == c->wfd && (c->restore_block & CHANNEL_RESTORE_WFD) != 0) || 42819261079SEd Maste (*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0)) 42919261079SEd Maste (void)fcntl(*fdp, F_SETFL, 0); /* restore blocking */ 43019261079SEd Maste 4311323ec57SEd Maste if (*fdp == c->rfd) { 4321323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_RFD; 4331323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_RFD; 4341323ec57SEd Maste c->rfd = -1; 43587c1498dSEd Maste c->pfds[0] = -1; 4361323ec57SEd Maste } 4371323ec57SEd Maste if (*fdp == c->wfd) { 4381323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_WFD; 4391323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_WFD; 4401323ec57SEd Maste c->wfd = -1; 44187c1498dSEd Maste c->pfds[1] = -1; 4421323ec57SEd Maste } 4431323ec57SEd Maste if (*fdp == c->efd) { 4441323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_EFD; 4451323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_EFD; 4461323ec57SEd Maste c->efd = -1; 44787c1498dSEd Maste c->pfds[2] = -1; 4481323ec57SEd Maste } 4491323ec57SEd Maste if (*fdp == c->sock) { 4501323ec57SEd Maste c->io_want &= ~SSH_CHAN_IO_SOCK; 4511323ec57SEd Maste c->io_ready &= ~SSH_CHAN_IO_SOCK; 4521323ec57SEd Maste c->sock = -1; 45387c1498dSEd Maste c->pfds[3] = -1; 4541323ec57SEd Maste } 4551323ec57SEd Maste 456af12a3e7SDag-Erling Smørgrav ret = close(fd); 4571323ec57SEd Maste *fdp = -1; /* probably redundant */ 458af12a3e7SDag-Erling Smørgrav return ret; 459af12a3e7SDag-Erling Smørgrav } 460a04a10f8SKris Kennaway 461a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 462af12a3e7SDag-Erling Smørgrav static void 4634f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c) 464511b41d2SMark Murray { 46547dd1d1bSDag-Erling Smørgrav int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; 46647dd1d1bSDag-Erling Smørgrav 46719261079SEd Maste channel_close_fd(ssh, c, &c->sock); 46847dd1d1bSDag-Erling Smørgrav if (rfd != sock) 46919261079SEd Maste channel_close_fd(ssh, c, &c->rfd); 47047dd1d1bSDag-Erling Smørgrav if (wfd != sock && wfd != rfd) 47119261079SEd Maste channel_close_fd(ssh, c, &c->wfd); 47247dd1d1bSDag-Erling Smørgrav if (efd != sock && efd != rfd && efd != wfd) 47319261079SEd Maste channel_close_fd(ssh, c, &c->efd); 4744f52dfbbSDag-Erling Smørgrav } 4754f52dfbbSDag-Erling Smørgrav 4764f52dfbbSDag-Erling Smørgrav static void 477190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm) 4784f52dfbbSDag-Erling Smørgrav { 479190cef3dSDag-Erling Smørgrav free(perm->host_to_connect); 480190cef3dSDag-Erling Smørgrav free(perm->listen_host); 481190cef3dSDag-Erling Smørgrav free(perm->listen_path); 48219261079SEd Maste memset(perm, 0, sizeof(*perm)); 4834f52dfbbSDag-Erling Smørgrav } 4844f52dfbbSDag-Erling Smørgrav 485190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */ 486190cef3dSDag-Erling Smørgrav static const char * 487190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where) 488190cef3dSDag-Erling Smørgrav { 489190cef3dSDag-Erling Smørgrav if (who == FORWARD_ADM) { 490190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 491190cef3dSDag-Erling Smørgrav return "admin local"; 492190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 493190cef3dSDag-Erling Smørgrav return "admin remote"; 494190cef3dSDag-Erling Smørgrav } else if (who == FORWARD_USER) { 495190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 496190cef3dSDag-Erling Smørgrav return "user local"; 497190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 498190cef3dSDag-Erling Smørgrav return "user remote"; 499190cef3dSDag-Erling Smørgrav } 500190cef3dSDag-Erling Smørgrav fatal("Unknown forward permission list %d/%d", who, where); 501190cef3dSDag-Erling Smørgrav } 5024f52dfbbSDag-Erling Smørgrav 503190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */ 504190cef3dSDag-Erling Smørgrav static struct permission_set * 505190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where) 506190cef3dSDag-Erling Smørgrav { 507190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 508190cef3dSDag-Erling Smørgrav 509190cef3dSDag-Erling Smørgrav switch (where) { 510190cef3dSDag-Erling Smørgrav case FORWARD_LOCAL: 511190cef3dSDag-Erling Smørgrav return &sc->local_perms; 512190cef3dSDag-Erling Smørgrav break; 513190cef3dSDag-Erling Smørgrav case FORWARD_REMOTE: 514190cef3dSDag-Erling Smørgrav return &sc->remote_perms; 515190cef3dSDag-Erling Smørgrav break; 516190cef3dSDag-Erling Smørgrav default: 51719261079SEd Maste fatal_f("invalid forwarding direction %d", where); 518190cef3dSDag-Erling Smørgrav } 519190cef3dSDag-Erling Smørgrav } 520190cef3dSDag-Erling Smørgrav 52119261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */ 522190cef3dSDag-Erling Smørgrav static void 523190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where, 524190cef3dSDag-Erling Smørgrav struct permission ***permpp, u_int **npermpp) 525190cef3dSDag-Erling Smørgrav { 526190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 527190cef3dSDag-Erling Smørgrav 528190cef3dSDag-Erling Smørgrav switch (who) { 529190cef3dSDag-Erling Smørgrav case FORWARD_USER: 530190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_user; 531190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_user; 532190cef3dSDag-Erling Smørgrav break; 533190cef3dSDag-Erling Smørgrav case FORWARD_ADM: 534190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_admin; 535190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_admin; 536190cef3dSDag-Erling Smørgrav break; 537190cef3dSDag-Erling Smørgrav default: 53819261079SEd Maste fatal_f("invalid forwarding client %d", who); 539190cef3dSDag-Erling Smørgrav } 540190cef3dSDag-Erling Smørgrav } 541190cef3dSDag-Erling Smørgrav 5421323ec57SEd Maste /* Adds an entry to the specified forwarding list */ 5434f52dfbbSDag-Erling Smørgrav static int 544190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where, 5454f52dfbbSDag-Erling Smørgrav const char *host_to_connect, int port_to_connect, 5464f52dfbbSDag-Erling Smørgrav const char *listen_host, const char *listen_path, int listen_port, 5474f52dfbbSDag-Erling Smørgrav Channel *downstream) 5484f52dfbbSDag-Erling Smørgrav { 549190cef3dSDag-Erling Smørgrav struct permission **permp; 550190cef3dSDag-Erling Smørgrav u_int n, *npermp; 5514f52dfbbSDag-Erling Smørgrav 552190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 5534f52dfbbSDag-Erling Smørgrav 554190cef3dSDag-Erling Smørgrav if (*npermp >= INT_MAX) 55519261079SEd Maste fatal_f("%s overflow", fwd_ident(who, where)); 5564f52dfbbSDag-Erling Smørgrav 557190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); 558190cef3dSDag-Erling Smørgrav n = (*npermp)++; 5594f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) 560190cef3dSDag-Erling Smørgrav (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); 561190cef3dSDag-Erling Smørgrav (*permp)[n].port_to_connect = port_to_connect; 562190cef3dSDag-Erling Smørgrav (*permp)[n].listen_host = MAYBE_DUP(listen_host); 563190cef3dSDag-Erling Smørgrav (*permp)[n].listen_path = MAYBE_DUP(listen_path); 564190cef3dSDag-Erling Smørgrav (*permp)[n].listen_port = listen_port; 565190cef3dSDag-Erling Smørgrav (*permp)[n].downstream = downstream; 5664f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP 5674f52dfbbSDag-Erling Smørgrav return (int)n; 5684f52dfbbSDag-Erling Smørgrav } 5694f52dfbbSDag-Erling Smørgrav 5704f52dfbbSDag-Erling Smørgrav static void 5714f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) 5724f52dfbbSDag-Erling Smørgrav { 5734f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 574190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 575190cef3dSDag-Erling Smørgrav struct permission *perm; 5764f52dfbbSDag-Erling Smørgrav int r; 5774f52dfbbSDag-Erling Smørgrav u_int i; 5784f52dfbbSDag-Erling Smørgrav 579190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 580190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 581190cef3dSDag-Erling Smørgrav if (perm->downstream != c) 5824f52dfbbSDag-Erling Smørgrav continue; 5834f52dfbbSDag-Erling Smørgrav 5844f52dfbbSDag-Erling Smørgrav /* cancel on the server, since mux client is gone */ 5854f52dfbbSDag-Erling Smørgrav debug("channel %d: cleanup remote forward for %s:%u", 586190cef3dSDag-Erling Smørgrav c->self, perm->listen_host, perm->listen_port); 5874f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 5884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 5894f52dfbbSDag-Erling Smørgrav "cancel-tcpip-forward")) != 0 || 5904f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || 5914f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 592190cef3dSDag-Erling Smørgrav channel_rfwd_bind_host(perm->listen_host))) != 0 || 593190cef3dSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || 5944f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 59519261079SEd Maste fatal_fr(r, "channel %i", c->self); 5964f52dfbbSDag-Erling Smørgrav } 597190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 5984f52dfbbSDag-Erling Smørgrav } 599a04a10f8SKris Kennaway } 600511b41d2SMark Murray 601a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 602a04a10f8SKris Kennaway void 6034f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c) 604a04a10f8SKris Kennaway { 6054f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 606af12a3e7SDag-Erling Smørgrav char *s; 60721e764dfSDag-Erling Smørgrav u_int i, n; 608ca86bcf2SDag-Erling Smørgrav Channel *other; 609d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 610ca3176e7SBrian Feldman 6114f52dfbbSDag-Erling Smørgrav for (n = 0, i = 0; i < sc->channels_alloc; i++) { 6124f52dfbbSDag-Erling Smørgrav if ((other = sc->channels[i]) == NULL) 6134f52dfbbSDag-Erling Smørgrav continue; 614af12a3e7SDag-Erling Smørgrav n++; 615ca86bcf2SDag-Erling Smørgrav /* detach from mux client and prepare for closing */ 616ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT && 617ca86bcf2SDag-Erling Smørgrav other->type == SSH_CHANNEL_MUX_PROXY && 618ca86bcf2SDag-Erling Smørgrav other->mux_ctx == c) { 619ca86bcf2SDag-Erling Smørgrav other->mux_ctx = NULL; 620ca86bcf2SDag-Erling Smørgrav other->type = SSH_CHANNEL_OPEN; 621ca86bcf2SDag-Erling Smørgrav other->istate = CHAN_INPUT_CLOSED; 622ca86bcf2SDag-Erling Smørgrav other->ostate = CHAN_OUTPUT_CLOSED; 623ca86bcf2SDag-Erling Smørgrav } 624ca86bcf2SDag-Erling Smørgrav } 62521e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 626af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 627af12a3e7SDag-Erling Smørgrav 628e9e8876aSEd Maste if (c->type == SSH_CHANNEL_MUX_CLIENT) { 6294f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(ssh, c); 630e9e8876aSEd Maste free(c->mux_ctx); 631e9e8876aSEd Maste c->mux_ctx = NULL; 632e9e8876aSEd Maste } else if (c->type == SSH_CHANNEL_MUX_LISTENER) { 63319261079SEd Maste free(c->mux_ctx); 63419261079SEd Maste c->mux_ctx = NULL; 63519261079SEd Maste } 636ca86bcf2SDag-Erling Smørgrav 637190cef3dSDag-Erling Smørgrav if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { 6384f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 639221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 640e4a9863fSDag-Erling Smørgrav free(s); 641190cef3dSDag-Erling Smørgrav } 642ca3176e7SBrian Feldman 6434f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, c); 6444f52dfbbSDag-Erling Smørgrav sshbuf_free(c->input); 6454f52dfbbSDag-Erling Smørgrav sshbuf_free(c->output); 6464f52dfbbSDag-Erling Smørgrav sshbuf_free(c->extended); 6474f52dfbbSDag-Erling Smørgrav c->input = c->output = c->extended = NULL; 648e4a9863fSDag-Erling Smørgrav free(c->remote_name); 649a04a10f8SKris Kennaway c->remote_name = NULL; 650e4a9863fSDag-Erling Smørgrav free(c->path); 651cce7d346SDag-Erling Smørgrav c->path = NULL; 652e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 653462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 654d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 655d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 6564f52dfbbSDag-Erling Smørgrav cc->abandon_cb(ssh, c, cc->ctx); 657d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 65819261079SEd Maste freezero(cc, sizeof(*cc)); 659d4af9e69SDag-Erling Smørgrav } 660d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 6614f52dfbbSDag-Erling Smørgrav c->filter_cleanup(ssh, c->self, c->filter_ctx); 6624f52dfbbSDag-Erling Smørgrav sc->channels[c->self] = NULL; 66319261079SEd Maste freezero(c, sizeof(*c)); 664af12a3e7SDag-Erling Smørgrav } 665af12a3e7SDag-Erling Smørgrav 666af12a3e7SDag-Erling Smørgrav void 6674f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh) 668af12a3e7SDag-Erling Smørgrav { 66921e764dfSDag-Erling Smørgrav u_int i; 67019261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 671af12a3e7SDag-Erling Smørgrav 67219261079SEd Maste for (i = 0; i < sc->channels_alloc; i++) 67319261079SEd Maste if (sc->channels[i] != NULL) 67419261079SEd Maste channel_free(ssh, sc->channels[i]); 67519261079SEd Maste 67619261079SEd Maste free(sc->channels); 67719261079SEd Maste sc->channels = NULL; 67819261079SEd Maste sc->channels_alloc = 0; 67919261079SEd Maste 68019261079SEd Maste free(sc->x11_saved_display); 68119261079SEd Maste sc->x11_saved_display = NULL; 68219261079SEd Maste 68319261079SEd Maste free(sc->x11_saved_proto); 68419261079SEd Maste sc->x11_saved_proto = NULL; 68519261079SEd Maste 68619261079SEd Maste free(sc->x11_saved_data); 68719261079SEd Maste sc->x11_saved_data = NULL; 68819261079SEd Maste sc->x11_saved_data_len = 0; 68919261079SEd Maste 69019261079SEd Maste free(sc->x11_fake_data); 69119261079SEd Maste sc->x11_fake_data = NULL; 69219261079SEd Maste sc->x11_fake_data_len = 0; 693af12a3e7SDag-Erling Smørgrav } 694af12a3e7SDag-Erling Smørgrav 695af12a3e7SDag-Erling Smørgrav /* 696af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 697af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 698af12a3e7SDag-Erling Smørgrav */ 699af12a3e7SDag-Erling Smørgrav void 7004f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh) 701af12a3e7SDag-Erling Smørgrav { 70221e764dfSDag-Erling Smørgrav u_int i; 703af12a3e7SDag-Erling Smørgrav 7044f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) 7054f52dfbbSDag-Erling Smørgrav if (ssh->chanctxt->channels[i] != NULL) 7064f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, ssh->chanctxt->channels[i]); 707af12a3e7SDag-Erling Smørgrav } 708af12a3e7SDag-Erling Smørgrav 709af12a3e7SDag-Erling Smørgrav /* 710af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 711af12a3e7SDag-Erling Smørgrav */ 712af12a3e7SDag-Erling Smørgrav void 7134f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh) 714af12a3e7SDag-Erling Smørgrav { 71521e764dfSDag-Erling Smørgrav u_int i; 716af12a3e7SDag-Erling Smørgrav Channel *c; 717af12a3e7SDag-Erling Smørgrav 7184f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 7194f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 720af12a3e7SDag-Erling Smørgrav if (c != NULL) { 721af12a3e7SDag-Erling Smørgrav switch (c->type) { 722af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 723af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 724af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 725af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 726a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 727a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 72819261079SEd Maste channel_close_fd(ssh, c, &c->sock); 7294f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 730af12a3e7SDag-Erling Smørgrav break; 731af12a3e7SDag-Erling Smørgrav } 732af12a3e7SDag-Erling Smørgrav } 733af12a3e7SDag-Erling Smørgrav } 734af12a3e7SDag-Erling Smørgrav } 735af12a3e7SDag-Erling Smørgrav 736af12a3e7SDag-Erling Smørgrav /* 737af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 738af12a3e7SDag-Erling Smørgrav * more channel is overfull. 739af12a3e7SDag-Erling Smørgrav */ 740af12a3e7SDag-Erling Smørgrav int 7414f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh) 742af12a3e7SDag-Erling Smørgrav { 743af12a3e7SDag-Erling Smørgrav u_int i; 7444f52dfbbSDag-Erling Smørgrav u_int maxsize = ssh_packet_get_maxsize(ssh); 745af12a3e7SDag-Erling Smørgrav Channel *c; 746af12a3e7SDag-Erling Smørgrav 7474f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 7484f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 7494f52dfbbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_OPEN) 7504f52dfbbSDag-Erling Smørgrav continue; 7514f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > maxsize) { 7524f52dfbbSDag-Erling Smørgrav debug2("channel %d: big output buffer %zu > %u", 7534f52dfbbSDag-Erling Smørgrav c->self, sshbuf_len(c->output), maxsize); 754af12a3e7SDag-Erling Smørgrav return 0; 755af12a3e7SDag-Erling Smørgrav } 756af12a3e7SDag-Erling Smørgrav } 757af12a3e7SDag-Erling Smørgrav return 1; 758af12a3e7SDag-Erling Smørgrav } 759af12a3e7SDag-Erling Smørgrav 760af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 761af12a3e7SDag-Erling Smørgrav int 7624f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh) 763af12a3e7SDag-Erling Smørgrav { 76421e764dfSDag-Erling Smørgrav u_int i; 765af12a3e7SDag-Erling Smørgrav Channel *c; 766af12a3e7SDag-Erling Smørgrav 7674f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 7684f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 769af12a3e7SDag-Erling Smørgrav if (c == NULL) 770af12a3e7SDag-Erling Smørgrav continue; 771af12a3e7SDag-Erling Smørgrav switch (c->type) { 772af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 773af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 774af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 775b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 776af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 777af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 778af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 7794f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 780af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 781af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 782e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 783a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 784a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 785af12a3e7SDag-Erling Smørgrav continue; 786af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 787af12a3e7SDag-Erling Smørgrav continue; 788af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 789af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 7904f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 791af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 792b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 793ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 794af12a3e7SDag-Erling Smørgrav return 1; 795af12a3e7SDag-Erling Smørgrav default: 79619261079SEd Maste fatal_f("bad channel type %d", c->type); 797af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 798af12a3e7SDag-Erling Smørgrav } 799af12a3e7SDag-Erling Smørgrav } 800af12a3e7SDag-Erling Smørgrav return 0; 801af12a3e7SDag-Erling Smørgrav } 802af12a3e7SDag-Erling Smørgrav 803af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 804af12a3e7SDag-Erling Smørgrav int 8054f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh) 806af12a3e7SDag-Erling Smørgrav { 80721e764dfSDag-Erling Smørgrav u_int i; 808af12a3e7SDag-Erling Smørgrav Channel *c; 809af12a3e7SDag-Erling Smørgrav 8104f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8114f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 8124f52dfbbSDag-Erling Smørgrav if (c == NULL || !c->have_remote_id) 813af12a3e7SDag-Erling Smørgrav continue; 814af12a3e7SDag-Erling Smørgrav switch (c->type) { 815af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 816af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 8174f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 8184f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 819af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 820af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 821af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 822b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 823b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 824ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 825af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 826af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 827af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 828e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 829a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 830a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 831af12a3e7SDag-Erling Smørgrav continue; 832af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 833af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 834af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 835af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 836af12a3e7SDag-Erling Smørgrav return i; 837af12a3e7SDag-Erling Smørgrav default: 83819261079SEd Maste fatal_f("bad channel type %d", c->type); 839af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 840af12a3e7SDag-Erling Smørgrav } 841af12a3e7SDag-Erling Smørgrav } 842af12a3e7SDag-Erling Smørgrav return -1; 843af12a3e7SDag-Erling Smørgrav } 844af12a3e7SDag-Erling Smørgrav 8452f513db7SEd Maste /* Returns the state of the channel's extended usage flag */ 8462f513db7SEd Maste const char * 8472f513db7SEd Maste channel_format_extended_usage(const Channel *c) 8482f513db7SEd Maste { 8492f513db7SEd Maste if (c->efd == -1) 8502f513db7SEd Maste return "closed"; 8512f513db7SEd Maste 8522f513db7SEd Maste switch (c->extended_usage) { 8532f513db7SEd Maste case CHAN_EXTENDED_WRITE: 8542f513db7SEd Maste return "write"; 8552f513db7SEd Maste case CHAN_EXTENDED_READ: 8562f513db7SEd Maste return "read"; 8572f513db7SEd Maste case CHAN_EXTENDED_IGNORE: 8582f513db7SEd Maste return "ignore"; 8592f513db7SEd Maste default: 8602f513db7SEd Maste return "UNKNOWN"; 8612f513db7SEd Maste } 8622f513db7SEd Maste } 8632f513db7SEd Maste 8642f513db7SEd Maste static char * 8652f513db7SEd Maste channel_format_status(const Channel *c) 8662f513db7SEd Maste { 8672f513db7SEd Maste char *ret = NULL; 8682f513db7SEd Maste 8692f513db7SEd Maste xasprintf(&ret, "t%d %s%u i%u/%zu o%u/%zu e[%s]/%zu " 8701323ec57SEd Maste "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", 8712f513db7SEd Maste c->type, 8722f513db7SEd Maste c->have_remote_id ? "r" : "nr", c->remote_id, 8732f513db7SEd Maste c->istate, sshbuf_len(c->input), 8742f513db7SEd Maste c->ostate, sshbuf_len(c->output), 8752f513db7SEd Maste channel_format_extended_usage(c), sshbuf_len(c->extended), 8761323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, 8771323ec57SEd Maste c->io_want, c->io_ready); 8782f513db7SEd Maste return ret; 8792f513db7SEd Maste } 8802f513db7SEd Maste 881af12a3e7SDag-Erling Smørgrav /* 882af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 883af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 884af12a3e7SDag-Erling Smørgrav * newlines. 885af12a3e7SDag-Erling Smørgrav */ 886af12a3e7SDag-Erling Smørgrav char * 8874f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh) 888af12a3e7SDag-Erling Smørgrav { 8894f52dfbbSDag-Erling Smørgrav struct sshbuf *buf; 890af12a3e7SDag-Erling Smørgrav Channel *c; 89121e764dfSDag-Erling Smørgrav u_int i; 8924f52dfbbSDag-Erling Smørgrav int r; 8932f513db7SEd Maste char *cp, *ret; 894af12a3e7SDag-Erling Smørgrav 8954f52dfbbSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 89619261079SEd Maste fatal_f("sshbuf_new"); 8974f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(buf, 8984f52dfbbSDag-Erling Smørgrav "The following connections are open:\r\n")) != 0) 89919261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9004f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 9014f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 902af12a3e7SDag-Erling Smørgrav if (c == NULL) 903af12a3e7SDag-Erling Smørgrav continue; 904af12a3e7SDag-Erling Smørgrav switch (c->type) { 905af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 906af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 907af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 908af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 909af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 910af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 911e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 912b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 913a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 914a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 915af12a3e7SDag-Erling Smørgrav continue; 916af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 917af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 918af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 919af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 9204f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 9214f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 922af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 923af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 924ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 925ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 9262f513db7SEd Maste cp = channel_format_status(c); 9272f513db7SEd Maste if ((r = sshbuf_putf(buf, " #%d %.300s (%s)\r\n", 9282f513db7SEd Maste c->self, c->remote_name, cp)) != 0) { 9292f513db7SEd Maste free(cp); 93019261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9312f513db7SEd Maste } 9322f513db7SEd Maste free(cp); 933af12a3e7SDag-Erling Smørgrav continue; 934af12a3e7SDag-Erling Smørgrav default: 93519261079SEd Maste fatal_f("bad channel type %d", c->type); 936af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 937af12a3e7SDag-Erling Smørgrav } 938af12a3e7SDag-Erling Smørgrav } 9394f52dfbbSDag-Erling Smørgrav if ((ret = sshbuf_dup_string(buf)) == NULL) 94019261079SEd Maste fatal_f("sshbuf_dup_string"); 9414f52dfbbSDag-Erling Smørgrav sshbuf_free(buf); 9424f52dfbbSDag-Erling Smørgrav return ret; 9434f52dfbbSDag-Erling Smørgrav } 9444f52dfbbSDag-Erling Smørgrav 9454f52dfbbSDag-Erling Smørgrav static void 9464f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) 9474f52dfbbSDag-Erling Smørgrav { 9484f52dfbbSDag-Erling Smørgrav int r; 9494f52dfbbSDag-Erling Smørgrav 9504f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 9514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, type)) != 0 || 9524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 9534f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 9544f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { 95519261079SEd Maste fatal_r(r, "%s: channel %i: open", where, c->self); 9564f52dfbbSDag-Erling Smørgrav } 957af12a3e7SDag-Erling Smørgrav } 958af12a3e7SDag-Erling Smørgrav 959af12a3e7SDag-Erling Smørgrav void 9604f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id) 961af12a3e7SDag-Erling Smørgrav { 9624f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 9634f52dfbbSDag-Erling Smørgrav int r; 964f388f5efSDag-Erling Smørgrav 965af12a3e7SDag-Erling Smørgrav if (c == NULL) { 966221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 967af12a3e7SDag-Erling Smørgrav return; 968af12a3e7SDag-Erling Smørgrav } 969e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 9704f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, c->ctype); 9714f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 97219261079SEd Maste fatal_fr(r, "channel %i", c->self); 973af12a3e7SDag-Erling Smørgrav } 974af12a3e7SDag-Erling Smørgrav 975af12a3e7SDag-Erling Smørgrav void 9764f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) 977af12a3e7SDag-Erling Smørgrav { 9784f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 9794f52dfbbSDag-Erling Smørgrav int r; 980f388f5efSDag-Erling Smørgrav 981af12a3e7SDag-Erling Smørgrav if (c == NULL) { 98219261079SEd Maste logit_f("%d: unknown channel id", id); 983af12a3e7SDag-Erling Smørgrav return; 984af12a3e7SDag-Erling Smørgrav } 9854f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 98619261079SEd Maste fatal_f("channel %d: no remote id", c->self); 9874f52dfbbSDag-Erling Smørgrav 98821e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 9894f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || 9904f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 9914f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, service)) != 0 || 9924f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { 99319261079SEd Maste fatal_fr(r, "channel %i", c->self); 9944f52dfbbSDag-Erling Smørgrav } 995af12a3e7SDag-Erling Smørgrav } 996333ee039SDag-Erling Smørgrav 997af12a3e7SDag-Erling Smørgrav void 9984f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id, 9994f52dfbbSDag-Erling Smørgrav channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) 1000d4af9e69SDag-Erling Smørgrav { 1001d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 1002d4af9e69SDag-Erling Smørgrav Channel *c; 1003d4af9e69SDag-Erling Smørgrav 10044f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 100519261079SEd Maste fatal_f("%d: bad id", id); 1006d4af9e69SDag-Erling Smørgrav 10070a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 1008d4af9e69SDag-Erling Smørgrav cc->cb = cb; 1009d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 1010d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 1011d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 1012d4af9e69SDag-Erling Smørgrav } 1013d4af9e69SDag-Erling Smørgrav 1014d4af9e69SDag-Erling Smørgrav void 10154f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id, 10164f52dfbbSDag-Erling Smørgrav channel_open_fn *fn, void *ctx) 1017af12a3e7SDag-Erling Smørgrav { 10184f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1019f388f5efSDag-Erling Smørgrav 1020af12a3e7SDag-Erling Smørgrav if (c == NULL) { 102119261079SEd Maste logit_f("%d: bad id", id); 1022af12a3e7SDag-Erling Smørgrav return; 1023af12a3e7SDag-Erling Smørgrav } 1024d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 1025d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 1026af12a3e7SDag-Erling Smørgrav } 1027333ee039SDag-Erling Smørgrav 1028af12a3e7SDag-Erling Smørgrav void 10294f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id, 10304f52dfbbSDag-Erling Smørgrav channel_callback_fn *fn, int do_close) 1031af12a3e7SDag-Erling Smørgrav { 10324f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1033f388f5efSDag-Erling Smørgrav 1034af12a3e7SDag-Erling Smørgrav if (c == NULL) { 103519261079SEd Maste logit_f("%d: bad id", id); 1036af12a3e7SDag-Erling Smørgrav return; 1037af12a3e7SDag-Erling Smørgrav } 1038af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 1039b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 1040af12a3e7SDag-Erling Smørgrav } 1041333ee039SDag-Erling Smørgrav 1042af12a3e7SDag-Erling Smørgrav void 10434f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id) 1044af12a3e7SDag-Erling Smørgrav { 10454f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 1046f388f5efSDag-Erling Smørgrav 1047af12a3e7SDag-Erling Smørgrav if (c == NULL) { 104819261079SEd Maste logit_f("%d: bad id", id); 1049af12a3e7SDag-Erling Smørgrav return; 1050af12a3e7SDag-Erling Smørgrav } 1051af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 1052b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 1053af12a3e7SDag-Erling Smørgrav } 1054333ee039SDag-Erling Smørgrav 1055af12a3e7SDag-Erling Smørgrav void 10564f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, 1057d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 1058af12a3e7SDag-Erling Smørgrav { 10594f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 1060f388f5efSDag-Erling Smørgrav 1061af12a3e7SDag-Erling Smørgrav if (c == NULL) { 106219261079SEd Maste logit_f("%d: bad id", id); 1063af12a3e7SDag-Erling Smørgrav return; 1064af12a3e7SDag-Erling Smørgrav } 1065b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 1066b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 1067d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 1068d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 1069af12a3e7SDag-Erling Smørgrav } 1070af12a3e7SDag-Erling Smørgrav 1071af12a3e7SDag-Erling Smørgrav void 10724f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, 1073d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 1074af12a3e7SDag-Erling Smørgrav { 10754f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 10764f52dfbbSDag-Erling Smørgrav int r; 1077f388f5efSDag-Erling Smørgrav 1078af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 1079af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 10804f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 108119261079SEd Maste fatal_f("channel %d: no remote id", c->self); 10824f52dfbbSDag-Erling Smørgrav 10834f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); 1084af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1085af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 10864f52dfbbSDag-Erling Smørgrav 10874f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 10884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 10894f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 10904f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 109119261079SEd Maste fatal_fr(r, "channel %i", c->self); 1092511b41d2SMark Murray } 1093511b41d2SMark Murray 1094af12a3e7SDag-Erling Smørgrav static void 10951323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c) 1096511b41d2SMark Murray { 10971323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_R; 1098a04a10f8SKris Kennaway } 1099a04a10f8SKris Kennaway 1100af12a3e7SDag-Erling Smørgrav static void 11011323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c) 1102ca3176e7SBrian Feldman { 1103ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 11041323ec57SEd Maste c->io_want = SSH_CHAN_IO_SOCK_W; 1105ca3176e7SBrian Feldman } 1106ca3176e7SBrian Feldman 1107af12a3e7SDag-Erling Smørgrav static void 11081323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c) 1109a04a10f8SKris Kennaway { 11101323ec57SEd Maste c->io_want = 0; 1111a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 11124f52dfbbSDag-Erling Smørgrav c->remote_window > 0 && 11134f52dfbbSDag-Erling Smørgrav sshbuf_len(c->input) < c->remote_window && 11144f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 11151323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1116a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 1117a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 11184f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) { 11191323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1120a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 112180628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 11224f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 11234f52dfbbSDag-Erling Smørgrav "obuf_empty delayed efd %d/(%zu)", c->self, 11244f52dfbbSDag-Erling Smørgrav c->efd, sshbuf_len(c->extended)); 112580628bacSDag-Erling Smørgrav else 11264f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1127a04a10f8SKris Kennaway } 1128a04a10f8SKris Kennaway } 1129a04a10f8SKris Kennaway /** XXX check close conditions, too */ 11304f52dfbbSDag-Erling Smørgrav if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && 11314f52dfbbSDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_CLOSED)) { 1132a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 11334f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) > 0) 11341323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_W; 1135e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 1136e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1137e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 11384f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) < c->remote_window) 11391323ec57SEd Maste c->io_want |= SSH_CHAN_IO_EFD_R; 1140a04a10f8SKris Kennaway } 114121e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 1142a04a10f8SKris Kennaway } 1143a04a10f8SKris Kennaway 1144a04a10f8SKris Kennaway /* 1145a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 1146a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 1147a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 1148a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 1149a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 1150a04a10f8SKris Kennaway * XXX All this happens at the client side. 1151af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 1152a04a10f8SKris Kennaway */ 1153af12a3e7SDag-Erling Smørgrav static int 11544f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b) 1155a04a10f8SKris Kennaway { 11564f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 1157ca3176e7SBrian Feldman u_char *ucp; 1158ca3176e7SBrian Feldman u_int proto_len, data_len; 1159511b41d2SMark Murray 1160557f75e5SDag-Erling Smørgrav /* Is this being called after the refusal deadline? */ 11614f52dfbbSDag-Erling Smørgrav if (sc->x11_refuse_time != 0 && 11624f52dfbbSDag-Erling Smørgrav (u_int)monotime() >= sc->x11_refuse_time) { 1163557f75e5SDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1164557f75e5SDag-Erling Smørgrav "expired"); 1165557f75e5SDag-Erling Smørgrav return -1; 1166557f75e5SDag-Erling Smørgrav } 1167557f75e5SDag-Erling Smørgrav 1168511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 11694f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 12) 1170a04a10f8SKris Kennaway return 0; 1171511b41d2SMark Murray 1172511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 11734f52dfbbSDag-Erling Smørgrav ucp = sshbuf_mutable_ptr(b); 1174511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 1175511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 1176511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 1177511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 1178511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 1179511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 1180511b41d2SMark Murray } else { 1181221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 1182511b41d2SMark Murray ucp[0]); 1183a04a10f8SKris Kennaway return -1; 1184511b41d2SMark Murray } 1185511b41d2SMark Murray 1186511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 11874f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 1188511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 1189a04a10f8SKris Kennaway return 0; 1190511b41d2SMark Murray 1191511b41d2SMark Murray /* Check if authentication protocol matches. */ 11924f52dfbbSDag-Erling Smørgrav if (proto_len != strlen(sc->x11_saved_proto) || 11934f52dfbbSDag-Erling Smørgrav memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { 1194221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 1195a04a10f8SKris Kennaway return -1; 1196511b41d2SMark Murray } 1197511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 11984f52dfbbSDag-Erling Smørgrav if (data_len != sc->x11_fake_data_len || 1199e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 12004f52dfbbSDag-Erling Smørgrav sc->x11_fake_data, sc->x11_fake_data_len) != 0) { 1201221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 1202a04a10f8SKris Kennaway return -1; 1203511b41d2SMark Murray } 1204511b41d2SMark Murray /* Check fake data length */ 12054f52dfbbSDag-Erling Smørgrav if (sc->x11_fake_data_len != sc->x11_saved_data_len) { 1206511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 12074f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len, sc->x11_saved_data_len); 1208a04a10f8SKris Kennaway return -1; 1209511b41d2SMark Murray } 1210511b41d2SMark Murray /* 1211511b41d2SMark Murray * Received authentication protocol and data match 1212511b41d2SMark Murray * our fake data. Substitute the fake data with real 1213511b41d2SMark Murray * data. 1214511b41d2SMark Murray */ 1215511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 12164f52dfbbSDag-Erling Smørgrav sc->x11_saved_data, sc->x11_saved_data_len); 1217a04a10f8SKris Kennaway return 1; 1218a04a10f8SKris Kennaway } 1219511b41d2SMark Murray 1220af12a3e7SDag-Erling Smørgrav static void 12211323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c) 1222a04a10f8SKris Kennaway { 12234f52dfbbSDag-Erling Smørgrav int ret = x11_open_helper(ssh, c->output); 1224af12a3e7SDag-Erling Smørgrav 1225af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 1226af12a3e7SDag-Erling Smørgrav 1227a04a10f8SKris Kennaway if (ret == 1) { 1228a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 12291323ec57SEd Maste channel_pre_open(ssh, c); 1230a04a10f8SKris Kennaway } else if (ret == -1) { 1231221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 12324f52dfbbSDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", 12334f52dfbbSDag-Erling Smørgrav c->self, c->istate, c->ostate); 12344f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 12354f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 12364f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 12374f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->output); 12384f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 1239221552e4SDag-Erling Smørgrav debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 1240a04a10f8SKris Kennaway } 1241a04a10f8SKris Kennaway } 1242a04a10f8SKris Kennaway 1243b15c8340SDag-Erling Smørgrav static void 12441323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c) 1245b15c8340SDag-Erling Smørgrav { 12461323ec57SEd Maste c->io_want = 0; 1247e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 12484f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 12491323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1250b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1251b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 12524f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 12534f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 1254b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 12554f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 1256b15c8340SDag-Erling Smørgrav } 1257b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1258b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 12594f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) 12601323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1261b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 12624f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1263b15c8340SDag-Erling Smørgrav } 1264b15c8340SDag-Erling Smørgrav } 1265b15c8340SDag-Erling Smørgrav 1266ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1267af12a3e7SDag-Erling Smørgrav static int 12684f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) 1269ca3176e7SBrian Feldman { 12704f52dfbbSDag-Erling Smørgrav const u_char *p; 12714f52dfbbSDag-Erling Smørgrav char *host; 1272cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1273ca3176e7SBrian Feldman char username[256]; 1274ca3176e7SBrian Feldman struct { 1275ca3176e7SBrian Feldman u_int8_t version; 1276ca3176e7SBrian Feldman u_int8_t command; 1277ca3176e7SBrian Feldman u_int16_t dest_port; 1278ca3176e7SBrian Feldman struct in_addr dest_addr; 1279ca3176e7SBrian Feldman } s4_req, s4_rsp; 12804f52dfbbSDag-Erling Smørgrav int r; 1281ca3176e7SBrian Feldman 1282ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1283ca3176e7SBrian Feldman 12844f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1285ca3176e7SBrian Feldman len = sizeof(s4_req); 1286ca3176e7SBrian Feldman if (have < len) 1287ca3176e7SBrian Feldman return 0; 12884f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1289cce7d346SDag-Erling Smørgrav 1290cce7d346SDag-Erling Smørgrav need = 1; 1291cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1292cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1293cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1294cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1295cce7d346SDag-Erling Smørgrav need = 2; 1296cce7d346SDag-Erling Smørgrav } 1297cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1298ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1299ca3176e7SBrian Feldman if (p[i] == '\0') { 1300cce7d346SDag-Erling Smørgrav found++; 1301cce7d346SDag-Erling Smørgrav if (found == need) 1302ca3176e7SBrian Feldman break; 1303ca3176e7SBrian Feldman } 1304ca3176e7SBrian Feldman if (i > 1024) { 1305ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1306ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1307ca3176e7SBrian Feldman c->self); 1308ca3176e7SBrian Feldman return -1; 1309ca3176e7SBrian Feldman } 1310ca3176e7SBrian Feldman } 1311cce7d346SDag-Erling Smørgrav if (found < need) 1312ca3176e7SBrian Feldman return 0; 13134f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || 13144f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || 13154f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || 13164f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { 131719261079SEd Maste debug_r(r, "channels %d: decode socks4", c->self); 13184f52dfbbSDag-Erling Smørgrav return -1; 13194f52dfbbSDag-Erling Smørgrav } 13204f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 13214f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 13224f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 132319261079SEd Maste error("channel %d: decode socks4: unterminated user", c->self); 13244f52dfbbSDag-Erling Smørgrav return -1; 13254f52dfbbSDag-Erling Smørgrav } 1326ca3176e7SBrian Feldman len = strlen(p); 1327ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1328cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1329ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 133019261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 133119261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1332e4a9863fSDag-Erling Smørgrav free(c->path); 1333cce7d346SDag-Erling Smørgrav c->path = NULL; 1334cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1335ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1336cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1337cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 13384f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 13394f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 13404f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 13414f52dfbbSDag-Erling Smørgrav error("channel %d: decode socks4a: host not nul " 13424f52dfbbSDag-Erling Smørgrav "terminated", c->self); 13434f52dfbbSDag-Erling Smørgrav return -1; 13444f52dfbbSDag-Erling Smørgrav } 1345cce7d346SDag-Erling Smørgrav len = strlen(p); 1346cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1347cce7d346SDag-Erling Smørgrav c->self, p, len); 1348cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1349cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1350cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1351cce7d346SDag-Erling Smørgrav c->self, p); 1352cce7d346SDag-Erling Smørgrav return -1; 1353cce7d346SDag-Erling Smørgrav } 1354cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 135519261079SEd Maste if ((r = sshbuf_consume(input, len)) != 0) 135619261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 1357cce7d346SDag-Erling Smørgrav } 1358ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1359ca3176e7SBrian Feldman 1360221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1361cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1362ca3176e7SBrian Feldman 1363ca3176e7SBrian Feldman if (s4_req.command != 1) { 1364cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1365cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1366ca3176e7SBrian Feldman return -1; 1367ca3176e7SBrian Feldman } 1368ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1369ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1370ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1371ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 137219261079SEd Maste if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) 137319261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1374ca3176e7SBrian Feldman return 1; 1375ca3176e7SBrian Feldman } 1376ca3176e7SBrian Feldman 1377221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1378221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1379221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1380221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1381221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1382221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1383221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1384221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1385221552e4SDag-Erling Smørgrav 1386221552e4SDag-Erling Smørgrav static int 13874f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) 1388221552e4SDag-Erling Smørgrav { 13894f52dfbbSDag-Erling Smørgrav /* XXX use get/put_u8 instead of trusting struct padding */ 1390221552e4SDag-Erling Smørgrav struct { 1391221552e4SDag-Erling Smørgrav u_int8_t version; 1392221552e4SDag-Erling Smørgrav u_int8_t command; 1393221552e4SDag-Erling Smørgrav u_int8_t reserved; 1394221552e4SDag-Erling Smørgrav u_int8_t atyp; 1395221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1396221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1397e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 13984f52dfbbSDag-Erling Smørgrav const u_char *p; 1399333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 14004f52dfbbSDag-Erling Smørgrav int r; 1401221552e4SDag-Erling Smørgrav 1402221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 14034f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1404221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1405221552e4SDag-Erling Smørgrav return -1; 14064f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1407221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1408221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1409221552e4SDag-Erling Smørgrav if (have < 2) 1410221552e4SDag-Erling Smørgrav return 0; 1411221552e4SDag-Erling Smørgrav nmethods = p[1]; 1412221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1413221552e4SDag-Erling Smørgrav return 0; 1414221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1415221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1416221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1417221552e4SDag-Erling Smørgrav found = 1; 1418221552e4SDag-Erling Smørgrav break; 1419221552e4SDag-Erling Smørgrav } 1420221552e4SDag-Erling Smørgrav } 1421221552e4SDag-Erling Smørgrav if (!found) { 1422221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1423221552e4SDag-Erling Smørgrav c->self); 1424221552e4SDag-Erling Smørgrav return -1; 1425221552e4SDag-Erling Smørgrav } 142619261079SEd Maste if ((r = sshbuf_consume(input, nmethods + 2)) != 0) 142719261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 14284f52dfbbSDag-Erling Smørgrav /* version, method */ 14294f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(output, 0x05)) != 0 || 143019261079SEd Maste (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) 143119261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1432221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1433221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1434221552e4SDag-Erling Smørgrav return 0; /* need more */ 1435221552e4SDag-Erling Smørgrav } 1436221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1437221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1438221552e4SDag-Erling Smørgrav return 0; /* need more */ 1439333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1440221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1441221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1442221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1443221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1444221552e4SDag-Erling Smørgrav return -1; 1445221552e4SDag-Erling Smørgrav } 1446221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1447221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1448221552e4SDag-Erling Smørgrav addrlen = 4; 1449221552e4SDag-Erling Smørgrav af = AF_INET; 1450221552e4SDag-Erling Smørgrav break; 1451221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1452221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1453221552e4SDag-Erling Smørgrav af = -1; 1454221552e4SDag-Erling Smørgrav break; 1455221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1456221552e4SDag-Erling Smørgrav addrlen = 16; 1457221552e4SDag-Erling Smørgrav af = AF_INET6; 1458221552e4SDag-Erling Smørgrav break; 1459221552e4SDag-Erling Smørgrav default: 1460221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1461221552e4SDag-Erling Smørgrav return -1; 1462221552e4SDag-Erling Smørgrav } 1463333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1464333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1465333ee039SDag-Erling Smørgrav need++; 1466333ee039SDag-Erling Smørgrav if (have < need) 1467221552e4SDag-Erling Smørgrav return 0; 146819261079SEd Maste if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) 146919261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 14704f52dfbbSDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 14714f52dfbbSDag-Erling Smørgrav /* host string length */ 147219261079SEd Maste if ((r = sshbuf_consume(input, 1)) != 0) 147319261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 14744f52dfbbSDag-Erling Smørgrav } 14754f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || 14764f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &dest_port, 2)) != 0) { 147719261079SEd Maste debug_r(r, "channel %d: parse addr/port", c->self); 14784f52dfbbSDag-Erling Smørgrav return -1; 14794f52dfbbSDag-Erling Smørgrav } 1480221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1481e4a9863fSDag-Erling Smørgrav free(c->path); 1482cce7d346SDag-Erling Smørgrav c->path = NULL; 1483cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1484cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1485cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1486cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1487221552e4SDag-Erling Smørgrav return -1; 1488cce7d346SDag-Erling Smørgrav } 1489cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1490cce7d346SDag-Erling Smørgrav } else { 1491cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1492cce7d346SDag-Erling Smørgrav return -1; 1493cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1494cce7d346SDag-Erling Smørgrav } 1495221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1496221552e4SDag-Erling Smørgrav 1497221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1498221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1499221552e4SDag-Erling Smørgrav 1500221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1501221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1502221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1503221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1504221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1505221552e4SDag-Erling Smørgrav 15064f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || 15074f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || 15084f52dfbbSDag-Erling Smørgrav (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) 150919261079SEd Maste fatal_fr(r, "channel %d: append reply", c->self); 1510221552e4SDag-Erling Smørgrav return 1; 1511221552e4SDag-Erling Smørgrav } 1512221552e4SDag-Erling Smørgrav 1513b15c8340SDag-Erling Smørgrav Channel * 15144f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh, 151519261079SEd Maste const char *host_to_connect, u_short port_to_connect, 151619261079SEd Maste int in, int out, int nonblock) 1517b15c8340SDag-Erling Smørgrav { 1518b15c8340SDag-Erling Smørgrav Channel *c; 1519b15c8340SDag-Erling Smørgrav 152019261079SEd Maste debug_f("%s:%d", host_to_connect, port_to_connect); 1521b15c8340SDag-Erling Smørgrav 15224f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, 1523b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 152419261079SEd Maste 0, "stdio-forward", nonblock); 1525b15c8340SDag-Erling Smørgrav 1526b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1527b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1528b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1529b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1530b15c8340SDag-Erling Smørgrav 15314f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); 15324f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 1533b15c8340SDag-Erling Smørgrav 1534b15c8340SDag-Erling Smørgrav return c; 1535b15c8340SDag-Erling Smørgrav } 1536b15c8340SDag-Erling Smørgrav 1537ca3176e7SBrian Feldman /* dynamic port forwarding */ 1538af12a3e7SDag-Erling Smørgrav static void 15391323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c) 1540ca3176e7SBrian Feldman { 15414f52dfbbSDag-Erling Smørgrav const u_char *p; 1542d4ecd108SDag-Erling Smørgrav u_int have; 1543d4ecd108SDag-Erling Smørgrav int ret; 1544ca3176e7SBrian Feldman 15451323ec57SEd Maste c->io_want = 0; 15464f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->input); 1547ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 15484f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->input, stderr); */ 1549ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1550221552e4SDag-Erling Smørgrav if (have < 3) { 1551ca3176e7SBrian Feldman /* need more */ 15521323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 1553ca3176e7SBrian Feldman return; 1554ca3176e7SBrian Feldman } 1555ca3176e7SBrian Feldman /* try to guess the protocol */ 15564f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->input); 15574f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u8? */ 1558ca3176e7SBrian Feldman switch (p[0]) { 1559ca3176e7SBrian Feldman case 0x04: 15604f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->input, c->output); 1561ca3176e7SBrian Feldman break; 1562221552e4SDag-Erling Smørgrav case 0x05: 15634f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->input, c->output); 1564221552e4SDag-Erling Smørgrav break; 1565ca3176e7SBrian Feldman default: 1566ca3176e7SBrian Feldman ret = -1; 1567ca3176e7SBrian Feldman break; 1568ca3176e7SBrian Feldman } 1569ca3176e7SBrian Feldman if (ret < 0) { 15704f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1571ca3176e7SBrian Feldman } else if (ret == 0) { 1572ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1573ca3176e7SBrian Feldman /* need more */ 15741323ec57SEd Maste c->io_want |= SSH_CHAN_IO_RFD; 15754f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output)) 15761323ec57SEd Maste c->io_want |= SSH_CHAN_IO_WFD; 1577ca3176e7SBrian Feldman } else { 1578ca3176e7SBrian Feldman /* switch to the next state */ 1579ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 15804f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 15814f52dfbbSDag-Erling Smørgrav } 15824f52dfbbSDag-Erling Smørgrav } 15834f52dfbbSDag-Erling Smørgrav 15844f52dfbbSDag-Erling Smørgrav /* simulate read-error */ 15854f52dfbbSDag-Erling Smørgrav static void 15864f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c) 15874f52dfbbSDag-Erling Smørgrav { 15884f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 15894f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 15904f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 15914f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 15924f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->output); 15934f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 15944f52dfbbSDag-Erling Smørgrav } 15954f52dfbbSDag-Erling Smørgrav 15964f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */ 15974f52dfbbSDag-Erling Smørgrav static void 15981323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) 15994f52dfbbSDag-Erling Smørgrav { 16004f52dfbbSDag-Erling Smørgrav const u_char *p; 16014f52dfbbSDag-Erling Smørgrav u_int have, len; 16024f52dfbbSDag-Erling Smørgrav int r, ret; 16034f52dfbbSDag-Erling Smørgrav 16044f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->output); 16054f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: have %d", c->self, have); 16064f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->output, stderr); */ 16074f52dfbbSDag-Erling Smørgrav /* EOF received */ 16084f52dfbbSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 160919261079SEd Maste if ((r = sshbuf_consume(c->output, have)) != 0) 161019261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 16114f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 16124f52dfbbSDag-Erling Smørgrav return; 16134f52dfbbSDag-Erling Smørgrav } 16144f52dfbbSDag-Erling Smørgrav /* check if the fixed size part of the packet is in buffer. */ 16154f52dfbbSDag-Erling Smørgrav if (have < 3) 16164f52dfbbSDag-Erling Smørgrav return; 16174f52dfbbSDag-Erling Smørgrav /* try to guess the protocol */ 16184f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->output); 16194f52dfbbSDag-Erling Smørgrav switch (p[0]) { 16204f52dfbbSDag-Erling Smørgrav case 0x04: 16214f52dfbbSDag-Erling Smørgrav /* switch input/output for reverse forwarding */ 16224f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->output, c->input); 16234f52dfbbSDag-Erling Smørgrav break; 16244f52dfbbSDag-Erling Smørgrav case 0x05: 16254f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->output, c->input); 16264f52dfbbSDag-Erling Smørgrav break; 16274f52dfbbSDag-Erling Smørgrav default: 16284f52dfbbSDag-Erling Smørgrav ret = -1; 16294f52dfbbSDag-Erling Smørgrav break; 16304f52dfbbSDag-Erling Smørgrav } 16314f52dfbbSDag-Erling Smørgrav if (ret < 0) { 16324f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 16334f52dfbbSDag-Erling Smørgrav } else if (ret == 0) { 16344f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: need more", c->self); 16354f52dfbbSDag-Erling Smørgrav /* send socks request to peer */ 16364f52dfbbSDag-Erling Smørgrav len = sshbuf_len(c->input); 16374f52dfbbSDag-Erling Smørgrav if (len > 0 && len < c->remote_window) { 16384f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 16394f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 16404f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_stringb(ssh, c->input)) != 0 || 16414f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 164219261079SEd Maste fatal_fr(r, "channel %i: rdynamic", c->self); 16434f52dfbbSDag-Erling Smørgrav } 164419261079SEd Maste if ((r = sshbuf_consume(c->input, len)) != 0) 164519261079SEd Maste fatal_fr(r, "channel %d: consume", c->self); 16464f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 16474f52dfbbSDag-Erling Smørgrav } 16484f52dfbbSDag-Erling Smørgrav } else if (rdynamic_connect_finish(ssh, c) < 0) { 16494f52dfbbSDag-Erling Smørgrav /* the connect failed */ 16504f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1651ca3176e7SBrian Feldman } 1652ca3176e7SBrian Feldman } 1653ca3176e7SBrian Feldman 1654a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1655af12a3e7SDag-Erling Smørgrav static void 16561323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c) 1657511b41d2SMark Murray { 1658af12a3e7SDag-Erling Smørgrav Channel *nc; 1659d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 16604f52dfbbSDag-Erling Smørgrav int r, newsock, oerrno, remote_port; 1661511b41d2SMark Murray socklen_t addrlen; 1662ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1663511b41d2SMark Murray 16641323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 16654f52dfbbSDag-Erling Smørgrav return; 16664f52dfbbSDag-Erling Smørgrav 1667511b41d2SMark Murray debug("X11 connection requested."); 1668511b41d2SMark Murray addrlen = sizeof(addr); 1669d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1670af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1671e4a9863fSDag-Erling Smørgrav oerrno = errno; 1672221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 167319261079SEd Maste channel_close_fd(ssh, c, &c->sock); 16744f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1675e4a9863fSDag-Erling Smørgrav errno = oerrno; 1676af12a3e7SDag-Erling Smørgrav } 167719261079SEd Maste if (newsock == -1) { 1678e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1679e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1680511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1681462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1682e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1683a04a10f8SKris Kennaway return; 1684511b41d2SMark Murray } 1685af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1686ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1687a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1688511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1689ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1690a04a10f8SKris Kennaway 16914f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "accepted x11 socket", 1692a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1693221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 16944f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "x11"); 169547dd1d1bSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 169647dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, remote_port)) != 0) { 169719261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1698511b41d2SMark Murray } 16994f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 170019261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1701e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1702a04a10f8SKris Kennaway } 1703511b41d2SMark Murray 1704af12a3e7SDag-Erling Smørgrav static void 17054f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype) 1706ca3176e7SBrian Feldman { 1707f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1708076ad2f8SDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 1709ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1710d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 17114f52dfbbSDag-Erling Smørgrav int r; 1712ca3176e7SBrian Feldman 1713b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1714b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1715e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1716b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1717b15c8340SDag-Erling Smørgrav remote_port = 65535; 1718b15c8340SDag-Erling Smørgrav } 1719b15c8340SDag-Erling Smørgrav 17204f52dfbbSDag-Erling Smørgrav free(c->remote_name); 17214f52dfbbSDag-Erling Smørgrav xasprintf(&c->remote_name, 1722ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1723f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1724ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1725f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1726ca3176e7SBrian Feldman 17274f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, rtype); 1728a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1729ca3176e7SBrian Feldman /* target host, port */ 17304f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 173119261079SEd Maste (r = sshpkt_put_u32(ssh, c->host_port)) != 0) 173219261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1733a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1734a0ee8cc6SDag-Erling Smørgrav /* target path */ 173519261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 173619261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1737a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1738a0ee8cc6SDag-Erling Smørgrav /* listen path */ 173919261079SEd Maste if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) 174019261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1741ca3176e7SBrian Feldman } else { 1742ca3176e7SBrian Feldman /* listen address, port */ 17434f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 174419261079SEd Maste (r = sshpkt_put_u32(ssh, local_port)) != 0) 174519261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1746ca3176e7SBrian Feldman } 1747a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1748a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 174919261079SEd Maste if ((r = sshpkt_put_cstring(ssh, "")) != 0) 175019261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1751a0ee8cc6SDag-Erling Smørgrav } else { 1752ca3176e7SBrian Feldman /* originator host and port */ 17534f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 175419261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) 175519261079SEd Maste fatal_fr(r, "channel %i: reply", c->self); 1756ca3176e7SBrian Feldman } 17574f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 175819261079SEd Maste fatal_fr(r, "channel %i: send", c->self); 1759e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1760f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1761ca3176e7SBrian Feldman } 1762ca3176e7SBrian Feldman 1763557f75e5SDag-Erling Smørgrav void 17644f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) 1765557f75e5SDag-Erling Smørgrav { 17664f52dfbbSDag-Erling Smørgrav ssh->chanctxt->x11_refuse_time = refuse_time; 1767557f75e5SDag-Erling Smørgrav } 1768557f75e5SDag-Erling Smørgrav 1769511b41d2SMark Murray /* 1770a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1771511b41d2SMark Murray */ 1772af12a3e7SDag-Erling Smørgrav static void 17731323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c) 1774a04a10f8SKris Kennaway { 1775ca3176e7SBrian Feldman Channel *nc; 1776d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1777af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1778a04a10f8SKris Kennaway socklen_t addrlen; 1779ca3176e7SBrian Feldman char *rtype; 1780a04a10f8SKris Kennaway 17811323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 17824f52dfbbSDag-Erling Smørgrav return; 17834f52dfbbSDag-Erling Smørgrav 17844f52dfbbSDag-Erling Smørgrav debug("Connection to port %d forwarding to %.100s port %d requested.", 1785a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1786ca3176e7SBrian Feldman 1787af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1788af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1789af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1790a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1791a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1792a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1793a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1794a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1795a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1796a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1797af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1798af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1799af12a3e7SDag-Erling Smørgrav } else { 1800af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1801af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1802af12a3e7SDag-Erling Smørgrav } 1803ca3176e7SBrian Feldman 1804511b41d2SMark Murray addrlen = sizeof(addr); 1805d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 180619261079SEd Maste if (newsock == -1) { 1807e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1808e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1809511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1810462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1811e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1812a04a10f8SKris Kennaway return; 1813511b41d2SMark Murray } 1814a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1815af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 18164f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, 1817221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1818ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1819ca3176e7SBrian Feldman nc->host_port = c->host_port; 1820cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1821cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 1822ca3176e7SBrian Feldman 1823b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 18244f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, nc, rtype); 1825a04a10f8SKris Kennaway } 1826511b41d2SMark Murray 1827511b41d2SMark Murray /* 1828a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1829a04a10f8SKris Kennaway * clients. 1830511b41d2SMark Murray */ 1831af12a3e7SDag-Erling Smørgrav static void 18321323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c) 1833a04a10f8SKris Kennaway { 1834af12a3e7SDag-Erling Smørgrav Channel *nc; 18354f52dfbbSDag-Erling Smørgrav int r, newsock; 1836d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1837a04a10f8SKris Kennaway socklen_t addrlen; 1838a04a10f8SKris Kennaway 18391323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 18404f52dfbbSDag-Erling Smørgrav return; 18414f52dfbbSDag-Erling Smørgrav 1842511b41d2SMark Murray addrlen = sizeof(addr); 1843d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 184419261079SEd Maste if (newsock == -1) { 18454f52dfbbSDag-Erling Smørgrav error("accept from auth socket: %.100s", strerror(errno)); 1846462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1847e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1848a04a10f8SKris Kennaway return; 1849511b41d2SMark Murray } 18504f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "accepted auth socket", 1851ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1852ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1853221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 18544f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); 18554f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 185619261079SEd Maste fatal_fr(r, "channel %i", c->self); 1857a04a10f8SKris Kennaway } 1858511b41d2SMark Murray 1859af12a3e7SDag-Erling Smørgrav static void 18601323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c) 1861ca3176e7SBrian Feldman { 18624f52dfbbSDag-Erling Smørgrav int err = 0, sock, isopen, r; 1863af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1864af12a3e7SDag-Erling Smørgrav 18651323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) 18664f52dfbbSDag-Erling Smørgrav return; 18674f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 186819261079SEd Maste fatal_f("channel %d: no remote id", c->self); 18694f52dfbbSDag-Erling Smørgrav /* for rdynamic the OPEN_CONFIRMATION has been sent already */ 18704f52dfbbSDag-Erling Smørgrav isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); 187119261079SEd Maste if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { 1872af12a3e7SDag-Erling Smørgrav err = errno; 1873af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1874af12a3e7SDag-Erling Smørgrav } 1875ca3176e7SBrian Feldman if (err == 0) { 1876d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 1877d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 1878d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1879af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 18804f52dfbbSDag-Erling Smørgrav if (isopen) { 18814f52dfbbSDag-Erling Smørgrav /* no message necessary */ 1882af12a3e7SDag-Erling Smørgrav } else { 18834f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 18844f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 18854f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 18864f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 18874f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 188819261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 188919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 189019261079SEd Maste fatal_fr(r, "channel %i open confirm", c->self); 1891af12a3e7SDag-Erling Smørgrav } 1892ca3176e7SBrian Feldman } else { 1893d4af9e69SDag-Erling Smørgrav debug("channel %d: connection failed: %s", 1894ca3176e7SBrian Feldman c->self, strerror(err)); 1895d4af9e69SDag-Erling Smørgrav /* Try next address, if any */ 1896d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&c->connect_ctx)) > 0) { 1897d4af9e69SDag-Erling Smørgrav close(c->sock); 1898d4af9e69SDag-Erling Smørgrav c->sock = c->rfd = c->wfd = sock; 1899d4af9e69SDag-Erling Smørgrav return; 1900d4af9e69SDag-Erling Smørgrav } 1901d4af9e69SDag-Erling Smørgrav /* Exhausted all addresses */ 1902d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 1903d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 1904d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 19054f52dfbbSDag-Erling Smørgrav if (isopen) { 19064f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1907af12a3e7SDag-Erling Smørgrav } else { 19084f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 19094f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 19104f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 191147dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, 191247dd1d1bSDag-Erling Smørgrav SSH2_OPEN_CONNECT_FAILED)) != 0 || 191347dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || 191419261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 191519261079SEd Maste (r = sshpkt_send(ssh)) != 0) 191619261079SEd Maste fatal_fr(r, "channel %i: failure", c->self); 19174f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1918ca3176e7SBrian Feldman } 1919ca3176e7SBrian Feldman } 1920ca3176e7SBrian Feldman } 1921ca3176e7SBrian Feldman 1922af12a3e7SDag-Erling Smørgrav static int 19231323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c) 1924a04a10f8SKris Kennaway { 1925aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 19264f52dfbbSDag-Erling Smørgrav ssize_t len; 19274f52dfbbSDag-Erling Smørgrav int r, force; 19281323ec57SEd Maste size_t have, avail, maxlen = CHANNEL_MAX_READ; 19291323ec57SEd Maste int pty_zeroread = 0; 19301323ec57SEd Maste 19311323ec57SEd Maste #ifdef PTY_ZEROREAD 19321323ec57SEd Maste /* Bug on AIX: read(1) can return 0 for a non-closed fd */ 19331323ec57SEd Maste pty_zeroread = c->isatty; 19341323ec57SEd Maste #endif 1935511b41d2SMark Murray 1936d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 19374f52dfbbSDag-Erling Smørgrav 19381323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0) 19394f52dfbbSDag-Erling Smørgrav return 1; 19401323ec57SEd Maste if ((avail = sshbuf_avail(c->input)) == 0) 19411323ec57SEd Maste return 1; /* Shouldn't happen */ 19421323ec57SEd Maste 19431323ec57SEd Maste /* 19441323ec57SEd Maste * For "simple" channels (i.e. not datagram or filtered), we can 19451323ec57SEd Maste * read directly to the channel buffer. 19461323ec57SEd Maste */ 19471323ec57SEd Maste if (!pty_zeroread && c->input_filter == NULL && !c->datagram) { 19481323ec57SEd Maste /* Only OPEN channels have valid rwin */ 19491323ec57SEd Maste if (c->type == SSH_CHANNEL_OPEN) { 19501323ec57SEd Maste if ((have = sshbuf_len(c->input)) >= c->remote_window) 19511323ec57SEd Maste return 1; /* shouldn't happen */ 19521323ec57SEd Maste if (maxlen > c->remote_window - have) 19531323ec57SEd Maste maxlen = c->remote_window - have; 19541323ec57SEd Maste } 19551323ec57SEd Maste if (maxlen > avail) 19561323ec57SEd Maste maxlen = avail; 19571323ec57SEd Maste if ((r = sshbuf_read(c->rfd, c->input, maxlen, NULL)) != 0) { 19581323ec57SEd Maste if (errno == EINTR || (!force && 19591323ec57SEd Maste (errno == EAGAIN || errno == EWOULDBLOCK))) 19601323ec57SEd Maste return 1; 19611323ec57SEd Maste debug2("channel %d: read failed rfd %d maxlen %zu: %s", 19621323ec57SEd Maste c->self, c->rfd, maxlen, ssh_err(r)); 19631323ec57SEd Maste goto rfail; 19641323ec57SEd Maste } 19651323ec57SEd Maste return 1; 19661323ec57SEd Maste } 19674f52dfbbSDag-Erling Smørgrav 1968333ee039SDag-Erling Smørgrav errno = 0; 1969a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 19701323ec57SEd Maste /* fixup AIX zero-length read with errno set to look more like errors */ 19711323ec57SEd Maste if (pty_zeroread && len == 0 && errno != 0) 19721323ec57SEd Maste len = -1; 197319261079SEd Maste if (len == -1 && (errno == EINTR || 1974d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 1975a04a10f8SKris Kennaway return 1; 19761323ec57SEd Maste if (len < 0 || (!pty_zeroread && len == 0)) { 19771323ec57SEd Maste debug2("channel %d: read<=0 rfd %d len %zd: %s", 19781323ec57SEd Maste c->self, c->rfd, len, 19791323ec57SEd Maste len == 0 ? "closed" : strerror(errno)); 19801323ec57SEd Maste rfail: 1981ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1982221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 19834f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1984ca3176e7SBrian Feldman return -1; 1985a04a10f8SKris Kennaway } else { 19864f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 1987a04a10f8SKris Kennaway } 1988a04a10f8SKris Kennaway return -1; 1989a04a10f8SKris Kennaway } 1990b66f2d16SKris Kennaway if (c->input_filter != NULL) { 19914f52dfbbSDag-Erling Smørgrav if (c->input_filter(ssh, c, buf, len) == -1) { 1992221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 19934f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 1994b66f2d16SKris Kennaway } 1995b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 19964f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->input, buf, len)) != 0) 199719261079SEd Maste fatal_fr(r, "channel %i: put datagram", c->self); 199819261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 199919261079SEd Maste fatal_fr(r, "channel %i: put data", c->self); 20001323ec57SEd Maste 2001a04a10f8SKris Kennaway return 1; 2002a04a10f8SKris Kennaway } 2003333ee039SDag-Erling Smørgrav 2004af12a3e7SDag-Erling Smørgrav static int 20051323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c) 2006a04a10f8SKris Kennaway { 2007ca3176e7SBrian Feldman struct termios tio; 20084f52dfbbSDag-Erling Smørgrav u_char *data = NULL, *buf; /* XXX const; need filter API change */ 20094f52dfbbSDag-Erling Smørgrav size_t dlen, olen = 0; 20104f52dfbbSDag-Erling Smørgrav int r, len; 20114f52dfbbSDag-Erling Smørgrav 20121323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 20131323ec57SEd Maste return 1; 20141323ec57SEd Maste if (sshbuf_len(c->output) == 0) 20154f52dfbbSDag-Erling Smørgrav return 1; 2016a04a10f8SKris Kennaway 2017a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 20184f52dfbbSDag-Erling Smørgrav olen = sshbuf_len(c->output); 2019b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 20204f52dfbbSDag-Erling Smørgrav if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { 2021b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 2022b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 20234f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2024b74df5b2SDag-Erling Smørgrav else 20254f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2026b74df5b2SDag-Erling Smørgrav return -1; 2027b74df5b2SDag-Erling Smørgrav } 2028b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 20294f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) 203019261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 20314f52dfbbSDag-Erling Smørgrav buf = data; 2032b74df5b2SDag-Erling Smørgrav } else { 20334f52dfbbSDag-Erling Smørgrav buf = data = sshbuf_mutable_ptr(c->output); 20344f52dfbbSDag-Erling Smørgrav dlen = sshbuf_len(c->output); 2035b74df5b2SDag-Erling Smørgrav } 2036b74df5b2SDag-Erling Smørgrav 2037b74df5b2SDag-Erling Smørgrav if (c->datagram) { 2038b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 2039b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 2040e4a9863fSDag-Erling Smørgrav free(data); 204119261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2042d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2043b74df5b2SDag-Erling Smørgrav return 1; 20444f52dfbbSDag-Erling Smørgrav if (len <= 0) 20454f52dfbbSDag-Erling Smørgrav goto write_fail; 2046e2f6069cSDag-Erling Smørgrav goto out; 2047b74df5b2SDag-Erling Smørgrav } 20484f52dfbbSDag-Erling Smørgrav 2049f388f5efSDag-Erling Smørgrav #ifdef _AIX 2050f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 20514f52dfbbSDag-Erling Smørgrav if (c->wfd_isatty) 20521323ec57SEd Maste dlen = MINIMUM(dlen, 8*1024); 2053f388f5efSDag-Erling Smørgrav #endif 2054b74df5b2SDag-Erling Smørgrav 2055b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 205619261079SEd Maste if (len == -1 && 2057d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 2058a04a10f8SKris Kennaway return 1; 2059511b41d2SMark Murray if (len <= 0) { 20604f52dfbbSDag-Erling Smørgrav write_fail: 2061ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2062221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 20634f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2064ca3176e7SBrian Feldman return -1; 2065511b41d2SMark Murray } else { 20664f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2067511b41d2SMark Murray } 2068a04a10f8SKris Kennaway return -1; 2069511b41d2SMark Murray } 20707aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 20714f52dfbbSDag-Erling Smørgrav if (c->isatty && dlen >= 1 && buf[0] != '\r') { 2072e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 2073e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 2074e0fbb1d2SBrian Feldman /* 2075e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 2076ca3176e7SBrian Feldman * traffic analysis. We need to match the 2077ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 2078b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 2079e0fbb1d2SBrian Feldman */ 20804f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || 20814f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 208219261079SEd Maste fatal_fr(r, "channel %i: ignore", c->self); 2083e0fbb1d2SBrian Feldman } 2084e0fbb1d2SBrian Feldman } 20854f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */ 208619261079SEd Maste if ((r = sshbuf_consume(c->output, len)) != 0) 208719261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2088e2f6069cSDag-Erling Smørgrav out: 20894f52dfbbSDag-Erling Smørgrav c->local_consumed += olen - sshbuf_len(c->output); 20904f52dfbbSDag-Erling Smørgrav 2091a04a10f8SKris Kennaway return 1; 2092511b41d2SMark Murray } 2093333ee039SDag-Erling Smørgrav 2094af12a3e7SDag-Erling Smørgrav static int 20951323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c) 2096a04a10f8SKris Kennaway { 20974f52dfbbSDag-Erling Smørgrav int r; 20984f52dfbbSDag-Erling Smørgrav ssize_t len; 2099511b41d2SMark Murray 21001323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) 21011323ec57SEd Maste return 1; 21021323ec57SEd Maste if (sshbuf_len(c->extended) == 0) 21034f52dfbbSDag-Erling Smørgrav return 1; 21044f52dfbbSDag-Erling Smørgrav 21054f52dfbbSDag-Erling Smørgrav len = write(c->efd, sshbuf_ptr(c->extended), 21064f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended)); 21074f52dfbbSDag-Erling Smørgrav debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); 210819261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN || 2109d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2110ca3176e7SBrian Feldman return 1; 2111ca3176e7SBrian Feldman if (len <= 0) { 21124f52dfbbSDag-Erling Smørgrav debug2("channel %d: closing write-efd %d", c->self, c->efd); 211319261079SEd Maste channel_close_fd(ssh, c, &c->efd); 2114ca3176e7SBrian Feldman } else { 211519261079SEd Maste if ((r = sshbuf_consume(c->extended, len)) != 0) 211619261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 2117a04a10f8SKris Kennaway c->local_consumed += len; 2118a04a10f8SKris Kennaway } 21194f52dfbbSDag-Erling Smørgrav return 1; 21204f52dfbbSDag-Erling Smørgrav } 21214f52dfbbSDag-Erling Smørgrav 21224f52dfbbSDag-Erling Smørgrav static int 21231323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c) 21244f52dfbbSDag-Erling Smørgrav { 21254f52dfbbSDag-Erling Smørgrav char buf[CHAN_RBUF]; 21264f52dfbbSDag-Erling Smørgrav ssize_t len; 212719261079SEd Maste int r, force; 21284f52dfbbSDag-Erling Smørgrav 212919261079SEd Maste force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 213019261079SEd Maste 21311323ec57SEd Maste if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0) 21324f52dfbbSDag-Erling Smørgrav return 1; 21334f52dfbbSDag-Erling Smørgrav 2134a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 21354f52dfbbSDag-Erling Smørgrav debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); 213619261079SEd Maste if (len == -1 && (errno == EINTR || ((errno == EAGAIN || 213719261079SEd Maste errno == EWOULDBLOCK) && !force))) 2138ca3176e7SBrian Feldman return 1; 2139ca3176e7SBrian Feldman if (len <= 0) { 214019261079SEd Maste debug2("channel %d: closing read-efd %d", c->self, c->efd); 214119261079SEd Maste channel_close_fd(ssh, c, &c->efd); 214219261079SEd Maste } else if (c->extended_usage == CHAN_EXTENDED_IGNORE) 214319261079SEd Maste debug3("channel %d: discard efd", c->self); 214419261079SEd Maste else if ((r = sshbuf_put(c->extended, buf, len)) != 0) 214519261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 2146a04a10f8SKris Kennaway return 1; 2147a04a10f8SKris Kennaway } 2148333ee039SDag-Erling Smørgrav 214921e764dfSDag-Erling Smørgrav static int 21501323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c) 2151a04a10f8SKris Kennaway { 21524f52dfbbSDag-Erling Smørgrav if (c->efd == -1) 21534f52dfbbSDag-Erling Smørgrav return 1; 21544f52dfbbSDag-Erling Smørgrav 21554f52dfbbSDag-Erling Smørgrav /** XXX handle drain efd, too */ 21564f52dfbbSDag-Erling Smørgrav 21574f52dfbbSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_WRITE) 21581323ec57SEd Maste return channel_handle_efd_write(ssh, c); 21594f52dfbbSDag-Erling Smørgrav else if (c->extended_usage == CHAN_EXTENDED_READ || 21604f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) 21611323ec57SEd Maste return channel_handle_efd_read(ssh, c); 21624f52dfbbSDag-Erling Smørgrav 21634f52dfbbSDag-Erling Smørgrav return 1; 21644f52dfbbSDag-Erling Smørgrav } 21654f52dfbbSDag-Erling Smørgrav 21664f52dfbbSDag-Erling Smørgrav static int 21674f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c) 21684f52dfbbSDag-Erling Smørgrav { 21694f52dfbbSDag-Erling Smørgrav int r; 21704f52dfbbSDag-Erling Smørgrav 2171ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 2172ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 2173d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 2174d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 2175d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 2176a04a10f8SKris Kennaway c->local_consumed > 0) { 21774f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 217819261079SEd Maste fatal_f("channel %d: no remote id", c->self); 21794f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 21804f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 21814f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 21824f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || 21834f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 218419261079SEd Maste fatal_fr(r, "channel %i", c->self); 21854f52dfbbSDag-Erling Smørgrav } 218619261079SEd Maste debug2("channel %d: window %d sent adjust %d", c->self, 218719261079SEd Maste c->local_window, c->local_consumed); 218860c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 2189a04a10f8SKris Kennaway c->local_consumed = 0; 2190a04a10f8SKris Kennaway } 2191a04a10f8SKris Kennaway return 1; 2192a04a10f8SKris Kennaway } 2193a04a10f8SKris Kennaway 2194af12a3e7SDag-Erling Smørgrav static void 21951323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c) 2196a04a10f8SKris Kennaway { 21971323ec57SEd Maste channel_handle_rfd(ssh, c); 21981323ec57SEd Maste channel_handle_wfd(ssh, c); 21991323ec57SEd Maste channel_handle_efd(ssh, c); 22004f52dfbbSDag-Erling Smørgrav channel_check_window(ssh, c); 2201a04a10f8SKris Kennaway } 2202a04a10f8SKris Kennaway 2203b15c8340SDag-Erling Smørgrav static u_int 22044f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need) 2205b15c8340SDag-Erling Smørgrav { 2206b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 22074f52dfbbSDag-Erling Smørgrav ssize_t len; 2208b15c8340SDag-Erling Smørgrav u_int rlen; 22094f52dfbbSDag-Erling Smørgrav int r; 2210b15c8340SDag-Erling Smørgrav 22114f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) < need) { 22124f52dfbbSDag-Erling Smørgrav rlen = need - sshbuf_len(c->input); 2213ca86bcf2SDag-Erling Smørgrav len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); 221419261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 22154f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2216b15c8340SDag-Erling Smørgrav if (len <= 0) { 22174f52dfbbSDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %zd", 2218b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 22194f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2220b15c8340SDag-Erling Smørgrav return 0; 222119261079SEd Maste } else if ((r = sshbuf_put(c->input, buf, len)) != 0) 222219261079SEd Maste fatal_fr(r, "channel %i: append", c->self); 22234f52dfbbSDag-Erling Smørgrav } 22244f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2225b15c8340SDag-Erling Smørgrav } 2226b15c8340SDag-Erling Smørgrav 2227b15c8340SDag-Erling Smørgrav static void 22281323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c) 2229b15c8340SDag-Erling Smørgrav { 2230b15c8340SDag-Erling Smørgrav u_int need; 2231b15c8340SDag-Erling Smørgrav 22321323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) 22334f52dfbbSDag-Erling Smørgrav return; 22344f52dfbbSDag-Erling Smørgrav if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) 22354f52dfbbSDag-Erling Smørgrav return; 22364f52dfbbSDag-Erling Smørgrav if (c->mux_pause) 22374f52dfbbSDag-Erling Smørgrav return; 2238b15c8340SDag-Erling Smørgrav 2239b15c8340SDag-Erling Smørgrav /* 2240b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 2241b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 2242b15c8340SDag-Erling Smørgrav */ 22434f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, 4) < 4) /* read header */ 2244b15c8340SDag-Erling Smørgrav return; 22454f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u32 */ 22464f52dfbbSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(c->input)); 2247b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 2248b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 2249b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 2250b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 22514f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 2252b15c8340SDag-Erling Smørgrav return; 2253b15c8340SDag-Erling Smørgrav } 22544f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ 2255b15c8340SDag-Erling Smørgrav return; 22564f52dfbbSDag-Erling Smørgrav if (c->mux_rcb(ssh, c) != 0) { 2257b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 22584f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2259b15c8340SDag-Erling Smørgrav return; 2260b15c8340SDag-Erling Smørgrav } 2261b15c8340SDag-Erling Smørgrav } 2262b15c8340SDag-Erling Smørgrav 2263b15c8340SDag-Erling Smørgrav static void 22641323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c) 22654f52dfbbSDag-Erling Smørgrav { 22664f52dfbbSDag-Erling Smørgrav ssize_t len; 22674f52dfbbSDag-Erling Smørgrav int r; 22684f52dfbbSDag-Erling Smørgrav 22691323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) 22701323ec57SEd Maste return; 22711323ec57SEd Maste if (sshbuf_len(c->output) == 0) 22724f52dfbbSDag-Erling Smørgrav return; 22734f52dfbbSDag-Erling Smørgrav 22744f52dfbbSDag-Erling Smørgrav len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); 227519261079SEd Maste if (len == -1 && (errno == EINTR || errno == EAGAIN)) 22764f52dfbbSDag-Erling Smørgrav return; 22774f52dfbbSDag-Erling Smørgrav if (len <= 0) { 22784f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 22794f52dfbbSDag-Erling Smørgrav return; 22804f52dfbbSDag-Erling Smørgrav } 22814f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, len)) != 0) 228219261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 22834f52dfbbSDag-Erling Smørgrav } 22844f52dfbbSDag-Erling Smørgrav 22854f52dfbbSDag-Erling Smørgrav static void 22861323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c) 22874f52dfbbSDag-Erling Smørgrav { 22881323ec57SEd Maste channel_post_mux_client_read(ssh, c); 22891323ec57SEd Maste channel_post_mux_client_write(ssh, c); 22904f52dfbbSDag-Erling Smørgrav } 22914f52dfbbSDag-Erling Smørgrav 22924f52dfbbSDag-Erling Smørgrav static void 22931323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c) 2294b15c8340SDag-Erling Smørgrav { 2295b15c8340SDag-Erling Smørgrav Channel *nc; 2296b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 2297b15c8340SDag-Erling Smørgrav socklen_t addrlen; 2298b15c8340SDag-Erling Smørgrav int newsock; 2299b15c8340SDag-Erling Smørgrav uid_t euid; 2300b15c8340SDag-Erling Smørgrav gid_t egid; 2301b15c8340SDag-Erling Smørgrav 23021323ec57SEd Maste if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) 2303b15c8340SDag-Erling Smørgrav return; 2304b15c8340SDag-Erling Smørgrav 2305b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 2306b15c8340SDag-Erling Smørgrav 2307b15c8340SDag-Erling Smørgrav /* 2308b15c8340SDag-Erling Smørgrav * Accept connection on control socket 2309b15c8340SDag-Erling Smørgrav */ 2310b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 2311b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 2312b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 2313b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 231419261079SEd Maste error_f("accept: %s", strerror(errno)); 2315462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2316e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2317b15c8340SDag-Erling Smørgrav return; 2318b15c8340SDag-Erling Smørgrav } 2319b15c8340SDag-Erling Smørgrav 232019261079SEd Maste if (getpeereid(newsock, &euid, &egid) == -1) { 232119261079SEd Maste error_f("getpeereid failed: %s", strerror(errno)); 2322b15c8340SDag-Erling Smørgrav close(newsock); 2323b15c8340SDag-Erling Smørgrav return; 2324b15c8340SDag-Erling Smørgrav } 2325b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 2326b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 2327b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 2328b15c8340SDag-Erling Smørgrav close(newsock); 2329b15c8340SDag-Erling Smørgrav return; 2330b15c8340SDag-Erling Smørgrav } 23314f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT, 2332b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 2333b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 2334b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 233519261079SEd Maste debug3_f("new mux channel %d fd %d", nc->self, nc->sock); 2336b15c8340SDag-Erling Smørgrav /* establish state */ 23374f52dfbbSDag-Erling Smørgrav nc->mux_rcb(ssh, nc); 2338b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 2339b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 2340b15c8340SDag-Erling Smørgrav } 2341b15c8340SDag-Erling Smørgrav 2342af12a3e7SDag-Erling Smørgrav static void 23434f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc) 2344a04a10f8SKris Kennaway { 23454f52dfbbSDag-Erling Smørgrav chan_fn **pre, **post; 2346f388f5efSDag-Erling Smørgrav 23474f52dfbbSDag-Erling Smørgrav if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || 23484f52dfbbSDag-Erling Smørgrav (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) 234919261079SEd Maste fatal_f("allocation failed"); 2350511b41d2SMark Murray 23514f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 23524f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 23534f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 23544f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 23554f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 23564f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 23574f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 23584f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 23594f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 23604f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 23614f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; 23624f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 23634f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2364a04a10f8SKris Kennaway 23654f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_OPEN] = &channel_post_open; 23664f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 23674f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 23684f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 23694f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 23704f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 23714f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 23724f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 23734f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 23744f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; 23754f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 23764f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2377a04a10f8SKris Kennaway 23784f52dfbbSDag-Erling Smørgrav sc->channel_pre = pre; 23794f52dfbbSDag-Erling Smørgrav sc->channel_post = post; 2380a04a10f8SKris Kennaway } 2381a04a10f8SKris Kennaway 2382af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2383af12a3e7SDag-Erling Smørgrav static void 23844f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c) 2385af12a3e7SDag-Erling Smørgrav { 2386af12a3e7SDag-Erling Smørgrav if (c == NULL) 2387af12a3e7SDag-Erling Smørgrav return; 2388af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 23894f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, c->detach_close)) 2390af12a3e7SDag-Erling Smørgrav return; 23912f513db7SEd Maste 2392221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 23934f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, c->self, NULL); 2394af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2395af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2396af12a3e7SDag-Erling Smørgrav return; 2397221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2398af12a3e7SDag-Erling Smørgrav } 23994f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, 1)) 2400af12a3e7SDag-Erling Smørgrav return; 2401221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 24024f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2403af12a3e7SDag-Erling Smørgrav } 2404af12a3e7SDag-Erling Smørgrav 24054f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST }; 24064f52dfbbSDag-Erling Smørgrav 2407af12a3e7SDag-Erling Smørgrav static void 24081323ec57SEd Maste channel_handler(struct ssh *ssh, int table, time_t *unpause_secs) 2409a04a10f8SKris Kennaway { 24104f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 24114f52dfbbSDag-Erling Smørgrav chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; 2412b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2413a04a10f8SKris Kennaway Channel *c; 2414462c32cbSDag-Erling Smørgrav time_t now; 2415a04a10f8SKris Kennaway 2416e4a9863fSDag-Erling Smørgrav now = monotime(); 2417462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL) 2418462c32cbSDag-Erling Smørgrav *unpause_secs = 0; 24194f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 24204f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2421af12a3e7SDag-Erling Smørgrav if (c == NULL) 2422511b41d2SMark Murray continue; 2423b15c8340SDag-Erling Smørgrav if (c->delayed) { 24244f52dfbbSDag-Erling Smørgrav if (table == CHAN_PRE) 2425b15c8340SDag-Erling Smørgrav c->delayed = 0; 2426b15c8340SDag-Erling Smørgrav else 2427b15c8340SDag-Erling Smørgrav continue; 2428b15c8340SDag-Erling Smørgrav } 2429462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2430462c32cbSDag-Erling Smørgrav /* 2431462c32cbSDag-Erling Smørgrav * Run handlers that are not paused. 2432462c32cbSDag-Erling Smørgrav */ 2433462c32cbSDag-Erling Smørgrav if (c->notbefore <= now) 24341323ec57SEd Maste (*ftab[c->type])(ssh, c); 2435462c32cbSDag-Erling Smørgrav else if (unpause_secs != NULL) { 2436462c32cbSDag-Erling Smørgrav /* 2437462c32cbSDag-Erling Smørgrav * Collect the time that the earliest 2438462c32cbSDag-Erling Smørgrav * channel comes off pause. 2439462c32cbSDag-Erling Smørgrav */ 244019261079SEd Maste debug3_f("chan %d: skip for %d more " 244119261079SEd Maste "seconds", c->self, 2442462c32cbSDag-Erling Smørgrav (int)(c->notbefore - now)); 2443462c32cbSDag-Erling Smørgrav if (*unpause_secs == 0 || 2444462c32cbSDag-Erling Smørgrav (c->notbefore - now) < *unpause_secs) 2445462c32cbSDag-Erling Smørgrav *unpause_secs = c->notbefore - now; 2446462c32cbSDag-Erling Smørgrav } 2447462c32cbSDag-Erling Smørgrav } 24484f52dfbbSDag-Erling Smørgrav channel_garbage_collect(ssh, c); 2449511b41d2SMark Murray } 2450462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL && *unpause_secs != 0) 245119261079SEd Maste debug3_f("first channel unpauses in %d seconds", 245219261079SEd Maste (int)*unpause_secs); 2453511b41d2SMark Murray } 2454a04a10f8SKris Kennaway 2455af12a3e7SDag-Erling Smørgrav /* 24561323ec57SEd Maste * Create sockets before preparing IO. 24574f52dfbbSDag-Erling Smørgrav * This is necessary for things that need to happen after reading 24581323ec57SEd Maste * the network-input but need to be completed before IO event setup, e.g. 24591323ec57SEd Maste * because they may create new channels. 24604f52dfbbSDag-Erling Smørgrav */ 24614f52dfbbSDag-Erling Smørgrav static void 24621323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh) 24634f52dfbbSDag-Erling Smørgrav { 24644f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 24654f52dfbbSDag-Erling Smørgrav Channel *c; 24664f52dfbbSDag-Erling Smørgrav u_int i, oalloc; 24674f52dfbbSDag-Erling Smørgrav 24684f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 24694f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 24704f52dfbbSDag-Erling Smørgrav if (c == NULL) 24714f52dfbbSDag-Erling Smørgrav continue; 24724f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) 24731323ec57SEd Maste channel_before_prepare_io_rdynamic(ssh, c); 24744f52dfbbSDag-Erling Smørgrav } 24754f52dfbbSDag-Erling Smørgrav } 24764f52dfbbSDag-Erling Smørgrav 24771323ec57SEd Maste static void 24781323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c, 24791323ec57SEd Maste u_int pollfd_offset, struct pollfd *pfd) 2480a04a10f8SKris Kennaway { 24811323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 248287c1498dSEd Maste debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] " 248387c1498dSEd Maste "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d " 248487c1498dSEd Maste "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what, 248587c1498dSEd Maste c->rfd, c->wfd, c->efd, c->sock, 248687c1498dSEd Maste c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3], 248787c1498dSEd Maste c->io_want, c->io_ready, 248887c1498dSEd Maste pollfd_offset, pfd->fd, pfd->events, pfd->revents); 24891323ec57SEd Maste #endif 2490ca3176e7SBrian Feldman } 2491ca3176e7SBrian Feldman 24921323ec57SEd Maste /* Prepare pollfd entries for a single channel */ 24931323ec57SEd Maste static void 24941323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd, 24951323ec57SEd Maste struct pollfd *pfd, u_int npfd) 24961323ec57SEd Maste { 249787c1498dSEd Maste u_int ev, p = *next_pollfd; 24981323ec57SEd Maste 24991323ec57SEd Maste if (c == NULL) 25001323ec57SEd Maste return; 25011323ec57SEd Maste if (p + 4 > npfd) { 25021323ec57SEd Maste /* Shouldn't happen */ 25031323ec57SEd Maste fatal_f("channel %d: bad pfd offset %u (max %u)", 25041323ec57SEd Maste c->self, p, npfd); 25051323ec57SEd Maste } 250687c1498dSEd Maste c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1; 25071323ec57SEd Maste /* 25081323ec57SEd Maste * prepare c->rfd 25091323ec57SEd Maste * 25101323ec57SEd Maste * This is a special case, since c->rfd might be the same as 25111323ec57SEd Maste * c->wfd, c->efd and/or c->sock. Handle those here if they want 25121323ec57SEd Maste * IO too. 25131323ec57SEd Maste */ 25141323ec57SEd Maste if (c->rfd != -1) { 251587c1498dSEd Maste ev = 0; 25161323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_RFD) != 0) 251787c1498dSEd Maste ev |= POLLIN; 25181323ec57SEd Maste /* rfd == wfd */ 251987c1498dSEd Maste if (c->wfd == c->rfd) { 252087c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD) != 0) 252187c1498dSEd Maste ev |= POLLOUT; 252287c1498dSEd Maste } 25231323ec57SEd Maste /* rfd == efd */ 252487c1498dSEd Maste if (c->efd == c->rfd) { 252587c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 252687c1498dSEd Maste ev |= POLLIN; 252787c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 252887c1498dSEd Maste ev |= POLLOUT; 252987c1498dSEd Maste } 25301323ec57SEd Maste /* rfd == sock */ 253187c1498dSEd Maste if (c->sock == c->rfd) { 253287c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 253387c1498dSEd Maste ev |= POLLIN; 253487c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 253587c1498dSEd Maste ev |= POLLOUT; 253687c1498dSEd Maste } 253787c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 253887c1498dSEd Maste if (ev != 0) { 253987c1498dSEd Maste c->pfds[0] = p; 254087c1498dSEd Maste pfd[p].fd = c->rfd; 254187c1498dSEd Maste pfd[p].events = ev; 25421323ec57SEd Maste dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); 25431323ec57SEd Maste p++; 25441323ec57SEd Maste } 254587c1498dSEd Maste } 254687c1498dSEd Maste /* prepare c->wfd if wanting IO and not already handled above */ 25471323ec57SEd Maste if (c->wfd != -1 && c->rfd != c->wfd) { 254887c1498dSEd Maste ev = 0; 254987c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_WFD)) 255087c1498dSEd Maste ev |= POLLOUT; 255187c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 255287c1498dSEd Maste if (ev != 0) { 255387c1498dSEd Maste c->pfds[1] = p; 25541323ec57SEd Maste pfd[p].fd = c->wfd; 255587c1498dSEd Maste pfd[p].events = ev; 25561323ec57SEd Maste dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); 25571323ec57SEd Maste p++; 25581323ec57SEd Maste } 255987c1498dSEd Maste } 256087c1498dSEd Maste /* prepare c->efd if wanting IO and not already handled above */ 25611323ec57SEd Maste if (c->efd != -1 && c->rfd != c->efd) { 256287c1498dSEd Maste ev = 0; 25631323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) 256487c1498dSEd Maste ev |= POLLIN; 25651323ec57SEd Maste if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) 256687c1498dSEd Maste ev |= POLLOUT; 256787c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 256887c1498dSEd Maste if (ev != 0) { 256987c1498dSEd Maste c->pfds[2] = p; 257087c1498dSEd Maste pfd[p].fd = c->efd; 257187c1498dSEd Maste pfd[p].events = ev; 25721323ec57SEd Maste dump_channel_poll(__func__, "efd", c, p, &pfd[p]); 25731323ec57SEd Maste p++; 25741323ec57SEd Maste } 257587c1498dSEd Maste } 257687c1498dSEd Maste /* prepare c->sock if wanting IO and not already handled above */ 25771323ec57SEd Maste if (c->sock != -1 && c->rfd != c->sock) { 257887c1498dSEd Maste ev = 0; 257987c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) 258087c1498dSEd Maste ev |= POLLIN; 258187c1498dSEd Maste if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) 258287c1498dSEd Maste ev |= POLLOUT; 258387c1498dSEd Maste /* Pack a pfd entry if any event armed for this fd */ 258487c1498dSEd Maste if (ev != 0) { 258587c1498dSEd Maste c->pfds[3] = p; 25861323ec57SEd Maste pfd[p].fd = c->sock; 25871323ec57SEd Maste pfd[p].events = 0; 25881323ec57SEd Maste dump_channel_poll(__func__, "sock", c, p, &pfd[p]); 25891323ec57SEd Maste p++; 25901323ec57SEd Maste } 259187c1498dSEd Maste } 25921323ec57SEd Maste *next_pollfd = p; 25931323ec57SEd Maste } 25941323ec57SEd Maste 25951323ec57SEd Maste /* * Allocate/prepare poll structure */ 25961323ec57SEd Maste void 25971323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, 25981323ec57SEd Maste u_int *npfd_activep, u_int npfd_reserved, time_t *minwait_secs) 25991323ec57SEd Maste { 26001323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 26011323ec57SEd Maste u_int i, oalloc, p, npfd = npfd_reserved; 26021323ec57SEd Maste 26031323ec57SEd Maste channel_before_prepare_io(ssh); /* might create a new channel */ 2604*9fce8d41SEd Maste /* clear out I/O flags from last poll */ 2605*9fce8d41SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 2606*9fce8d41SEd Maste if (sc->channels[i] == NULL) 2607*9fce8d41SEd Maste continue; 2608*9fce8d41SEd Maste sc->channels[i]->io_want = sc->channels[i]->io_ready = 0; 2609*9fce8d41SEd Maste } 26101323ec57SEd Maste /* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ 26111323ec57SEd Maste if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) 26121323ec57SEd Maste fatal_f("too many channels"); /* shouldn't happen */ 26134f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 26141323ec57SEd Maste npfd += sc->channels_alloc * 4; 26151323ec57SEd Maste if (npfd > *npfd_allocp) { 26161323ec57SEd Maste *pfdp = xrecallocarray(*pfdp, *npfd_allocp, 26171323ec57SEd Maste npfd, sizeof(**pfdp)); 26181323ec57SEd Maste *npfd_allocp = npfd; 26191323ec57SEd Maste } 26201323ec57SEd Maste *npfd_activep = npfd_reserved; 26211323ec57SEd Maste if (ssh_packet_is_rekeying(ssh)) 26221323ec57SEd Maste return; 26231323ec57SEd Maste 26241323ec57SEd Maste oalloc = sc->channels_alloc; 26251323ec57SEd Maste 26261323ec57SEd Maste channel_handler(ssh, CHAN_PRE, minwait_secs); 26271323ec57SEd Maste 26281323ec57SEd Maste if (oalloc != sc->channels_alloc) { 26291323ec57SEd Maste /* shouldn't happen */ 26301323ec57SEd Maste fatal_f("channels_alloc changed during CHAN_PRE " 26311323ec57SEd Maste "(was %u, now %u)", oalloc, sc->channels_alloc); 26321323ec57SEd Maste } 26331323ec57SEd Maste 26341323ec57SEd Maste /* Prepare pollfd */ 26351323ec57SEd Maste p = npfd_reserved; 26361323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) 26371323ec57SEd Maste channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); 26381323ec57SEd Maste *npfd_activep = p; 26391323ec57SEd Maste } 26401323ec57SEd Maste 26411323ec57SEd Maste static void 264287c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd, 26431323ec57SEd Maste const char *what, u_int revents_mask, u_int ready) 26441323ec57SEd Maste { 26451323ec57SEd Maste struct pollfd *pfd = &pfds[p]; 26461323ec57SEd Maste 26471323ec57SEd Maste if (fd == -1) 26481323ec57SEd Maste return; 264987c1498dSEd Maste if (p == -1 || (u_int)p >= npfd) 265087c1498dSEd Maste fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd); 26511323ec57SEd Maste dump_channel_poll(__func__, what, c, p, pfd); 26521323ec57SEd Maste if (pfd->fd != fd) { 26531323ec57SEd Maste fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " 26541323ec57SEd Maste "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, 26551323ec57SEd Maste c->rfd, c->wfd, c->efd, c->sock); 26561323ec57SEd Maste } 26571323ec57SEd Maste if ((pfd->revents & POLLNVAL) != 0) { 26581323ec57SEd Maste fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", 26591323ec57SEd Maste c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); 26601323ec57SEd Maste } 26611323ec57SEd Maste if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) 26621323ec57SEd Maste c->io_ready |= ready & c->io_want; 2663a04a10f8SKris Kennaway } 2664a04a10f8SKris Kennaway 2665af12a3e7SDag-Erling Smørgrav /* 26661323ec57SEd Maste * After poll, perform any appropriate operations for channels which have 2667af12a3e7SDag-Erling Smørgrav * events pending. 2668af12a3e7SDag-Erling Smørgrav */ 2669a04a10f8SKris Kennaway void 26701323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) 2671a04a10f8SKris Kennaway { 26721323ec57SEd Maste struct ssh_channels *sc = ssh->chanctxt; 267387c1498dSEd Maste u_int i; 267487c1498dSEd Maste int p; 26751323ec57SEd Maste Channel *c; 26761323ec57SEd Maste 26771323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL 267887c1498dSEd Maste for (p = 0; p < (int)npfd; p++) { 26791323ec57SEd Maste if (pfd[p].revents == 0) 26801323ec57SEd Maste continue; 26811323ec57SEd Maste debug_f("pfd[%u].fd %d rev 0x%04x", 26821323ec57SEd Maste p, pfd[p].fd, pfd[p].revents); 26831323ec57SEd Maste } 26841323ec57SEd Maste #endif 26851323ec57SEd Maste 26861323ec57SEd Maste /* Convert pollfd into c->io_ready */ 26871323ec57SEd Maste for (i = 0; i < sc->channels_alloc; i++) { 26881323ec57SEd Maste c = sc->channels[i]; 268987c1498dSEd Maste if (c == NULL) 26901323ec57SEd Maste continue; 26911323ec57SEd Maste /* if rfd is shared with efd/sock then wfd should be too */ 26921323ec57SEd Maste if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && 26931323ec57SEd Maste (c->rfd == c->efd || c->rfd == c->sock)) { 26941323ec57SEd Maste /* Shouldn't happen */ 26951323ec57SEd Maste fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", 26961323ec57SEd Maste c->self, c->rfd, c->wfd, c->efd, c->sock); 26971323ec57SEd Maste } 26981323ec57SEd Maste c->io_ready = 0; 26991323ec57SEd Maste /* rfd, potentially shared with wfd, efd and sock */ 270087c1498dSEd Maste if (c->rfd != -1 && (p = c->pfds[0]) != -1) { 270187c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->rfd, 270287c1498dSEd Maste "rfd", POLLIN, SSH_CHAN_IO_RFD); 27031323ec57SEd Maste if (c->rfd == c->wfd) { 270487c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 270587c1498dSEd Maste "wfd/r", POLLOUT, SSH_CHAN_IO_WFD); 27061323ec57SEd Maste } 27071323ec57SEd Maste if (c->rfd == c->efd) { 270887c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 270987c1498dSEd Maste "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R); 271087c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 271187c1498dSEd Maste "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W); 27121323ec57SEd Maste } 27131323ec57SEd Maste if (c->rfd == c->sock) { 271487c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 271587c1498dSEd Maste "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R); 271687c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 271787c1498dSEd Maste "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W); 27181323ec57SEd Maste } 271987c1498dSEd Maste dump_channel_poll(__func__, "rfd", c, p, pfd); 27201323ec57SEd Maste } 27211323ec57SEd Maste /* wfd */ 272287c1498dSEd Maste if (c->wfd != -1 && c->wfd != c->rfd && 272387c1498dSEd Maste (p = c->pfds[1]) != -1) { 272487c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->wfd, 272587c1498dSEd Maste "wfd", POLLOUT, SSH_CHAN_IO_WFD); 272687c1498dSEd Maste dump_channel_poll(__func__, "wfd", c, p, pfd); 27271323ec57SEd Maste } 27281323ec57SEd Maste /* efd */ 272987c1498dSEd Maste if (c->efd != -1 && c->efd != c->rfd && 273087c1498dSEd Maste (p = c->pfds[2]) != -1) { 273187c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 273287c1498dSEd Maste "efdr", POLLIN, SSH_CHAN_IO_EFD_R); 273387c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->efd, 273487c1498dSEd Maste "efdw", POLLOUT, SSH_CHAN_IO_EFD_W); 273587c1498dSEd Maste dump_channel_poll(__func__, "efd", c, p, pfd); 27361323ec57SEd Maste } 27371323ec57SEd Maste /* sock */ 273887c1498dSEd Maste if (c->sock != -1 && c->sock != c->rfd && 273987c1498dSEd Maste (p = c->pfds[3]) != -1) { 274087c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 274187c1498dSEd Maste "sockr", POLLIN, SSH_CHAN_IO_SOCK_R); 274287c1498dSEd Maste fd_ready(c, p, pfd, npfd, c->sock, 274387c1498dSEd Maste "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W); 274487c1498dSEd Maste dump_channel_poll(__func__, "sock", c, p, pfd); 27451323ec57SEd Maste } 27461323ec57SEd Maste } 27471323ec57SEd Maste channel_handler(ssh, CHAN_POST, NULL); 2748511b41d2SMark Murray } 2749511b41d2SMark Murray 27504f52dfbbSDag-Erling Smørgrav /* 27514f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open or draining c->input. 27524f52dfbbSDag-Erling Smørgrav */ 27534f52dfbbSDag-Erling Smørgrav static void 27544f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c) 27554f52dfbbSDag-Erling Smørgrav { 27564f52dfbbSDag-Erling Smørgrav size_t len, plen; 27574f52dfbbSDag-Erling Smørgrav const u_char *pkt; 27584f52dfbbSDag-Erling Smørgrav int r; 27594f52dfbbSDag-Erling Smørgrav 27604f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->input)) == 0) { 27614f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 27624f52dfbbSDag-Erling Smørgrav /* 27634f52dfbbSDag-Erling Smørgrav * input-buffer is empty and read-socket shutdown: 27644f52dfbbSDag-Erling Smørgrav * tell peer, that we will not send more data: 27654f52dfbbSDag-Erling Smørgrav * send IEOF. 27664f52dfbbSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still 27674f52dfbbSDag-Erling Smørgrav * in use. 27684f52dfbbSDag-Erling Smørgrav */ 27694f52dfbbSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 27704f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 27714f52dfbbSDag-Erling Smørgrav "ibuf_empty delayed efd %d/(%zu)", 27724f52dfbbSDag-Erling Smørgrav c->self, c->efd, sshbuf_len(c->extended)); 27734f52dfbbSDag-Erling Smørgrav else 27744f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 27754f52dfbbSDag-Erling Smørgrav } 27764f52dfbbSDag-Erling Smørgrav return; 27774f52dfbbSDag-Erling Smørgrav } 27784f52dfbbSDag-Erling Smørgrav 27794f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 278019261079SEd Maste fatal_f("channel %d: no remote id", c->self); 27814f52dfbbSDag-Erling Smørgrav 27824f52dfbbSDag-Erling Smørgrav if (c->datagram) { 27834f52dfbbSDag-Erling Smørgrav /* Check datagram will fit; drop if not */ 27844f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) 278519261079SEd Maste fatal_fr(r, "channel %i: get datagram", c->self); 27864f52dfbbSDag-Erling Smørgrav /* 27874f52dfbbSDag-Erling Smørgrav * XXX this does tail-drop on the datagram queue which is 27884f52dfbbSDag-Erling Smørgrav * usually suboptimal compared to head-drop. Better to have 27894f52dfbbSDag-Erling Smørgrav * backpressure at read time? (i.e. read + discard) 27904f52dfbbSDag-Erling Smørgrav */ 27914f52dfbbSDag-Erling Smørgrav if (plen > c->remote_window || plen > c->remote_maxpacket) { 27924f52dfbbSDag-Erling Smørgrav debug("channel %d: datagram too big", c->self); 27934f52dfbbSDag-Erling Smørgrav return; 27944f52dfbbSDag-Erling Smørgrav } 27954f52dfbbSDag-Erling Smørgrav /* Enqueue it */ 27964f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 27974f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 27984f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || 279919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 280019261079SEd Maste fatal_fr(r, "channel %i: send datagram", c->self); 28014f52dfbbSDag-Erling Smørgrav c->remote_window -= plen; 28024f52dfbbSDag-Erling Smørgrav return; 28034f52dfbbSDag-Erling Smørgrav } 28044f52dfbbSDag-Erling Smørgrav 28054f52dfbbSDag-Erling Smørgrav /* Enqueue packet for buffered data. */ 28064f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 28074f52dfbbSDag-Erling Smørgrav len = c->remote_window; 28084f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 28094f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 28104f52dfbbSDag-Erling Smørgrav if (len == 0) 28114f52dfbbSDag-Erling Smørgrav return; 28124f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 28134f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 28144f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || 281519261079SEd Maste (r = sshpkt_send(ssh)) != 0) 281619261079SEd Maste fatal_fr(r, "channel %i: send data", c->self); 28174f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->input, len)) != 0) 281819261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 28194f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 28204f52dfbbSDag-Erling Smørgrav } 28214f52dfbbSDag-Erling Smørgrav 28224f52dfbbSDag-Erling Smørgrav /* 28234f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open c->extended in read mode. 28244f52dfbbSDag-Erling Smørgrav */ 28254f52dfbbSDag-Erling Smørgrav static void 28264f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c) 28274f52dfbbSDag-Erling Smørgrav { 28284f52dfbbSDag-Erling Smørgrav size_t len; 28294f52dfbbSDag-Erling Smørgrav int r; 28304f52dfbbSDag-Erling Smørgrav 28314f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->extended)) == 0) 28324f52dfbbSDag-Erling Smørgrav return; 28334f52dfbbSDag-Erling Smørgrav 28344f52dfbbSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %zu euse %d", c->self, 28354f52dfbbSDag-Erling Smørgrav c->remote_window, sshbuf_len(c->extended), c->extended_usage); 28364f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 28374f52dfbbSDag-Erling Smørgrav len = c->remote_window; 28384f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 28394f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 28404f52dfbbSDag-Erling Smørgrav if (len == 0) 28414f52dfbbSDag-Erling Smørgrav return; 28424f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 284319261079SEd Maste fatal_f("channel %d: no remote id", c->self); 28444f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || 28454f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 28464f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || 28474f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || 284819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 284919261079SEd Maste fatal_fr(r, "channel %i: data", c->self); 28504f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->extended, len)) != 0) 285119261079SEd Maste fatal_fr(r, "channel %i: consume", c->self); 28524f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 28534f52dfbbSDag-Erling Smørgrav debug2("channel %d: sent ext data %zu", c->self, len); 28544f52dfbbSDag-Erling Smørgrav } 2855af12a3e7SDag-Erling Smørgrav 2856ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 2857511b41d2SMark Murray void 28584f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh) 2859511b41d2SMark Murray { 28604f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 2861a04a10f8SKris Kennaway Channel *c; 28624f52dfbbSDag-Erling Smørgrav u_int i; 2863511b41d2SMark Murray 28644f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 28654f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2866af12a3e7SDag-Erling Smørgrav if (c == NULL) 2867af12a3e7SDag-Erling Smørgrav continue; 2868511b41d2SMark Murray 2869af12a3e7SDag-Erling Smørgrav /* 2870af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 2871af12a3e7SDag-Erling Smørgrav * incoming data. 2872af12a3e7SDag-Erling Smørgrav */ 2873a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 2874511b41d2SMark Murray continue; 28754f52dfbbSDag-Erling Smørgrav if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 2876ca3176e7SBrian Feldman /* XXX is this true? */ 28774f52dfbbSDag-Erling Smørgrav debug3("channel %d: will not send data after close", 28784f52dfbbSDag-Erling Smørgrav c->self); 2879511b41d2SMark Murray continue; 2880511b41d2SMark Murray } 2881511b41d2SMark Murray 2882511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 28834f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN || 28844f52dfbbSDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN) 28854f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(ssh, c); 2886a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 28874f52dfbbSDag-Erling Smørgrav if (!(c->flags & CHAN_EOF_SENT) && 28884f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ) 28894f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(ssh, c); 2890511b41d2SMark Murray } 2891511b41d2SMark Murray } 2892511b41d2SMark Murray 2893ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support */ 2894ca86bcf2SDag-Erling Smørgrav 2895ca86bcf2SDag-Erling Smørgrav /* 2896ca86bcf2SDag-Erling Smørgrav * When multiplexing channel messages for mux clients we have to deal 2897ca86bcf2SDag-Erling Smørgrav * with downstream messages from the mux client and upstream messages 2898ca86bcf2SDag-Erling Smørgrav * from the ssh server: 2899ca86bcf2SDag-Erling Smørgrav * 1) Handling downstream messages is straightforward and happens 2900ca86bcf2SDag-Erling Smørgrav * in channel_proxy_downstream(): 2901ca86bcf2SDag-Erling Smørgrav * - We forward all messages (mostly) unmodified to the server. 2902ca86bcf2SDag-Erling Smørgrav * - However, in order to route messages from upstream to the correct 2903ca86bcf2SDag-Erling Smørgrav * downstream client, we have to replace the channel IDs used by the 2904ca86bcf2SDag-Erling Smørgrav * mux clients with a unique channel ID because the mux clients might 2905ca86bcf2SDag-Erling Smørgrav * use conflicting channel IDs. 2906ca86bcf2SDag-Erling Smørgrav * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and 2907ca86bcf2SDag-Erling Smørgrav * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local 2908ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID 2909ca86bcf2SDag-Erling Smørgrav * with the newly allocated channel ID. 2910ca86bcf2SDag-Erling Smørgrav * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY 2911190cef3dSDag-Erling Smørgrav * channels and processed by channel_proxy_upstream(). The local channel ID 2912ca86bcf2SDag-Erling Smørgrav * is then translated back to the original mux client ID. 2913ca86bcf2SDag-Erling Smørgrav * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE 2914ca86bcf2SDag-Erling Smørgrav * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. 2915ca86bcf2SDag-Erling Smørgrav * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the 2916ca86bcf2SDag-Erling Smørgrav * downstream mux client are removed. 2917ca86bcf2SDag-Erling Smørgrav * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server 2918ca86bcf2SDag-Erling Smørgrav * requires more work, because they are not addressed to a specific 2919ca86bcf2SDag-Erling Smørgrav * channel. E.g. client_request_forwarded_tcpip() needs to figure 2920ca86bcf2SDag-Erling Smørgrav * out whether the request is addressed to the local client or a 2921ca86bcf2SDag-Erling Smørgrav * specific downstream client based on the listen-address/port. 2922190cef3dSDag-Erling Smørgrav * 6) Agent and X11-Forwarding have a similar problem and are currently 2923ca86bcf2SDag-Erling Smørgrav * not supported as the matching session/channel cannot be identified 2924ca86bcf2SDag-Erling Smørgrav * easily. 2925ca86bcf2SDag-Erling Smørgrav */ 2926ca86bcf2SDag-Erling Smørgrav 2927ca86bcf2SDag-Erling Smørgrav /* 2928ca86bcf2SDag-Erling Smørgrav * receive packets from downstream mux clients: 2929ca86bcf2SDag-Erling Smørgrav * channel callback fired on read from mux client, creates 2930ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs 2931ca86bcf2SDag-Erling Smørgrav * on channel creation. 2932ca86bcf2SDag-Erling Smørgrav */ 2933ca86bcf2SDag-Erling Smørgrav int 29344f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream) 2935ca86bcf2SDag-Erling Smørgrav { 2936ca86bcf2SDag-Erling Smørgrav Channel *c = NULL; 2937ca86bcf2SDag-Erling Smørgrav struct sshbuf *original = NULL, *modified = NULL; 2938ca86bcf2SDag-Erling Smørgrav const u_char *cp; 2939ca86bcf2SDag-Erling Smørgrav char *ctype = NULL, *listen_host = NULL; 2940ca86bcf2SDag-Erling Smørgrav u_char type; 2941ca86bcf2SDag-Erling Smørgrav size_t have; 29424f52dfbbSDag-Erling Smørgrav int ret = -1, r; 2943ca86bcf2SDag-Erling Smørgrav u_int id, remote_id, listen_port; 2944ca86bcf2SDag-Erling Smørgrav 29454f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(downstream->input, stderr); */ 29464f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) 2947ca86bcf2SDag-Erling Smørgrav != 0) { 294819261079SEd Maste error_fr(r, "parse"); 2949ca86bcf2SDag-Erling Smørgrav return -1; 2950ca86bcf2SDag-Erling Smørgrav } 2951ca86bcf2SDag-Erling Smørgrav if (have < 2) { 295219261079SEd Maste error_f("short message"); 2953ca86bcf2SDag-Erling Smørgrav return -1; 2954ca86bcf2SDag-Erling Smørgrav } 2955ca86bcf2SDag-Erling Smørgrav type = cp[1]; 2956ca86bcf2SDag-Erling Smørgrav /* skip padlen + type */ 2957ca86bcf2SDag-Erling Smørgrav cp += 2; 2958ca86bcf2SDag-Erling Smørgrav have -= 2; 2959ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 296019261079SEd Maste debug3_f("channel %u: down->up: type %u", 2961ca86bcf2SDag-Erling Smørgrav downstream->self, type); 2962ca86bcf2SDag-Erling Smørgrav 2963ca86bcf2SDag-Erling Smørgrav switch (type) { 2964ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN: 2965ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2966ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 296719261079SEd Maste error_f("alloc"); 2968ca86bcf2SDag-Erling Smørgrav goto out; 2969ca86bcf2SDag-Erling Smørgrav } 2970ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || 2971ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 297219261079SEd Maste error_fr(r, "parse"); 2973ca86bcf2SDag-Erling Smørgrav goto out; 2974ca86bcf2SDag-Erling Smørgrav } 29754f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, 2976ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, ctype, 1); 2977ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 2978ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; /* original downstream id */ 2979ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || 2980ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 2981ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 298219261079SEd Maste error_fr(r, "compose"); 29834f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2984ca86bcf2SDag-Erling Smørgrav goto out; 2985ca86bcf2SDag-Erling Smørgrav } 2986ca86bcf2SDag-Erling Smørgrav break; 2987ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2988ca86bcf2SDag-Erling Smørgrav /* 2989ca86bcf2SDag-Erling Smørgrav * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we 2990ca86bcf2SDag-Erling Smørgrav * need to parse 'remote_id' instead of 'ctype'. 2991ca86bcf2SDag-Erling Smørgrav */ 2992ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2993ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 299419261079SEd Maste error_f("alloc"); 2995ca86bcf2SDag-Erling Smørgrav goto out; 2996ca86bcf2SDag-Erling Smørgrav } 2997ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || 2998ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 299919261079SEd Maste error_fr(r, "parse"); 3000ca86bcf2SDag-Erling Smørgrav goto out; 3001ca86bcf2SDag-Erling Smørgrav } 30024f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, 3003ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); 3004ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 3005ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; 3006ca86bcf2SDag-Erling Smørgrav c->remote_id = remote_id; 30074f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 3008ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || 3009ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 3010ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 301119261079SEd Maste error_fr(r, "compose"); 30124f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3013ca86bcf2SDag-Erling Smørgrav goto out; 3014ca86bcf2SDag-Erling Smørgrav } 3015ca86bcf2SDag-Erling Smørgrav break; 3016ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_GLOBAL_REQUEST: 3017ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL) { 301819261079SEd Maste error_f("alloc"); 3019ca86bcf2SDag-Erling Smørgrav goto out; 3020ca86bcf2SDag-Erling Smørgrav } 3021ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { 302219261079SEd Maste error_fr(r, "parse"); 3023ca86bcf2SDag-Erling Smørgrav goto out; 3024ca86bcf2SDag-Erling Smørgrav } 3025ca86bcf2SDag-Erling Smørgrav if (strcmp(ctype, "tcpip-forward") != 0) { 302619261079SEd Maste error_f("unsupported request %s", ctype); 3027ca86bcf2SDag-Erling Smørgrav goto out; 3028ca86bcf2SDag-Erling Smørgrav } 3029ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u8(original, NULL)) != 0 || 3030ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || 3031ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &listen_port)) != 0) { 303219261079SEd Maste error_fr(r, "parse"); 3033ca86bcf2SDag-Erling Smørgrav goto out; 3034ca86bcf2SDag-Erling Smørgrav } 3035ca86bcf2SDag-Erling Smørgrav if (listen_port > 65535) { 303619261079SEd Maste error_f("tcpip-forward for %s: bad port %u", 303719261079SEd Maste listen_host, listen_port); 3038ca86bcf2SDag-Erling Smørgrav goto out; 3039ca86bcf2SDag-Erling Smørgrav } 3040ca86bcf2SDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 3041190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1, 30424f52dfbbSDag-Erling Smørgrav listen_host, NULL, (int)listen_port, downstream); 3043ca86bcf2SDag-Erling Smørgrav listen_host = NULL; 3044ca86bcf2SDag-Erling Smørgrav break; 3045ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3046ca86bcf2SDag-Erling Smørgrav if (have < 4) 3047ca86bcf2SDag-Erling Smørgrav break; 3048ca86bcf2SDag-Erling Smørgrav remote_id = PEEK_U32(cp); 30494f52dfbbSDag-Erling Smørgrav if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { 3050ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) 30514f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3052ca86bcf2SDag-Erling Smørgrav else 3053ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT; 3054ca86bcf2SDag-Erling Smørgrav } 3055ca86bcf2SDag-Erling Smørgrav break; 3056ca86bcf2SDag-Erling Smørgrav } 3057ca86bcf2SDag-Erling Smørgrav if (modified) { 3058ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3059ca86bcf2SDag-Erling Smørgrav (r = sshpkt_putb(ssh, modified)) != 0 || 3060ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 306119261079SEd Maste error_fr(r, "send"); 3062ca86bcf2SDag-Erling Smørgrav goto out; 3063ca86bcf2SDag-Erling Smørgrav } 3064ca86bcf2SDag-Erling Smørgrav } else { 3065ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 3066ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put(ssh, cp, have)) != 0 || 3067ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 306819261079SEd Maste error_fr(r, "send"); 3069ca86bcf2SDag-Erling Smørgrav goto out; 3070ca86bcf2SDag-Erling Smørgrav } 3071ca86bcf2SDag-Erling Smørgrav } 3072ca86bcf2SDag-Erling Smørgrav ret = 0; 3073ca86bcf2SDag-Erling Smørgrav out: 3074ca86bcf2SDag-Erling Smørgrav free(ctype); 3075ca86bcf2SDag-Erling Smørgrav free(listen_host); 3076ca86bcf2SDag-Erling Smørgrav sshbuf_free(original); 3077ca86bcf2SDag-Erling Smørgrav sshbuf_free(modified); 3078ca86bcf2SDag-Erling Smørgrav return ret; 3079ca86bcf2SDag-Erling Smørgrav } 3080ca86bcf2SDag-Erling Smørgrav 3081ca86bcf2SDag-Erling Smørgrav /* 3082ca86bcf2SDag-Erling Smørgrav * receive packets from upstream server and de-multiplex packets 3083ca86bcf2SDag-Erling Smørgrav * to correct downstream: 3084ca86bcf2SDag-Erling Smørgrav * implemented as a helper for channel input handlers, 3085ca86bcf2SDag-Erling Smørgrav * replaces local (proxy) channel ID with downstream channel ID. 3086ca86bcf2SDag-Erling Smørgrav */ 3087ca86bcf2SDag-Erling Smørgrav int 30884f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) 3089ca86bcf2SDag-Erling Smørgrav { 3090ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 3091ca86bcf2SDag-Erling Smørgrav Channel *downstream; 3092ca86bcf2SDag-Erling Smørgrav const u_char *cp = NULL; 3093ca86bcf2SDag-Erling Smørgrav size_t len; 3094ca86bcf2SDag-Erling Smørgrav int r; 3095ca86bcf2SDag-Erling Smørgrav 3096ca86bcf2SDag-Erling Smørgrav /* 3097ca86bcf2SDag-Erling Smørgrav * When receiving packets from the peer we need to check whether we 3098ca86bcf2SDag-Erling Smørgrav * need to forward the packets to the mux client. In this case we 3099190cef3dSDag-Erling Smørgrav * restore the original channel id and keep track of CLOSE messages, 3100ca86bcf2SDag-Erling Smørgrav * so we can cleanup the channel. 3101ca86bcf2SDag-Erling Smørgrav */ 3102ca86bcf2SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) 3103ca86bcf2SDag-Erling Smørgrav return 0; 3104ca86bcf2SDag-Erling Smørgrav if ((downstream = c->mux_ctx) == NULL) 3105ca86bcf2SDag-Erling Smørgrav return 0; 3106ca86bcf2SDag-Erling Smørgrav switch (type) { 3107ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3108ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_DATA: 3109ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EOF: 3110ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EXTENDED_DATA: 3111ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3112ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_FAILURE: 3113ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 3114ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_SUCCESS: 3115ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_FAILURE: 3116ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_REQUEST: 3117ca86bcf2SDag-Erling Smørgrav break; 3118ca86bcf2SDag-Erling Smørgrav default: 311919261079SEd Maste debug2_f("channel %u: unsupported type %u", c->self, type); 3120ca86bcf2SDag-Erling Smørgrav return 0; 3121ca86bcf2SDag-Erling Smørgrav } 3122ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 312319261079SEd Maste error_f("alloc reply"); 3124ca86bcf2SDag-Erling Smørgrav goto out; 3125ca86bcf2SDag-Erling Smørgrav } 3126ca86bcf2SDag-Erling Smørgrav /* get remaining payload (after id) */ 3127ca86bcf2SDag-Erling Smørgrav cp = sshpkt_ptr(ssh, &len); 3128ca86bcf2SDag-Erling Smørgrav if (cp == NULL) { 312919261079SEd Maste error_f("no packet"); 3130ca86bcf2SDag-Erling Smørgrav goto out; 3131ca86bcf2SDag-Erling Smørgrav } 3132ca86bcf2SDag-Erling Smørgrav /* translate id and send to muxclient */ 3133ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 3134ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, type)) != 0 || 3135ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || 3136ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put(b, cp, len)) != 0 || 31374f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(downstream->output, b)) != 0) { 313819261079SEd Maste error_fr(r, "compose muxclient"); 3139ca86bcf2SDag-Erling Smørgrav goto out; 3140ca86bcf2SDag-Erling Smørgrav } 3141ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(b, stderr); */ 3142ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 314319261079SEd Maste debug3_f("channel %u: up->down: type %u", c->self, type); 3144ca86bcf2SDag-Erling Smørgrav out: 3145ca86bcf2SDag-Erling Smørgrav /* update state */ 3146ca86bcf2SDag-Erling Smørgrav switch (type) { 3147ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 3148ca86bcf2SDag-Erling Smørgrav /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ 31494f52dfbbSDag-Erling Smørgrav if (cp && len > 4) { 3150ca86bcf2SDag-Erling Smørgrav c->remote_id = PEEK_U32(cp); 31514f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 31524f52dfbbSDag-Erling Smørgrav } 3153ca86bcf2SDag-Erling Smørgrav break; 3154ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 3155ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_SENT) 31564f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3157ca86bcf2SDag-Erling Smørgrav else 3158ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_RCVD; 3159ca86bcf2SDag-Erling Smørgrav break; 3160ca86bcf2SDag-Erling Smørgrav } 3161ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 3162ca86bcf2SDag-Erling Smørgrav return 1; 3163ca86bcf2SDag-Erling Smørgrav } 3164af12a3e7SDag-Erling Smørgrav 3165af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 3166511b41d2SMark Murray 31674f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */ 31684f52dfbbSDag-Erling Smørgrav static int 31694f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what) 3170511b41d2SMark Murray { 31714f52dfbbSDag-Erling Smørgrav u_int32_t id; 31724f52dfbbSDag-Erling Smørgrav int r; 31734f52dfbbSDag-Erling Smørgrav 31744f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &id)) != 0) { 317519261079SEd Maste error_r(r, "%s: parse id", where); 31764f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s message", what); 31774f52dfbbSDag-Erling Smørgrav } 31784f52dfbbSDag-Erling Smørgrav if (id > INT_MAX) { 317919261079SEd Maste error_r(r, "%s: bad channel id %u", where, id); 31804f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s channel id", what); 31814f52dfbbSDag-Erling Smørgrav } 31824f52dfbbSDag-Erling Smørgrav return (int)id; 31834f52dfbbSDag-Erling Smørgrav } 31844f52dfbbSDag-Erling Smørgrav 31854f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */ 31864f52dfbbSDag-Erling Smørgrav static Channel * 31874f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) 31884f52dfbbSDag-Erling Smørgrav { 31894f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, where, what); 3190a04a10f8SKris Kennaway Channel *c; 3191511b41d2SMark Murray 31924f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 31934f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, 31944f52dfbbSDag-Erling Smørgrav "%s packet referred to nonexistent channel %d", what, id); 31954f52dfbbSDag-Erling Smørgrav } 31964f52dfbbSDag-Erling Smørgrav return c; 31974f52dfbbSDag-Erling Smørgrav } 31984f52dfbbSDag-Erling Smørgrav 31994f52dfbbSDag-Erling Smørgrav int 32004f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh) 32014f52dfbbSDag-Erling Smørgrav { 32024f52dfbbSDag-Erling Smørgrav const u_char *data; 32034f52dfbbSDag-Erling Smørgrav size_t data_len, win_len; 32044f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "data"); 32054f52dfbbSDag-Erling Smørgrav int r; 32064f52dfbbSDag-Erling Smørgrav 32074f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3208ca86bcf2SDag-Erling Smørgrav return 0; 3209511b41d2SMark Murray 3210511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 3211a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 32124f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_OPEN && 32134f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_FINISH && 3214a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 3215bc5531deSDag-Erling Smørgrav return 0; 3216511b41d2SMark Murray 3217511b41d2SMark Murray /* Get the data. */ 321819261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 321919261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 322019261079SEd Maste fatal_fr(r, "channel %i: get data", c->self); 32214f52dfbbSDag-Erling Smørgrav 3222e2f6069cSDag-Erling Smørgrav win_len = data_len; 3223e2f6069cSDag-Erling Smørgrav if (c->datagram) 3224e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 3225a04a10f8SKris Kennaway 3226476cd3b2SDag-Erling Smørgrav /* 32274f52dfbbSDag-Erling Smørgrav * The sending side reduces its window as it sends data, so we 32284f52dfbbSDag-Erling Smørgrav * must 'fake' consumption of the data in order to ensure that window 32294f52dfbbSDag-Erling Smørgrav * updates are sent back. Otherwise the connection might deadlock. 3230476cd3b2SDag-Erling Smørgrav */ 32314f52dfbbSDag-Erling Smørgrav if (c->ostate != CHAN_OUTPUT_OPEN) { 3232e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 3233e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 3234bc5531deSDag-Erling Smørgrav return 0; 3235476cd3b2SDag-Erling Smørgrav } 3236476cd3b2SDag-Erling Smørgrav 3237e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 32384f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd big packet %zu, maxpack %u", 3239e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 32404f52dfbbSDag-Erling Smørgrav return 0; 3241a04a10f8SKris Kennaway } 3242e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 32434f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much data %zu, win %u", 3244e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_window); 3245bc5531deSDag-Erling Smørgrav return 0; 3246a04a10f8SKris Kennaway } 3247e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 32484f52dfbbSDag-Erling Smørgrav 32494f52dfbbSDag-Erling Smørgrav if (c->datagram) { 32504f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) 325119261079SEd Maste fatal_fr(r, "channel %i: append datagram", c->self); 32524f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) 325319261079SEd Maste fatal_fr(r, "channel %i: append data", c->self); 32544f52dfbbSDag-Erling Smørgrav 3255bc5531deSDag-Erling Smørgrav return 0; 3256511b41d2SMark Murray } 3257af12a3e7SDag-Erling Smørgrav 3258bc5531deSDag-Erling Smørgrav int 32594f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) 3260a04a10f8SKris Kennaway { 32614f52dfbbSDag-Erling Smørgrav const u_char *data; 32624f52dfbbSDag-Erling Smørgrav size_t data_len; 32634f52dfbbSDag-Erling Smørgrav u_int32_t tcode; 32644f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); 32654f52dfbbSDag-Erling Smørgrav int r; 3266a04a10f8SKris Kennaway 32674f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3268ca86bcf2SDag-Erling Smørgrav return 0; 3269a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 32704f52dfbbSDag-Erling Smørgrav logit("channel %d: ext data for non open", c->self); 3271bc5531deSDag-Erling Smørgrav return 0; 3272a04a10f8SKris Kennaway } 327380628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 327419261079SEd Maste if (ssh->compat & SSH_BUG_EXTEOF) 32754f52dfbbSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", 32764f52dfbbSDag-Erling Smørgrav c->self); 327780628bacSDag-Erling Smørgrav else 32784f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Received extended_data " 32794f52dfbbSDag-Erling Smørgrav "after EOF on channel %d.", c->self); 328080628bacSDag-Erling Smørgrav } 32814f52dfbbSDag-Erling Smørgrav 32824f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { 328319261079SEd Maste error_fr(r, "parse tcode"); 32844f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 32854f52dfbbSDag-Erling Smørgrav } 3286a04a10f8SKris Kennaway if (c->efd == -1 || 3287a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 3288a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 3289221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 3290bc5531deSDag-Erling Smørgrav return 0; 3291a04a10f8SKris Kennaway } 329219261079SEd Maste if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || 329319261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 329419261079SEd Maste error_fr(r, "parse data"); 32954f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 32964f52dfbbSDag-Erling Smørgrav } 32974f52dfbbSDag-Erling Smørgrav 3298a04a10f8SKris Kennaway if (data_len > c->local_window) { 32994f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %zu, win %u", 3300a04a10f8SKris Kennaway c->self, data_len, c->local_window); 3301bc5531deSDag-Erling Smørgrav return 0; 3302a04a10f8SKris Kennaway } 33034f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd ext data %zu", c->self, data_len); 33044f52dfbbSDag-Erling Smørgrav /* XXX sshpkt_getb? */ 33054f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, data, data_len)) != 0) 330619261079SEd Maste error_fr(r, "append"); 3307a04a10f8SKris Kennaway c->local_window -= data_len; 3308bc5531deSDag-Erling Smørgrav return 0; 3309a04a10f8SKris Kennaway } 3310a04a10f8SKris Kennaway 3311bc5531deSDag-Erling Smørgrav int 33124f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) 3313a04a10f8SKris Kennaway { 33144f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); 331519261079SEd Maste int r; 3316a04a10f8SKris Kennaway 331719261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 331819261079SEd Maste error_fr(r, "parse data"); 331919261079SEd Maste ssh_packet_disconnect(ssh, "Invalid ieof message"); 332019261079SEd Maste } 33214f52dfbbSDag-Erling Smørgrav 33224f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3323ca86bcf2SDag-Erling Smørgrav return 0; 33244f52dfbbSDag-Erling Smørgrav chan_rcvd_ieof(ssh, c); 3325af12a3e7SDag-Erling Smørgrav 3326af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 3327af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 3328af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 3329af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 33304f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) == 0) 33314f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 3332af12a3e7SDag-Erling Smørgrav } 3333bc5531deSDag-Erling Smørgrav return 0; 3334a04a10f8SKris Kennaway } 3335511b41d2SMark Murray 3336bc5531deSDag-Erling Smørgrav int 33374f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) 3338511b41d2SMark Murray { 33394f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); 334019261079SEd Maste int r; 3341511b41d2SMark Murray 33424f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3343ca86bcf2SDag-Erling Smørgrav return 0; 334419261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 334519261079SEd Maste error_fr(r, "parse data"); 334619261079SEd Maste ssh_packet_disconnect(ssh, "Invalid oclose message"); 334719261079SEd Maste } 33484f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 3349bc5531deSDag-Erling Smørgrav return 0; 3350511b41d2SMark Murray } 3351511b41d2SMark Murray 3352bc5531deSDag-Erling Smørgrav int 33534f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) 3354a04a10f8SKris Kennaway { 33554f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); 33564f52dfbbSDag-Erling Smørgrav u_int32_t remote_window, remote_maxpacket; 33574f52dfbbSDag-Erling Smørgrav int r; 3358af12a3e7SDag-Erling Smørgrav 33594f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3360ca86bcf2SDag-Erling Smørgrav return 0; 3361ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 336219261079SEd Maste ssh_packet_disconnect(ssh, "Received open confirmation for " 33634f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 33644f52dfbbSDag-Erling Smørgrav /* 33654f52dfbbSDag-Erling Smørgrav * Record the remote channel number and mark that the channel 33664f52dfbbSDag-Erling Smørgrav * is now open. 33674f52dfbbSDag-Erling Smørgrav */ 33684f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || 33694f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || 337019261079SEd Maste (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || 337119261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 337219261079SEd Maste error_fr(r, "window/maxpacket"); 337319261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open confirmation message"); 33744f52dfbbSDag-Erling Smørgrav } 3375a04a10f8SKris Kennaway 33764f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 33774f52dfbbSDag-Erling Smørgrav c->remote_window = remote_window; 33784f52dfbbSDag-Erling Smørgrav c->remote_maxpacket = remote_maxpacket; 33794f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 3380d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 338119261079SEd Maste debug2_f("channel %d: callback start", c->self); 33824f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); 338319261079SEd Maste debug2_f("channel %d: callback done", c->self); 3384a04a10f8SKris Kennaway } 3385221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 3386a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 3387bc5531deSDag-Erling Smørgrav return 0; 3388af12a3e7SDag-Erling Smørgrav } 3389af12a3e7SDag-Erling Smørgrav 3390af12a3e7SDag-Erling Smørgrav static char * 3391af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 3392af12a3e7SDag-Erling Smørgrav { 3393af12a3e7SDag-Erling Smørgrav switch (reason) { 3394af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 3395af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 3396af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 3397af12a3e7SDag-Erling Smørgrav return "connect failed"; 3398af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 3399af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 3400af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 3401af12a3e7SDag-Erling Smørgrav return "resource shortage"; 3402af12a3e7SDag-Erling Smørgrav } 3403af12a3e7SDag-Erling Smørgrav return "unknown reason"; 3404a04a10f8SKris Kennaway } 3405a04a10f8SKris Kennaway 3406bc5531deSDag-Erling Smørgrav int 34074f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) 3408a04a10f8SKris Kennaway { 34094f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); 34104f52dfbbSDag-Erling Smørgrav u_int32_t reason; 34114f52dfbbSDag-Erling Smørgrav char *msg = NULL; 34124f52dfbbSDag-Erling Smørgrav int r; 3413a04a10f8SKris Kennaway 34144f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3415ca86bcf2SDag-Erling Smørgrav return 0; 3416ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 341719261079SEd Maste ssh_packet_disconnect(ssh, "Received open failure for " 34184f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 34194f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { 342019261079SEd Maste error_fr(r, "parse reason"); 342119261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 3422ca3176e7SBrian Feldman } 34234f52dfbbSDag-Erling Smørgrav /* skip language */ 34244f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || 342519261079SEd Maste (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || 342619261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 342719261079SEd Maste error_fr(r, "parse msg/lang"); 342819261079SEd Maste ssh_packet_disconnect(ssh, "Invalid open failure message"); 34294f52dfbbSDag-Erling Smørgrav } 34304f52dfbbSDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", c->self, 3431af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 3432e4a9863fSDag-Erling Smørgrav free(msg); 3433e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 343419261079SEd Maste debug2_f("channel %d: callback start", c->self); 34354f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); 343619261079SEd Maste debug2_f("channel %d: callback done", c->self); 3437e2f6069cSDag-Erling Smørgrav } 3438cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 34394f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 3440bc5531deSDag-Erling Smørgrav return 0; 3441a04a10f8SKris Kennaway } 3442a04a10f8SKris Kennaway 3443bc5531deSDag-Erling Smørgrav int 34444f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) 3445a04a10f8SKris Kennaway { 34464f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "window adjust"); 3447a04a10f8SKris Kennaway Channel *c; 34484f52dfbbSDag-Erling Smørgrav u_int32_t adjust; 34494f52dfbbSDag-Erling Smørgrav u_int new_rwin; 34504f52dfbbSDag-Erling Smørgrav int r; 3451a04a10f8SKris Kennaway 34524f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 3453b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 3454bc5531deSDag-Erling Smørgrav return 0; 3455511b41d2SMark Murray } 34564f52dfbbSDag-Erling Smørgrav 34574f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3458ca86bcf2SDag-Erling Smørgrav return 0; 345919261079SEd Maste if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || 346019261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) { 346119261079SEd Maste error_fr(r, "parse adjust"); 346219261079SEd Maste ssh_packet_disconnect(ssh, "Invalid window adjust message"); 34634f52dfbbSDag-Erling Smørgrav } 34644f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", c->self, adjust); 34654f52dfbbSDag-Erling Smørgrav if ((new_rwin = c->remote_window + adjust) < c->remote_window) { 3466557f75e5SDag-Erling Smørgrav fatal("channel %d: adjust %u overflows remote window %u", 34674f52dfbbSDag-Erling Smørgrav c->self, adjust, c->remote_window); 34684f52dfbbSDag-Erling Smørgrav } 34694f52dfbbSDag-Erling Smørgrav c->remote_window = new_rwin; 3470bc5531deSDag-Erling Smørgrav return 0; 3471511b41d2SMark Murray } 3472511b41d2SMark Murray 3473bc5531deSDag-Erling Smørgrav int 34744f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) 3475af12a3e7SDag-Erling Smørgrav { 34764f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "status confirm"); 3477d4af9e69SDag-Erling Smørgrav Channel *c; 3478d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 3479d4af9e69SDag-Erling Smørgrav 3480d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 348119261079SEd Maste ssh_packet_set_alive_timeouts(ssh, 0); 3482d4af9e69SDag-Erling Smørgrav 348319261079SEd Maste debug2_f("type %d id %d", type, id); 3484d4af9e69SDag-Erling Smørgrav 34854f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 348619261079SEd Maste logit_f("%d: unknown", id); 3487bc5531deSDag-Erling Smørgrav return 0; 3488d4af9e69SDag-Erling Smørgrav } 34894f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3490ca86bcf2SDag-Erling Smørgrav return 0; 349119261079SEd Maste if (sshpkt_get_end(ssh) != 0) 349219261079SEd Maste ssh_packet_disconnect(ssh, "Invalid status confirm message"); 3493d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 3494bc5531deSDag-Erling Smørgrav return 0; 34954f52dfbbSDag-Erling Smørgrav cc->cb(ssh, type, c, cc->ctx); 3496d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 349719261079SEd Maste freezero(cc, sizeof(*cc)); 3498bc5531deSDag-Erling Smørgrav return 0; 3499d4af9e69SDag-Erling Smørgrav } 3500af12a3e7SDag-Erling Smørgrav 3501af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 3502511b41d2SMark Murray 3503511b41d2SMark Murray void 35044f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af) 3505511b41d2SMark Murray { 35064f52dfbbSDag-Erling Smørgrav ssh->chanctxt->IPv4or6 = af; 3507511b41d2SMark Murray } 3508511b41d2SMark Murray 350989986192SBrooks Davis 3510462c32cbSDag-Erling Smørgrav /* 3511462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 3512462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 3513462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 3514462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 3515462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 3516462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 3517462c32cbSDag-Erling Smørgrav * 3518462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 3519462c32cbSDag-Erling Smørgrav * 3520462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 3521462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 3522462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 3523a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 3524462c32cbSDag-Erling Smørgrav */ 3525462c32cbSDag-Erling Smørgrav static const char * 352619261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, 3527a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 3528462c32cbSDag-Erling Smørgrav { 3529462c32cbSDag-Erling Smørgrav const char *addr = NULL; 3530462c32cbSDag-Erling Smørgrav int wildcard = 0; 3531462c32cbSDag-Erling Smørgrav 3532462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 3533462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 3534a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 3535462c32cbSDag-Erling Smørgrav wildcard = 1; 3536a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 353719261079SEd Maste if (((ssh->compat & SSH_OLD_FORWARD_ADDR) && 3538462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 3539462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 3540a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 3541462c32cbSDag-Erling Smørgrav wildcard = 1; 3542f7167e0eSDag-Erling Smørgrav /* 3543f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 3544f7167e0eSDag-Erling Smørgrav * address and it was overridden. 3545f7167e0eSDag-Erling Smørgrav */ 3546f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 3547f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 3548f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 354919261079SEd Maste ssh_packet_send_debug(ssh, 355019261079SEd Maste "Forwarding listen address " 3551f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 3552f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 3553f7167e0eSDag-Erling Smørgrav } 3554a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 3555a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 3556a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 355719261079SEd Maste /* 355819261079SEd Maste * Accept explicit localhost address when 355919261079SEd Maste * GatewayPorts=yes. The "localhost" hostname is 356019261079SEd Maste * deliberately skipped here so it will listen on all 356119261079SEd Maste * available local address families. 356219261079SEd Maste */ 3563a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 3564f7167e0eSDag-Erling Smørgrav } 3565a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 3566a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3567a0ee8cc6SDag-Erling Smørgrav /* 3568a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 3569a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 3570a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 3571a0ee8cc6SDag-Erling Smørgrav */ 3572462c32cbSDag-Erling Smørgrav addr = listen_addr; 3573462c32cbSDag-Erling Smørgrav } 3574462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 3575462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 3576462c32cbSDag-Erling Smørgrav return addr; 3577462c32cbSDag-Erling Smørgrav } 3578462c32cbSDag-Erling Smørgrav 3579af12a3e7SDag-Erling Smørgrav static int 35804f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, 35814f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int *allocated_listen_port, 35824f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3583511b41d2SMark Murray { 3584af12a3e7SDag-Erling Smørgrav Channel *c; 3585b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 3586511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3587aa49c926SDag-Erling Smørgrav const char *host, *addr; 3588af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 3589cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 3590511b41d2SMark Murray 3591aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 3592511b41d2SMark Murray 3593557f75e5SDag-Erling Smørgrav if (is_client && fwd->connect_path != NULL) { 3594557f75e5SDag-Erling Smørgrav host = fwd->connect_path; 3595557f75e5SDag-Erling Smørgrav } else { 3596557f75e5SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 3597557f75e5SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 3598af12a3e7SDag-Erling Smørgrav if (host == NULL) { 3599af12a3e7SDag-Erling Smørgrav error("No forward host name."); 3600d4ecd108SDag-Erling Smørgrav return 0; 3601ca3176e7SBrian Feldman } 3602cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 3603ca3176e7SBrian Feldman error("Forward host name too long."); 3604d4ecd108SDag-Erling Smørgrav return 0; 3605ca3176e7SBrian Feldman } 3606557f75e5SDag-Erling Smørgrav } 3607ca3176e7SBrian Feldman 3608462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 360919261079SEd Maste addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, 3610a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 361119261079SEd Maste debug3_f("type %d wildcard %d addr %s", type, wildcard, 361219261079SEd Maste (addr == NULL) ? "NULL" : addr); 3613aa49c926SDag-Erling Smørgrav 3614aa49c926SDag-Erling Smørgrav /* 3615511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 3616511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 3617511b41d2SMark Murray */ 3618511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 36194f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 3620aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 3621511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3622a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 3623aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 3624aa49c926SDag-Erling Smørgrav if (addr == NULL) { 3625aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 362619261079SEd Maste ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", 3627d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3628aa49c926SDag-Erling Smørgrav } else { 362919261079SEd Maste error_f("getaddrinfo(%.64s): %s", addr, 3630d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3631aa49c926SDag-Erling Smørgrav } 3632d4ecd108SDag-Erling Smørgrav return 0; 3633aa49c926SDag-Erling Smørgrav } 3634cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 3635cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 3636511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3637cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 3638cce7d346SDag-Erling Smørgrav case AF_INET: 3639cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 3640cce7d346SDag-Erling Smørgrav sin_port; 3641cce7d346SDag-Erling Smørgrav break; 3642cce7d346SDag-Erling Smørgrav case AF_INET6: 3643cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 3644cce7d346SDag-Erling Smørgrav sin6_port; 3645cce7d346SDag-Erling Smørgrav break; 3646cce7d346SDag-Erling Smørgrav default: 3647511b41d2SMark Murray continue; 3648cce7d346SDag-Erling Smørgrav } 3649cce7d346SDag-Erling Smørgrav /* 3650cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 3651cce7d346SDag-Erling Smørgrav * same port for all address families. 3652cce7d346SDag-Erling Smørgrav */ 36534f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 36544f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && allocated_listen_port != NULL && 36554f52dfbbSDag-Erling Smørgrav *allocated_listen_port > 0) 3656cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 3657cce7d346SDag-Erling Smørgrav 3658511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 36594f52dfbbSDag-Erling Smørgrav strport, sizeof(strport), 36604f52dfbbSDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 366119261079SEd Maste error_f("getnameinfo failed"); 3662511b41d2SMark Murray continue; 3663511b41d2SMark Murray } 3664511b41d2SMark Murray /* Create a port to listen for the host. */ 3665221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 366619261079SEd Maste if (sock == -1) { 3667511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 366847dd1d1bSDag-Erling Smørgrav verbose("socket [%s]:%s: %.100s", ntop, strport, 366947dd1d1bSDag-Erling Smørgrav strerror(errno)); 3670511b41d2SMark Murray continue; 3671511b41d2SMark Murray } 3672b74df5b2SDag-Erling Smørgrav 367347dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 3674b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3675b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3676f388f5efSDag-Erling Smørgrav 3677cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 3678cce7d346SDag-Erling Smørgrav ntop, strport); 3679511b41d2SMark Murray 3680511b41d2SMark Murray /* Bind the socket to the address. */ 368119261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 36824f52dfbbSDag-Erling Smørgrav /* 36834f52dfbbSDag-Erling Smørgrav * address can be in if use ipv6 address is 36844f52dfbbSDag-Erling Smørgrav * already bound 36854f52dfbbSDag-Erling Smørgrav */ 3686989dd127SDag-Erling Smørgrav if (!ai->ai_next) 368747dd1d1bSDag-Erling Smørgrav error("bind [%s]:%s: %.100s", 368847dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3689989dd127SDag-Erling Smørgrav else 369047dd1d1bSDag-Erling Smørgrav verbose("bind [%s]:%s: %.100s", 369147dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3692989dd127SDag-Erling Smørgrav 3693511b41d2SMark Murray close(sock); 3694511b41d2SMark Murray continue; 3695511b41d2SMark Murray } 3696511b41d2SMark Murray /* Start listening for connections on the socket. */ 369719261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 369847dd1d1bSDag-Erling Smørgrav error("listen [%s]:%s: %.100s", ntop, strport, 369947dd1d1bSDag-Erling Smørgrav strerror(errno)); 3700511b41d2SMark Murray close(sock); 3701511b41d2SMark Murray continue; 3702511b41d2SMark Murray } 3703cce7d346SDag-Erling Smørgrav 3704cce7d346SDag-Erling Smørgrav /* 3705a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 3706cce7d346SDag-Erling Smørgrav * record what we got. 3707cce7d346SDag-Erling Smørgrav */ 37084f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 37094f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && 3710cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 3711cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 3712076ad2f8SDag-Erling Smørgrav *allocated_listen_port = get_local_port(sock); 3713cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 3714cce7d346SDag-Erling Smørgrav *allocated_listen_port); 3715cce7d346SDag-Erling Smørgrav } 3716cce7d346SDag-Erling Smørgrav 371760c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 37184f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "port listener", type, sock, sock, -1, 3719a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3720221552e4SDag-Erling Smørgrav 0, "port listener", 1); 3721cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 3722a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 3723462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 3724a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 372519261079SEd Maste !(ssh->compat & SSH_BUG_DYNAMIC_RPORT)) 3726462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 3727462c32cbSDag-Erling Smørgrav else 3728a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 3729511b41d2SMark Murray success = 1; 3730511b41d2SMark Murray } 3731511b41d2SMark Murray if (success == 0) 373219261079SEd Maste error_f("cannot listen to port: %d", fwd->listen_port); 3733511b41d2SMark Murray freeaddrinfo(aitop); 3734ca3176e7SBrian Feldman return success; 3735511b41d2SMark Murray } 3736511b41d2SMark Murray 3737a0ee8cc6SDag-Erling Smørgrav static int 37384f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, 37394f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 3740a0ee8cc6SDag-Erling Smørgrav { 3741a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 3742a0ee8cc6SDag-Erling Smørgrav const char *path; 3743a0ee8cc6SDag-Erling Smørgrav Channel *c; 3744a0ee8cc6SDag-Erling Smørgrav int port, sock; 3745a0ee8cc6SDag-Erling Smørgrav mode_t omask; 3746a0ee8cc6SDag-Erling Smørgrav 3747a0ee8cc6SDag-Erling Smørgrav switch (type) { 3748a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 3749a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3750a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 3751a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 3752a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 3753a0ee8cc6SDag-Erling Smørgrav return 0; 3754a0ee8cc6SDag-Erling Smørgrav } 3755a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 3756a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3757a0ee8cc6SDag-Erling Smørgrav } else { 3758a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 3759a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 3760a0ee8cc6SDag-Erling Smørgrav return 0; 3761a0ee8cc6SDag-Erling Smørgrav } 3762a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 3763a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 3764a0ee8cc6SDag-Erling Smørgrav return 0; 3765a0ee8cc6SDag-Erling Smørgrav } 3766a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 3767a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 3768a0ee8cc6SDag-Erling Smørgrav } 3769a0ee8cc6SDag-Erling Smørgrav break; 3770a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 3771a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 3772a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3773a0ee8cc6SDag-Erling Smørgrav break; 3774a0ee8cc6SDag-Erling Smørgrav default: 377519261079SEd Maste error_f("unexpected channel type %d", type); 3776a0ee8cc6SDag-Erling Smørgrav return 0; 3777a0ee8cc6SDag-Erling Smørgrav } 3778a0ee8cc6SDag-Erling Smørgrav 3779a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 3780a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 3781a0ee8cc6SDag-Erling Smørgrav return 0; 3782a0ee8cc6SDag-Erling Smørgrav } 3783a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 3784a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 3785a0ee8cc6SDag-Erling Smørgrav return 0; 3786a0ee8cc6SDag-Erling Smørgrav } 3787a0ee8cc6SDag-Erling Smørgrav 378819261079SEd Maste debug3_f("type %d path %s", type, fwd->listen_path); 3789a0ee8cc6SDag-Erling Smørgrav 3790a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 3791a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 3792a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 3793a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 3794a0ee8cc6SDag-Erling Smørgrav umask(omask); 3795a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 3796a0ee8cc6SDag-Erling Smørgrav return 0; 3797a0ee8cc6SDag-Erling Smørgrav 3798a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 3799a0ee8cc6SDag-Erling Smørgrav 3800a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 38014f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "unix listener", type, sock, sock, -1, 3802a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3803a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 3804a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 3805a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 3806a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 3807a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 3808a0ee8cc6SDag-Erling Smørgrav return 1; 3809a0ee8cc6SDag-Erling Smørgrav } 3810a0ee8cc6SDag-Erling Smørgrav 3811a0ee8cc6SDag-Erling Smørgrav static int 38124f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh, 38134f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 381421e764dfSDag-Erling Smørgrav { 381521e764dfSDag-Erling Smørgrav u_int i; 381621e764dfSDag-Erling Smørgrav int found = 0; 381721e764dfSDag-Erling Smørgrav 38184f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 38194f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3820462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3821462c32cbSDag-Erling Smørgrav continue; 3822462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 382319261079SEd Maste debug2_f("close channel %d", i); 38244f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3825462c32cbSDag-Erling Smørgrav found = 1; 3826462c32cbSDag-Erling Smørgrav } 3827462c32cbSDag-Erling Smørgrav } 382821e764dfSDag-Erling Smørgrav 38294f52dfbbSDag-Erling Smørgrav return found; 3830462c32cbSDag-Erling Smørgrav } 3831462c32cbSDag-Erling Smørgrav 3832a0ee8cc6SDag-Erling Smørgrav static int 38334f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) 3834462c32cbSDag-Erling Smørgrav { 3835462c32cbSDag-Erling Smørgrav u_int i; 3836462c32cbSDag-Erling Smørgrav int found = 0; 3837a0ee8cc6SDag-Erling Smørgrav 38384f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 38394f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3840a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3841a0ee8cc6SDag-Erling Smørgrav continue; 3842a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 3843a0ee8cc6SDag-Erling Smørgrav continue; 3844a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 384519261079SEd Maste debug2_f("close channel %d", i); 38464f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3847a0ee8cc6SDag-Erling Smørgrav found = 1; 3848a0ee8cc6SDag-Erling Smørgrav } 3849a0ee8cc6SDag-Erling Smørgrav } 3850a0ee8cc6SDag-Erling Smørgrav 38514f52dfbbSDag-Erling Smørgrav return found; 3852a0ee8cc6SDag-Erling Smørgrav } 3853a0ee8cc6SDag-Erling Smørgrav 3854a0ee8cc6SDag-Erling Smørgrav int 38554f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) 3856a0ee8cc6SDag-Erling Smørgrav { 38574f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 38584f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(ssh, 38594f52dfbbSDag-Erling Smørgrav fwd->listen_path); 38604f52dfbbSDag-Erling Smørgrav } else { 38614f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(ssh, 38624f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 38634f52dfbbSDag-Erling Smørgrav } 3864a0ee8cc6SDag-Erling Smørgrav } 3865a0ee8cc6SDag-Erling Smørgrav 3866a0ee8cc6SDag-Erling Smørgrav static int 38674f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh, 38684f52dfbbSDag-Erling Smørgrav const char *lhost, u_short lport, int cport, 38694f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3870a0ee8cc6SDag-Erling Smørgrav { 3871a0ee8cc6SDag-Erling Smørgrav u_int i; 3872a0ee8cc6SDag-Erling Smørgrav int found = 0; 387319261079SEd Maste const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); 3874462c32cbSDag-Erling Smørgrav 38754f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 38764f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3877462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 3878462c32cbSDag-Erling Smørgrav continue; 3879462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 3880462c32cbSDag-Erling Smørgrav continue; 3881462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 3882462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 3883462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 3884462c32cbSDag-Erling Smørgrav continue; 3885462c32cbSDag-Erling Smørgrav } else { 3886462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 3887462c32cbSDag-Erling Smørgrav continue; 3888462c32cbSDag-Erling Smørgrav } 3889462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 3890462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 3891462c32cbSDag-Erling Smørgrav continue; 3892462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 389319261079SEd Maste debug2_f("close channel %d", i); 38944f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 389521e764dfSDag-Erling Smørgrav found = 1; 389621e764dfSDag-Erling Smørgrav } 389721e764dfSDag-Erling Smørgrav } 389821e764dfSDag-Erling Smørgrav 38994f52dfbbSDag-Erling Smørgrav return found; 390021e764dfSDag-Erling Smørgrav } 390121e764dfSDag-Erling Smørgrav 3902a0ee8cc6SDag-Erling Smørgrav static int 39034f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) 3904a0ee8cc6SDag-Erling Smørgrav { 3905a0ee8cc6SDag-Erling Smørgrav u_int i; 3906a0ee8cc6SDag-Erling Smørgrav int found = 0; 3907a0ee8cc6SDag-Erling Smørgrav 3908a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 390919261079SEd Maste error_f("no path specified."); 3910a0ee8cc6SDag-Erling Smørgrav return 0; 3911a0ee8cc6SDag-Erling Smørgrav } 3912a0ee8cc6SDag-Erling Smørgrav 39134f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 39144f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3915a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 3916a0ee8cc6SDag-Erling Smørgrav continue; 3917a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 3918a0ee8cc6SDag-Erling Smørgrav continue; 3919a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 392019261079SEd Maste debug2_f("close channel %d", i); 39214f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3922a0ee8cc6SDag-Erling Smørgrav found = 1; 3923a0ee8cc6SDag-Erling Smørgrav } 3924a0ee8cc6SDag-Erling Smørgrav } 3925a0ee8cc6SDag-Erling Smørgrav 39264f52dfbbSDag-Erling Smørgrav return found; 3927a0ee8cc6SDag-Erling Smørgrav } 3928a0ee8cc6SDag-Erling Smørgrav 3929a0ee8cc6SDag-Erling Smørgrav int 39304f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh, 39314f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 3932af12a3e7SDag-Erling Smørgrav { 3933a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 39344f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(ssh, 39354f52dfbbSDag-Erling Smørgrav fwd->listen_path); 39364f52dfbbSDag-Erling Smørgrav } else { 39374f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(ssh, 39384f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, cport, fwd_opts); 39394f52dfbbSDag-Erling Smørgrav } 39404f52dfbbSDag-Erling Smørgrav } 39414f52dfbbSDag-Erling Smørgrav 39424f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */ 39434f52dfbbSDag-Erling Smørgrav int 39444f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh, 39454f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 39464f52dfbbSDag-Erling Smørgrav { 39474f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 39484f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 3949a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 3950a0ee8cc6SDag-Erling Smørgrav } else { 39514f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 39524f52dfbbSDag-Erling Smørgrav SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); 3953a0ee8cc6SDag-Erling Smørgrav } 3954af12a3e7SDag-Erling Smørgrav } 3955af12a3e7SDag-Erling Smørgrav 3956190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */ 3957190cef3dSDag-Erling Smørgrav static int 3958190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd) 3959190cef3dSDag-Erling Smørgrav { 3960190cef3dSDag-Erling Smørgrav int ret; 3961190cef3dSDag-Erling Smørgrav char *lhost; 3962190cef3dSDag-Erling Smørgrav 3963190cef3dSDag-Erling Smørgrav /* XXX add ACLs for streamlocal */ 3964190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 3965190cef3dSDag-Erling Smørgrav return 1; 3966190cef3dSDag-Erling Smørgrav 3967190cef3dSDag-Erling Smørgrav if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) 3968190cef3dSDag-Erling Smørgrav return 0; 3969190cef3dSDag-Erling Smørgrav 3970190cef3dSDag-Erling Smørgrav if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && 3971190cef3dSDag-Erling Smørgrav allowed_open->listen_port != fwd->listen_port) 3972190cef3dSDag-Erling Smørgrav return 0; 3973190cef3dSDag-Erling Smørgrav 3974190cef3dSDag-Erling Smørgrav /* Match hostnames case-insensitively */ 3975190cef3dSDag-Erling Smørgrav lhost = xstrdup(fwd->listen_host); 3976190cef3dSDag-Erling Smørgrav lowercase(lhost); 3977190cef3dSDag-Erling Smørgrav ret = match_pattern(lhost, allowed_open->listen_host); 3978190cef3dSDag-Erling Smørgrav free(lhost); 3979190cef3dSDag-Erling Smørgrav 3980190cef3dSDag-Erling Smørgrav return ret; 3981190cef3dSDag-Erling Smørgrav } 3982190cef3dSDag-Erling Smørgrav 3983190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */ 3984190cef3dSDag-Erling Smørgrav static int 3985190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) 3986190cef3dSDag-Erling Smørgrav { 3987190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3988190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->remote_perms; 3989190cef3dSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 3990190cef3dSDag-Erling Smørgrav struct permission *perm; 3991190cef3dSDag-Erling Smørgrav 3992190cef3dSDag-Erling Smørgrav /* XXX apply GatewayPorts override before checking? */ 3993190cef3dSDag-Erling Smørgrav 3994190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 3995190cef3dSDag-Erling Smørgrav if (!permit) { 3996190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 3997190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 3998190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 3999190cef3dSDag-Erling Smørgrav permit = 1; 4000190cef3dSDag-Erling Smørgrav break; 4001190cef3dSDag-Erling Smørgrav } 4002190cef3dSDag-Erling Smørgrav } 4003190cef3dSDag-Erling Smørgrav } 4004190cef3dSDag-Erling Smørgrav 4005190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4006190cef3dSDag-Erling Smørgrav permit_adm = 0; 4007190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4008190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4009190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 4010190cef3dSDag-Erling Smørgrav permit_adm = 1; 4011190cef3dSDag-Erling Smørgrav break; 4012190cef3dSDag-Erling Smørgrav } 4013190cef3dSDag-Erling Smørgrav } 4014190cef3dSDag-Erling Smørgrav } 4015190cef3dSDag-Erling Smørgrav 4016190cef3dSDag-Erling Smørgrav return permit && permit_adm; 4017190cef3dSDag-Erling Smørgrav } 4018190cef3dSDag-Erling Smørgrav 4019af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 4020af12a3e7SDag-Erling Smørgrav int 40214f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, 4022a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 4023af12a3e7SDag-Erling Smørgrav { 4024190cef3dSDag-Erling Smørgrav if (!check_rfwd_permission(ssh, fwd)) { 402519261079SEd Maste ssh_packet_send_debug(ssh, "port forwarding refused"); 402619261079SEd Maste if (fwd->listen_path != NULL) 402719261079SEd Maste /* XXX always allowed, see remote_open_match() */ 402819261079SEd Maste logit("Received request from %.100s port %d to " 402919261079SEd Maste "remote forward to path \"%.100s\", " 403019261079SEd Maste "but the request was denied.", 403119261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 403219261079SEd Maste fwd->listen_path); 403319261079SEd Maste else if(fwd->listen_host != NULL) 403419261079SEd Maste logit("Received request from %.100s port %d to " 403519261079SEd Maste "remote forward to host %.100s port %d, " 403619261079SEd Maste "but the request was denied.", 403719261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 403819261079SEd Maste fwd->listen_host, fwd->listen_port ); 403919261079SEd Maste else 404019261079SEd Maste logit("Received request from %.100s port %d to remote " 404119261079SEd Maste "forward, but the request was denied.", 404219261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 4043190cef3dSDag-Erling Smørgrav return 0; 4044190cef3dSDag-Erling Smørgrav } 4045a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 40464f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 4047a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 4048a0ee8cc6SDag-Erling Smørgrav } else { 40494f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 4050a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 4051a0ee8cc6SDag-Erling Smørgrav fwd_opts); 4052a0ee8cc6SDag-Erling Smørgrav } 4053af12a3e7SDag-Erling Smørgrav } 4054af12a3e7SDag-Erling Smørgrav 4055511b41d2SMark Murray /* 4056462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 4057462c32cbSDag-Erling Smørgrav * this server. 4058462c32cbSDag-Erling Smørgrav */ 4059462c32cbSDag-Erling Smørgrav static const char * 4060462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 4061462c32cbSDag-Erling Smørgrav { 4062462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 4063462c32cbSDag-Erling Smørgrav return "localhost"; 4064462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 4065462c32cbSDag-Erling Smørgrav return ""; 4066462c32cbSDag-Erling Smørgrav } else 4067462c32cbSDag-Erling Smørgrav return listen_host; 4068462c32cbSDag-Erling Smørgrav } 4069462c32cbSDag-Erling Smørgrav 4070462c32cbSDag-Erling Smørgrav /* 4071511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 4072511b41d2SMark Murray * the secure channel to host:port from local side. 4073462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 4074190cef3dSDag-Erling Smørgrav * channel_update_permission(). 4075511b41d2SMark Murray */ 4076333ee039SDag-Erling Smørgrav int 40774f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) 4078511b41d2SMark Murray { 40794f52dfbbSDag-Erling Smørgrav int r, success = 0, idx = -1; 40804f52dfbbSDag-Erling Smørgrav char *host_to_connect, *listen_host, *listen_path; 40814f52dfbbSDag-Erling Smørgrav int port_to_connect, listen_port; 4082ca3176e7SBrian Feldman 4083511b41d2SMark Murray /* Send the forward request to the remote side. */ 4084a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 40854f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 40864f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 40874f52dfbbSDag-Erling Smørgrav "streamlocal-forward@openssh.com")) != 0 || 40884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 40894f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || 40904f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 40914f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 409219261079SEd Maste fatal_fr(r, "request streamlocal"); 4093a0ee8cc6SDag-Erling Smørgrav } else { 40944f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 40954f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || 40964f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 40974f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 40984f52dfbbSDag-Erling Smørgrav channel_rfwd_bind_host(fwd->listen_host))) != 0 || 40994f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || 41004f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 41014f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 410219261079SEd Maste fatal_fr(r, "request tcpip-forward"); 4103a0ee8cc6SDag-Erling Smørgrav } 4104ca3176e7SBrian Feldman /* Assume that server accepts the request */ 4105ca3176e7SBrian Feldman success = 1; 4106ca3176e7SBrian Feldman if (success) { 4107e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 41084f52dfbbSDag-Erling Smørgrav host_to_connect = listen_host = listen_path = NULL; 41094f52dfbbSDag-Erling Smørgrav port_to_connect = listen_port = 0; 4110a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 41114f52dfbbSDag-Erling Smørgrav host_to_connect = xstrdup(fwd->connect_path); 41124f52dfbbSDag-Erling Smørgrav port_to_connect = PORT_STREAMLOCAL; 4113a0ee8cc6SDag-Erling Smørgrav } else { 41144f52dfbbSDag-Erling Smørgrav host_to_connect = xstrdup(fwd->connect_host); 41154f52dfbbSDag-Erling Smørgrav port_to_connect = fwd->connect_port; 4116a0ee8cc6SDag-Erling Smørgrav } 4117a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 41184f52dfbbSDag-Erling Smørgrav listen_path = xstrdup(fwd->listen_path); 41194f52dfbbSDag-Erling Smørgrav listen_port = PORT_STREAMLOCAL; 4120a0ee8cc6SDag-Erling Smørgrav } else { 41214f52dfbbSDag-Erling Smørgrav if (fwd->listen_host != NULL) 41224f52dfbbSDag-Erling Smørgrav listen_host = xstrdup(fwd->listen_host); 41234f52dfbbSDag-Erling Smørgrav listen_port = fwd->listen_port; 4124a0ee8cc6SDag-Erling Smørgrav } 4125190cef3dSDag-Erling Smørgrav idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, 41264f52dfbbSDag-Erling Smørgrav host_to_connect, port_to_connect, 41274f52dfbbSDag-Erling Smørgrav listen_host, listen_path, listen_port, NULL); 4128511b41d2SMark Murray } 41294f52dfbbSDag-Erling Smørgrav return idx; 4130a04a10f8SKris Kennaway } 4131511b41d2SMark Murray 4132a0ee8cc6SDag-Erling Smørgrav static int 4133190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost, 4134a0ee8cc6SDag-Erling Smørgrav int requestedport) 4135a0ee8cc6SDag-Erling Smørgrav { 4136a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4137a0ee8cc6SDag-Erling Smørgrav return 0; 4138a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 4139a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 4140a0ee8cc6SDag-Erling Smørgrav return 0; 4141076ad2f8SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 4142076ad2f8SDag-Erling Smørgrav strcmp(allowed_open->host_to_connect, requestedhost) != 0) 4143a0ee8cc6SDag-Erling Smørgrav return 0; 4144a0ee8cc6SDag-Erling Smørgrav return 1; 4145a0ee8cc6SDag-Erling Smørgrav } 4146a0ee8cc6SDag-Erling Smørgrav 4147a0ee8cc6SDag-Erling Smørgrav /* 4148a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 4149a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 4150a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 4151a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 4152a0ee8cc6SDag-Erling Smørgrav */ 4153a0ee8cc6SDag-Erling Smørgrav static int 4154190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open, 4155a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 4156a0ee8cc6SDag-Erling Smørgrav { 4157a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 4158a0ee8cc6SDag-Erling Smørgrav 4159a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4160a0ee8cc6SDag-Erling Smørgrav return 0; 4161a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 4162a0ee8cc6SDag-Erling Smørgrav return 0; 4163a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 4164a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 4165a0ee8cc6SDag-Erling Smørgrav return 1; 4166a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 4167a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 4168a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 4169190cef3dSDag-Erling Smørgrav if (allowed_host == NULL || requestedhost == NULL || 4170a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 4171a0ee8cc6SDag-Erling Smørgrav return 0; 4172a0ee8cc6SDag-Erling Smørgrav return 1; 4173a0ee8cc6SDag-Erling Smørgrav } 4174a0ee8cc6SDag-Erling Smørgrav 4175a0ee8cc6SDag-Erling Smørgrav static int 4176190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open, 4177a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 4178a0ee8cc6SDag-Erling Smørgrav { 4179a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 4180a0ee8cc6SDag-Erling Smørgrav return 0; 4181a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 4182a0ee8cc6SDag-Erling Smørgrav return 0; 4183a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 4184a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 4185a0ee8cc6SDag-Erling Smørgrav return 0; 4186a0ee8cc6SDag-Erling Smørgrav return 1; 4187a0ee8cc6SDag-Erling Smørgrav } 4188a0ee8cc6SDag-Erling Smørgrav 4189511b41d2SMark Murray /* 419021e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 419121e764dfSDag-Erling Smørgrav * local side. 419221e764dfSDag-Erling Smørgrav */ 4193a0ee8cc6SDag-Erling Smørgrav static int 41944f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh, 41954f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 419621e764dfSDag-Erling Smørgrav { 41974f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4198190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 41994f52dfbbSDag-Erling Smørgrav int r; 42004f52dfbbSDag-Erling Smørgrav u_int i; 420119261079SEd Maste struct permission *perm = NULL; 420221e764dfSDag-Erling Smørgrav 4203190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4204190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4205190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, host, port, 0)) 420621e764dfSDag-Erling Smørgrav break; 4207190cef3dSDag-Erling Smørgrav perm = NULL; 420821e764dfSDag-Erling Smørgrav } 4209190cef3dSDag-Erling Smørgrav if (perm == NULL) { 421019261079SEd Maste debug_f("requested forward not found"); 4211462c32cbSDag-Erling Smørgrav return -1; 421221e764dfSDag-Erling Smørgrav } 42134f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 42144f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || 42154f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 42164f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || 42174f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, port)) != 0 || 42184f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 421919261079SEd Maste fatal_fr(r, "send cancel"); 422021e764dfSDag-Erling Smørgrav 4221190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4222462c32cbSDag-Erling Smørgrav 4223462c32cbSDag-Erling Smørgrav return 0; 422421e764dfSDag-Erling Smørgrav } 422521e764dfSDag-Erling Smørgrav 422621e764dfSDag-Erling Smørgrav /* 4227a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 4228a0ee8cc6SDag-Erling Smørgrav * path from local side. 4229a0ee8cc6SDag-Erling Smørgrav */ 4230a0ee8cc6SDag-Erling Smørgrav static int 42314f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) 4232a0ee8cc6SDag-Erling Smørgrav { 42334f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4234190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 42354f52dfbbSDag-Erling Smørgrav int r; 42364f52dfbbSDag-Erling Smørgrav u_int i; 423719261079SEd Maste struct permission *perm = NULL; 4238a0ee8cc6SDag-Erling Smørgrav 4239190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4240190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4241190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) 4242a0ee8cc6SDag-Erling Smørgrav break; 4243190cef3dSDag-Erling Smørgrav perm = NULL; 4244a0ee8cc6SDag-Erling Smørgrav } 4245190cef3dSDag-Erling Smørgrav if (perm == NULL) { 424619261079SEd Maste debug_f("requested forward not found"); 4247a0ee8cc6SDag-Erling Smørgrav return -1; 4248a0ee8cc6SDag-Erling Smørgrav } 42494f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 42504f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 42514f52dfbbSDag-Erling Smørgrav "cancel-streamlocal-forward@openssh.com")) != 0 || 42524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 42534f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, path)) != 0 || 42544f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 425519261079SEd Maste fatal_fr(r, "send cancel"); 4256a0ee8cc6SDag-Erling Smørgrav 4257190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 4258a0ee8cc6SDag-Erling Smørgrav 4259a0ee8cc6SDag-Erling Smørgrav return 0; 4260a0ee8cc6SDag-Erling Smørgrav } 4261a0ee8cc6SDag-Erling Smørgrav 4262a0ee8cc6SDag-Erling Smørgrav /* 4263a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 4264a0ee8cc6SDag-Erling Smørgrav */ 4265a0ee8cc6SDag-Erling Smørgrav int 42664f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) 4267a0ee8cc6SDag-Erling Smørgrav { 4268a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 42694f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_streamlocal(ssh, 42704f52dfbbSDag-Erling Smørgrav fwd->listen_path); 4271a0ee8cc6SDag-Erling Smørgrav } else { 42724f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_tcpip(ssh, 42734f52dfbbSDag-Erling Smørgrav fwd->listen_host, 42744f52dfbbSDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port); 4275a0ee8cc6SDag-Erling Smørgrav } 4276a0ee8cc6SDag-Erling Smørgrav } 4277a0ee8cc6SDag-Erling Smørgrav 4278a0ee8cc6SDag-Erling Smørgrav /* 4279190cef3dSDag-Erling Smørgrav * Permits opening to any host/port if permitted_user[] is empty. This is 4280ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 4281ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 4282ca3176e7SBrian Feldman */ 4283ca3176e7SBrian Feldman void 4284190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where) 4285ca3176e7SBrian Feldman { 4286190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 4287190cef3dSDag-Erling Smørgrav 4288190cef3dSDag-Erling Smørgrav if (pset->num_permitted_user == 0) 4289190cef3dSDag-Erling Smørgrav pset->all_permitted = 1; 4290ca3176e7SBrian Feldman } 4291ca3176e7SBrian Feldman 4292190cef3dSDag-Erling Smørgrav /* 4293190cef3dSDag-Erling Smørgrav * Permit the specified host/port for forwarding. 4294190cef3dSDag-Erling Smørgrav */ 4295ca3176e7SBrian Feldman void 4296190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where, 4297190cef3dSDag-Erling Smørgrav char *host, int port) 4298ca3176e7SBrian Feldman { 4299190cef3dSDag-Erling Smørgrav int local = where == FORWARD_LOCAL; 4300190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 43014f52dfbbSDag-Erling Smørgrav 4302190cef3dSDag-Erling Smørgrav debug("allow %s forwarding to host %s port %d", 4303190cef3dSDag-Erling Smørgrav fwd_ident(who, where), host, port); 4304190cef3dSDag-Erling Smørgrav /* 4305190cef3dSDag-Erling Smørgrav * Remote forwards set listen_host/port, local forwards set 4306190cef3dSDag-Erling Smørgrav * host/port_to_connect. 4307190cef3dSDag-Erling Smørgrav */ 4308190cef3dSDag-Erling Smørgrav permission_set_add(ssh, who, where, 4309190cef3dSDag-Erling Smørgrav local ? host : 0, local ? port : 0, 4310190cef3dSDag-Erling Smørgrav local ? NULL : host, NULL, local ? 0 : port, NULL); 4311190cef3dSDag-Erling Smørgrav pset->all_permitted = 0; 4312190cef3dSDag-Erling Smørgrav } 4313190cef3dSDag-Erling Smørgrav 4314190cef3dSDag-Erling Smørgrav /* 4315190cef3dSDag-Erling Smørgrav * Administratively disable forwarding. 4316190cef3dSDag-Erling Smørgrav */ 4317190cef3dSDag-Erling Smørgrav void 4318190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where) 4319190cef3dSDag-Erling Smørgrav { 4320190cef3dSDag-Erling Smørgrav channel_clear_permission(ssh, FORWARD_ADM, where); 4321190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_ADM, where, 4322190cef3dSDag-Erling Smørgrav NULL, 0, NULL, NULL, 0, NULL); 4323190cef3dSDag-Erling Smørgrav } 4324190cef3dSDag-Erling Smørgrav 4325190cef3dSDag-Erling Smørgrav /* 4326190cef3dSDag-Erling Smørgrav * Clear a list of permitted opens. 4327190cef3dSDag-Erling Smørgrav */ 4328190cef3dSDag-Erling Smørgrav void 4329190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where) 4330190cef3dSDag-Erling Smørgrav { 4331190cef3dSDag-Erling Smørgrav struct permission **permp; 4332190cef3dSDag-Erling Smørgrav u_int *npermp; 4333190cef3dSDag-Erling Smørgrav 4334190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 4335190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); 4336190cef3dSDag-Erling Smørgrav *npermp = 0; 4337ca3176e7SBrian Feldman } 4338ca3176e7SBrian Feldman 4339462c32cbSDag-Erling Smørgrav /* 4340462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 4341462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 4342462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 4343462c32cbSDag-Erling Smørgrav */ 4344462c32cbSDag-Erling Smørgrav void 4345190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport) 4346462c32cbSDag-Erling Smørgrav { 4347190cef3dSDag-Erling Smørgrav struct permission_set *pset = &ssh->chanctxt->local_perms; 43484f52dfbbSDag-Erling Smørgrav 4349190cef3dSDag-Erling Smørgrav if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { 435019261079SEd Maste debug_f("index out of range: %d num_permitted_user %d", 435119261079SEd Maste idx, pset->num_permitted_user); 4352462c32cbSDag-Erling Smørgrav return; 4353462c32cbSDag-Erling Smørgrav } 4354462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 4355462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 4356462c32cbSDag-Erling Smørgrav newport, 4357190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].host_to_connect, 4358190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].port_to_connect); 43594f52dfbbSDag-Erling Smørgrav if (newport <= 0) 4360190cef3dSDag-Erling Smørgrav fwd_perm_clear(&pset->permitted_user[idx]); 43614f52dfbbSDag-Erling Smørgrav else { 4362190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].listen_port = 436319261079SEd Maste (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 4364462c32cbSDag-Erling Smørgrav } 4365462c32cbSDag-Erling Smørgrav } 4366462c32cbSDag-Erling Smørgrav 4367462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 4368462c32cbSDag-Erling Smørgrav int 4369462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 4370462c32cbSDag-Erling Smørgrav { 4371462c32cbSDag-Erling Smørgrav int port; 4372462c32cbSDag-Erling Smørgrav 4373462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 4374462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 4375462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 4376462c32cbSDag-Erling Smørgrav return port; 4377462c32cbSDag-Erling Smørgrav return -1; 4378462c32cbSDag-Erling Smørgrav } 4379462c32cbSDag-Erling Smørgrav 4380d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 4381d4af9e69SDag-Erling Smørgrav static int 4382d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 4383d4af9e69SDag-Erling Smørgrav { 4384d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 4385a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 43864f52dfbbSDag-Erling Smørgrav char ntop[NI_MAXHOST]; 43874f52dfbbSDag-Erling Smørgrav char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; 4388d4af9e69SDag-Erling Smørgrav 4389d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 4390a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 4391a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 4392a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 4393a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 4394a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 4395a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 4396a0ee8cc6SDag-Erling Smørgrav break; 4397a0ee8cc6SDag-Erling Smørgrav case AF_INET: 4398a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 4399d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 4400d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 4401d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 4402d4af9e69SDag-Erling Smørgrav error("connect_next: getnameinfo failed"); 4403511b41d2SMark Murray continue; 4404511b41d2SMark Murray } 4405a0ee8cc6SDag-Erling Smørgrav break; 4406a0ee8cc6SDag-Erling Smørgrav default: 4407a0ee8cc6SDag-Erling Smørgrav continue; 4408a0ee8cc6SDag-Erling Smørgrav } 4409d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 4410d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 4411d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 4412511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4413e73e9afaSDag-Erling Smørgrav else 4414e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 4415511b41d2SMark Murray continue; 4416511b41d2SMark Murray } 441721e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 441819261079SEd Maste fatal_f("set_nonblock(%d)", sock); 4419d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 4420d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 4421d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s): " 4422d4af9e69SDag-Erling Smørgrav "%.100s", cctx->host, ntop, strport, 4423511b41d2SMark Murray strerror(errno)); 4424d4af9e69SDag-Erling Smørgrav saved_errno = errno; 4425511b41d2SMark Murray close(sock); 4426d4af9e69SDag-Erling Smørgrav errno = saved_errno; 4427511b41d2SMark Murray continue; /* fail -- try next */ 4428511b41d2SMark Murray } 4429a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 4430a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 4431d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s) " 4432d4af9e69SDag-Erling Smørgrav "in progress, fd=%d", cctx->host, ntop, strport, sock); 4433d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 4434a04a10f8SKris Kennaway return sock; 4435a04a10f8SKris Kennaway } 4436ca3176e7SBrian Feldman return -1; 4437ca3176e7SBrian Feldman } 4438ca3176e7SBrian Feldman 4439d4af9e69SDag-Erling Smørgrav static void 4440d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 4441d4af9e69SDag-Erling Smørgrav { 4442e4a9863fSDag-Erling Smørgrav free(cctx->host); 4443a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 4444a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 4445a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 4446a0ee8cc6SDag-Erling Smørgrav else 4447d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 4448a0ee8cc6SDag-Erling Smørgrav } 4449b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 4450d4af9e69SDag-Erling Smørgrav } 4451d4af9e69SDag-Erling Smørgrav 4452d93a896eSDag-Erling Smørgrav /* 44534f52dfbbSDag-Erling Smørgrav * Return connecting socket to remote host:port or local socket path, 4454d93a896eSDag-Erling Smørgrav * passing back the failure reason if appropriate. 4455d93a896eSDag-Erling Smørgrav */ 44564f52dfbbSDag-Erling Smørgrav static int 44574f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, 44584f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, struct channel_connect *cctx, 4459d93a896eSDag-Erling Smørgrav int *reason, const char **errmsg) 4460d4af9e69SDag-Erling Smørgrav { 4461d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 4462d4af9e69SDag-Erling Smørgrav int gaierr; 4463d4af9e69SDag-Erling Smørgrav int sock = -1; 4464d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 4465a0ee8cc6SDag-Erling Smørgrav 4466a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 4467a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 4468a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 4469a0ee8cc6SDag-Erling Smørgrav 4470a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 4471a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 44724f52dfbbSDag-Erling Smørgrav return -1; 4473a0ee8cc6SDag-Erling Smørgrav } 4474a0ee8cc6SDag-Erling Smørgrav 4475a0ee8cc6SDag-Erling Smørgrav /* 4476a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 4477a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 4478a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 4479a0ee8cc6SDag-Erling Smørgrav */ 4480a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 4481a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 4482a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 4483a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 4484a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 44854f52dfbbSDag-Erling Smørgrav ai->ai_socktype = socktype; 4486a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 4487a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 4488a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 4489a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 44904f52dfbbSDag-Erling Smørgrav cctx->aitop = ai; 4491a0ee8cc6SDag-Erling Smørgrav } else { 4492d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 44934f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 44944f52dfbbSDag-Erling Smørgrav hints.ai_socktype = socktype; 4495d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 44964f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) 4497d93a896eSDag-Erling Smørgrav != 0) { 4498d93a896eSDag-Erling Smørgrav if (errmsg != NULL) 4499d93a896eSDag-Erling Smørgrav *errmsg = ssh_gai_strerror(gaierr); 4500d93a896eSDag-Erling Smørgrav if (reason != NULL) 4501d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_CONNECT_FAILED; 4502a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 4503d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 45044f52dfbbSDag-Erling Smørgrav return -1; 4505d4af9e69SDag-Erling Smørgrav } 4506a0ee8cc6SDag-Erling Smørgrav } 4507d4af9e69SDag-Erling Smørgrav 45084f52dfbbSDag-Erling Smørgrav cctx->host = xstrdup(name); 45094f52dfbbSDag-Erling Smørgrav cctx->port = port; 45104f52dfbbSDag-Erling Smørgrav cctx->ai = cctx->aitop; 4511d4af9e69SDag-Erling Smørgrav 45124f52dfbbSDag-Erling Smørgrav if ((sock = connect_next(cctx)) == -1) { 4513d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 4514a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 45154f52dfbbSDag-Erling Smørgrav return -1; 4516d4af9e69SDag-Erling Smørgrav } 45174f52dfbbSDag-Erling Smørgrav 45184f52dfbbSDag-Erling Smørgrav return sock; 4519d4af9e69SDag-Erling Smørgrav } 4520d4af9e69SDag-Erling Smørgrav 4521d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 4522d93a896eSDag-Erling Smørgrav static Channel * 45234f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port, 45244f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4525d93a896eSDag-Erling Smørgrav { 45264f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 45274f52dfbbSDag-Erling Smørgrav Channel *c; 45284f52dfbbSDag-Erling Smørgrav int sock; 45294f52dfbbSDag-Erling Smørgrav 45304f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 45314f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 45324f52dfbbSDag-Erling Smørgrav &cctx, NULL, NULL); 45334f52dfbbSDag-Erling Smørgrav if (sock == -1) { 45344f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 45354f52dfbbSDag-Erling Smørgrav return NULL; 45364f52dfbbSDag-Erling Smørgrav } 45374f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 45384f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 45394f52dfbbSDag-Erling Smørgrav c->host_port = port; 45404f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 45414f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 45424f52dfbbSDag-Erling Smørgrav 45434f52dfbbSDag-Erling Smørgrav return c; 4544d93a896eSDag-Erling Smørgrav } 4545d93a896eSDag-Erling Smørgrav 4546ca86bcf2SDag-Erling Smørgrav /* 4547ca86bcf2SDag-Erling Smørgrav * returns either the newly connected channel or the downstream channel 4548ca86bcf2SDag-Erling Smørgrav * that needs to deal with this connection. 4549ca86bcf2SDag-Erling Smørgrav */ 4550d4af9e69SDag-Erling Smørgrav Channel * 45514f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, 4552a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 4553d4af9e69SDag-Erling Smørgrav { 45544f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4555190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 45564f52dfbbSDag-Erling Smørgrav u_int i; 4557190cef3dSDag-Erling Smørgrav struct permission *perm; 4558d4af9e69SDag-Erling Smørgrav 4559190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4560190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4561190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, 4562190cef3dSDag-Erling Smørgrav listen_host, listen_port, 1)) { 4563190cef3dSDag-Erling Smørgrav if (perm->downstream) 4564190cef3dSDag-Erling Smørgrav return perm->downstream; 4565190cef3dSDag-Erling Smørgrav if (perm->port_to_connect == 0) 45664f52dfbbSDag-Erling Smørgrav return rdynamic_connect_prepare(ssh, 45674f52dfbbSDag-Erling Smørgrav ctype, rname); 45684f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4569190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 45704f52dfbbSDag-Erling Smørgrav ctype, rname); 4571d4af9e69SDag-Erling Smørgrav } 4572d4af9e69SDag-Erling Smørgrav } 4573d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 4574d4af9e69SDag-Erling Smørgrav listen_port); 4575d4af9e69SDag-Erling Smørgrav return NULL; 4576d4af9e69SDag-Erling Smørgrav } 4577d4af9e69SDag-Erling Smørgrav 4578a0ee8cc6SDag-Erling Smørgrav Channel * 45794f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path, 45804f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4581a0ee8cc6SDag-Erling Smørgrav { 45824f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4583190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 45844f52dfbbSDag-Erling Smørgrav u_int i; 4585190cef3dSDag-Erling Smørgrav struct permission *perm; 4586a0ee8cc6SDag-Erling Smørgrav 4587190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4588190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4589190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) { 45904f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4591190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 45924f52dfbbSDag-Erling Smørgrav ctype, rname); 4593a0ee8cc6SDag-Erling Smørgrav } 4594a0ee8cc6SDag-Erling Smørgrav } 4595a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 4596a0ee8cc6SDag-Erling Smørgrav path); 4597a0ee8cc6SDag-Erling Smørgrav return NULL; 4598a0ee8cc6SDag-Erling Smørgrav } 4599a0ee8cc6SDag-Erling Smørgrav 4600ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 4601d4af9e69SDag-Erling Smørgrav Channel * 46024f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, 46034f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, int *reason, const char **errmsg) 4604ca3176e7SBrian Feldman { 46054f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4606190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 46074f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 46084f52dfbbSDag-Erling Smørgrav Channel *c; 46094f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 46104f52dfbbSDag-Erling Smørgrav int sock; 4611190cef3dSDag-Erling Smørgrav struct permission *perm; 4612ca3176e7SBrian Feldman 4613190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4614ca3176e7SBrian Feldman if (!permit) { 4615190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4616190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4617190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4618ca3176e7SBrian Feldman permit = 1; 4619a0ee8cc6SDag-Erling Smørgrav break; 4620a0ee8cc6SDag-Erling Smørgrav } 4621ca3176e7SBrian Feldman } 46224f52dfbbSDag-Erling Smørgrav } 4623333ee039SDag-Erling Smørgrav 4624190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4625333ee039SDag-Erling Smørgrav permit_adm = 0; 4626190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4627190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4628190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4629333ee039SDag-Erling Smørgrav permit_adm = 1; 4630a0ee8cc6SDag-Erling Smørgrav break; 4631a0ee8cc6SDag-Erling Smørgrav } 4632333ee039SDag-Erling Smørgrav } 46334f52dfbbSDag-Erling Smørgrav } 4634333ee039SDag-Erling Smørgrav 4635333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 463619261079SEd Maste logit("Received request from %.100s port %d to connect to " 463719261079SEd Maste "host %.100s port %d, but the request was denied.", 463819261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); 4639d93a896eSDag-Erling Smørgrav if (reason != NULL) 4640d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; 4641d4af9e69SDag-Erling Smørgrav return NULL; 4642ca3176e7SBrian Feldman } 46434f52dfbbSDag-Erling Smørgrav 46444f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 46454f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 46464f52dfbbSDag-Erling Smørgrav &cctx, reason, errmsg); 46474f52dfbbSDag-Erling Smørgrav if (sock == -1) { 46484f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 46494f52dfbbSDag-Erling Smørgrav return NULL; 46504f52dfbbSDag-Erling Smørgrav } 46514f52dfbbSDag-Erling Smørgrav 46524f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 46534f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 46544f52dfbbSDag-Erling Smørgrav c->host_port = port; 46554f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 46564f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 46574f52dfbbSDag-Erling Smørgrav 46584f52dfbbSDag-Erling Smørgrav return c; 4659ca3176e7SBrian Feldman } 4660ca3176e7SBrian Feldman 4661a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 4662a0ee8cc6SDag-Erling Smørgrav Channel * 46634f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path, 46644f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4665a0ee8cc6SDag-Erling Smørgrav { 46664f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4667190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 46684f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4669190cef3dSDag-Erling Smørgrav struct permission *perm; 4670a0ee8cc6SDag-Erling Smørgrav 4671190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4672a0ee8cc6SDag-Erling Smørgrav if (!permit) { 4673190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4674190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4675190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4676a0ee8cc6SDag-Erling Smørgrav permit = 1; 4677a0ee8cc6SDag-Erling Smørgrav break; 4678a0ee8cc6SDag-Erling Smørgrav } 4679a0ee8cc6SDag-Erling Smørgrav } 46804f52dfbbSDag-Erling Smørgrav } 4681a0ee8cc6SDag-Erling Smørgrav 4682190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4683a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 4684190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4685190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4686190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4687a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 4688a0ee8cc6SDag-Erling Smørgrav break; 4689a0ee8cc6SDag-Erling Smørgrav } 4690a0ee8cc6SDag-Erling Smørgrav } 46914f52dfbbSDag-Erling Smørgrav } 4692a0ee8cc6SDag-Erling Smørgrav 4693a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 4694a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 4695a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 4696a0ee8cc6SDag-Erling Smørgrav return NULL; 4697a0ee8cc6SDag-Erling Smørgrav } 46984f52dfbbSDag-Erling Smørgrav return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); 4699a0ee8cc6SDag-Erling Smørgrav } 4700a0ee8cc6SDag-Erling Smørgrav 470121e764dfSDag-Erling Smørgrav void 47024f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh) 470321e764dfSDag-Erling Smørgrav { 47044f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 470521e764dfSDag-Erling Smørgrav struct winsize ws; 47064f52dfbbSDag-Erling Smørgrav int r; 47074f52dfbbSDag-Erling Smørgrav u_int i; 470821e764dfSDag-Erling Smørgrav 47094f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 47104f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || 47114f52dfbbSDag-Erling Smørgrav sc->channels[i]->type != SSH_CHANNEL_OPEN) 471221e764dfSDag-Erling Smørgrav continue; 471319261079SEd Maste if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) 471421e764dfSDag-Erling Smørgrav continue; 47154f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, i, "window-change", 0); 47164f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 47174f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 47184f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 47194f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || 47204f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 472119261079SEd Maste fatal_fr(r, "channel %u; send window-change", i); 472221e764dfSDag-Erling Smørgrav } 472321e764dfSDag-Erling Smørgrav } 472421e764dfSDag-Erling Smørgrav 47254f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ 47264f52dfbbSDag-Erling Smørgrav static Channel * 47274f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) 47284f52dfbbSDag-Erling Smørgrav { 47294f52dfbbSDag-Erling Smørgrav Channel *c; 47304f52dfbbSDag-Erling Smørgrav int r; 47314f52dfbbSDag-Erling Smørgrav 47324f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, 47334f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 47344f52dfbbSDag-Erling Smørgrav c->host_port = 0; 47354f52dfbbSDag-Erling Smørgrav c->path = NULL; 47364f52dfbbSDag-Erling Smørgrav 47374f52dfbbSDag-Erling Smørgrav /* 47384f52dfbbSDag-Erling Smørgrav * We need to open the channel before we have a FD, 47394f52dfbbSDag-Erling Smørgrav * so that we can get SOCKS header from peer. 47404f52dfbbSDag-Erling Smørgrav */ 47414f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 47424f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 47434f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 47444f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 474519261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) 474619261079SEd Maste fatal_fr(r, "channel %i; confirm", c->self); 47474f52dfbbSDag-Erling Smørgrav return c; 47484f52dfbbSDag-Erling Smørgrav } 47494f52dfbbSDag-Erling Smørgrav 47504f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */ 47514f52dfbbSDag-Erling Smørgrav static int 47524f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c) 47534f52dfbbSDag-Erling Smørgrav { 475419261079SEd Maste struct ssh_channels *sc = ssh->chanctxt; 475519261079SEd Maste struct permission_set *pset = &sc->local_perms; 475619261079SEd Maste struct permission *perm; 47574f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 475819261079SEd Maste u_int i, permit_adm = 1; 47594f52dfbbSDag-Erling Smørgrav int sock; 47604f52dfbbSDag-Erling Smørgrav 476119261079SEd Maste if (pset->num_permitted_admin > 0) { 476219261079SEd Maste permit_adm = 0; 476319261079SEd Maste for (i = 0; i < pset->num_permitted_admin; i++) { 476419261079SEd Maste perm = &pset->permitted_admin[i]; 476519261079SEd Maste if (open_match(perm, c->path, c->host_port)) { 476619261079SEd Maste permit_adm = 1; 476719261079SEd Maste break; 476819261079SEd Maste } 476919261079SEd Maste } 477019261079SEd Maste } 477119261079SEd Maste if (!permit_adm) { 477219261079SEd Maste debug_f("requested forward not permitted"); 477319261079SEd Maste return -1; 477419261079SEd Maste } 477519261079SEd Maste 47764f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 47774f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, 47784f52dfbbSDag-Erling Smørgrav NULL, &cctx, NULL, NULL); 47794f52dfbbSDag-Erling Smørgrav if (sock == -1) 47804f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 47814f52dfbbSDag-Erling Smørgrav else { 47824f52dfbbSDag-Erling Smørgrav /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ 47834f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_RDYNAMIC_FINISH; 47844f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 47854f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); 47864f52dfbbSDag-Erling Smørgrav } 47874f52dfbbSDag-Erling Smørgrav return sock; 47884f52dfbbSDag-Erling Smørgrav } 47894f52dfbbSDag-Erling Smørgrav 4790af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 4791511b41d2SMark Murray 4792511b41d2SMark Murray /* 4793511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 4794a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 4795a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 4796511b41d2SMark Murray */ 4797af12a3e7SDag-Erling Smørgrav int 47984f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset, 47994f52dfbbSDag-Erling Smørgrav int x11_use_localhost, int single_connection, 48004f52dfbbSDag-Erling Smørgrav u_int *display_numberp, int **chanids) 4801511b41d2SMark Murray { 4802af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 4803511b41d2SMark Murray int display_number, sock; 4804511b41d2SMark Murray u_short port; 4805511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4806511b41d2SMark Murray char strport[NI_MAXSERV]; 4807511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 4808511b41d2SMark Murray 4809b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 4810b74df5b2SDag-Erling Smørgrav return -1; 4811b74df5b2SDag-Erling Smørgrav 4812511b41d2SMark Murray for (display_number = x11_display_offset; 4813511b41d2SMark Murray display_number < MAX_DISPLAYS; 4814511b41d2SMark Murray display_number++) { 4815511b41d2SMark Murray port = 6000 + display_number; 4816511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 48174f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 4818af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 4819511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4820511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 48214f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(NULL, strport, 48224f52dfbbSDag-Erling Smørgrav &hints, &aitop)) != 0) { 4823d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 4824af12a3e7SDag-Erling Smørgrav return -1; 4825511b41d2SMark Murray } 4826511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 48274f52dfbbSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 48284f52dfbbSDag-Erling Smørgrav ai->ai_family != AF_INET6) 4829511b41d2SMark Murray continue; 4830221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 4831221552e4SDag-Erling Smørgrav ai->ai_protocol); 483219261079SEd Maste if (sock == -1) { 48338ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 48348ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 48358ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 48368ad9b54aSDag-Erling Smørgrav #endif 48378ad9b54aSDag-Erling Smørgrav ) { 4838511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 483921e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 4840af12a3e7SDag-Erling Smørgrav return -1; 4841989dd127SDag-Erling Smørgrav } else { 4842989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 4843989dd127SDag-Erling Smørgrav ai->ai_family); 4844989dd127SDag-Erling Smørgrav continue; 4845511b41d2SMark Murray } 4846989dd127SDag-Erling Smørgrav } 4847b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 4848b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 4849d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 485047dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 485119261079SEd Maste if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 485219261079SEd Maste debug2_f("bind port %d: %.100s", port, 485319261079SEd Maste strerror(errno)); 4854511b41d2SMark Murray close(sock); 48554f52dfbbSDag-Erling Smørgrav for (n = 0; n < num_socks; n++) 4856511b41d2SMark Murray close(socks[n]); 4857511b41d2SMark Murray num_socks = 0; 4858511b41d2SMark Murray break; 4859511b41d2SMark Murray } 4860511b41d2SMark Murray socks[num_socks++] = sock; 4861511b41d2SMark Murray if (num_socks == NUM_SOCKS) 4862511b41d2SMark Murray break; 4863511b41d2SMark Murray } 4864ca3176e7SBrian Feldman freeaddrinfo(aitop); 4865511b41d2SMark Murray if (num_socks > 0) 4866511b41d2SMark Murray break; 4867511b41d2SMark Murray } 4868511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 4869511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 4870af12a3e7SDag-Erling Smørgrav return -1; 4871511b41d2SMark Murray } 4872511b41d2SMark Murray /* Start listening for connections on the socket. */ 4873511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4874511b41d2SMark Murray sock = socks[n]; 487519261079SEd Maste if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { 4876511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 4877511b41d2SMark Murray close(sock); 4878af12a3e7SDag-Erling Smørgrav return -1; 4879511b41d2SMark Murray } 4880511b41d2SMark Murray } 4881511b41d2SMark Murray 4882511b41d2SMark Murray /* Allocate a channel for each socket. */ 4883333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 4884511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4885511b41d2SMark Murray sock = socks[n]; 48864f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "x11 listener", 4887a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 4888a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 4889221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 4890af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 4891d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 4892511b41d2SMark Murray } 4893d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 4894511b41d2SMark Murray 4895af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 4896a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 48974f52dfbbSDag-Erling Smørgrav return 0; 4898511b41d2SMark Murray } 4899511b41d2SMark Murray 4900af12a3e7SDag-Erling Smørgrav static int 4901cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 4902511b41d2SMark Murray { 4903511b41d2SMark Murray int sock; 4904511b41d2SMark Murray struct sockaddr_un addr; 4905511b41d2SMark Murray 4906511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 490719261079SEd Maste if (sock == -1) 4908511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4909511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 4910511b41d2SMark Murray addr.sun_family = AF_UNIX; 4911cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 4912511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 4913511b41d2SMark Murray return sock; 4914511b41d2SMark Murray close(sock); 4915511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 4916511b41d2SMark Murray return -1; 4917511b41d2SMark Murray } 4918511b41d2SMark Murray 4919cce7d346SDag-Erling Smørgrav static int 4920cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 4921cce7d346SDag-Erling Smørgrav { 4922cce7d346SDag-Erling Smørgrav char buf[1024]; 4923cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 4924cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 4925cce7d346SDag-Erling Smørgrav } 4926cce7d346SDag-Erling Smørgrav 4927d93a896eSDag-Erling Smørgrav #ifdef __APPLE__ 4928d93a896eSDag-Erling Smørgrav static int 4929d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen) 4930d93a896eSDag-Erling Smørgrav { 4931d93a896eSDag-Erling Smørgrav struct stat sbuf; 4932d93a896eSDag-Erling Smørgrav 4933d93a896eSDag-Erling Smørgrav if (strlcpy(path, display, pathlen) >= pathlen) { 4934d93a896eSDag-Erling Smørgrav error("%s: display path too long", __func__); 4935d93a896eSDag-Erling Smørgrav return 0; 4936d93a896eSDag-Erling Smørgrav } 4937d93a896eSDag-Erling Smørgrav if (display[0] != '/') 4938d93a896eSDag-Erling Smørgrav return 0; 4939d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 4940d93a896eSDag-Erling Smørgrav return 1; 4941d93a896eSDag-Erling Smørgrav } else { 4942d93a896eSDag-Erling Smørgrav char *dot = strrchr(path, '.'); 4943d93a896eSDag-Erling Smørgrav if (dot != NULL) { 4944d93a896eSDag-Erling Smørgrav *dot = '\0'; 4945d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 4946d93a896eSDag-Erling Smørgrav return 1; 4947d93a896eSDag-Erling Smørgrav } 4948d93a896eSDag-Erling Smørgrav } 4949d93a896eSDag-Erling Smørgrav } 4950d93a896eSDag-Erling Smørgrav return 0; 4951d93a896eSDag-Erling Smørgrav } 4952d93a896eSDag-Erling Smørgrav #endif 4953d93a896eSDag-Erling Smørgrav 4954a04a10f8SKris Kennaway int 49554f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh) 4956511b41d2SMark Murray { 4957333ee039SDag-Erling Smørgrav u_int display_number; 4958511b41d2SMark Murray const char *display; 4959a04a10f8SKris Kennaway char buf[1024], *cp; 4960511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4961511b41d2SMark Murray char strport[NI_MAXSERV]; 4962333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 4963511b41d2SMark Murray 4964511b41d2SMark Murray /* Try to open a socket for the local X server. */ 4965511b41d2SMark Murray display = getenv("DISPLAY"); 4966511b41d2SMark Murray if (!display) { 4967511b41d2SMark Murray error("DISPLAY not set."); 4968a04a10f8SKris Kennaway return -1; 4969511b41d2SMark Murray } 4970511b41d2SMark Murray /* 4971511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 4972511b41d2SMark Murray * connection to the real X server. 4973511b41d2SMark Murray */ 4974511b41d2SMark Murray 4975cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 4976d93a896eSDag-Erling Smørgrav /* Check if display is a path to a socket (as set by launchd). */ 4977d93a896eSDag-Erling Smørgrav { 4978d93a896eSDag-Erling Smørgrav char path[PATH_MAX]; 4979d93a896eSDag-Erling Smørgrav 4980d93a896eSDag-Erling Smørgrav if (is_path_to_xsocket(display, path, sizeof(path))) { 4981d93a896eSDag-Erling Smørgrav debug("x11_connect_display: $DISPLAY is launchd"); 4982d93a896eSDag-Erling Smørgrav 4983d93a896eSDag-Erling Smørgrav /* Create a socket. */ 4984d93a896eSDag-Erling Smørgrav sock = connect_local_xsocket_path(path); 4985cce7d346SDag-Erling Smørgrav if (sock < 0) 4986cce7d346SDag-Erling Smørgrav return -1; 4987cce7d346SDag-Erling Smørgrav 4988cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 4989cce7d346SDag-Erling Smørgrav return sock; 4990cce7d346SDag-Erling Smørgrav } 4991d93a896eSDag-Erling Smørgrav } 4992cce7d346SDag-Erling Smørgrav #endif 4993511b41d2SMark Murray /* 4994511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 4995511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 4996511b41d2SMark Murray */ 4997511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 4998511b41d2SMark Murray display[0] == ':') { 4999511b41d2SMark Murray /* Connect to the unix domain socket. */ 50004f52dfbbSDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", 50014f52dfbbSDag-Erling Smørgrav &display_number) != 1) { 50024f52dfbbSDag-Erling Smørgrav error("Could not parse display number from DISPLAY: " 50034f52dfbbSDag-Erling Smørgrav "%.100s", display); 5004a04a10f8SKris Kennaway return -1; 5005511b41d2SMark Murray } 5006511b41d2SMark Murray /* Create a socket. */ 5007511b41d2SMark Murray sock = connect_local_xsocket(display_number); 5008511b41d2SMark Murray if (sock < 0) 5009a04a10f8SKris Kennaway return -1; 5010511b41d2SMark Murray 5011511b41d2SMark Murray /* OK, we now have a connection to the display. */ 5012a04a10f8SKris Kennaway return sock; 5013511b41d2SMark Murray } 5014511b41d2SMark Murray /* 5015511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 5016511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 5017511b41d2SMark Murray */ 5018af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 5019511b41d2SMark Murray cp = strchr(buf, ':'); 5020511b41d2SMark Murray if (!cp) { 5021511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 5022a04a10f8SKris Kennaway return -1; 5023511b41d2SMark Murray } 5024511b41d2SMark Murray *cp = 0; 50254f52dfbbSDag-Erling Smørgrav /* 50264f52dfbbSDag-Erling Smørgrav * buf now contains the host name. But first we parse the 50274f52dfbbSDag-Erling Smørgrav * display number. 50284f52dfbbSDag-Erling Smørgrav */ 5029333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 5030511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 5031511b41d2SMark Murray display); 5032a04a10f8SKris Kennaway return -1; 5033511b41d2SMark Murray } 5034511b41d2SMark Murray 5035511b41d2SMark Murray /* Look up the host address */ 5036511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 50374f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 5038511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 5039333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 5040511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 5041d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 5042d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 5043a04a10f8SKris Kennaway return -1; 5044511b41d2SMark Murray } 5045511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 5046511b41d2SMark Murray /* Create a socket. */ 5047221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 504819261079SEd Maste if (sock == -1) { 5049221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 5050511b41d2SMark Murray continue; 5051511b41d2SMark Murray } 5052511b41d2SMark Murray /* Connect it to the display. */ 505319261079SEd Maste if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 5054333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 5055a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 5056511b41d2SMark Murray close(sock); 5057511b41d2SMark Murray continue; 5058511b41d2SMark Murray } 5059511b41d2SMark Murray /* Success */ 5060511b41d2SMark Murray break; 5061a04a10f8SKris Kennaway } 5062511b41d2SMark Murray freeaddrinfo(aitop); 5063511b41d2SMark Murray if (!ai) { 50644f52dfbbSDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 50654f52dfbbSDag-Erling Smørgrav 6000 + display_number, strerror(errno)); 5066a04a10f8SKris Kennaway return -1; 5067511b41d2SMark Murray } 5068af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 5069a04a10f8SKris Kennaway return sock; 5070a04a10f8SKris Kennaway } 5071511b41d2SMark Murray 5072a04a10f8SKris Kennaway /* 5073511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 5074511b41d2SMark Murray * data, and enables authentication spoofing. 5075af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 5076511b41d2SMark Murray */ 5077511b41d2SMark Murray void 50784f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, 50794f52dfbbSDag-Erling Smørgrav const char *disp, const char *proto, const char *data, int want_reply) 5080511b41d2SMark Murray { 50814f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 5082ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 5083d4ecd108SDag-Erling Smørgrav u_int i, value; 5084511b41d2SMark Murray const char *cp; 50854f52dfbbSDag-Erling Smørgrav char *new_data; 50864f52dfbbSDag-Erling Smørgrav int r, screen_number; 5087511b41d2SMark Murray 50884f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_display == NULL) 50894f52dfbbSDag-Erling Smørgrav sc->x11_saved_display = xstrdup(disp); 50904f52dfbbSDag-Erling Smørgrav else if (strcmp(disp, sc->x11_saved_display) != 0) { 5091d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 5092d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 5093d4ecd108SDag-Erling Smørgrav return; 5094d4ecd108SDag-Erling Smørgrav } 5095d4ecd108SDag-Erling Smørgrav 5096d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 5097511b41d2SMark Murray if (cp) 5098511b41d2SMark Murray cp = strchr(cp, '.'); 5099511b41d2SMark Murray if (cp) 5100333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 5101511b41d2SMark Murray else 5102511b41d2SMark Murray screen_number = 0; 5103511b41d2SMark Murray 51044f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_proto == NULL) { 5105511b41d2SMark Murray /* Save protocol name. */ 51064f52dfbbSDag-Erling Smørgrav sc->x11_saved_proto = xstrdup(proto); 5107ca86bcf2SDag-Erling Smørgrav 5108ca86bcf2SDag-Erling Smørgrav /* Extract real authentication data. */ 51094f52dfbbSDag-Erling Smørgrav sc->x11_saved_data = xmalloc(data_len); 5110511b41d2SMark Murray for (i = 0; i < data_len; i++) { 511119261079SEd Maste if (sscanf(data + 2 * i, "%2x", &value) != 1) { 5112d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 5113d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 511419261079SEd Maste } 51154f52dfbbSDag-Erling Smørgrav sc->x11_saved_data[i] = value; 5116511b41d2SMark Murray } 51174f52dfbbSDag-Erling Smørgrav sc->x11_saved_data_len = data_len; 5118ca86bcf2SDag-Erling Smørgrav 5119ca86bcf2SDag-Erling Smørgrav /* Generate fake data of the same length. */ 51204f52dfbbSDag-Erling Smørgrav sc->x11_fake_data = xmalloc(data_len); 51214f52dfbbSDag-Erling Smørgrav arc4random_buf(sc->x11_fake_data, data_len); 51224f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len = data_len; 5123d4ecd108SDag-Erling Smørgrav } 5124511b41d2SMark Murray 5125511b41d2SMark Murray /* Convert the fake data into hex. */ 51264f52dfbbSDag-Erling Smørgrav new_data = tohex(sc->x11_fake_data, data_len); 5127511b41d2SMark Murray 5128511b41d2SMark Murray /* Send the request packet. */ 51294f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, client_session_id, "x11-req", want_reply); 51304f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ 51314f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, proto)) != 0 || 51324f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, new_data)) != 0 || 51334f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, screen_number)) != 0 || 51344f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 51354f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 513619261079SEd Maste fatal_fr(r, "send x11-req"); 5137e4a9863fSDag-Erling Smørgrav free(new_data); 5138511b41d2SMark Murray } 5139