1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: channels.c,v 1.384 2018/07/27 12:03:17 markus 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> 604f52dfbbSDag-Erling Smørgrav #include <stdarg.h> 61bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H 62bc5531deSDag-Erling Smørgrav #include <stdint.h> 63bc5531deSDag-Erling Smørgrav #endif 64333ee039SDag-Erling Smørgrav #include <stdio.h> 65333ee039SDag-Erling Smørgrav #include <stdlib.h> 66333ee039SDag-Erling Smørgrav #include <string.h> 67333ee039SDag-Erling Smørgrav #include <termios.h> 68333ee039SDag-Erling Smørgrav #include <unistd.h> 69333ee039SDag-Erling Smørgrav 70d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 71333ee039SDag-Erling Smørgrav #include "xmalloc.h" 72ca3176e7SBrian Feldman #include "ssh.h" 73ca3176e7SBrian Feldman #include "ssh2.h" 74ca86bcf2SDag-Erling Smørgrav #include "ssherr.h" 754f52dfbbSDag-Erling Smørgrav #include "sshbuf.h" 76ca3176e7SBrian Feldman #include "packet.h" 77ca3176e7SBrian Feldman #include "log.h" 78ca3176e7SBrian Feldman #include "misc.h" 79ca3176e7SBrian Feldman #include "channels.h" 80ca3176e7SBrian Feldman #include "compat.h" 81ca3176e7SBrian Feldman #include "canohost.h" 82*190cef3dSDag-Erling Smørgrav #include "sshkey.h" 83b66f2d16SKris Kennaway #include "authfd.h" 84af12a3e7SDag-Erling Smørgrav #include "pathnames.h" 85*190cef3dSDag-Erling Smørgrav #include "match.h" 86511b41d2SMark Murray 874f52dfbbSDag-Erling Smørgrav /* -- agent forwarding */ 884f52dfbbSDag-Erling Smørgrav #define NUM_SOCKS 10 89511b41d2SMark Murray 90af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 914f52dfbbSDag-Erling Smørgrav /* special-case port number meaning allow any port */ 924f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT 0 934f52dfbbSDag-Erling Smørgrav 944f52dfbbSDag-Erling Smørgrav /* special-case wildcard meaning allow any host */ 954f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST "*" 964f52dfbbSDag-Erling Smørgrav 974f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */ 984f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */ 994f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS 1000 100511b41d2SMark Murray 101*190cef3dSDag-Erling Smørgrav /* Per-channel callback for pre/post select() actions */ 102*190cef3dSDag-Erling Smørgrav typedef void chan_fn(struct ssh *, Channel *c, 103*190cef3dSDag-Erling Smørgrav fd_set *readset, fd_set *writeset); 104*190cef3dSDag-Erling Smørgrav 105511b41d2SMark Murray /* 106511b41d2SMark Murray * Data structure for storing which hosts are permitted for forward requests. 107511b41d2SMark Murray * The local sides of any remote forwards are stored in this array to prevent 108511b41d2SMark Murray * a corrupt remote server from accessing arbitrary TCP/IP ports on our local 109511b41d2SMark Murray * network (which might be behind a firewall). 110511b41d2SMark Murray */ 111a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */ 112a0ee8cc6SDag-Erling Smørgrav /* Overload host_to_connect; we could just make this match Forward */ 113a0ee8cc6SDag-Erling Smørgrav /* XXX - can we use listen_host instead of listen_path? */ 114*190cef3dSDag-Erling Smørgrav struct permission { 115a04a10f8SKris Kennaway char *host_to_connect; /* Connect to 'host'. */ 116a0ee8cc6SDag-Erling Smørgrav int port_to_connect; /* Connect to 'port'. */ 117a0ee8cc6SDag-Erling Smørgrav char *listen_host; /* Remote side should listen address. */ 118a0ee8cc6SDag-Erling Smørgrav char *listen_path; /* Remote side should listen path. */ 119a0ee8cc6SDag-Erling Smørgrav int listen_port; /* Remote side should listen port. */ 120ca86bcf2SDag-Erling Smørgrav Channel *downstream; /* Downstream mux*/ 121*190cef3dSDag-Erling Smørgrav }; 122511b41d2SMark Murray 123*190cef3dSDag-Erling Smørgrav /* 124*190cef3dSDag-Erling Smørgrav * Stores the forwarding permission state for a single direction (local or 125*190cef3dSDag-Erling Smørgrav * remote). 126*190cef3dSDag-Erling Smørgrav */ 127*190cef3dSDag-Erling Smørgrav struct permission_set { 128*190cef3dSDag-Erling Smørgrav /* 129*190cef3dSDag-Erling Smørgrav * List of all local permitted host/port pairs to allow for the 130*190cef3dSDag-Erling Smørgrav * user. 131*190cef3dSDag-Erling Smørgrav */ 132*190cef3dSDag-Erling Smørgrav u_int num_permitted_user; 133*190cef3dSDag-Erling Smørgrav struct permission *permitted_user; 134*190cef3dSDag-Erling Smørgrav 135*190cef3dSDag-Erling Smørgrav /* 136*190cef3dSDag-Erling Smørgrav * List of all permitted host/port pairs to allow for the admin. 137*190cef3dSDag-Erling Smørgrav */ 138*190cef3dSDag-Erling Smørgrav u_int num_permitted_admin; 139*190cef3dSDag-Erling Smørgrav struct permission *permitted_admin; 140*190cef3dSDag-Erling Smørgrav 141*190cef3dSDag-Erling Smørgrav /* 142*190cef3dSDag-Erling Smørgrav * If this is true, all opens/listens are permitted. This is the 143*190cef3dSDag-Erling Smørgrav * case on the server on which we have to trust the client anyway, 144*190cef3dSDag-Erling Smørgrav * and the user could do anything after logging in. 145*190cef3dSDag-Erling Smørgrav */ 146*190cef3dSDag-Erling Smørgrav int all_permitted; 147*190cef3dSDag-Erling Smørgrav }; 148af12a3e7SDag-Erling Smørgrav 1494f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */ 1504f52dfbbSDag-Erling Smørgrav struct ssh_channels { 1514f52dfbbSDag-Erling Smørgrav /* 1524f52dfbbSDag-Erling Smørgrav * Pointer to an array containing all allocated channels. The array 1534f52dfbbSDag-Erling Smørgrav * is dynamically extended as needed. 1544f52dfbbSDag-Erling Smørgrav */ 1554f52dfbbSDag-Erling Smørgrav Channel **channels; 156076ad2f8SDag-Erling Smørgrav 157511b41d2SMark Murray /* 1584f52dfbbSDag-Erling Smørgrav * Size of the channel array. All slots of the array must always be 1594f52dfbbSDag-Erling Smørgrav * initialized (at least the type field); unused slots set to NULL 160511b41d2SMark Murray */ 1614f52dfbbSDag-Erling Smørgrav u_int channels_alloc; 162511b41d2SMark Murray 1634f52dfbbSDag-Erling Smørgrav /* 1644f52dfbbSDag-Erling Smørgrav * Maximum file descriptor value used in any of the channels. This is 1654f52dfbbSDag-Erling Smørgrav * updated in channel_new. 1664f52dfbbSDag-Erling Smørgrav */ 1674f52dfbbSDag-Erling Smørgrav int channel_max_fd; 1684f52dfbbSDag-Erling Smørgrav 1694f52dfbbSDag-Erling Smørgrav /* 1704f52dfbbSDag-Erling Smørgrav * 'channel_pre*' are called just before select() to add any bits 1714f52dfbbSDag-Erling Smørgrav * relevant to channels in the select bitmasks. 1724f52dfbbSDag-Erling Smørgrav * 1734f52dfbbSDag-Erling Smørgrav * 'channel_post*': perform any appropriate operations for 1744f52dfbbSDag-Erling Smørgrav * channels which have events pending. 1754f52dfbbSDag-Erling Smørgrav */ 1764f52dfbbSDag-Erling Smørgrav chan_fn **channel_pre; 1774f52dfbbSDag-Erling Smørgrav chan_fn **channel_post; 1784f52dfbbSDag-Erling Smørgrav 1794f52dfbbSDag-Erling Smørgrav /* -- tcp forwarding */ 180*190cef3dSDag-Erling Smørgrav struct permission_set local_perms; 181*190cef3dSDag-Erling Smørgrav struct permission_set remote_perms; 182af12a3e7SDag-Erling Smørgrav 183af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 184af12a3e7SDag-Erling Smørgrav 185d4ecd108SDag-Erling Smørgrav /* Saved X11 local (client) display. */ 1864f52dfbbSDag-Erling Smørgrav char *x11_saved_display; 187d4ecd108SDag-Erling Smørgrav 188af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication protocol name. */ 1894f52dfbbSDag-Erling Smørgrav char *x11_saved_proto; 190af12a3e7SDag-Erling Smørgrav 191af12a3e7SDag-Erling Smørgrav /* Saved X11 authentication data. This is the real data. */ 1924f52dfbbSDag-Erling Smørgrav char *x11_saved_data; 1934f52dfbbSDag-Erling Smørgrav u_int x11_saved_data_len; 194af12a3e7SDag-Erling Smørgrav 195557f75e5SDag-Erling Smørgrav /* Deadline after which all X11 connections are refused */ 1964f52dfbbSDag-Erling Smørgrav u_int x11_refuse_time; 197557f75e5SDag-Erling Smørgrav 198af12a3e7SDag-Erling Smørgrav /* 1994f52dfbbSDag-Erling Smørgrav * Fake X11 authentication data. This is what the server will be 2004f52dfbbSDag-Erling Smørgrav * sending us; we should replace any occurrences of this by the 2014f52dfbbSDag-Erling Smørgrav * real data. 202af12a3e7SDag-Erling Smørgrav */ 2034f52dfbbSDag-Erling Smørgrav u_char *x11_fake_data; 2044f52dfbbSDag-Erling Smørgrav u_int x11_fake_data_len; 205af12a3e7SDag-Erling Smørgrav 206ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 2074f52dfbbSDag-Erling Smørgrav int IPv4or6; 2084f52dfbbSDag-Erling Smørgrav }; 209ca3176e7SBrian Feldman 210af12a3e7SDag-Erling Smørgrav /* helper */ 2114f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); 212ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host); 213ca3176e7SBrian Feldman 214d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */ 215d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *); 216d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *); 2174f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); 2184f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *); 2194f52dfbbSDag-Erling Smørgrav 2204f52dfbbSDag-Erling Smørgrav /* Setup helper */ 2214f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc); 222d4af9e69SDag-Erling Smørgrav 223af12a3e7SDag-Erling Smørgrav /* -- channel core */ 224a04a10f8SKris Kennaway 2254f52dfbbSDag-Erling Smørgrav void 2264f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh) 2274f52dfbbSDag-Erling Smørgrav { 2284f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc; 2294f52dfbbSDag-Erling Smørgrav 2304f52dfbbSDag-Erling Smørgrav if ((sc = calloc(1, sizeof(*sc))) == NULL || 2314f52dfbbSDag-Erling Smørgrav (sc->channel_pre = calloc(SSH_CHANNEL_MAX_TYPE, 2324f52dfbbSDag-Erling Smørgrav sizeof(*sc->channel_pre))) == NULL || 2334f52dfbbSDag-Erling Smørgrav (sc->channel_post = calloc(SSH_CHANNEL_MAX_TYPE, 2344f52dfbbSDag-Erling Smørgrav sizeof(*sc->channel_post))) == NULL) 2354f52dfbbSDag-Erling Smørgrav fatal("%s: allocation failed", __func__); 2364f52dfbbSDag-Erling Smørgrav sc->channels_alloc = 10; 2374f52dfbbSDag-Erling Smørgrav sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); 2384f52dfbbSDag-Erling Smørgrav sc->IPv4or6 = AF_UNSPEC; 2394f52dfbbSDag-Erling Smørgrav channel_handler_init(sc); 2404f52dfbbSDag-Erling Smørgrav 2414f52dfbbSDag-Erling Smørgrav ssh->chanctxt = sc; 2424f52dfbbSDag-Erling Smørgrav } 2434f52dfbbSDag-Erling Smørgrav 244a04a10f8SKris Kennaway Channel * 2454f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id) 246a04a10f8SKris Kennaway { 247a04a10f8SKris Kennaway Channel *c; 248af12a3e7SDag-Erling Smørgrav 2494f52dfbbSDag-Erling Smørgrav if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { 2504f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id", __func__, id); 251a04a10f8SKris Kennaway return NULL; 252a04a10f8SKris Kennaway } 2534f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[id]; 254af12a3e7SDag-Erling Smørgrav if (c == NULL) { 2554f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id: channel free", __func__, id); 256a04a10f8SKris Kennaway return NULL; 257a04a10f8SKris Kennaway } 258a04a10f8SKris Kennaway return c; 259a04a10f8SKris Kennaway } 260a04a10f8SKris Kennaway 261ca86bcf2SDag-Erling Smørgrav Channel * 2624f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id) 263ca86bcf2SDag-Erling Smørgrav { 264ca86bcf2SDag-Erling Smørgrav Channel *c; 265ca86bcf2SDag-Erling Smørgrav u_int i; 266ca86bcf2SDag-Erling Smørgrav 2674f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 2684f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 2694f52dfbbSDag-Erling Smørgrav if (c != NULL && c->have_remote_id && c->remote_id == remote_id) 270ca86bcf2SDag-Erling Smørgrav return c; 271ca86bcf2SDag-Erling Smørgrav } 272ca86bcf2SDag-Erling Smørgrav return NULL; 273ca86bcf2SDag-Erling Smørgrav } 274ca86bcf2SDag-Erling Smørgrav 275a04a10f8SKris Kennaway /* 276b74df5b2SDag-Erling Smørgrav * Returns the channel if it is allowed to receive protocol messages. 277b74df5b2SDag-Erling Smørgrav * Private channels, like listening sockets, may not receive messages. 278b74df5b2SDag-Erling Smørgrav */ 279b74df5b2SDag-Erling Smørgrav Channel * 2804f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id) 281b74df5b2SDag-Erling Smørgrav { 282b74df5b2SDag-Erling Smørgrav Channel *c; 283b74df5b2SDag-Erling Smørgrav 2844f52dfbbSDag-Erling Smørgrav if ((c = channel_by_id(ssh, id)) == NULL) 2854f52dfbbSDag-Erling Smørgrav return NULL; 286b74df5b2SDag-Erling Smørgrav 287b74df5b2SDag-Erling Smørgrav switch (c->type) { 288b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 289b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 290b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 291b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 2924f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 2934f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 294b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 295b74df5b2SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 296e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 297ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 2984f52dfbbSDag-Erling Smørgrav return c; 299b74df5b2SDag-Erling Smørgrav } 300b74df5b2SDag-Erling Smørgrav logit("Non-public channel %d, type %d.", id, c->type); 3014f52dfbbSDag-Erling Smørgrav return NULL; 302b74df5b2SDag-Erling Smørgrav } 303b74df5b2SDag-Erling Smørgrav 304b74df5b2SDag-Erling Smørgrav /* 305a04a10f8SKris Kennaway * Register filedescriptors for a channel, used when allocating a channel or 306a04a10f8SKris Kennaway * when the channel consumer/producer is ready, e.g. shell exec'd 307a04a10f8SKris Kennaway */ 308af12a3e7SDag-Erling Smørgrav static void 3094f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, 310d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty) 311a04a10f8SKris Kennaway { 3124f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3134f52dfbbSDag-Erling Smørgrav 314a04a10f8SKris Kennaway /* Update the maximum file descriptor value. */ 3154f52dfbbSDag-Erling Smørgrav sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, rfd); 3164f52dfbbSDag-Erling Smørgrav sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, wfd); 3174f52dfbbSDag-Erling Smørgrav sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, efd); 318ca3176e7SBrian Feldman 319b15c8340SDag-Erling Smørgrav if (rfd != -1) 320b15c8340SDag-Erling Smørgrav fcntl(rfd, F_SETFD, FD_CLOEXEC); 321b15c8340SDag-Erling Smørgrav if (wfd != -1 && wfd != rfd) 322b15c8340SDag-Erling Smørgrav fcntl(wfd, F_SETFD, FD_CLOEXEC); 323b15c8340SDag-Erling Smørgrav if (efd != -1 && efd != rfd && efd != wfd) 324b15c8340SDag-Erling Smørgrav fcntl(efd, F_SETFD, FD_CLOEXEC); 325a04a10f8SKris Kennaway 326a04a10f8SKris Kennaway c->rfd = rfd; 327a04a10f8SKris Kennaway c->wfd = wfd; 328a04a10f8SKris Kennaway c->sock = (rfd == wfd) ? rfd : -1; 329a04a10f8SKris Kennaway c->efd = efd; 330a04a10f8SKris Kennaway c->extended_usage = extusage; 3315b9b2fafSBrian Feldman 332d4af9e69SDag-Erling Smørgrav if ((c->isatty = is_tty) != 0) 333221552e4SDag-Erling Smørgrav debug2("channel %d: rfd %d isatty", c->self, c->rfd); 334e4a9863fSDag-Erling Smørgrav #ifdef _AIX 335e4a9863fSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 336d4af9e69SDag-Erling Smørgrav c->wfd_isatty = is_tty || isatty(c->wfd); 337e4a9863fSDag-Erling Smørgrav #endif 338e0fbb1d2SBrian Feldman 3395b9b2fafSBrian Feldman /* enable nonblocking mode */ 3405b9b2fafSBrian Feldman if (nonblock) { 341a04a10f8SKris Kennaway if (rfd != -1) 342a04a10f8SKris Kennaway set_nonblock(rfd); 343a04a10f8SKris Kennaway if (wfd != -1) 344a04a10f8SKris Kennaway set_nonblock(wfd); 345a04a10f8SKris Kennaway if (efd != -1) 346a04a10f8SKris Kennaway set_nonblock(efd); 347a04a10f8SKris Kennaway } 3485b9b2fafSBrian Feldman } 349a04a10f8SKris Kennaway 350511b41d2SMark Murray /* 351511b41d2SMark Murray * Allocate a new channel object and set its type and socket. This will cause 352511b41d2SMark Murray * remote_name to be freed. 353511b41d2SMark Murray */ 354af12a3e7SDag-Erling Smørgrav Channel * 3554f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, 356a82e551fSDag-Erling Smørgrav u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) 357511b41d2SMark Murray { 3584f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3594f52dfbbSDag-Erling Smørgrav u_int i, found; 360511b41d2SMark Murray Channel *c; 361511b41d2SMark Murray 362511b41d2SMark Murray /* Try to find a free slot where to put the new channel. */ 3634f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 3644f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL) { 365511b41d2SMark Murray /* Found a free slot. */ 3664f52dfbbSDag-Erling Smørgrav found = i; 367511b41d2SMark Murray break; 368511b41d2SMark Murray } 3694f52dfbbSDag-Erling Smørgrav } 3704f52dfbbSDag-Erling Smørgrav if (i >= sc->channels_alloc) { 3714f52dfbbSDag-Erling Smørgrav /* 3724f52dfbbSDag-Erling Smørgrav * There are no free slots. Take last+1 slot and expand 3734f52dfbbSDag-Erling Smørgrav * the array. 3744f52dfbbSDag-Erling Smørgrav */ 3754f52dfbbSDag-Erling Smørgrav found = sc->channels_alloc; 3764f52dfbbSDag-Erling Smørgrav if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) 3774f52dfbbSDag-Erling Smørgrav fatal("%s: internal error: channels_alloc %d too big", 3784f52dfbbSDag-Erling Smørgrav __func__, sc->channels_alloc); 3794f52dfbbSDag-Erling Smørgrav sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, 3804f52dfbbSDag-Erling Smørgrav sc->channels_alloc + 10, sizeof(*sc->channels)); 3814f52dfbbSDag-Erling Smørgrav sc->channels_alloc += 10; 3824f52dfbbSDag-Erling Smørgrav debug2("channel: expanding %d", sc->channels_alloc); 383511b41d2SMark Murray } 384af12a3e7SDag-Erling Smørgrav /* Initialize and return new channel. */ 3854f52dfbbSDag-Erling Smørgrav c = sc->channels[found] = xcalloc(1, sizeof(Channel)); 3864f52dfbbSDag-Erling Smørgrav if ((c->input = sshbuf_new()) == NULL || 3874f52dfbbSDag-Erling Smørgrav (c->output = sshbuf_new()) == NULL || 3884f52dfbbSDag-Erling Smørgrav (c->extended = sshbuf_new()) == NULL) 3894f52dfbbSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 390af12a3e7SDag-Erling Smørgrav c->ostate = CHAN_OUTPUT_OPEN; 391af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_OPEN; 3924f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); 393511b41d2SMark Murray c->self = found; 394511b41d2SMark Murray c->type = type; 395a04a10f8SKris Kennaway c->ctype = ctype; 396a04a10f8SKris Kennaway c->local_window = window; 397a04a10f8SKris Kennaway c->local_window_max = window; 398a04a10f8SKris Kennaway c->local_maxpacket = maxpack; 399221552e4SDag-Erling Smørgrav c->remote_name = xstrdup(remote_name); 400b15c8340SDag-Erling Smørgrav c->ctl_chan = -1; 401b15c8340SDag-Erling Smørgrav c->delayed = 1; /* prevent call to channel_post handler */ 402d4af9e69SDag-Erling Smørgrav TAILQ_INIT(&c->status_confirms); 403511b41d2SMark Murray debug("channel %d: new [%s]", found, remote_name); 404af12a3e7SDag-Erling Smørgrav return c; 405a04a10f8SKris Kennaway } 406511b41d2SMark Murray 4074f52dfbbSDag-Erling Smørgrav static void 4084f52dfbbSDag-Erling Smørgrav channel_find_maxfd(struct ssh_channels *sc) 409af12a3e7SDag-Erling Smørgrav { 41021e764dfSDag-Erling Smørgrav u_int i; 41121e764dfSDag-Erling Smørgrav int max = 0; 412af12a3e7SDag-Erling Smørgrav Channel *c; 413af12a3e7SDag-Erling Smørgrav 4144f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 4154f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 416af12a3e7SDag-Erling Smørgrav if (c != NULL) { 417ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->rfd); 418ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->wfd); 419ca86bcf2SDag-Erling Smørgrav max = MAXIMUM(max, c->efd); 420af12a3e7SDag-Erling Smørgrav } 421af12a3e7SDag-Erling Smørgrav } 4224f52dfbbSDag-Erling Smørgrav sc->channel_max_fd = max; 423af12a3e7SDag-Erling Smørgrav } 424af12a3e7SDag-Erling Smørgrav 425af12a3e7SDag-Erling Smørgrav int 4264f52dfbbSDag-Erling Smørgrav channel_close_fd(struct ssh *ssh, int *fdp) 427af12a3e7SDag-Erling Smørgrav { 4284f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 429af12a3e7SDag-Erling Smørgrav int ret = 0, fd = *fdp; 430af12a3e7SDag-Erling Smørgrav 431af12a3e7SDag-Erling Smørgrav if (fd != -1) { 432af12a3e7SDag-Erling Smørgrav ret = close(fd); 433af12a3e7SDag-Erling Smørgrav *fdp = -1; 4344f52dfbbSDag-Erling Smørgrav if (fd == sc->channel_max_fd) 4354f52dfbbSDag-Erling Smørgrav channel_find_maxfd(sc); 436af12a3e7SDag-Erling Smørgrav } 437af12a3e7SDag-Erling Smørgrav return ret; 438af12a3e7SDag-Erling Smørgrav } 439a04a10f8SKris Kennaway 440a04a10f8SKris Kennaway /* Close all channel fd/socket. */ 441af12a3e7SDag-Erling Smørgrav static void 4424f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c) 443511b41d2SMark Murray { 44447dd1d1bSDag-Erling Smørgrav int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; 44547dd1d1bSDag-Erling Smørgrav 4464f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->sock); 44747dd1d1bSDag-Erling Smørgrav if (rfd != sock) 4484f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->rfd); 44947dd1d1bSDag-Erling Smørgrav if (wfd != sock && wfd != rfd) 4504f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->wfd); 45147dd1d1bSDag-Erling Smørgrav if (efd != sock && efd != rfd && efd != wfd) 4524f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->efd); 4534f52dfbbSDag-Erling Smørgrav } 4544f52dfbbSDag-Erling Smørgrav 4554f52dfbbSDag-Erling Smørgrav static void 456*190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm) 4574f52dfbbSDag-Erling Smørgrav { 458*190cef3dSDag-Erling Smørgrav free(perm->host_to_connect); 459*190cef3dSDag-Erling Smørgrav free(perm->listen_host); 460*190cef3dSDag-Erling Smørgrav free(perm->listen_path); 461*190cef3dSDag-Erling Smørgrav bzero(perm, sizeof(*perm)); 4624f52dfbbSDag-Erling Smørgrav } 4634f52dfbbSDag-Erling Smørgrav 464*190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */ 465*190cef3dSDag-Erling Smørgrav static const char * 466*190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where) 467*190cef3dSDag-Erling Smørgrav { 468*190cef3dSDag-Erling Smørgrav if (who == FORWARD_ADM) { 469*190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 470*190cef3dSDag-Erling Smørgrav return "admin local"; 471*190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 472*190cef3dSDag-Erling Smørgrav return "admin remote"; 473*190cef3dSDag-Erling Smørgrav } else if (who == FORWARD_USER) { 474*190cef3dSDag-Erling Smørgrav if (where == FORWARD_LOCAL) 475*190cef3dSDag-Erling Smørgrav return "user local"; 476*190cef3dSDag-Erling Smørgrav else if (where == FORWARD_REMOTE) 477*190cef3dSDag-Erling Smørgrav return "user remote"; 478*190cef3dSDag-Erling Smørgrav } 479*190cef3dSDag-Erling Smørgrav fatal("Unknown forward permission list %d/%d", who, where); 480*190cef3dSDag-Erling Smørgrav } 4814f52dfbbSDag-Erling Smørgrav 482*190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */ 483*190cef3dSDag-Erling Smørgrav static struct permission_set * 484*190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where) 485*190cef3dSDag-Erling Smørgrav { 486*190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 487*190cef3dSDag-Erling Smørgrav 488*190cef3dSDag-Erling Smørgrav switch (where) { 489*190cef3dSDag-Erling Smørgrav case FORWARD_LOCAL: 490*190cef3dSDag-Erling Smørgrav return &sc->local_perms; 491*190cef3dSDag-Erling Smørgrav break; 492*190cef3dSDag-Erling Smørgrav case FORWARD_REMOTE: 493*190cef3dSDag-Erling Smørgrav return &sc->remote_perms; 494*190cef3dSDag-Erling Smørgrav break; 495*190cef3dSDag-Erling Smørgrav default: 496*190cef3dSDag-Erling Smørgrav fatal("%s: invalid forwarding direction %d", __func__, where); 497*190cef3dSDag-Erling Smørgrav } 498*190cef3dSDag-Erling Smørgrav } 499*190cef3dSDag-Erling Smørgrav 500*190cef3dSDag-Erling Smørgrav /* Reutrns pointers to the specified forwarding list and its element count */ 501*190cef3dSDag-Erling Smørgrav static void 502*190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where, 503*190cef3dSDag-Erling Smørgrav struct permission ***permpp, u_int **npermpp) 504*190cef3dSDag-Erling Smørgrav { 505*190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 506*190cef3dSDag-Erling Smørgrav 507*190cef3dSDag-Erling Smørgrav switch (who) { 508*190cef3dSDag-Erling Smørgrav case FORWARD_USER: 509*190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_user; 510*190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_user; 511*190cef3dSDag-Erling Smørgrav break; 512*190cef3dSDag-Erling Smørgrav case FORWARD_ADM: 513*190cef3dSDag-Erling Smørgrav *permpp = &pset->permitted_admin; 514*190cef3dSDag-Erling Smørgrav *npermpp = &pset->num_permitted_admin; 515*190cef3dSDag-Erling Smørgrav break; 516*190cef3dSDag-Erling Smørgrav default: 517*190cef3dSDag-Erling Smørgrav fatal("%s: invalid forwarding client %d", __func__, who); 518*190cef3dSDag-Erling Smørgrav } 519*190cef3dSDag-Erling Smørgrav } 520*190cef3dSDag-Erling Smørgrav 521*190cef3dSDag-Erling Smørgrav /* Adds an entry to the spcified forwarding list */ 5224f52dfbbSDag-Erling Smørgrav static int 523*190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where, 5244f52dfbbSDag-Erling Smørgrav const char *host_to_connect, int port_to_connect, 5254f52dfbbSDag-Erling Smørgrav const char *listen_host, const char *listen_path, int listen_port, 5264f52dfbbSDag-Erling Smørgrav Channel *downstream) 5274f52dfbbSDag-Erling Smørgrav { 528*190cef3dSDag-Erling Smørgrav struct permission **permp; 529*190cef3dSDag-Erling Smørgrav u_int n, *npermp; 5304f52dfbbSDag-Erling Smørgrav 531*190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 5324f52dfbbSDag-Erling Smørgrav 533*190cef3dSDag-Erling Smørgrav if (*npermp >= INT_MAX) 534*190cef3dSDag-Erling Smørgrav fatal("%s: %s overflow", __func__, fwd_ident(who, where)); 5354f52dfbbSDag-Erling Smørgrav 536*190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); 537*190cef3dSDag-Erling Smørgrav n = (*npermp)++; 5384f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) 539*190cef3dSDag-Erling Smørgrav (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); 540*190cef3dSDag-Erling Smørgrav (*permp)[n].port_to_connect = port_to_connect; 541*190cef3dSDag-Erling Smørgrav (*permp)[n].listen_host = MAYBE_DUP(listen_host); 542*190cef3dSDag-Erling Smørgrav (*permp)[n].listen_path = MAYBE_DUP(listen_path); 543*190cef3dSDag-Erling Smørgrav (*permp)[n].listen_port = listen_port; 544*190cef3dSDag-Erling Smørgrav (*permp)[n].downstream = downstream; 5454f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP 5464f52dfbbSDag-Erling Smørgrav return (int)n; 5474f52dfbbSDag-Erling Smørgrav } 5484f52dfbbSDag-Erling Smørgrav 5494f52dfbbSDag-Erling Smørgrav static void 5504f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) 5514f52dfbbSDag-Erling Smørgrav { 5524f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 553*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 554*190cef3dSDag-Erling Smørgrav struct permission *perm; 5554f52dfbbSDag-Erling Smørgrav int r; 5564f52dfbbSDag-Erling Smørgrav u_int i; 5574f52dfbbSDag-Erling Smørgrav 558*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 559*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 560*190cef3dSDag-Erling Smørgrav if (perm->downstream != c) 5614f52dfbbSDag-Erling Smørgrav continue; 5624f52dfbbSDag-Erling Smørgrav 5634f52dfbbSDag-Erling Smørgrav /* cancel on the server, since mux client is gone */ 5644f52dfbbSDag-Erling Smørgrav debug("channel %d: cleanup remote forward for %s:%u", 565*190cef3dSDag-Erling Smørgrav c->self, perm->listen_host, perm->listen_port); 5664f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 5674f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 5684f52dfbbSDag-Erling Smørgrav "cancel-tcpip-forward")) != 0 || 5694f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || 5704f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 571*190cef3dSDag-Erling Smørgrav channel_rfwd_bind_host(perm->listen_host))) != 0 || 572*190cef3dSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || 5734f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 5744f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, 5754f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 5764f52dfbbSDag-Erling Smørgrav } 577*190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 5784f52dfbbSDag-Erling Smørgrav } 579a04a10f8SKris Kennaway } 580511b41d2SMark Murray 581a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */ 582a04a10f8SKris Kennaway void 5834f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c) 584a04a10f8SKris Kennaway { 5854f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 586af12a3e7SDag-Erling Smørgrav char *s; 58721e764dfSDag-Erling Smørgrav u_int i, n; 588ca86bcf2SDag-Erling Smørgrav Channel *other; 589d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 590ca3176e7SBrian Feldman 5914f52dfbbSDag-Erling Smørgrav for (n = 0, i = 0; i < sc->channels_alloc; i++) { 5924f52dfbbSDag-Erling Smørgrav if ((other = sc->channels[i]) == NULL) 5934f52dfbbSDag-Erling Smørgrav continue; 594af12a3e7SDag-Erling Smørgrav n++; 595ca86bcf2SDag-Erling Smørgrav /* detach from mux client and prepare for closing */ 596ca86bcf2SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT && 597ca86bcf2SDag-Erling Smørgrav other->type == SSH_CHANNEL_MUX_PROXY && 598ca86bcf2SDag-Erling Smørgrav other->mux_ctx == c) { 599ca86bcf2SDag-Erling Smørgrav other->mux_ctx = NULL; 600ca86bcf2SDag-Erling Smørgrav other->type = SSH_CHANNEL_OPEN; 601ca86bcf2SDag-Erling Smørgrav other->istate = CHAN_INPUT_CLOSED; 602ca86bcf2SDag-Erling Smørgrav other->ostate = CHAN_OUTPUT_CLOSED; 603ca86bcf2SDag-Erling Smørgrav } 604ca86bcf2SDag-Erling Smørgrav } 60521e764dfSDag-Erling Smørgrav debug("channel %d: free: %s, nchannels %u", c->self, 606af12a3e7SDag-Erling Smørgrav c->remote_name ? c->remote_name : "???", n); 607af12a3e7SDag-Erling Smørgrav 6084f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_MUX_CLIENT) 6094f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(ssh, c); 610ca86bcf2SDag-Erling Smørgrav 611*190cef3dSDag-Erling Smørgrav if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { 6124f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 613221552e4SDag-Erling Smørgrav debug3("channel %d: status: %s", c->self, s); 614e4a9863fSDag-Erling Smørgrav free(s); 615*190cef3dSDag-Erling Smørgrav } 616ca3176e7SBrian Feldman 6174f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, c); 6184f52dfbbSDag-Erling Smørgrav sshbuf_free(c->input); 6194f52dfbbSDag-Erling Smørgrav sshbuf_free(c->output); 6204f52dfbbSDag-Erling Smørgrav sshbuf_free(c->extended); 6214f52dfbbSDag-Erling Smørgrav c->input = c->output = c->extended = NULL; 622e4a9863fSDag-Erling Smørgrav free(c->remote_name); 623a04a10f8SKris Kennaway c->remote_name = NULL; 624e4a9863fSDag-Erling Smørgrav free(c->path); 625cce7d346SDag-Erling Smørgrav c->path = NULL; 626e4a9863fSDag-Erling Smørgrav free(c->listening_addr); 627462c32cbSDag-Erling Smørgrav c->listening_addr = NULL; 628d4af9e69SDag-Erling Smørgrav while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { 629d4af9e69SDag-Erling Smørgrav if (cc->abandon_cb != NULL) 6304f52dfbbSDag-Erling Smørgrav cc->abandon_cb(ssh, c, cc->ctx); 631d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 632b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 633e4a9863fSDag-Erling Smørgrav free(cc); 634d4af9e69SDag-Erling Smørgrav } 635d4af9e69SDag-Erling Smørgrav if (c->filter_cleanup != NULL && c->filter_ctx != NULL) 6364f52dfbbSDag-Erling Smørgrav c->filter_cleanup(ssh, c->self, c->filter_ctx); 6374f52dfbbSDag-Erling Smørgrav sc->channels[c->self] = NULL; 6384f52dfbbSDag-Erling Smørgrav explicit_bzero(c, sizeof(*c)); 639e4a9863fSDag-Erling Smørgrav free(c); 640af12a3e7SDag-Erling Smørgrav } 641af12a3e7SDag-Erling Smørgrav 642af12a3e7SDag-Erling Smørgrav void 6434f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh) 644af12a3e7SDag-Erling Smørgrav { 64521e764dfSDag-Erling Smørgrav u_int i; 646af12a3e7SDag-Erling Smørgrav 6474f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) 6484f52dfbbSDag-Erling Smørgrav if (ssh->chanctxt->channels[i] != NULL) 6494f52dfbbSDag-Erling Smørgrav channel_free(ssh, ssh->chanctxt->channels[i]); 650af12a3e7SDag-Erling Smørgrav } 651af12a3e7SDag-Erling Smørgrav 652af12a3e7SDag-Erling Smørgrav /* 653af12a3e7SDag-Erling Smørgrav * Closes the sockets/fds of all channels. This is used to close extra file 654af12a3e7SDag-Erling Smørgrav * descriptors after a fork. 655af12a3e7SDag-Erling Smørgrav */ 656af12a3e7SDag-Erling Smørgrav void 6574f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh) 658af12a3e7SDag-Erling Smørgrav { 65921e764dfSDag-Erling Smørgrav u_int i; 660af12a3e7SDag-Erling Smørgrav 6614f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) 6624f52dfbbSDag-Erling Smørgrav if (ssh->chanctxt->channels[i] != NULL) 6634f52dfbbSDag-Erling Smørgrav channel_close_fds(ssh, ssh->chanctxt->channels[i]); 664af12a3e7SDag-Erling Smørgrav } 665af12a3e7SDag-Erling Smørgrav 666af12a3e7SDag-Erling Smørgrav /* 667af12a3e7SDag-Erling Smørgrav * Stop listening to channels. 668af12a3e7SDag-Erling Smørgrav */ 669af12a3e7SDag-Erling Smørgrav void 6704f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh) 671af12a3e7SDag-Erling Smørgrav { 67221e764dfSDag-Erling Smørgrav u_int i; 673af12a3e7SDag-Erling Smørgrav Channel *c; 674af12a3e7SDag-Erling Smørgrav 6754f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 6764f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 677af12a3e7SDag-Erling Smørgrav if (c != NULL) { 678af12a3e7SDag-Erling Smørgrav switch (c->type) { 679af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 680af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 681af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 682af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 683a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 684a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 6854f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->sock); 6864f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 687af12a3e7SDag-Erling Smørgrav break; 688af12a3e7SDag-Erling Smørgrav } 689af12a3e7SDag-Erling Smørgrav } 690af12a3e7SDag-Erling Smørgrav } 691af12a3e7SDag-Erling Smørgrav } 692af12a3e7SDag-Erling Smørgrav 693af12a3e7SDag-Erling Smørgrav /* 694af12a3e7SDag-Erling Smørgrav * Returns true if no channel has too much buffered data, and false if one or 695af12a3e7SDag-Erling Smørgrav * more channel is overfull. 696af12a3e7SDag-Erling Smørgrav */ 697af12a3e7SDag-Erling Smørgrav int 6984f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh) 699af12a3e7SDag-Erling Smørgrav { 700af12a3e7SDag-Erling Smørgrav u_int i; 7014f52dfbbSDag-Erling Smørgrav u_int maxsize = ssh_packet_get_maxsize(ssh); 702af12a3e7SDag-Erling Smørgrav Channel *c; 703af12a3e7SDag-Erling Smørgrav 7044f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 7054f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 7064f52dfbbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_OPEN) 7074f52dfbbSDag-Erling Smørgrav continue; 7084f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > maxsize) { 7094f52dfbbSDag-Erling Smørgrav debug2("channel %d: big output buffer %zu > %u", 7104f52dfbbSDag-Erling Smørgrav c->self, sshbuf_len(c->output), maxsize); 711af12a3e7SDag-Erling Smørgrav return 0; 712af12a3e7SDag-Erling Smørgrav } 713af12a3e7SDag-Erling Smørgrav } 714af12a3e7SDag-Erling Smørgrav return 1; 715af12a3e7SDag-Erling Smørgrav } 716af12a3e7SDag-Erling Smørgrav 717af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */ 718af12a3e7SDag-Erling Smørgrav int 7194f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh) 720af12a3e7SDag-Erling Smørgrav { 72121e764dfSDag-Erling Smørgrav u_int i; 722af12a3e7SDag-Erling Smørgrav Channel *c; 723af12a3e7SDag-Erling Smørgrav 7244f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 7254f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 726af12a3e7SDag-Erling Smørgrav if (c == NULL) 727af12a3e7SDag-Erling Smørgrav continue; 728af12a3e7SDag-Erling Smørgrav switch (c->type) { 729af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 730af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 731af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 732b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 733af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 734af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 735af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 7364f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 737af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 738af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 739e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 740a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 741a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 742af12a3e7SDag-Erling Smørgrav continue; 743af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 744af12a3e7SDag-Erling Smørgrav continue; 745af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 746af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 7474f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 748af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 749b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 750ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 751af12a3e7SDag-Erling Smørgrav return 1; 752af12a3e7SDag-Erling Smørgrav default: 7534f52dfbbSDag-Erling Smørgrav fatal("%s: bad channel type %d", __func__, c->type); 754af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 755af12a3e7SDag-Erling Smørgrav } 756af12a3e7SDag-Erling Smørgrav } 757af12a3e7SDag-Erling Smørgrav return 0; 758af12a3e7SDag-Erling Smørgrav } 759af12a3e7SDag-Erling Smørgrav 760af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */ 761af12a3e7SDag-Erling Smørgrav int 7624f52dfbbSDag-Erling Smørgrav channel_find_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]; 7694f52dfbbSDag-Erling Smørgrav if (c == NULL || !c->have_remote_id) 770af12a3e7SDag-Erling Smørgrav continue; 771af12a3e7SDag-Erling Smørgrav switch (c->type) { 772af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 773af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 7744f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 7754f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 776af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 777af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 778af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 779b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 780b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 781ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 782af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 783af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 784af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 785e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 786a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 787a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 788af12a3e7SDag-Erling Smørgrav continue; 789af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 790af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 791af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 792af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 793af12a3e7SDag-Erling Smørgrav return i; 794af12a3e7SDag-Erling Smørgrav default: 7954f52dfbbSDag-Erling Smørgrav fatal("%s: bad channel type %d", __func__, c->type); 796af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 797af12a3e7SDag-Erling Smørgrav } 798af12a3e7SDag-Erling Smørgrav } 799af12a3e7SDag-Erling Smørgrav return -1; 800af12a3e7SDag-Erling Smørgrav } 801af12a3e7SDag-Erling Smørgrav 802af12a3e7SDag-Erling Smørgrav /* 803af12a3e7SDag-Erling Smørgrav * Returns a message describing the currently open forwarded connections, 804af12a3e7SDag-Erling Smørgrav * suitable for sending to the client. The message contains crlf pairs for 805af12a3e7SDag-Erling Smørgrav * newlines. 806af12a3e7SDag-Erling Smørgrav */ 807af12a3e7SDag-Erling Smørgrav char * 8084f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh) 809af12a3e7SDag-Erling Smørgrav { 8104f52dfbbSDag-Erling Smørgrav struct sshbuf *buf; 811af12a3e7SDag-Erling Smørgrav Channel *c; 81221e764dfSDag-Erling Smørgrav u_int i; 8134f52dfbbSDag-Erling Smørgrav int r; 8144f52dfbbSDag-Erling Smørgrav char *ret; 815af12a3e7SDag-Erling Smørgrav 8164f52dfbbSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 8174f52dfbbSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 8184f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(buf, 8194f52dfbbSDag-Erling Smørgrav "The following connections are open:\r\n")) != 0) 8204f52dfbbSDag-Erling Smørgrav fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r)); 8214f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 8224f52dfbbSDag-Erling Smørgrav c = ssh->chanctxt->channels[i]; 823af12a3e7SDag-Erling Smørgrav if (c == NULL) 824af12a3e7SDag-Erling Smørgrav continue; 825af12a3e7SDag-Erling Smørgrav switch (c->type) { 826af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_LISTENER: 827af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_PORT_LISTENER: 828af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_RPORT_LISTENER: 829af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CLOSED: 830af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_AUTH_SOCKET: 831af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_ZOMBIE: 832e4a9863fSDag-Erling Smørgrav case SSH_CHANNEL_ABANDONED: 833b15c8340SDag-Erling Smørgrav case SSH_CHANNEL_MUX_LISTENER: 834a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 835a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 836af12a3e7SDag-Erling Smørgrav continue; 837af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_LARVAL: 838af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPENING: 839af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_CONNECTING: 840af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_DYNAMIC: 8414f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_OPEN: 8424f52dfbbSDag-Erling Smørgrav case SSH_CHANNEL_RDYNAMIC_FINISH: 843af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_OPEN: 844af12a3e7SDag-Erling Smørgrav case SSH_CHANNEL_X11_OPEN: 845ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_PROXY: 846ca86bcf2SDag-Erling Smørgrav case SSH_CHANNEL_MUX_CLIENT: 8474f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(buf, " #%d %.300s " 8484f52dfbbSDag-Erling Smørgrav "(t%d %s%u i%u/%zu o%u/%zu fd %d/%d cc %d)\r\n", 849af12a3e7SDag-Erling Smørgrav c->self, c->remote_name, 8504f52dfbbSDag-Erling Smørgrav c->type, 8514f52dfbbSDag-Erling Smørgrav c->have_remote_id ? "r" : "nr", c->remote_id, 8524f52dfbbSDag-Erling Smørgrav c->istate, sshbuf_len(c->input), 8534f52dfbbSDag-Erling Smørgrav c->ostate, sshbuf_len(c->output), 8544f52dfbbSDag-Erling Smørgrav c->rfd, c->wfd, c->ctl_chan)) != 0) 8554f52dfbbSDag-Erling Smørgrav fatal("%s: sshbuf_putf: %s", 8564f52dfbbSDag-Erling Smørgrav __func__, ssh_err(r)); 857af12a3e7SDag-Erling Smørgrav continue; 858af12a3e7SDag-Erling Smørgrav default: 8594f52dfbbSDag-Erling Smørgrav fatal("%s: bad channel type %d", __func__, c->type); 860af12a3e7SDag-Erling Smørgrav /* NOTREACHED */ 861af12a3e7SDag-Erling Smørgrav } 862af12a3e7SDag-Erling Smørgrav } 8634f52dfbbSDag-Erling Smørgrav if ((ret = sshbuf_dup_string(buf)) == NULL) 8644f52dfbbSDag-Erling Smørgrav fatal("%s: sshbuf_dup_string", __func__); 8654f52dfbbSDag-Erling Smørgrav sshbuf_free(buf); 8664f52dfbbSDag-Erling Smørgrav return ret; 8674f52dfbbSDag-Erling Smørgrav } 8684f52dfbbSDag-Erling Smørgrav 8694f52dfbbSDag-Erling Smørgrav static void 8704f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) 8714f52dfbbSDag-Erling Smørgrav { 8724f52dfbbSDag-Erling Smørgrav int r; 8734f52dfbbSDag-Erling Smørgrav 8744f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 8754f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, type)) != 0 || 8764f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 8774f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 8784f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { 8794f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: open: %s", where, c->self, ssh_err(r)); 8804f52dfbbSDag-Erling Smørgrav } 881af12a3e7SDag-Erling Smørgrav } 882af12a3e7SDag-Erling Smørgrav 883af12a3e7SDag-Erling Smørgrav void 8844f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id) 885af12a3e7SDag-Erling Smørgrav { 8864f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 8874f52dfbbSDag-Erling Smørgrav int r; 888f388f5efSDag-Erling Smørgrav 889af12a3e7SDag-Erling Smørgrav if (c == NULL) { 890221552e4SDag-Erling Smørgrav logit("channel_send_open: %d: bad id", id); 891af12a3e7SDag-Erling Smørgrav return; 892af12a3e7SDag-Erling Smørgrav } 893e73e9afaSDag-Erling Smørgrav debug2("channel %d: send open", id); 8944f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, c->ctype); 8954f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 8964f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); 897af12a3e7SDag-Erling Smørgrav } 898af12a3e7SDag-Erling Smørgrav 899af12a3e7SDag-Erling Smørgrav void 9004f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) 901af12a3e7SDag-Erling Smørgrav { 9024f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 9034f52dfbbSDag-Erling Smørgrav int r; 904f388f5efSDag-Erling Smørgrav 905af12a3e7SDag-Erling Smørgrav if (c == NULL) { 9064f52dfbbSDag-Erling Smørgrav logit("%s: %d: unknown channel id", __func__, id); 907af12a3e7SDag-Erling Smørgrav return; 908af12a3e7SDag-Erling Smørgrav } 9094f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 9104f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", __func__, c->self); 9114f52dfbbSDag-Erling Smørgrav 91221e764dfSDag-Erling Smørgrav debug2("channel %d: request %s confirm %d", id, service, wantconfirm); 9134f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || 9144f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 9154f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, service)) != 0 || 9164f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { 9174f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); 9184f52dfbbSDag-Erling Smørgrav } 919af12a3e7SDag-Erling Smørgrav } 920333ee039SDag-Erling Smørgrav 921af12a3e7SDag-Erling Smørgrav void 9224f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id, 9234f52dfbbSDag-Erling Smørgrav channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) 924d4af9e69SDag-Erling Smørgrav { 925d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 926d4af9e69SDag-Erling Smørgrav Channel *c; 927d4af9e69SDag-Erling Smørgrav 9284f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 9294f52dfbbSDag-Erling Smørgrav fatal("%s: %d: bad id", __func__, id); 930d4af9e69SDag-Erling Smørgrav 9310a37d4a3SXin LI cc = xcalloc(1, sizeof(*cc)); 932d4af9e69SDag-Erling Smørgrav cc->cb = cb; 933d4af9e69SDag-Erling Smørgrav cc->abandon_cb = abandon_cb; 934d4af9e69SDag-Erling Smørgrav cc->ctx = ctx; 935d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); 936d4af9e69SDag-Erling Smørgrav } 937d4af9e69SDag-Erling Smørgrav 938d4af9e69SDag-Erling Smørgrav void 9394f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id, 9404f52dfbbSDag-Erling Smørgrav channel_open_fn *fn, void *ctx) 941af12a3e7SDag-Erling Smørgrav { 9424f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 943f388f5efSDag-Erling Smørgrav 944af12a3e7SDag-Erling Smørgrav if (c == NULL) { 9454f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id", __func__, id); 946af12a3e7SDag-Erling Smørgrav return; 947af12a3e7SDag-Erling Smørgrav } 948d4af9e69SDag-Erling Smørgrav c->open_confirm = fn; 949d4af9e69SDag-Erling Smørgrav c->open_confirm_ctx = ctx; 950af12a3e7SDag-Erling Smørgrav } 951333ee039SDag-Erling Smørgrav 952af12a3e7SDag-Erling Smørgrav void 9534f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id, 9544f52dfbbSDag-Erling Smørgrav channel_callback_fn *fn, int do_close) 955af12a3e7SDag-Erling Smørgrav { 9564f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 957f388f5efSDag-Erling Smørgrav 958af12a3e7SDag-Erling Smørgrav if (c == NULL) { 9594f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id", __func__, id); 960af12a3e7SDag-Erling Smørgrav return; 961af12a3e7SDag-Erling Smørgrav } 962af12a3e7SDag-Erling Smørgrav c->detach_user = fn; 963b74df5b2SDag-Erling Smørgrav c->detach_close = do_close; 964af12a3e7SDag-Erling Smørgrav } 965333ee039SDag-Erling Smørgrav 966af12a3e7SDag-Erling Smørgrav void 9674f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id) 968af12a3e7SDag-Erling Smørgrav { 9694f52dfbbSDag-Erling Smørgrav Channel *c = channel_by_id(ssh, id); 970f388f5efSDag-Erling Smørgrav 971af12a3e7SDag-Erling Smørgrav if (c == NULL) { 9724f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id", __func__, id); 973af12a3e7SDag-Erling Smørgrav return; 974af12a3e7SDag-Erling Smørgrav } 975af12a3e7SDag-Erling Smørgrav c->detach_user = NULL; 976b74df5b2SDag-Erling Smørgrav c->detach_close = 0; 977af12a3e7SDag-Erling Smørgrav } 978333ee039SDag-Erling Smørgrav 979af12a3e7SDag-Erling Smørgrav void 9804f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, 981d4af9e69SDag-Erling Smørgrav channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) 982af12a3e7SDag-Erling Smørgrav { 9834f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 984f388f5efSDag-Erling Smørgrav 985af12a3e7SDag-Erling Smørgrav if (c == NULL) { 9864f52dfbbSDag-Erling Smørgrav logit("%s: %d: bad id", __func__, id); 987af12a3e7SDag-Erling Smørgrav return; 988af12a3e7SDag-Erling Smørgrav } 989b74df5b2SDag-Erling Smørgrav c->input_filter = ifn; 990b74df5b2SDag-Erling Smørgrav c->output_filter = ofn; 991d4af9e69SDag-Erling Smørgrav c->filter_ctx = ctx; 992d4af9e69SDag-Erling Smørgrav c->filter_cleanup = cfn; 993af12a3e7SDag-Erling Smørgrav } 994af12a3e7SDag-Erling Smørgrav 995af12a3e7SDag-Erling Smørgrav void 9964f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, 997d4af9e69SDag-Erling Smørgrav int extusage, int nonblock, int is_tty, u_int window_max) 998af12a3e7SDag-Erling Smørgrav { 9994f52dfbbSDag-Erling Smørgrav Channel *c = channel_lookup(ssh, id); 10004f52dfbbSDag-Erling Smørgrav int r; 1001f388f5efSDag-Erling Smørgrav 1002af12a3e7SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_LARVAL) 1003af12a3e7SDag-Erling Smørgrav fatal("channel_activate for non-larval channel %d.", id); 10044f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 10054f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", __func__, c->self); 10064f52dfbbSDag-Erling Smørgrav 10074f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); 1008af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 1009af12a3e7SDag-Erling Smørgrav c->local_window = c->local_window_max = window_max; 10104f52dfbbSDag-Erling Smørgrav 10114f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 10124f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 10134f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 10144f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 10154f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); 1016511b41d2SMark Murray } 1017511b41d2SMark Murray 1018af12a3e7SDag-Erling Smørgrav static void 10194f52dfbbSDag-Erling Smørgrav channel_pre_listener(struct ssh *ssh, Channel *c, 10204f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1021511b41d2SMark Murray { 1022a04a10f8SKris Kennaway FD_SET(c->sock, readset); 1023a04a10f8SKris Kennaway } 1024a04a10f8SKris Kennaway 1025af12a3e7SDag-Erling Smørgrav static void 10264f52dfbbSDag-Erling Smørgrav channel_pre_connecting(struct ssh *ssh, Channel *c, 10274f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1028ca3176e7SBrian Feldman { 1029ca3176e7SBrian Feldman debug3("channel %d: waiting for connection", c->self); 1030ca3176e7SBrian Feldman FD_SET(c->sock, writeset); 1031ca3176e7SBrian Feldman } 1032ca3176e7SBrian Feldman 1033af12a3e7SDag-Erling Smørgrav static void 10344f52dfbbSDag-Erling Smørgrav channel_pre_open(struct ssh *ssh, Channel *c, 10354f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1036a04a10f8SKris Kennaway { 1037a04a10f8SKris Kennaway if (c->istate == CHAN_INPUT_OPEN && 10384f52dfbbSDag-Erling Smørgrav c->remote_window > 0 && 10394f52dfbbSDag-Erling Smørgrav sshbuf_len(c->input) < c->remote_window && 10404f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 1041a04a10f8SKris Kennaway FD_SET(c->rfd, readset); 1042a04a10f8SKris Kennaway if (c->ostate == CHAN_OUTPUT_OPEN || 1043a04a10f8SKris Kennaway c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 10444f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) { 1045a04a10f8SKris Kennaway FD_SET(c->wfd, writeset); 1046a04a10f8SKris Kennaway } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 104780628bacSDag-Erling Smørgrav if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) 10484f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 10494f52dfbbSDag-Erling Smørgrav "obuf_empty delayed efd %d/(%zu)", c->self, 10504f52dfbbSDag-Erling Smørgrav c->efd, sshbuf_len(c->extended)); 105180628bacSDag-Erling Smørgrav else 10524f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1053a04a10f8SKris Kennaway } 1054a04a10f8SKris Kennaway } 1055a04a10f8SKris Kennaway /** XXX check close conditions, too */ 10564f52dfbbSDag-Erling Smørgrav if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && 10574f52dfbbSDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_CLOSED)) { 1058a04a10f8SKris Kennaway if (c->extended_usage == CHAN_EXTENDED_WRITE && 10594f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) > 0) 1060a04a10f8SKris Kennaway FD_SET(c->efd, writeset); 1061e2f6069cSDag-Erling Smørgrav else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && 1062e2f6069cSDag-Erling Smørgrav (c->extended_usage == CHAN_EXTENDED_READ || 1063e2f6069cSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) && 10644f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended) < c->remote_window) 1065a04a10f8SKris Kennaway FD_SET(c->efd, readset); 1066a04a10f8SKris Kennaway } 106721e764dfSDag-Erling Smørgrav /* XXX: What about efd? races? */ 1068a04a10f8SKris Kennaway } 1069a04a10f8SKris Kennaway 1070a04a10f8SKris Kennaway /* 1071a04a10f8SKris Kennaway * This is a special state for X11 authentication spoofing. An opened X11 1072a04a10f8SKris Kennaway * connection (when authentication spoofing is being done) remains in this 1073a04a10f8SKris Kennaway * state until the first packet has been completely read. The authentication 1074a04a10f8SKris Kennaway * data in that packet is then substituted by the real data if it matches the 1075a04a10f8SKris Kennaway * fake data, and the channel is put into normal mode. 1076a04a10f8SKris Kennaway * XXX All this happens at the client side. 1077af12a3e7SDag-Erling Smørgrav * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok 1078a04a10f8SKris Kennaway */ 1079af12a3e7SDag-Erling Smørgrav static int 10804f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b) 1081a04a10f8SKris Kennaway { 10824f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 1083ca3176e7SBrian Feldman u_char *ucp; 1084ca3176e7SBrian Feldman u_int proto_len, data_len; 1085511b41d2SMark Murray 1086557f75e5SDag-Erling Smørgrav /* Is this being called after the refusal deadline? */ 10874f52dfbbSDag-Erling Smørgrav if (sc->x11_refuse_time != 0 && 10884f52dfbbSDag-Erling Smørgrav (u_int)monotime() >= sc->x11_refuse_time) { 1089557f75e5SDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1090557f75e5SDag-Erling Smørgrav "expired"); 1091557f75e5SDag-Erling Smørgrav return -1; 1092557f75e5SDag-Erling Smørgrav } 1093557f75e5SDag-Erling Smørgrav 1094511b41d2SMark Murray /* Check if the fixed size part of the packet is in buffer. */ 10954f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 12) 1096a04a10f8SKris Kennaway return 0; 1097511b41d2SMark Murray 1098511b41d2SMark Murray /* Parse the lengths of variable-length fields. */ 10994f52dfbbSDag-Erling Smørgrav ucp = sshbuf_mutable_ptr(b); 1100511b41d2SMark Murray if (ucp[0] == 0x42) { /* Byte order MSB first. */ 1101511b41d2SMark Murray proto_len = 256 * ucp[6] + ucp[7]; 1102511b41d2SMark Murray data_len = 256 * ucp[8] + ucp[9]; 1103511b41d2SMark Murray } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 1104511b41d2SMark Murray proto_len = ucp[6] + 256 * ucp[7]; 1105511b41d2SMark Murray data_len = ucp[8] + 256 * ucp[9]; 1106511b41d2SMark Murray } else { 1107221552e4SDag-Erling Smørgrav debug2("Initial X11 packet contains bad byte order byte: 0x%x", 1108511b41d2SMark Murray ucp[0]); 1109a04a10f8SKris Kennaway return -1; 1110511b41d2SMark Murray } 1111511b41d2SMark Murray 1112511b41d2SMark Murray /* Check if the whole packet is in buffer. */ 11134f52dfbbSDag-Erling Smørgrav if (sshbuf_len(b) < 1114511b41d2SMark Murray 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) 1115a04a10f8SKris Kennaway return 0; 1116511b41d2SMark Murray 1117511b41d2SMark Murray /* Check if authentication protocol matches. */ 11184f52dfbbSDag-Erling Smørgrav if (proto_len != strlen(sc->x11_saved_proto) || 11194f52dfbbSDag-Erling Smørgrav memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { 1120221552e4SDag-Erling Smørgrav debug2("X11 connection uses different authentication protocol."); 1121a04a10f8SKris Kennaway return -1; 1122511b41d2SMark Murray } 1123511b41d2SMark Murray /* Check if authentication data matches our fake data. */ 11244f52dfbbSDag-Erling Smørgrav if (data_len != sc->x11_fake_data_len || 1125e2f6069cSDag-Erling Smørgrav timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), 11264f52dfbbSDag-Erling Smørgrav sc->x11_fake_data, sc->x11_fake_data_len) != 0) { 1127221552e4SDag-Erling Smørgrav debug2("X11 auth data does not match fake data."); 1128a04a10f8SKris Kennaway return -1; 1129511b41d2SMark Murray } 1130511b41d2SMark Murray /* Check fake data length */ 11314f52dfbbSDag-Erling Smørgrav if (sc->x11_fake_data_len != sc->x11_saved_data_len) { 1132511b41d2SMark Murray error("X11 fake_data_len %d != saved_data_len %d", 11334f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len, sc->x11_saved_data_len); 1134a04a10f8SKris Kennaway return -1; 1135511b41d2SMark Murray } 1136511b41d2SMark Murray /* 1137511b41d2SMark Murray * Received authentication protocol and data match 1138511b41d2SMark Murray * our fake data. Substitute the fake data with real 1139511b41d2SMark Murray * data. 1140511b41d2SMark Murray */ 1141511b41d2SMark Murray memcpy(ucp + 12 + ((proto_len + 3) & ~3), 11424f52dfbbSDag-Erling Smørgrav sc->x11_saved_data, sc->x11_saved_data_len); 1143a04a10f8SKris Kennaway return 1; 1144a04a10f8SKris Kennaway } 1145511b41d2SMark Murray 1146af12a3e7SDag-Erling Smørgrav static void 11474f52dfbbSDag-Erling Smørgrav channel_pre_x11_open(struct ssh *ssh, Channel *c, 11484f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1149a04a10f8SKris Kennaway { 11504f52dfbbSDag-Erling Smørgrav int ret = x11_open_helper(ssh, c->output); 1151af12a3e7SDag-Erling Smørgrav 1152af12a3e7SDag-Erling Smørgrav /* c->force_drain = 1; */ 1153af12a3e7SDag-Erling Smørgrav 1154a04a10f8SKris Kennaway if (ret == 1) { 1155a04a10f8SKris Kennaway c->type = SSH_CHANNEL_OPEN; 11564f52dfbbSDag-Erling Smørgrav channel_pre_open(ssh, c, readset, writeset); 1157a04a10f8SKris Kennaway } else if (ret == -1) { 1158221552e4SDag-Erling Smørgrav logit("X11 connection rejected because of wrong authentication."); 11594f52dfbbSDag-Erling Smørgrav debug2("X11 rejected %d i%d/o%d", 11604f52dfbbSDag-Erling Smørgrav c->self, c->istate, c->ostate); 11614f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 11624f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 11634f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 11644f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->output); 11654f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 1166221552e4SDag-Erling Smørgrav debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); 1167a04a10f8SKris Kennaway } 1168a04a10f8SKris Kennaway } 1169a04a10f8SKris Kennaway 1170b15c8340SDag-Erling Smørgrav static void 11714f52dfbbSDag-Erling Smørgrav channel_pre_mux_client(struct ssh *ssh, 11724f52dfbbSDag-Erling Smørgrav Channel *c, fd_set *readset, fd_set *writeset) 1173b15c8340SDag-Erling Smørgrav { 1174e2f6069cSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && 11754f52dfbbSDag-Erling Smørgrav sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) 1176b15c8340SDag-Erling Smørgrav FD_SET(c->rfd, readset); 1177b15c8340SDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 1178b15c8340SDag-Erling Smørgrav /* clear buffer immediately (discard any partial packet) */ 11794f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 11804f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 1181b15c8340SDag-Erling Smørgrav /* Start output drain. XXX just kill chan? */ 11824f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 1183b15c8340SDag-Erling Smørgrav } 1184b15c8340SDag-Erling Smørgrav if (c->ostate == CHAN_OUTPUT_OPEN || 1185b15c8340SDag-Erling Smørgrav c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { 11864f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output) > 0) 1187b15c8340SDag-Erling Smørgrav FD_SET(c->wfd, writeset); 1188b15c8340SDag-Erling Smørgrav else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) 11894f52dfbbSDag-Erling Smørgrav chan_obuf_empty(ssh, c); 1190b15c8340SDag-Erling Smørgrav } 1191b15c8340SDag-Erling Smørgrav } 1192b15c8340SDag-Erling Smørgrav 1193ca3176e7SBrian Feldman /* try to decode a socks4 header */ 1194af12a3e7SDag-Erling Smørgrav static int 11954f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) 1196ca3176e7SBrian Feldman { 11974f52dfbbSDag-Erling Smørgrav const u_char *p; 11984f52dfbbSDag-Erling Smørgrav char *host; 1199cce7d346SDag-Erling Smørgrav u_int len, have, i, found, need; 1200ca3176e7SBrian Feldman char username[256]; 1201ca3176e7SBrian Feldman struct { 1202ca3176e7SBrian Feldman u_int8_t version; 1203ca3176e7SBrian Feldman u_int8_t command; 1204ca3176e7SBrian Feldman u_int16_t dest_port; 1205ca3176e7SBrian Feldman struct in_addr dest_addr; 1206ca3176e7SBrian Feldman } s4_req, s4_rsp; 12074f52dfbbSDag-Erling Smørgrav int r; 1208ca3176e7SBrian Feldman 1209ca3176e7SBrian Feldman debug2("channel %d: decode socks4", c->self); 1210ca3176e7SBrian Feldman 12114f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1212ca3176e7SBrian Feldman len = sizeof(s4_req); 1213ca3176e7SBrian Feldman if (have < len) 1214ca3176e7SBrian Feldman return 0; 12154f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1216cce7d346SDag-Erling Smørgrav 1217cce7d346SDag-Erling Smørgrav need = 1; 1218cce7d346SDag-Erling Smørgrav /* SOCKS4A uses an invalid IP address 0.0.0.x */ 1219cce7d346SDag-Erling Smørgrav if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { 1220cce7d346SDag-Erling Smørgrav debug2("channel %d: socks4a request", c->self); 1221cce7d346SDag-Erling Smørgrav /* ... and needs an extra string (the hostname) */ 1222cce7d346SDag-Erling Smørgrav need = 2; 1223cce7d346SDag-Erling Smørgrav } 1224cce7d346SDag-Erling Smørgrav /* Check for terminating NUL on the string(s) */ 1225ca3176e7SBrian Feldman for (found = 0, i = len; i < have; i++) { 1226ca3176e7SBrian Feldman if (p[i] == '\0') { 1227cce7d346SDag-Erling Smørgrav found++; 1228cce7d346SDag-Erling Smørgrav if (found == need) 1229ca3176e7SBrian Feldman break; 1230ca3176e7SBrian Feldman } 1231ca3176e7SBrian Feldman if (i > 1024) { 1232ca3176e7SBrian Feldman /* the peer is probably sending garbage */ 1233ca3176e7SBrian Feldman debug("channel %d: decode socks4: too long", 1234ca3176e7SBrian Feldman c->self); 1235ca3176e7SBrian Feldman return -1; 1236ca3176e7SBrian Feldman } 1237ca3176e7SBrian Feldman } 1238cce7d346SDag-Erling Smørgrav if (found < need) 1239ca3176e7SBrian Feldman return 0; 12404f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || 12414f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || 12424f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || 12434f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { 12444f52dfbbSDag-Erling Smørgrav debug("channels %d: decode socks4: %s", c->self, ssh_err(r)); 12454f52dfbbSDag-Erling Smørgrav return -1; 12464f52dfbbSDag-Erling Smørgrav } 12474f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 12484f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 12494f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 12504f52dfbbSDag-Erling Smørgrav error("channel %d: decode socks4: user not nul terminated", 1251b83788ffSDag-Erling Smørgrav c->self); 12524f52dfbbSDag-Erling Smørgrav return -1; 12534f52dfbbSDag-Erling Smørgrav } 1254ca3176e7SBrian Feldman len = strlen(p); 1255ca3176e7SBrian Feldman debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); 1256cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1257ca3176e7SBrian Feldman strlcpy(username, p, sizeof(username)); 12584f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(input, len)) != 0) { 12594f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 12604f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 12614f52dfbbSDag-Erling Smørgrav } 1262e4a9863fSDag-Erling Smørgrav free(c->path); 1263cce7d346SDag-Erling Smørgrav c->path = NULL; 1264cce7d346SDag-Erling Smørgrav if (need == 1) { /* SOCKS4: one string */ 1265ca3176e7SBrian Feldman host = inet_ntoa(s4_req.dest_addr); 1266cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 1267cce7d346SDag-Erling Smørgrav } else { /* SOCKS4A: two strings */ 12684f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 12694f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 12704f52dfbbSDag-Erling Smørgrav if (memchr(p, '\0', have) == NULL) { 12714f52dfbbSDag-Erling Smørgrav error("channel %d: decode socks4a: host not nul " 12724f52dfbbSDag-Erling Smørgrav "terminated", c->self); 12734f52dfbbSDag-Erling Smørgrav return -1; 12744f52dfbbSDag-Erling Smørgrav } 1275cce7d346SDag-Erling Smørgrav len = strlen(p); 1276cce7d346SDag-Erling Smørgrav debug2("channel %d: decode socks4a: host %s/%d", 1277cce7d346SDag-Erling Smørgrav c->self, p, len); 1278cce7d346SDag-Erling Smørgrav len++; /* trailing '\0' */ 1279cce7d346SDag-Erling Smørgrav if (len > NI_MAXHOST) { 1280cce7d346SDag-Erling Smørgrav error("channel %d: hostname \"%.100s\" too long", 1281cce7d346SDag-Erling Smørgrav c->self, p); 1282cce7d346SDag-Erling Smørgrav return -1; 1283cce7d346SDag-Erling Smørgrav } 1284cce7d346SDag-Erling Smørgrav c->path = xstrdup(p); 12854f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(input, len)) != 0) { 12864f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 12874f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 12884f52dfbbSDag-Erling Smørgrav } 1289cce7d346SDag-Erling Smørgrav } 1290ca3176e7SBrian Feldman c->host_port = ntohs(s4_req.dest_port); 1291ca3176e7SBrian Feldman 1292221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks4 host %s port %u command %u", 1293cce7d346SDag-Erling Smørgrav c->self, c->path, c->host_port, s4_req.command); 1294ca3176e7SBrian Feldman 1295ca3176e7SBrian Feldman if (s4_req.command != 1) { 1296cce7d346SDag-Erling Smørgrav debug("channel %d: cannot handle: %s cn %d", 1297cce7d346SDag-Erling Smørgrav c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); 1298ca3176e7SBrian Feldman return -1; 1299ca3176e7SBrian Feldman } 1300ca3176e7SBrian Feldman s4_rsp.version = 0; /* vn: 0 for reply */ 1301ca3176e7SBrian Feldman s4_rsp.command = 90; /* cd: req granted */ 1302ca3176e7SBrian Feldman s4_rsp.dest_port = 0; /* ignored */ 1303ca3176e7SBrian Feldman s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ 13044f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) { 13054f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append reply: %s", __func__, 13064f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 13074f52dfbbSDag-Erling Smørgrav } 1308ca3176e7SBrian Feldman return 1; 1309ca3176e7SBrian Feldman } 1310ca3176e7SBrian Feldman 1311221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */ 1312221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE 0x1000 1313221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH 0x00 1314221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4 0x01 1315221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN 0x03 1316221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6 0x04 1317221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT 0x01 1318221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS 0x00 1319221552e4SDag-Erling Smørgrav 1320221552e4SDag-Erling Smørgrav static int 13214f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) 1322221552e4SDag-Erling Smørgrav { 13234f52dfbbSDag-Erling Smørgrav /* XXX use get/put_u8 instead of trusting struct padding */ 1324221552e4SDag-Erling Smørgrav struct { 1325221552e4SDag-Erling Smørgrav u_int8_t version; 1326221552e4SDag-Erling Smørgrav u_int8_t command; 1327221552e4SDag-Erling Smørgrav u_int8_t reserved; 1328221552e4SDag-Erling Smørgrav u_int8_t atyp; 1329221552e4SDag-Erling Smørgrav } s5_req, s5_rsp; 1330221552e4SDag-Erling Smørgrav u_int16_t dest_port; 1331e4a9863fSDag-Erling Smørgrav char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; 13324f52dfbbSDag-Erling Smørgrav const u_char *p; 1333333ee039SDag-Erling Smørgrav u_int have, need, i, found, nmethods, addrlen, af; 13344f52dfbbSDag-Erling Smørgrav int r; 1335221552e4SDag-Erling Smørgrav 1336221552e4SDag-Erling Smørgrav debug2("channel %d: decode socks5", c->self); 13374f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(input); 1338221552e4SDag-Erling Smørgrav if (p[0] != 0x05) 1339221552e4SDag-Erling Smørgrav return -1; 13404f52dfbbSDag-Erling Smørgrav have = sshbuf_len(input); 1341221552e4SDag-Erling Smørgrav if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { 1342221552e4SDag-Erling Smørgrav /* format: ver | nmethods | methods */ 1343221552e4SDag-Erling Smørgrav if (have < 2) 1344221552e4SDag-Erling Smørgrav return 0; 1345221552e4SDag-Erling Smørgrav nmethods = p[1]; 1346221552e4SDag-Erling Smørgrav if (have < nmethods + 2) 1347221552e4SDag-Erling Smørgrav return 0; 1348221552e4SDag-Erling Smørgrav /* look for method: "NO AUTHENTICATION REQUIRED" */ 1349221552e4SDag-Erling Smørgrav for (found = 0, i = 2; i < nmethods + 2; i++) { 1350221552e4SDag-Erling Smørgrav if (p[i] == SSH_SOCKS5_NOAUTH) { 1351221552e4SDag-Erling Smørgrav found = 1; 1352221552e4SDag-Erling Smørgrav break; 1353221552e4SDag-Erling Smørgrav } 1354221552e4SDag-Erling Smørgrav } 1355221552e4SDag-Erling Smørgrav if (!found) { 1356221552e4SDag-Erling Smørgrav debug("channel %d: method SSH_SOCKS5_NOAUTH not found", 1357221552e4SDag-Erling Smørgrav c->self); 1358221552e4SDag-Erling Smørgrav return -1; 1359221552e4SDag-Erling Smørgrav } 13604f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(input, nmethods + 2)) != 0) { 13614f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 13624f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 13634f52dfbbSDag-Erling Smørgrav } 13644f52dfbbSDag-Erling Smørgrav /* version, method */ 13654f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(output, 0x05)) != 0 || 13664f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) { 13674f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append reply: %s", __func__, 13684f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 13694f52dfbbSDag-Erling Smørgrav } 1370221552e4SDag-Erling Smørgrav c->flags |= SSH_SOCKS5_AUTHDONE; 1371221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 auth done", c->self); 1372221552e4SDag-Erling Smørgrav return 0; /* need more */ 1373221552e4SDag-Erling Smørgrav } 1374221552e4SDag-Erling Smørgrav debug2("channel %d: socks5 post auth", c->self); 1375221552e4SDag-Erling Smørgrav if (have < sizeof(s5_req)+1) 1376221552e4SDag-Erling Smørgrav return 0; /* need more */ 1377333ee039SDag-Erling Smørgrav memcpy(&s5_req, p, sizeof(s5_req)); 1378221552e4SDag-Erling Smørgrav if (s5_req.version != 0x05 || 1379221552e4SDag-Erling Smørgrav s5_req.command != SSH_SOCKS5_CONNECT || 1380221552e4SDag-Erling Smørgrav s5_req.reserved != 0x00) { 1381221552e4SDag-Erling Smørgrav debug2("channel %d: only socks5 connect supported", c->self); 1382221552e4SDag-Erling Smørgrav return -1; 1383221552e4SDag-Erling Smørgrav } 1384221552e4SDag-Erling Smørgrav switch (s5_req.atyp){ 1385221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV4: 1386221552e4SDag-Erling Smørgrav addrlen = 4; 1387221552e4SDag-Erling Smørgrav af = AF_INET; 1388221552e4SDag-Erling Smørgrav break; 1389221552e4SDag-Erling Smørgrav case SSH_SOCKS5_DOMAIN: 1390221552e4SDag-Erling Smørgrav addrlen = p[sizeof(s5_req)]; 1391221552e4SDag-Erling Smørgrav af = -1; 1392221552e4SDag-Erling Smørgrav break; 1393221552e4SDag-Erling Smørgrav case SSH_SOCKS5_IPV6: 1394221552e4SDag-Erling Smørgrav addrlen = 16; 1395221552e4SDag-Erling Smørgrav af = AF_INET6; 1396221552e4SDag-Erling Smørgrav break; 1397221552e4SDag-Erling Smørgrav default: 1398221552e4SDag-Erling Smørgrav debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 1399221552e4SDag-Erling Smørgrav return -1; 1400221552e4SDag-Erling Smørgrav } 1401333ee039SDag-Erling Smørgrav need = sizeof(s5_req) + addrlen + 2; 1402333ee039SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) 1403333ee039SDag-Erling Smørgrav need++; 1404333ee039SDag-Erling Smørgrav if (have < need) 1405221552e4SDag-Erling Smørgrav return 0; 14064f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) { 14074f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 14084f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 14094f52dfbbSDag-Erling Smørgrav } 14104f52dfbbSDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 14114f52dfbbSDag-Erling Smørgrav /* host string length */ 14124f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(input, 1)) != 0) { 14134f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 14144f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 14154f52dfbbSDag-Erling Smørgrav } 14164f52dfbbSDag-Erling Smørgrav } 14174f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || 14184f52dfbbSDag-Erling Smørgrav (r = sshbuf_get(input, &dest_port, 2)) != 0) { 14194f52dfbbSDag-Erling Smørgrav debug("channel %d: parse addr/port: %s", c->self, ssh_err(r)); 14204f52dfbbSDag-Erling Smørgrav return -1; 14214f52dfbbSDag-Erling Smørgrav } 1422221552e4SDag-Erling Smørgrav dest_addr[addrlen] = '\0'; 1423e4a9863fSDag-Erling Smørgrav free(c->path); 1424cce7d346SDag-Erling Smørgrav c->path = NULL; 1425cce7d346SDag-Erling Smørgrav if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { 1426cce7d346SDag-Erling Smørgrav if (addrlen >= NI_MAXHOST) { 1427cce7d346SDag-Erling Smørgrav error("channel %d: dynamic request: socks5 hostname " 1428cce7d346SDag-Erling Smørgrav "\"%.100s\" too long", c->self, dest_addr); 1429221552e4SDag-Erling Smørgrav return -1; 1430cce7d346SDag-Erling Smørgrav } 1431cce7d346SDag-Erling Smørgrav c->path = xstrdup(dest_addr); 1432cce7d346SDag-Erling Smørgrav } else { 1433cce7d346SDag-Erling Smørgrav if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) 1434cce7d346SDag-Erling Smørgrav return -1; 1435cce7d346SDag-Erling Smørgrav c->path = xstrdup(ntop); 1436cce7d346SDag-Erling Smørgrav } 1437221552e4SDag-Erling Smørgrav c->host_port = ntohs(dest_port); 1438221552e4SDag-Erling Smørgrav 1439221552e4SDag-Erling Smørgrav debug2("channel %d: dynamic request: socks5 host %s port %u command %u", 1440221552e4SDag-Erling Smørgrav c->self, c->path, c->host_port, s5_req.command); 1441221552e4SDag-Erling Smørgrav 1442221552e4SDag-Erling Smørgrav s5_rsp.version = 0x05; 1443221552e4SDag-Erling Smørgrav s5_rsp.command = SSH_SOCKS5_SUCCESS; 1444221552e4SDag-Erling Smørgrav s5_rsp.reserved = 0; /* ignored */ 1445221552e4SDag-Erling Smørgrav s5_rsp.atyp = SSH_SOCKS5_IPV4; 1446221552e4SDag-Erling Smørgrav dest_port = 0; /* ignored */ 1447221552e4SDag-Erling Smørgrav 14484f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || 14494f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || 14504f52dfbbSDag-Erling Smørgrav (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) 14514f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append reply: %s", __func__, 14524f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 1453221552e4SDag-Erling Smørgrav return 1; 1454221552e4SDag-Erling Smørgrav } 1455221552e4SDag-Erling Smørgrav 1456b15c8340SDag-Erling Smørgrav Channel * 14574f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh, 14584f52dfbbSDag-Erling Smørgrav const char *host_to_connect, u_short port_to_connect, int in, int out) 1459b15c8340SDag-Erling Smørgrav { 1460b15c8340SDag-Erling Smørgrav Channel *c; 1461b15c8340SDag-Erling Smørgrav 14624f52dfbbSDag-Erling Smørgrav debug("%s %s:%d", __func__, host_to_connect, port_to_connect); 1463b15c8340SDag-Erling Smørgrav 14644f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, 1465b15c8340SDag-Erling Smørgrav -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 1466b15c8340SDag-Erling Smørgrav 0, "stdio-forward", /*nonblock*/0); 1467b15c8340SDag-Erling Smørgrav 1468b15c8340SDag-Erling Smørgrav c->path = xstrdup(host_to_connect); 1469b15c8340SDag-Erling Smørgrav c->host_port = port_to_connect; 1470b15c8340SDag-Erling Smørgrav c->listening_port = 0; 1471b15c8340SDag-Erling Smørgrav c->force_drain = 1; 1472b15c8340SDag-Erling Smørgrav 14734f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); 14744f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 1475b15c8340SDag-Erling Smørgrav 1476b15c8340SDag-Erling Smørgrav return c; 1477b15c8340SDag-Erling Smørgrav } 1478b15c8340SDag-Erling Smørgrav 1479ca3176e7SBrian Feldman /* dynamic port forwarding */ 1480af12a3e7SDag-Erling Smørgrav static void 14814f52dfbbSDag-Erling Smørgrav channel_pre_dynamic(struct ssh *ssh, Channel *c, 14824f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1483ca3176e7SBrian Feldman { 14844f52dfbbSDag-Erling Smørgrav const u_char *p; 1485d4ecd108SDag-Erling Smørgrav u_int have; 1486d4ecd108SDag-Erling Smørgrav int ret; 1487ca3176e7SBrian Feldman 14884f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->input); 1489ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: have %d", c->self, have); 14904f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->input, stderr); */ 1491ca3176e7SBrian Feldman /* check if the fixed size part of the packet is in buffer. */ 1492221552e4SDag-Erling Smørgrav if (have < 3) { 1493ca3176e7SBrian Feldman /* need more */ 1494ca3176e7SBrian Feldman FD_SET(c->sock, readset); 1495ca3176e7SBrian Feldman return; 1496ca3176e7SBrian Feldman } 1497ca3176e7SBrian Feldman /* try to guess the protocol */ 14984f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->input); 14994f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u8? */ 1500ca3176e7SBrian Feldman switch (p[0]) { 1501ca3176e7SBrian Feldman case 0x04: 15024f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->input, c->output); 1503ca3176e7SBrian Feldman break; 1504221552e4SDag-Erling Smørgrav case 0x05: 15054f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->input, c->output); 1506221552e4SDag-Erling Smørgrav break; 1507ca3176e7SBrian Feldman default: 1508ca3176e7SBrian Feldman ret = -1; 1509ca3176e7SBrian Feldman break; 1510ca3176e7SBrian Feldman } 1511ca3176e7SBrian Feldman if (ret < 0) { 15124f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1513ca3176e7SBrian Feldman } else if (ret == 0) { 1514ca3176e7SBrian Feldman debug2("channel %d: pre_dynamic: need more", c->self); 1515ca3176e7SBrian Feldman /* need more */ 1516ca3176e7SBrian Feldman FD_SET(c->sock, readset); 15174f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->output)) 15184f52dfbbSDag-Erling Smørgrav FD_SET(c->sock, writeset); 1519ca3176e7SBrian Feldman } else { 1520ca3176e7SBrian Feldman /* switch to the next state */ 1521ca3176e7SBrian Feldman c->type = SSH_CHANNEL_OPENING; 15224f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, c, "direct-tcpip"); 15234f52dfbbSDag-Erling Smørgrav } 15244f52dfbbSDag-Erling Smørgrav } 15254f52dfbbSDag-Erling Smørgrav 15264f52dfbbSDag-Erling Smørgrav /* simulate read-error */ 15274f52dfbbSDag-Erling Smørgrav static void 15284f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c) 15294f52dfbbSDag-Erling Smørgrav { 15304f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 15314f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 15324f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->input); 15334f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 15344f52dfbbSDag-Erling Smørgrav sshbuf_reset(c->output); 15354f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 15364f52dfbbSDag-Erling Smørgrav } 15374f52dfbbSDag-Erling Smørgrav 15384f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */ 15394f52dfbbSDag-Erling Smørgrav static void 15404f52dfbbSDag-Erling Smørgrav channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c) 15414f52dfbbSDag-Erling Smørgrav { 15424f52dfbbSDag-Erling Smørgrav const u_char *p; 15434f52dfbbSDag-Erling Smørgrav u_int have, len; 15444f52dfbbSDag-Erling Smørgrav int r, ret; 15454f52dfbbSDag-Erling Smørgrav 15464f52dfbbSDag-Erling Smørgrav have = sshbuf_len(c->output); 15474f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: have %d", c->self, have); 15484f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(c->output, stderr); */ 15494f52dfbbSDag-Erling Smørgrav /* EOF received */ 15504f52dfbbSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 15514f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, have)) != 0) { 15524f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", 15534f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 15544f52dfbbSDag-Erling Smørgrav } 15554f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 15564f52dfbbSDag-Erling Smørgrav return; 15574f52dfbbSDag-Erling Smørgrav } 15584f52dfbbSDag-Erling Smørgrav /* check if the fixed size part of the packet is in buffer. */ 15594f52dfbbSDag-Erling Smørgrav if (have < 3) 15604f52dfbbSDag-Erling Smørgrav return; 15614f52dfbbSDag-Erling Smørgrav /* try to guess the protocol */ 15624f52dfbbSDag-Erling Smørgrav p = sshbuf_ptr(c->output); 15634f52dfbbSDag-Erling Smørgrav switch (p[0]) { 15644f52dfbbSDag-Erling Smørgrav case 0x04: 15654f52dfbbSDag-Erling Smørgrav /* switch input/output for reverse forwarding */ 15664f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks4(c, c->output, c->input); 15674f52dfbbSDag-Erling Smørgrav break; 15684f52dfbbSDag-Erling Smørgrav case 0x05: 15694f52dfbbSDag-Erling Smørgrav ret = channel_decode_socks5(c, c->output, c->input); 15704f52dfbbSDag-Erling Smørgrav break; 15714f52dfbbSDag-Erling Smørgrav default: 15724f52dfbbSDag-Erling Smørgrav ret = -1; 15734f52dfbbSDag-Erling Smørgrav break; 15744f52dfbbSDag-Erling Smørgrav } 15754f52dfbbSDag-Erling Smørgrav if (ret < 0) { 15764f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 15774f52dfbbSDag-Erling Smørgrav } else if (ret == 0) { 15784f52dfbbSDag-Erling Smørgrav debug2("channel %d: pre_rdynamic: need more", c->self); 15794f52dfbbSDag-Erling Smørgrav /* send socks request to peer */ 15804f52dfbbSDag-Erling Smørgrav len = sshbuf_len(c->input); 15814f52dfbbSDag-Erling Smørgrav if (len > 0 && len < c->remote_window) { 15824f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 15834f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 15844f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_stringb(ssh, c->input)) != 0 || 15854f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 15864f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: rdynamic: %s", __func__, 15874f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 15884f52dfbbSDag-Erling Smørgrav } 15894f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->input, len)) != 0) { 15904f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", 15914f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 15924f52dfbbSDag-Erling Smørgrav } 15934f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 15944f52dfbbSDag-Erling Smørgrav } 15954f52dfbbSDag-Erling Smørgrav } else if (rdynamic_connect_finish(ssh, c) < 0) { 15964f52dfbbSDag-Erling Smørgrav /* the connect failed */ 15974f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1598ca3176e7SBrian Feldman } 1599ca3176e7SBrian Feldman } 1600ca3176e7SBrian Feldman 1601a04a10f8SKris Kennaway /* This is our fake X11 server socket. */ 1602af12a3e7SDag-Erling Smørgrav static void 16034f52dfbbSDag-Erling Smørgrav channel_post_x11_listener(struct ssh *ssh, Channel *c, 16044f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1605511b41d2SMark Murray { 1606af12a3e7SDag-Erling Smørgrav Channel *nc; 1607d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 16084f52dfbbSDag-Erling Smørgrav int r, newsock, oerrno, remote_port; 1609511b41d2SMark Murray socklen_t addrlen; 1610ca3176e7SBrian Feldman char buf[16384], *remote_ipaddr; 1611511b41d2SMark Murray 16124f52dfbbSDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 16134f52dfbbSDag-Erling Smørgrav return; 16144f52dfbbSDag-Erling Smørgrav 1615511b41d2SMark Murray debug("X11 connection requested."); 1616511b41d2SMark Murray addrlen = sizeof(addr); 1617d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1618af12a3e7SDag-Erling Smørgrav if (c->single_connection) { 1619e4a9863fSDag-Erling Smørgrav oerrno = errno; 1620221552e4SDag-Erling Smørgrav debug2("single_connection: closing X11 listener."); 16214f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->sock); 16224f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1623e4a9863fSDag-Erling Smørgrav errno = oerrno; 1624af12a3e7SDag-Erling Smørgrav } 1625511b41d2SMark Murray if (newsock < 0) { 1626e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1627e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1628511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1629462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1630e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1631a04a10f8SKris Kennaway return; 1632511b41d2SMark Murray } 1633af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 1634ca3176e7SBrian Feldman remote_ipaddr = get_peer_ipaddr(newsock); 1635a04a10f8SKris Kennaway remote_port = get_peer_port(newsock); 1636511b41d2SMark Murray snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", 1637ca3176e7SBrian Feldman remote_ipaddr, remote_port); 1638a04a10f8SKris Kennaway 16394f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "accepted x11 socket", 1640a04a10f8SKris Kennaway SSH_CHANNEL_OPENING, newsock, newsock, -1, 1641221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, buf, 1); 16424f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "x11"); 164347dd1d1bSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 164447dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, remote_port)) != 0) { 16454f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 16464f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 1647511b41d2SMark Murray } 16484f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 16494f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r)); 1650e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1651a04a10f8SKris Kennaway } 1652511b41d2SMark Murray 1653af12a3e7SDag-Erling Smørgrav static void 16544f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype) 1655ca3176e7SBrian Feldman { 1656f7167e0eSDag-Erling Smørgrav char *local_ipaddr = get_local_ipaddr(c->sock); 1657076ad2f8SDag-Erling Smørgrav int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); 1658ca3176e7SBrian Feldman char *remote_ipaddr = get_peer_ipaddr(c->sock); 1659d4ecd108SDag-Erling Smørgrav int remote_port = get_peer_port(c->sock); 16604f52dfbbSDag-Erling Smørgrav int r; 1661ca3176e7SBrian Feldman 1662b15c8340SDag-Erling Smørgrav if (remote_port == -1) { 1663b15c8340SDag-Erling Smørgrav /* Fake addr/port to appease peers that validate it (Tectia) */ 1664e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1665b15c8340SDag-Erling Smørgrav remote_ipaddr = xstrdup("127.0.0.1"); 1666b15c8340SDag-Erling Smørgrav remote_port = 65535; 1667b15c8340SDag-Erling Smørgrav } 1668b15c8340SDag-Erling Smørgrav 16694f52dfbbSDag-Erling Smørgrav free(c->remote_name); 16704f52dfbbSDag-Erling Smørgrav xasprintf(&c->remote_name, 1671ca3176e7SBrian Feldman "%s: listening port %d for %.100s port %d, " 1672f7167e0eSDag-Erling Smørgrav "connect from %.200s port %d to %.100s port %d", 1673ca3176e7SBrian Feldman rtype, c->listening_port, c->path, c->host_port, 1674f7167e0eSDag-Erling Smørgrav remote_ipaddr, remote_port, local_ipaddr, local_port); 1675ca3176e7SBrian Feldman 16764f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, c, rtype); 1677a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "direct-tcpip") == 0) { 1678ca3176e7SBrian Feldman /* target host, port */ 16794f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 16804f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->host_port)) != 0) { 16814f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 16824f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 16834f52dfbbSDag-Erling Smørgrav } 1684a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { 1685a0ee8cc6SDag-Erling Smørgrav /* target path */ 16864f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) { 16874f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 16884f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 16894f52dfbbSDag-Erling Smørgrav } 1690a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1691a0ee8cc6SDag-Erling Smørgrav /* listen path */ 16924f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) { 16934f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 16944f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 16954f52dfbbSDag-Erling Smørgrav } 1696ca3176e7SBrian Feldman } else { 1697ca3176e7SBrian Feldman /* listen address, port */ 16984f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || 16994f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, local_port)) != 0) { 17004f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 17014f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 17024f52dfbbSDag-Erling Smørgrav } 1703ca3176e7SBrian Feldman } 1704a0ee8cc6SDag-Erling Smørgrav if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { 1705a0ee8cc6SDag-Erling Smørgrav /* reserved for future owner/mode info */ 17064f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, "")) != 0) { 17074f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 17084f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 17094f52dfbbSDag-Erling Smørgrav } 1710a0ee8cc6SDag-Erling Smørgrav } else { 1711ca3176e7SBrian Feldman /* originator host and port */ 17124f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || 17134f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) { 17144f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: reply %s", __func__, 17154f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 1716a0ee8cc6SDag-Erling Smørgrav } 1717ca3176e7SBrian Feldman } 17184f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 17194f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r)); 1720e4a9863fSDag-Erling Smørgrav free(remote_ipaddr); 1721f7167e0eSDag-Erling Smørgrav free(local_ipaddr); 1722ca3176e7SBrian Feldman } 1723ca3176e7SBrian Feldman 1724557f75e5SDag-Erling Smørgrav void 17254f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) 1726557f75e5SDag-Erling Smørgrav { 17274f52dfbbSDag-Erling Smørgrav ssh->chanctxt->x11_refuse_time = refuse_time; 1728557f75e5SDag-Erling Smørgrav } 1729557f75e5SDag-Erling Smørgrav 1730511b41d2SMark Murray /* 1731a04a10f8SKris Kennaway * This socket is listening for connections to a forwarded TCP/IP port. 1732511b41d2SMark Murray */ 1733af12a3e7SDag-Erling Smørgrav static void 17344f52dfbbSDag-Erling Smørgrav channel_post_port_listener(struct ssh *ssh, Channel *c, 17354f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1736a04a10f8SKris Kennaway { 1737ca3176e7SBrian Feldman Channel *nc; 1738d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1739af12a3e7SDag-Erling Smørgrav int newsock, nextstate; 1740a04a10f8SKris Kennaway socklen_t addrlen; 1741ca3176e7SBrian Feldman char *rtype; 1742a04a10f8SKris Kennaway 17434f52dfbbSDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 17444f52dfbbSDag-Erling Smørgrav return; 17454f52dfbbSDag-Erling Smørgrav 17464f52dfbbSDag-Erling Smørgrav debug("Connection to port %d forwarding to %.100s port %d requested.", 1747a04a10f8SKris Kennaway c->listening_port, c->path, c->host_port); 1748ca3176e7SBrian Feldman 1749af12a3e7SDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RPORT_LISTENER) { 1750af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1751af12a3e7SDag-Erling Smørgrav rtype = "forwarded-tcpip"; 1752a0ee8cc6SDag-Erling Smørgrav } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { 1753a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1754a0ee8cc6SDag-Erling Smørgrav rtype = "forwarded-streamlocal@openssh.com"; 1755a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == PORT_STREAMLOCAL) { 1756a0ee8cc6SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1757a0ee8cc6SDag-Erling Smørgrav rtype = "direct-streamlocal@openssh.com"; 1758a0ee8cc6SDag-Erling Smørgrav } else if (c->host_port == 0) { 1759af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_DYNAMIC; 1760af12a3e7SDag-Erling Smørgrav rtype = "dynamic-tcpip"; 1761af12a3e7SDag-Erling Smørgrav } else { 1762af12a3e7SDag-Erling Smørgrav nextstate = SSH_CHANNEL_OPENING; 1763af12a3e7SDag-Erling Smørgrav rtype = "direct-tcpip"; 1764af12a3e7SDag-Erling Smørgrav } 1765ca3176e7SBrian Feldman 1766511b41d2SMark Murray addrlen = sizeof(addr); 1767d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1768511b41d2SMark Murray if (newsock < 0) { 1769e4a9863fSDag-Erling Smørgrav if (errno != EINTR && errno != EWOULDBLOCK && 1770e4a9863fSDag-Erling Smørgrav errno != ECONNABORTED) 1771511b41d2SMark Murray error("accept: %.100s", strerror(errno)); 1772462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1773e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1774a04a10f8SKris Kennaway return; 1775511b41d2SMark Murray } 1776a0ee8cc6SDag-Erling Smørgrav if (c->host_port != PORT_STREAMLOCAL) 1777af12a3e7SDag-Erling Smørgrav set_nodelay(newsock); 17784f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, 1779221552e4SDag-Erling Smørgrav c->local_window_max, c->local_maxpacket, 0, rtype, 1); 1780ca3176e7SBrian Feldman nc->listening_port = c->listening_port; 1781ca3176e7SBrian Feldman nc->host_port = c->host_port; 1782cce7d346SDag-Erling Smørgrav if (c->path != NULL) 1783cce7d346SDag-Erling Smørgrav nc->path = xstrdup(c->path); 1784ca3176e7SBrian Feldman 1785b15c8340SDag-Erling Smørgrav if (nextstate != SSH_CHANNEL_DYNAMIC) 17864f52dfbbSDag-Erling Smørgrav port_open_helper(ssh, nc, rtype); 1787a04a10f8SKris Kennaway } 1788511b41d2SMark Murray 1789511b41d2SMark Murray /* 1790a04a10f8SKris Kennaway * This is the authentication agent socket listening for connections from 1791a04a10f8SKris Kennaway * clients. 1792511b41d2SMark Murray */ 1793af12a3e7SDag-Erling Smørgrav static void 17944f52dfbbSDag-Erling Smørgrav channel_post_auth_listener(struct ssh *ssh, Channel *c, 17954f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1796a04a10f8SKris Kennaway { 1797af12a3e7SDag-Erling Smørgrav Channel *nc; 17984f52dfbbSDag-Erling Smørgrav int r, newsock; 1799d4af9e69SDag-Erling Smørgrav struct sockaddr_storage addr; 1800a04a10f8SKris Kennaway socklen_t addrlen; 1801a04a10f8SKris Kennaway 18024f52dfbbSDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 18034f52dfbbSDag-Erling Smørgrav return; 18044f52dfbbSDag-Erling Smørgrav 1805511b41d2SMark Murray addrlen = sizeof(addr); 1806d4af9e69SDag-Erling Smørgrav newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); 1807511b41d2SMark Murray if (newsock < 0) { 18084f52dfbbSDag-Erling Smørgrav error("accept from auth socket: %.100s", strerror(errno)); 1809462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 1810e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 1811a04a10f8SKris Kennaway return; 1812511b41d2SMark Murray } 18134f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "accepted auth socket", 1814ca3176e7SBrian Feldman SSH_CHANNEL_OPENING, newsock, newsock, -1, 1815ca3176e7SBrian Feldman c->local_window_max, c->local_maxpacket, 1816221552e4SDag-Erling Smørgrav 0, "accepted auth socket", 1); 18174f52dfbbSDag-Erling Smørgrav open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); 18184f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 18194f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); 1820a04a10f8SKris Kennaway } 1821511b41d2SMark Murray 1822af12a3e7SDag-Erling Smørgrav static void 18234f52dfbbSDag-Erling Smørgrav channel_post_connecting(struct ssh *ssh, Channel *c, 18244f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1825ca3176e7SBrian Feldman { 18264f52dfbbSDag-Erling Smørgrav int err = 0, sock, isopen, r; 1827af12a3e7SDag-Erling Smørgrav socklen_t sz = sizeof(err); 1828af12a3e7SDag-Erling Smørgrav 18294f52dfbbSDag-Erling Smørgrav if (!FD_ISSET(c->sock, writeset)) 18304f52dfbbSDag-Erling Smørgrav return; 18314f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 18324f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", __func__, c->self); 18334f52dfbbSDag-Erling Smørgrav /* for rdynamic the OPEN_CONFIRMATION has been sent already */ 18344f52dfbbSDag-Erling Smørgrav isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); 1835af12a3e7SDag-Erling Smørgrav if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { 1836af12a3e7SDag-Erling Smørgrav err = errno; 1837af12a3e7SDag-Erling Smørgrav error("getsockopt SO_ERROR failed"); 1838af12a3e7SDag-Erling Smørgrav } 1839ca3176e7SBrian Feldman if (err == 0) { 1840d4af9e69SDag-Erling Smørgrav debug("channel %d: connected to %s port %d", 1841d4af9e69SDag-Erling Smørgrav c->self, c->connect_ctx.host, c->connect_ctx.port); 1842d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 1843af12a3e7SDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 18444f52dfbbSDag-Erling Smørgrav if (isopen) { 18454f52dfbbSDag-Erling Smørgrav /* no message necessary */ 1846af12a3e7SDag-Erling Smørgrav } else { 18474f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 18484f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 18494f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 18504f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 18514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 18524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) 18534f52dfbbSDag-Erling Smørgrav != 0) 18544f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: confirm: %s", __func__, 18554f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 18564f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 18574f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, 18584f52dfbbSDag-Erling Smørgrav ssh_err(r)); 1859af12a3e7SDag-Erling Smørgrav } 1860ca3176e7SBrian Feldman } else { 1861d4af9e69SDag-Erling Smørgrav debug("channel %d: connection failed: %s", 1862ca3176e7SBrian Feldman c->self, strerror(err)); 1863d4af9e69SDag-Erling Smørgrav /* Try next address, if any */ 1864d4af9e69SDag-Erling Smørgrav if ((sock = connect_next(&c->connect_ctx)) > 0) { 1865d4af9e69SDag-Erling Smørgrav close(c->sock); 1866d4af9e69SDag-Erling Smørgrav c->sock = c->rfd = c->wfd = sock; 18674f52dfbbSDag-Erling Smørgrav channel_find_maxfd(ssh->chanctxt); 1868d4af9e69SDag-Erling Smørgrav return; 1869d4af9e69SDag-Erling Smørgrav } 1870d4af9e69SDag-Erling Smørgrav /* Exhausted all addresses */ 1871d4af9e69SDag-Erling Smørgrav error("connect_to %.100s port %d: failed.", 1872d4af9e69SDag-Erling Smørgrav c->connect_ctx.host, c->connect_ctx.port); 1873d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(&c->connect_ctx); 18744f52dfbbSDag-Erling Smørgrav if (isopen) { 18754f52dfbbSDag-Erling Smørgrav rdynamic_close(ssh, c); 1876af12a3e7SDag-Erling Smørgrav } else { 18774f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 18784f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 18794f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 188047dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, 188147dd1d1bSDag-Erling Smørgrav SSH2_OPEN_CONNECT_FAILED)) != 0 || 188247dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || 188347dd1d1bSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "")) != 0) { 18844f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: failure: %s", __func__, 18854f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 188647dd1d1bSDag-Erling Smørgrav } 18874f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 18884f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, c->self, 18894f52dfbbSDag-Erling Smørgrav ssh_err(r)); 18904f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1891ca3176e7SBrian Feldman } 1892ca3176e7SBrian Feldman } 1893ca3176e7SBrian Feldman } 1894ca3176e7SBrian Feldman 1895af12a3e7SDag-Erling Smørgrav static int 18964f52dfbbSDag-Erling Smørgrav channel_handle_rfd(struct ssh *ssh, Channel *c, 18974f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1898a04a10f8SKris Kennaway { 1899aa49c926SDag-Erling Smørgrav char buf[CHAN_RBUF]; 19004f52dfbbSDag-Erling Smørgrav ssize_t len; 19014f52dfbbSDag-Erling Smørgrav int r, force; 1902511b41d2SMark Murray 1903d4af9e69SDag-Erling Smørgrav force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; 19044f52dfbbSDag-Erling Smørgrav 19054f52dfbbSDag-Erling Smørgrav if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset))) 19064f52dfbbSDag-Erling Smørgrav return 1; 19074f52dfbbSDag-Erling Smørgrav 1908333ee039SDag-Erling Smørgrav errno = 0; 1909a04a10f8SKris Kennaway len = read(c->rfd, buf, sizeof(buf)); 1910d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || 1911d4af9e69SDag-Erling Smørgrav ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) 1912a04a10f8SKris Kennaway return 1; 1913333ee039SDag-Erling Smørgrav #ifndef PTY_ZEROREAD 19140c82706bSBrian Feldman if (len <= 0) { 1915333ee039SDag-Erling Smørgrav #else 1916333ee039SDag-Erling Smørgrav if ((!c->isatty && len <= 0) || 1917333ee039SDag-Erling Smørgrav (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { 1918333ee039SDag-Erling Smørgrav #endif 19194f52dfbbSDag-Erling Smørgrav debug2("channel %d: read<=0 rfd %d len %zd", 1920a04a10f8SKris Kennaway c->self, c->rfd, len); 1921ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 1922221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 19234f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1924ca3176e7SBrian Feldman return -1; 1925a04a10f8SKris Kennaway } else { 19264f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 1927a04a10f8SKris Kennaway } 1928a04a10f8SKris Kennaway return -1; 1929a04a10f8SKris Kennaway } 1930b66f2d16SKris Kennaway if (c->input_filter != NULL) { 19314f52dfbbSDag-Erling Smørgrav if (c->input_filter(ssh, c, buf, len) == -1) { 1932221552e4SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 19334f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 1934b66f2d16SKris Kennaway } 1935b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 19364f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->input, buf, len)) != 0) 19374f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: put datagram: %s", __func__, 19384f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 19394f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->input, buf, len)) != 0) { 19404f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: put data: %s", __func__, 19414f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 1942b66f2d16SKris Kennaway } 1943a04a10f8SKris Kennaway return 1; 1944a04a10f8SKris Kennaway } 1945333ee039SDag-Erling Smørgrav 1946af12a3e7SDag-Erling Smørgrav static int 19474f52dfbbSDag-Erling Smørgrav channel_handle_wfd(struct ssh *ssh, Channel *c, 19484f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 1949a04a10f8SKris Kennaway { 1950ca3176e7SBrian Feldman struct termios tio; 19514f52dfbbSDag-Erling Smørgrav u_char *data = NULL, *buf; /* XXX const; need filter API change */ 19524f52dfbbSDag-Erling Smørgrav size_t dlen, olen = 0; 19534f52dfbbSDag-Erling Smørgrav int r, len; 19544f52dfbbSDag-Erling Smørgrav 19554f52dfbbSDag-Erling Smørgrav if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || 19564f52dfbbSDag-Erling Smørgrav sshbuf_len(c->output) == 0) 19574f52dfbbSDag-Erling Smørgrav return 1; 1958a04a10f8SKris Kennaway 1959a04a10f8SKris Kennaway /* Send buffered output data to the socket. */ 19604f52dfbbSDag-Erling Smørgrav olen = sshbuf_len(c->output); 1961b74df5b2SDag-Erling Smørgrav if (c->output_filter != NULL) { 19624f52dfbbSDag-Erling Smørgrav if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { 1963b74df5b2SDag-Erling Smørgrav debug2("channel %d: filter stops", c->self); 1964b74df5b2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPEN) 19654f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 1966b74df5b2SDag-Erling Smørgrav else 19674f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 1968b74df5b2SDag-Erling Smørgrav return -1; 1969b74df5b2SDag-Erling Smørgrav } 1970b74df5b2SDag-Erling Smørgrav } else if (c->datagram) { 19714f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) 19724f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: get datagram: %s", __func__, 19734f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 19744f52dfbbSDag-Erling Smørgrav buf = data; 1975b74df5b2SDag-Erling Smørgrav } else { 19764f52dfbbSDag-Erling Smørgrav buf = data = sshbuf_mutable_ptr(c->output); 19774f52dfbbSDag-Erling Smørgrav dlen = sshbuf_len(c->output); 1978b74df5b2SDag-Erling Smørgrav } 1979b74df5b2SDag-Erling Smørgrav 1980b74df5b2SDag-Erling Smørgrav if (c->datagram) { 1981b74df5b2SDag-Erling Smørgrav /* ignore truncated writes, datagrams might get lost */ 1982b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1983e4a9863fSDag-Erling Smørgrav free(data); 1984d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 1985d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 1986b74df5b2SDag-Erling Smørgrav return 1; 19874f52dfbbSDag-Erling Smørgrav if (len <= 0) 19884f52dfbbSDag-Erling Smørgrav goto write_fail; 1989e2f6069cSDag-Erling Smørgrav goto out; 1990b74df5b2SDag-Erling Smørgrav } 19914f52dfbbSDag-Erling Smørgrav 1992f388f5efSDag-Erling Smørgrav #ifdef _AIX 1993f388f5efSDag-Erling Smørgrav /* XXX: Later AIX versions can't push as much data to tty */ 19944f52dfbbSDag-Erling Smørgrav if (c->wfd_isatty) 1995476cd3b2SDag-Erling Smørgrav dlen = MIN(dlen, 8*1024); 1996f388f5efSDag-Erling Smørgrav #endif 1997b74df5b2SDag-Erling Smørgrav 1998b74df5b2SDag-Erling Smørgrav len = write(c->wfd, buf, dlen); 1999d4af9e69SDag-Erling Smørgrav if (len < 0 && 2000d4af9e69SDag-Erling Smørgrav (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) 2001a04a10f8SKris Kennaway return 1; 2002511b41d2SMark Murray if (len <= 0) { 20034f52dfbbSDag-Erling Smørgrav write_fail: 2004ca3176e7SBrian Feldman if (c->type != SSH_CHANNEL_OPEN) { 2005221552e4SDag-Erling Smørgrav debug2("channel %d: not open", c->self); 20064f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2007ca3176e7SBrian Feldman return -1; 2008511b41d2SMark Murray } else { 20094f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 2010511b41d2SMark Murray } 2011a04a10f8SKris Kennaway return -1; 2012511b41d2SMark Murray } 20137aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON 20144f52dfbbSDag-Erling Smørgrav if (c->isatty && dlen >= 1 && buf[0] != '\r') { 2015e0fbb1d2SBrian Feldman if (tcgetattr(c->wfd, &tio) == 0 && 2016e0fbb1d2SBrian Feldman !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 2017e0fbb1d2SBrian Feldman /* 2018e0fbb1d2SBrian Feldman * Simulate echo to reduce the impact of 2019ca3176e7SBrian Feldman * traffic analysis. We need to match the 2020ca3176e7SBrian Feldman * size of a SSH2_MSG_CHANNEL_DATA message 2021b74df5b2SDag-Erling Smørgrav * (4 byte channel id + buf) 2022e0fbb1d2SBrian Feldman */ 20234f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || 20244f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 20254f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: ignore: %s", 20264f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 2027e0fbb1d2SBrian Feldman } 2028e0fbb1d2SBrian Feldman } 20294f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */ 20304f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, len)) != 0) { 20314f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", 20324f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 2033511b41d2SMark Murray } 2034e2f6069cSDag-Erling Smørgrav out: 20354f52dfbbSDag-Erling Smørgrav c->local_consumed += olen - sshbuf_len(c->output); 20364f52dfbbSDag-Erling Smørgrav 2037a04a10f8SKris Kennaway return 1; 2038511b41d2SMark Murray } 2039333ee039SDag-Erling Smørgrav 2040af12a3e7SDag-Erling Smørgrav static int 20414f52dfbbSDag-Erling Smørgrav channel_handle_efd_write(struct ssh *ssh, Channel *c, 20424f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 2043a04a10f8SKris Kennaway { 20444f52dfbbSDag-Erling Smørgrav int r; 20454f52dfbbSDag-Erling Smørgrav ssize_t len; 2046511b41d2SMark Murray 20474f52dfbbSDag-Erling Smørgrav if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0) 20484f52dfbbSDag-Erling Smørgrav return 1; 20494f52dfbbSDag-Erling Smørgrav 20504f52dfbbSDag-Erling Smørgrav len = write(c->efd, sshbuf_ptr(c->extended), 20514f52dfbbSDag-Erling Smørgrav sshbuf_len(c->extended)); 20524f52dfbbSDag-Erling Smørgrav debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); 2053d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN || 2054d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK)) 2055ca3176e7SBrian Feldman return 1; 2056ca3176e7SBrian Feldman if (len <= 0) { 20574f52dfbbSDag-Erling Smørgrav debug2("channel %d: closing write-efd %d", c->self, c->efd); 20584f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->efd); 2059ca3176e7SBrian Feldman } else { 20604f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->extended, len)) != 0) { 20614f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", 20624f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 20634f52dfbbSDag-Erling Smørgrav } 2064a04a10f8SKris Kennaway c->local_consumed += len; 2065a04a10f8SKris Kennaway } 20664f52dfbbSDag-Erling Smørgrav return 1; 20674f52dfbbSDag-Erling Smørgrav } 20684f52dfbbSDag-Erling Smørgrav 20694f52dfbbSDag-Erling Smørgrav static int 20704f52dfbbSDag-Erling Smørgrav channel_handle_efd_read(struct ssh *ssh, Channel *c, 20714f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 20724f52dfbbSDag-Erling Smørgrav { 20734f52dfbbSDag-Erling Smørgrav char buf[CHAN_RBUF]; 20744f52dfbbSDag-Erling Smørgrav int r; 20754f52dfbbSDag-Erling Smørgrav ssize_t len; 20764f52dfbbSDag-Erling Smørgrav 20774f52dfbbSDag-Erling Smørgrav if (!c->detach_close && !FD_ISSET(c->efd, readset)) 20784f52dfbbSDag-Erling Smørgrav return 1; 20794f52dfbbSDag-Erling Smørgrav 2080a04a10f8SKris Kennaway len = read(c->efd, buf, sizeof(buf)); 20814f52dfbbSDag-Erling Smørgrav debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); 2082d4af9e69SDag-Erling Smørgrav if (len < 0 && (errno == EINTR || ((errno == EAGAIN || 2083d4af9e69SDag-Erling Smørgrav errno == EWOULDBLOCK) && !c->detach_close))) 2084ca3176e7SBrian Feldman return 1; 2085ca3176e7SBrian Feldman if (len <= 0) { 2086ca3176e7SBrian Feldman debug2("channel %d: closing read-efd %d", 2087a04a10f8SKris Kennaway c->self, c->efd); 20884f52dfbbSDag-Erling Smørgrav channel_close_fd(ssh, &c->efd); 2089ca3176e7SBrian Feldman } else { 2090e2f6069cSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_IGNORE) { 2091e2f6069cSDag-Erling Smørgrav debug3("channel %d: discard efd", 2092e2f6069cSDag-Erling Smørgrav c->self); 20934f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->extended, buf, len)) != 0) { 20944f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append: %s", 20954f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 2096a04a10f8SKris Kennaway } 2097ca3176e7SBrian Feldman } 2098a04a10f8SKris Kennaway return 1; 2099a04a10f8SKris Kennaway } 2100333ee039SDag-Erling Smørgrav 210121e764dfSDag-Erling Smørgrav static int 21024f52dfbbSDag-Erling Smørgrav channel_handle_efd(struct ssh *ssh, Channel *c, 21034f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 2104a04a10f8SKris Kennaway { 21054f52dfbbSDag-Erling Smørgrav if (c->efd == -1) 21064f52dfbbSDag-Erling Smørgrav return 1; 21074f52dfbbSDag-Erling Smørgrav 21084f52dfbbSDag-Erling Smørgrav /** XXX handle drain efd, too */ 21094f52dfbbSDag-Erling Smørgrav 21104f52dfbbSDag-Erling Smørgrav if (c->extended_usage == CHAN_EXTENDED_WRITE) 21114f52dfbbSDag-Erling Smørgrav return channel_handle_efd_write(ssh, c, readset, writeset); 21124f52dfbbSDag-Erling Smørgrav else if (c->extended_usage == CHAN_EXTENDED_READ || 21134f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_IGNORE) 21144f52dfbbSDag-Erling Smørgrav return channel_handle_efd_read(ssh, c, readset, writeset); 21154f52dfbbSDag-Erling Smørgrav 21164f52dfbbSDag-Erling Smørgrav return 1; 21174f52dfbbSDag-Erling Smørgrav } 21184f52dfbbSDag-Erling Smørgrav 21194f52dfbbSDag-Erling Smørgrav static int 21204f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c) 21214f52dfbbSDag-Erling Smørgrav { 21224f52dfbbSDag-Erling Smørgrav int r; 21234f52dfbbSDag-Erling Smørgrav 2124ca3176e7SBrian Feldman if (c->type == SSH_CHANNEL_OPEN && 2125ca3176e7SBrian Feldman !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && 2126d4af9e69SDag-Erling Smørgrav ((c->local_window_max - c->local_window > 2127d4af9e69SDag-Erling Smørgrav c->local_maxpacket*3) || 2128d4af9e69SDag-Erling Smørgrav c->local_window < c->local_window_max/2) && 2129a04a10f8SKris Kennaway c->local_consumed > 0) { 21304f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 21314f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", 21324f52dfbbSDag-Erling Smørgrav __func__, c->self); 21334f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, 21344f52dfbbSDag-Erling Smørgrav SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || 21354f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 21364f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || 21374f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 21384f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: %s", __func__, 21394f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 21404f52dfbbSDag-Erling Smørgrav } 21415b9b2fafSBrian Feldman debug2("channel %d: window %d sent adjust %d", 2142a04a10f8SKris Kennaway c->self, c->local_window, 2143a04a10f8SKris Kennaway c->local_consumed); 214460c59fadSDag-Erling Smørgrav c->local_window += c->local_consumed; 2145a04a10f8SKris Kennaway c->local_consumed = 0; 2146a04a10f8SKris Kennaway } 2147a04a10f8SKris Kennaway return 1; 2148a04a10f8SKris Kennaway } 2149a04a10f8SKris Kennaway 2150af12a3e7SDag-Erling Smørgrav static void 21514f52dfbbSDag-Erling Smørgrav channel_post_open(struct ssh *ssh, Channel *c, 21524f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 2153a04a10f8SKris Kennaway { 21544f52dfbbSDag-Erling Smørgrav channel_handle_rfd(ssh, c, readset, writeset); 21554f52dfbbSDag-Erling Smørgrav channel_handle_wfd(ssh, c, readset, writeset); 21564f52dfbbSDag-Erling Smørgrav channel_handle_efd(ssh, c, readset, writeset); 21574f52dfbbSDag-Erling Smørgrav channel_check_window(ssh, c); 2158a04a10f8SKris Kennaway } 2159a04a10f8SKris Kennaway 2160b15c8340SDag-Erling Smørgrav static u_int 21614f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need) 2162b15c8340SDag-Erling Smørgrav { 2163b15c8340SDag-Erling Smørgrav char buf[CHAN_RBUF]; 21644f52dfbbSDag-Erling Smørgrav ssize_t len; 2165b15c8340SDag-Erling Smørgrav u_int rlen; 21664f52dfbbSDag-Erling Smørgrav int r; 2167b15c8340SDag-Erling Smørgrav 21684f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) < need) { 21694f52dfbbSDag-Erling Smørgrav rlen = need - sshbuf_len(c->input); 2170ca86bcf2SDag-Erling Smørgrav len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); 2171acc1a9efSDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 21724f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2173b15c8340SDag-Erling Smørgrav if (len <= 0) { 21744f52dfbbSDag-Erling Smørgrav debug2("channel %d: ctl read<=0 rfd %d len %zd", 2175b15c8340SDag-Erling Smørgrav c->self, c->rfd, len); 21764f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 2177b15c8340SDag-Erling Smørgrav return 0; 21784f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->input, buf, len)) != 0) { 21794f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append: %s", 21804f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 2181b15c8340SDag-Erling Smørgrav } 21824f52dfbbSDag-Erling Smørgrav } 21834f52dfbbSDag-Erling Smørgrav return sshbuf_len(c->input); 2184b15c8340SDag-Erling Smørgrav } 2185b15c8340SDag-Erling Smørgrav 2186b15c8340SDag-Erling Smørgrav static void 21874f52dfbbSDag-Erling Smørgrav channel_post_mux_client_read(struct ssh *ssh, Channel *c, 21884f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 2189b15c8340SDag-Erling Smørgrav { 2190b15c8340SDag-Erling Smørgrav u_int need; 2191b15c8340SDag-Erling Smørgrav 21924f52dfbbSDag-Erling Smørgrav if (c->rfd == -1 || !FD_ISSET(c->rfd, readset)) 21934f52dfbbSDag-Erling Smørgrav return; 21944f52dfbbSDag-Erling Smørgrav if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) 21954f52dfbbSDag-Erling Smørgrav return; 21964f52dfbbSDag-Erling Smørgrav if (c->mux_pause) 21974f52dfbbSDag-Erling Smørgrav return; 2198b15c8340SDag-Erling Smørgrav 2199b15c8340SDag-Erling Smørgrav /* 2200b15c8340SDag-Erling Smørgrav * Don't not read past the precise end of packets to 2201b15c8340SDag-Erling Smørgrav * avoid disrupting fd passing. 2202b15c8340SDag-Erling Smørgrav */ 22034f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, 4) < 4) /* read header */ 2204b15c8340SDag-Erling Smørgrav return; 22054f52dfbbSDag-Erling Smørgrav /* XXX sshbuf_peek_u32 */ 22064f52dfbbSDag-Erling Smørgrav need = PEEK_U32(sshbuf_ptr(c->input)); 2207b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET (256 * 1024) 2208b15c8340SDag-Erling Smørgrav if (need > CHANNEL_MUX_MAX_PACKET) { 2209b15c8340SDag-Erling Smørgrav debug2("channel %d: packet too big %u > %u", 2210b15c8340SDag-Erling Smørgrav c->self, CHANNEL_MUX_MAX_PACKET, need); 22114f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 2212b15c8340SDag-Erling Smørgrav return; 2213b15c8340SDag-Erling Smørgrav } 22144f52dfbbSDag-Erling Smørgrav if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ 2215b15c8340SDag-Erling Smørgrav return; 22164f52dfbbSDag-Erling Smørgrav if (c->mux_rcb(ssh, c) != 0) { 2217b15c8340SDag-Erling Smørgrav debug("channel %d: mux_rcb failed", c->self); 22184f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 2219b15c8340SDag-Erling Smørgrav return; 2220b15c8340SDag-Erling Smørgrav } 2221b15c8340SDag-Erling Smørgrav } 2222b15c8340SDag-Erling Smørgrav 2223b15c8340SDag-Erling Smørgrav static void 22244f52dfbbSDag-Erling Smørgrav channel_post_mux_client_write(struct ssh *ssh, Channel *c, 22254f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 22264f52dfbbSDag-Erling Smørgrav { 22274f52dfbbSDag-Erling Smørgrav ssize_t len; 22284f52dfbbSDag-Erling Smørgrav int r; 22294f52dfbbSDag-Erling Smørgrav 22304f52dfbbSDag-Erling Smørgrav if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || 22314f52dfbbSDag-Erling Smørgrav sshbuf_len(c->output) == 0) 22324f52dfbbSDag-Erling Smørgrav return; 22334f52dfbbSDag-Erling Smørgrav 22344f52dfbbSDag-Erling Smørgrav len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); 22354f52dfbbSDag-Erling Smørgrav if (len < 0 && (errno == EINTR || errno == EAGAIN)) 22364f52dfbbSDag-Erling Smørgrav return; 22374f52dfbbSDag-Erling Smørgrav if (len <= 0) { 22384f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 22394f52dfbbSDag-Erling Smørgrav return; 22404f52dfbbSDag-Erling Smørgrav } 22414f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->output, len)) != 0) 22424f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: consume: %s", __func__, 22434f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 22444f52dfbbSDag-Erling Smørgrav } 22454f52dfbbSDag-Erling Smørgrav 22464f52dfbbSDag-Erling Smørgrav static void 22474f52dfbbSDag-Erling Smørgrav channel_post_mux_client(struct ssh *ssh, Channel *c, 22484f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 22494f52dfbbSDag-Erling Smørgrav { 22504f52dfbbSDag-Erling Smørgrav channel_post_mux_client_read(ssh, c, readset, writeset); 22514f52dfbbSDag-Erling Smørgrav channel_post_mux_client_write(ssh, c, readset, writeset); 22524f52dfbbSDag-Erling Smørgrav } 22534f52dfbbSDag-Erling Smørgrav 22544f52dfbbSDag-Erling Smørgrav static void 22554f52dfbbSDag-Erling Smørgrav channel_post_mux_listener(struct ssh *ssh, Channel *c, 22564f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset) 2257b15c8340SDag-Erling Smørgrav { 2258b15c8340SDag-Erling Smørgrav Channel *nc; 2259b15c8340SDag-Erling Smørgrav struct sockaddr_storage addr; 2260b15c8340SDag-Erling Smørgrav socklen_t addrlen; 2261b15c8340SDag-Erling Smørgrav int newsock; 2262b15c8340SDag-Erling Smørgrav uid_t euid; 2263b15c8340SDag-Erling Smørgrav gid_t egid; 2264b15c8340SDag-Erling Smørgrav 2265b15c8340SDag-Erling Smørgrav if (!FD_ISSET(c->sock, readset)) 2266b15c8340SDag-Erling Smørgrav return; 2267b15c8340SDag-Erling Smørgrav 2268b15c8340SDag-Erling Smørgrav debug("multiplexing control connection"); 2269b15c8340SDag-Erling Smørgrav 2270b15c8340SDag-Erling Smørgrav /* 2271b15c8340SDag-Erling Smørgrav * Accept connection on control socket 2272b15c8340SDag-Erling Smørgrav */ 2273b15c8340SDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 2274b15c8340SDag-Erling Smørgrav addrlen = sizeof(addr); 2275b15c8340SDag-Erling Smørgrav if ((newsock = accept(c->sock, (struct sockaddr*)&addr, 2276b15c8340SDag-Erling Smørgrav &addrlen)) == -1) { 2277b15c8340SDag-Erling Smørgrav error("%s accept: %s", __func__, strerror(errno)); 2278462c32cbSDag-Erling Smørgrav if (errno == EMFILE || errno == ENFILE) 2279e4a9863fSDag-Erling Smørgrav c->notbefore = monotime() + 1; 2280b15c8340SDag-Erling Smørgrav return; 2281b15c8340SDag-Erling Smørgrav } 2282b15c8340SDag-Erling Smørgrav 2283b15c8340SDag-Erling Smørgrav if (getpeereid(newsock, &euid, &egid) < 0) { 2284b15c8340SDag-Erling Smørgrav error("%s getpeereid failed: %s", __func__, 2285b15c8340SDag-Erling Smørgrav strerror(errno)); 2286b15c8340SDag-Erling Smørgrav close(newsock); 2287b15c8340SDag-Erling Smørgrav return; 2288b15c8340SDag-Erling Smørgrav } 2289b15c8340SDag-Erling Smørgrav if ((euid != 0) && (getuid() != euid)) { 2290b15c8340SDag-Erling Smørgrav error("multiplex uid mismatch: peer euid %u != uid %u", 2291b15c8340SDag-Erling Smørgrav (u_int)euid, (u_int)getuid()); 2292b15c8340SDag-Erling Smørgrav close(newsock); 2293b15c8340SDag-Erling Smørgrav return; 2294b15c8340SDag-Erling Smørgrav } 22954f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT, 2296b15c8340SDag-Erling Smørgrav newsock, newsock, -1, c->local_window_max, 2297b15c8340SDag-Erling Smørgrav c->local_maxpacket, 0, "mux-control", 1); 2298b15c8340SDag-Erling Smørgrav nc->mux_rcb = c->mux_rcb; 22994f52dfbbSDag-Erling Smørgrav debug3("%s: new mux channel %d fd %d", __func__, nc->self, nc->sock); 2300b15c8340SDag-Erling Smørgrav /* establish state */ 23014f52dfbbSDag-Erling Smørgrav nc->mux_rcb(ssh, nc); 2302b15c8340SDag-Erling Smørgrav /* mux state transitions must not elicit protocol messages */ 2303b15c8340SDag-Erling Smørgrav nc->flags |= CHAN_LOCAL; 2304b15c8340SDag-Erling Smørgrav } 2305b15c8340SDag-Erling Smørgrav 2306af12a3e7SDag-Erling Smørgrav static void 23074f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc) 2308a04a10f8SKris Kennaway { 23094f52dfbbSDag-Erling Smørgrav chan_fn **pre, **post; 2310f388f5efSDag-Erling Smørgrav 23114f52dfbbSDag-Erling Smørgrav if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || 23124f52dfbbSDag-Erling Smørgrav (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) 23134f52dfbbSDag-Erling Smørgrav fatal("%s: allocation failed", __func__); 2314511b41d2SMark Murray 23154f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_OPEN] = &channel_pre_open; 23164f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; 23174f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; 23184f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; 23194f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; 23204f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; 23214f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; 23224f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; 23234f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; 23244f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 23254f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; 23264f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; 23274f52dfbbSDag-Erling Smørgrav pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; 2328a04a10f8SKris Kennaway 23294f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_OPEN] = &channel_post_open; 23304f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; 23314f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; 23324f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; 23334f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; 23344f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; 23354f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; 23364f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; 23374f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; 23384f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; 23394f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; 23404f52dfbbSDag-Erling Smørgrav post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; 2341a04a10f8SKris Kennaway 23424f52dfbbSDag-Erling Smørgrav sc->channel_pre = pre; 23434f52dfbbSDag-Erling Smørgrav sc->channel_post = post; 2344a04a10f8SKris Kennaway } 2345a04a10f8SKris Kennaway 2346af12a3e7SDag-Erling Smørgrav /* gc dead channels */ 2347af12a3e7SDag-Erling Smørgrav static void 23484f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c) 2349af12a3e7SDag-Erling Smørgrav { 2350af12a3e7SDag-Erling Smørgrav if (c == NULL) 2351af12a3e7SDag-Erling Smørgrav return; 2352af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) { 23534f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, c->detach_close)) 2354af12a3e7SDag-Erling Smørgrav return; 2355221552e4SDag-Erling Smørgrav debug2("channel %d: gc: notify user", c->self); 23564f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, c->self, NULL); 2357af12a3e7SDag-Erling Smørgrav /* if we still have a callback */ 2358af12a3e7SDag-Erling Smørgrav if (c->detach_user != NULL) 2359af12a3e7SDag-Erling Smørgrav return; 2360221552e4SDag-Erling Smørgrav debug2("channel %d: gc: user detached", c->self); 2361af12a3e7SDag-Erling Smørgrav } 23624f52dfbbSDag-Erling Smørgrav if (!chan_is_dead(ssh, c, 1)) 2363af12a3e7SDag-Erling Smørgrav return; 2364221552e4SDag-Erling Smørgrav debug2("channel %d: garbage collecting", c->self); 23654f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2366af12a3e7SDag-Erling Smørgrav } 2367af12a3e7SDag-Erling Smørgrav 23684f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST }; 23694f52dfbbSDag-Erling Smørgrav 2370af12a3e7SDag-Erling Smørgrav static void 23714f52dfbbSDag-Erling Smørgrav channel_handler(struct ssh *ssh, int table, 23724f52dfbbSDag-Erling Smørgrav fd_set *readset, fd_set *writeset, time_t *unpause_secs) 2373a04a10f8SKris Kennaway { 23744f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 23754f52dfbbSDag-Erling Smørgrav chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; 2376b15c8340SDag-Erling Smørgrav u_int i, oalloc; 2377a04a10f8SKris Kennaway Channel *c; 2378462c32cbSDag-Erling Smørgrav time_t now; 2379a04a10f8SKris Kennaway 2380e4a9863fSDag-Erling Smørgrav now = monotime(); 2381462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL) 2382462c32cbSDag-Erling Smørgrav *unpause_secs = 0; 23834f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 23844f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2385af12a3e7SDag-Erling Smørgrav if (c == NULL) 2386511b41d2SMark Murray continue; 2387b15c8340SDag-Erling Smørgrav if (c->delayed) { 23884f52dfbbSDag-Erling Smørgrav if (table == CHAN_PRE) 2389b15c8340SDag-Erling Smørgrav c->delayed = 0; 2390b15c8340SDag-Erling Smørgrav else 2391b15c8340SDag-Erling Smørgrav continue; 2392b15c8340SDag-Erling Smørgrav } 2393462c32cbSDag-Erling Smørgrav if (ftab[c->type] != NULL) { 2394462c32cbSDag-Erling Smørgrav /* 2395462c32cbSDag-Erling Smørgrav * Run handlers that are not paused. 2396462c32cbSDag-Erling Smørgrav */ 2397462c32cbSDag-Erling Smørgrav if (c->notbefore <= now) 23984f52dfbbSDag-Erling Smørgrav (*ftab[c->type])(ssh, c, readset, writeset); 2399462c32cbSDag-Erling Smørgrav else if (unpause_secs != NULL) { 2400462c32cbSDag-Erling Smørgrav /* 2401462c32cbSDag-Erling Smørgrav * Collect the time that the earliest 2402462c32cbSDag-Erling Smørgrav * channel comes off pause. 2403462c32cbSDag-Erling Smørgrav */ 2404462c32cbSDag-Erling Smørgrav debug3("%s: chan %d: skip for %d more seconds", 2405462c32cbSDag-Erling Smørgrav __func__, c->self, 2406462c32cbSDag-Erling Smørgrav (int)(c->notbefore - now)); 2407462c32cbSDag-Erling Smørgrav if (*unpause_secs == 0 || 2408462c32cbSDag-Erling Smørgrav (c->notbefore - now) < *unpause_secs) 2409462c32cbSDag-Erling Smørgrav *unpause_secs = c->notbefore - now; 2410462c32cbSDag-Erling Smørgrav } 2411462c32cbSDag-Erling Smørgrav } 24124f52dfbbSDag-Erling Smørgrav channel_garbage_collect(ssh, c); 2413511b41d2SMark Murray } 2414462c32cbSDag-Erling Smørgrav if (unpause_secs != NULL && *unpause_secs != 0) 2415462c32cbSDag-Erling Smørgrav debug3("%s: first channel unpauses in %d seconds", 2416462c32cbSDag-Erling Smørgrav __func__, (int)*unpause_secs); 2417511b41d2SMark Murray } 2418a04a10f8SKris Kennaway 2419af12a3e7SDag-Erling Smørgrav /* 24204f52dfbbSDag-Erling Smørgrav * Create sockets before allocating the select bitmasks. 24214f52dfbbSDag-Erling Smørgrav * This is necessary for things that need to happen after reading 24224f52dfbbSDag-Erling Smørgrav * the network-input but before channel_prepare_select(). 24234f52dfbbSDag-Erling Smørgrav */ 24244f52dfbbSDag-Erling Smørgrav static void 24254f52dfbbSDag-Erling Smørgrav channel_before_prepare_select(struct ssh *ssh) 24264f52dfbbSDag-Erling Smørgrav { 24274f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 24284f52dfbbSDag-Erling Smørgrav Channel *c; 24294f52dfbbSDag-Erling Smørgrav u_int i, oalloc; 24304f52dfbbSDag-Erling Smørgrav 24314f52dfbbSDag-Erling Smørgrav for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { 24324f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 24334f52dfbbSDag-Erling Smørgrav if (c == NULL) 24344f52dfbbSDag-Erling Smørgrav continue; 24354f52dfbbSDag-Erling Smørgrav if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) 24364f52dfbbSDag-Erling Smørgrav channel_before_prepare_select_rdynamic(ssh, c); 24374f52dfbbSDag-Erling Smørgrav } 24384f52dfbbSDag-Erling Smørgrav } 24394f52dfbbSDag-Erling Smørgrav 24404f52dfbbSDag-Erling Smørgrav /* 2441af12a3e7SDag-Erling Smørgrav * Allocate/update select bitmasks and add any bits relevant to channels in 2442af12a3e7SDag-Erling Smørgrav * select bitmasks. 2443af12a3e7SDag-Erling Smørgrav */ 2444a04a10f8SKris Kennaway void 24454f52dfbbSDag-Erling Smørgrav channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp, 24464f52dfbbSDag-Erling Smørgrav int *maxfdp, u_int *nallocp, time_t *minwait_secs) 2447a04a10f8SKris Kennaway { 2448333ee039SDag-Erling Smørgrav u_int n, sz, nfdset; 2449ca3176e7SBrian Feldman 24504f52dfbbSDag-Erling Smørgrav channel_before_prepare_select(ssh); /* might update channel_max_fd */ 24514f52dfbbSDag-Erling Smørgrav 24524f52dfbbSDag-Erling Smørgrav n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd); 2453ca3176e7SBrian Feldman 2454333ee039SDag-Erling Smørgrav nfdset = howmany(n+1, NFDBITS); 2455333ee039SDag-Erling Smørgrav /* Explicitly test here, because xrealloc isn't always called */ 2456bc5531deSDag-Erling Smørgrav if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) 2457333ee039SDag-Erling Smørgrav fatal("channel_prepare_select: max_fd (%d) is too large", n); 2458333ee039SDag-Erling Smørgrav sz = nfdset * sizeof(fd_mask); 2459333ee039SDag-Erling Smørgrav 2460af12a3e7SDag-Erling Smørgrav /* perhaps check sz < nalloc/2 and shrink? */ 2461af12a3e7SDag-Erling Smørgrav if (*readsetp == NULL || sz > *nallocp) { 2462557f75e5SDag-Erling Smørgrav *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); 2463557f75e5SDag-Erling Smørgrav *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); 2464af12a3e7SDag-Erling Smørgrav *nallocp = sz; 2465ca3176e7SBrian Feldman } 2466af12a3e7SDag-Erling Smørgrav *maxfdp = n; 2467ca3176e7SBrian Feldman memset(*readsetp, 0, sz); 2468ca3176e7SBrian Feldman memset(*writesetp, 0, sz); 2469ca3176e7SBrian Feldman 24704f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 24714f52dfbbSDag-Erling Smørgrav channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp, 2472462c32cbSDag-Erling Smørgrav minwait_secs); 2473a04a10f8SKris Kennaway } 2474a04a10f8SKris Kennaway 2475af12a3e7SDag-Erling Smørgrav /* 2476af12a3e7SDag-Erling Smørgrav * After select, perform any appropriate operations for channels which have 2477af12a3e7SDag-Erling Smørgrav * events pending. 2478af12a3e7SDag-Erling Smørgrav */ 2479a04a10f8SKris Kennaway void 24804f52dfbbSDag-Erling Smørgrav channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset) 2481a04a10f8SKris Kennaway { 24824f52dfbbSDag-Erling Smørgrav channel_handler(ssh, CHAN_POST, readset, writeset, NULL); 2483511b41d2SMark Murray } 2484511b41d2SMark Murray 24854f52dfbbSDag-Erling Smørgrav /* 24864f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open or draining c->input. 24874f52dfbbSDag-Erling Smørgrav */ 24884f52dfbbSDag-Erling Smørgrav static void 24894f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c) 24904f52dfbbSDag-Erling Smørgrav { 24914f52dfbbSDag-Erling Smørgrav size_t len, plen; 24924f52dfbbSDag-Erling Smørgrav const u_char *pkt; 24934f52dfbbSDag-Erling Smørgrav int r; 24944f52dfbbSDag-Erling Smørgrav 24954f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->input)) == 0) { 24964f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_WAIT_DRAIN) { 24974f52dfbbSDag-Erling Smørgrav /* 24984f52dfbbSDag-Erling Smørgrav * input-buffer is empty and read-socket shutdown: 24994f52dfbbSDag-Erling Smørgrav * tell peer, that we will not send more data: 25004f52dfbbSDag-Erling Smørgrav * send IEOF. 25014f52dfbbSDag-Erling Smørgrav * hack for extended data: delay EOF if EFD still 25024f52dfbbSDag-Erling Smørgrav * in use. 25034f52dfbbSDag-Erling Smørgrav */ 25044f52dfbbSDag-Erling Smørgrav if (CHANNEL_EFD_INPUT_ACTIVE(c)) 25054f52dfbbSDag-Erling Smørgrav debug2("channel %d: " 25064f52dfbbSDag-Erling Smørgrav "ibuf_empty delayed efd %d/(%zu)", 25074f52dfbbSDag-Erling Smørgrav c->self, c->efd, sshbuf_len(c->extended)); 25084f52dfbbSDag-Erling Smørgrav else 25094f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 25104f52dfbbSDag-Erling Smørgrav } 25114f52dfbbSDag-Erling Smørgrav return; 25124f52dfbbSDag-Erling Smørgrav } 25134f52dfbbSDag-Erling Smørgrav 25144f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 25154f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", __func__, c->self); 25164f52dfbbSDag-Erling Smørgrav 25174f52dfbbSDag-Erling Smørgrav if (c->datagram) { 25184f52dfbbSDag-Erling Smørgrav /* Check datagram will fit; drop if not */ 25194f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) 25204f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: get datagram: %s", __func__, 25214f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25224f52dfbbSDag-Erling Smørgrav /* 25234f52dfbbSDag-Erling Smørgrav * XXX this does tail-drop on the datagram queue which is 25244f52dfbbSDag-Erling Smørgrav * usually suboptimal compared to head-drop. Better to have 25254f52dfbbSDag-Erling Smørgrav * backpressure at read time? (i.e. read + discard) 25264f52dfbbSDag-Erling Smørgrav */ 25274f52dfbbSDag-Erling Smørgrav if (plen > c->remote_window || plen > c->remote_maxpacket) { 25284f52dfbbSDag-Erling Smørgrav debug("channel %d: datagram too big", c->self); 25294f52dfbbSDag-Erling Smørgrav return; 25304f52dfbbSDag-Erling Smørgrav } 25314f52dfbbSDag-Erling Smørgrav /* Enqueue it */ 25324f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 25334f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 25344f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || 25354f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 25364f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: datagram: %s", __func__, 25374f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25384f52dfbbSDag-Erling Smørgrav } 25394f52dfbbSDag-Erling Smørgrav c->remote_window -= plen; 25404f52dfbbSDag-Erling Smørgrav return; 25414f52dfbbSDag-Erling Smørgrav } 25424f52dfbbSDag-Erling Smørgrav 25434f52dfbbSDag-Erling Smørgrav /* Enqueue packet for buffered data. */ 25444f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 25454f52dfbbSDag-Erling Smørgrav len = c->remote_window; 25464f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 25474f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 25484f52dfbbSDag-Erling Smørgrav if (len == 0) 25494f52dfbbSDag-Erling Smørgrav return; 25504f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || 25514f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 25524f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || 25534f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 25544f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: data: %s", __func__, 25554f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25564f52dfbbSDag-Erling Smørgrav } 25574f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->input, len)) != 0) 25584f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: consume: %s", __func__, 25594f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25604f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 25614f52dfbbSDag-Erling Smørgrav } 25624f52dfbbSDag-Erling Smørgrav 25634f52dfbbSDag-Erling Smørgrav /* 25644f52dfbbSDag-Erling Smørgrav * Enqueue data for channels with open c->extended in read mode. 25654f52dfbbSDag-Erling Smørgrav */ 25664f52dfbbSDag-Erling Smørgrav static void 25674f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c) 25684f52dfbbSDag-Erling Smørgrav { 25694f52dfbbSDag-Erling Smørgrav size_t len; 25704f52dfbbSDag-Erling Smørgrav int r; 25714f52dfbbSDag-Erling Smørgrav 25724f52dfbbSDag-Erling Smørgrav if ((len = sshbuf_len(c->extended)) == 0) 25734f52dfbbSDag-Erling Smørgrav return; 25744f52dfbbSDag-Erling Smørgrav 25754f52dfbbSDag-Erling Smørgrav debug2("channel %d: rwin %u elen %zu euse %d", c->self, 25764f52dfbbSDag-Erling Smørgrav c->remote_window, sshbuf_len(c->extended), c->extended_usage); 25774f52dfbbSDag-Erling Smørgrav if (len > c->remote_window) 25784f52dfbbSDag-Erling Smørgrav len = c->remote_window; 25794f52dfbbSDag-Erling Smørgrav if (len > c->remote_maxpacket) 25804f52dfbbSDag-Erling Smørgrav len = c->remote_maxpacket; 25814f52dfbbSDag-Erling Smørgrav if (len == 0) 25824f52dfbbSDag-Erling Smørgrav return; 25834f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 25844f52dfbbSDag-Erling Smørgrav fatal(":%s: channel %d: no remote id", __func__, c->self); 25854f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || 25864f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 25874f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || 25884f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || 25894f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 25904f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: data: %s", __func__, 25914f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25924f52dfbbSDag-Erling Smørgrav } 25934f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_consume(c->extended, len)) != 0) 25944f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: consume: %s", __func__, 25954f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 25964f52dfbbSDag-Erling Smørgrav c->remote_window -= len; 25974f52dfbbSDag-Erling Smørgrav debug2("channel %d: sent ext data %zu", c->self, len); 25984f52dfbbSDag-Erling Smørgrav } 2599af12a3e7SDag-Erling Smørgrav 2600ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */ 2601511b41d2SMark Murray void 26024f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh) 2603511b41d2SMark Murray { 26044f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 2605a04a10f8SKris Kennaway Channel *c; 26064f52dfbbSDag-Erling Smørgrav u_int i; 2607511b41d2SMark Murray 26084f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 26094f52dfbbSDag-Erling Smørgrav c = sc->channels[i]; 2610af12a3e7SDag-Erling Smørgrav if (c == NULL) 2611af12a3e7SDag-Erling Smørgrav continue; 2612511b41d2SMark Murray 2613af12a3e7SDag-Erling Smørgrav /* 2614af12a3e7SDag-Erling Smørgrav * We are only interested in channels that can have buffered 2615af12a3e7SDag-Erling Smørgrav * incoming data. 2616af12a3e7SDag-Erling Smørgrav */ 2617a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) 2618511b41d2SMark Murray continue; 26194f52dfbbSDag-Erling Smørgrav if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { 2620ca3176e7SBrian Feldman /* XXX is this true? */ 26214f52dfbbSDag-Erling Smørgrav debug3("channel %d: will not send data after close", 26224f52dfbbSDag-Erling Smørgrav c->self); 2623511b41d2SMark Murray continue; 2624511b41d2SMark Murray } 2625511b41d2SMark Murray 2626511b41d2SMark Murray /* Get the amount of buffered data for this channel. */ 26274f52dfbbSDag-Erling Smørgrav if (c->istate == CHAN_INPUT_OPEN || 26284f52dfbbSDag-Erling Smørgrav c->istate == CHAN_INPUT_WAIT_DRAIN) 26294f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(ssh, c); 2630a04a10f8SKris Kennaway /* Send extended data, i.e. stderr */ 26314f52dfbbSDag-Erling Smørgrav if (!(c->flags & CHAN_EOF_SENT) && 26324f52dfbbSDag-Erling Smørgrav c->extended_usage == CHAN_EXTENDED_READ) 26334f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(ssh, c); 2634511b41d2SMark Murray } 2635511b41d2SMark Murray } 2636511b41d2SMark Murray 2637ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support */ 2638ca86bcf2SDag-Erling Smørgrav 2639ca86bcf2SDag-Erling Smørgrav /* 2640ca86bcf2SDag-Erling Smørgrav * When multiplexing channel messages for mux clients we have to deal 2641ca86bcf2SDag-Erling Smørgrav * with downstream messages from the mux client and upstream messages 2642ca86bcf2SDag-Erling Smørgrav * from the ssh server: 2643ca86bcf2SDag-Erling Smørgrav * 1) Handling downstream messages is straightforward and happens 2644ca86bcf2SDag-Erling Smørgrav * in channel_proxy_downstream(): 2645ca86bcf2SDag-Erling Smørgrav * - We forward all messages (mostly) unmodified to the server. 2646ca86bcf2SDag-Erling Smørgrav * - However, in order to route messages from upstream to the correct 2647ca86bcf2SDag-Erling Smørgrav * downstream client, we have to replace the channel IDs used by the 2648ca86bcf2SDag-Erling Smørgrav * mux clients with a unique channel ID because the mux clients might 2649ca86bcf2SDag-Erling Smørgrav * use conflicting channel IDs. 2650ca86bcf2SDag-Erling Smørgrav * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and 2651ca86bcf2SDag-Erling Smørgrav * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local 2652ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID 2653ca86bcf2SDag-Erling Smørgrav * with the newly allocated channel ID. 2654ca86bcf2SDag-Erling Smørgrav * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY 2655*190cef3dSDag-Erling Smørgrav * channels and processed by channel_proxy_upstream(). The local channel ID 2656ca86bcf2SDag-Erling Smørgrav * is then translated back to the original mux client ID. 2657ca86bcf2SDag-Erling Smørgrav * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE 2658ca86bcf2SDag-Erling Smørgrav * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. 2659ca86bcf2SDag-Erling Smørgrav * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the 2660ca86bcf2SDag-Erling Smørgrav * downstream mux client are removed. 2661ca86bcf2SDag-Erling Smørgrav * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server 2662ca86bcf2SDag-Erling Smørgrav * requires more work, because they are not addressed to a specific 2663ca86bcf2SDag-Erling Smørgrav * channel. E.g. client_request_forwarded_tcpip() needs to figure 2664ca86bcf2SDag-Erling Smørgrav * out whether the request is addressed to the local client or a 2665ca86bcf2SDag-Erling Smørgrav * specific downstream client based on the listen-address/port. 2666*190cef3dSDag-Erling Smørgrav * 6) Agent and X11-Forwarding have a similar problem and are currently 2667ca86bcf2SDag-Erling Smørgrav * not supported as the matching session/channel cannot be identified 2668ca86bcf2SDag-Erling Smørgrav * easily. 2669ca86bcf2SDag-Erling Smørgrav */ 2670ca86bcf2SDag-Erling Smørgrav 2671ca86bcf2SDag-Erling Smørgrav /* 2672ca86bcf2SDag-Erling Smørgrav * receive packets from downstream mux clients: 2673ca86bcf2SDag-Erling Smørgrav * channel callback fired on read from mux client, creates 2674ca86bcf2SDag-Erling Smørgrav * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs 2675ca86bcf2SDag-Erling Smørgrav * on channel creation. 2676ca86bcf2SDag-Erling Smørgrav */ 2677ca86bcf2SDag-Erling Smørgrav int 26784f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream) 2679ca86bcf2SDag-Erling Smørgrav { 2680ca86bcf2SDag-Erling Smørgrav Channel *c = NULL; 2681ca86bcf2SDag-Erling Smørgrav struct sshbuf *original = NULL, *modified = NULL; 2682ca86bcf2SDag-Erling Smørgrav const u_char *cp; 2683ca86bcf2SDag-Erling Smørgrav char *ctype = NULL, *listen_host = NULL; 2684ca86bcf2SDag-Erling Smørgrav u_char type; 2685ca86bcf2SDag-Erling Smørgrav size_t have; 26864f52dfbbSDag-Erling Smørgrav int ret = -1, r; 2687ca86bcf2SDag-Erling Smørgrav u_int id, remote_id, listen_port; 2688ca86bcf2SDag-Erling Smørgrav 26894f52dfbbSDag-Erling Smørgrav /* sshbuf_dump(downstream->input, stderr); */ 26904f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) 2691ca86bcf2SDag-Erling Smørgrav != 0) { 2692ca86bcf2SDag-Erling Smørgrav error("%s: malformed message: %s", __func__, ssh_err(r)); 2693ca86bcf2SDag-Erling Smørgrav return -1; 2694ca86bcf2SDag-Erling Smørgrav } 2695ca86bcf2SDag-Erling Smørgrav if (have < 2) { 2696ca86bcf2SDag-Erling Smørgrav error("%s: short message", __func__); 2697ca86bcf2SDag-Erling Smørgrav return -1; 2698ca86bcf2SDag-Erling Smørgrav } 2699ca86bcf2SDag-Erling Smørgrav type = cp[1]; 2700ca86bcf2SDag-Erling Smørgrav /* skip padlen + type */ 2701ca86bcf2SDag-Erling Smørgrav cp += 2; 2702ca86bcf2SDag-Erling Smørgrav have -= 2; 2703ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 2704ca86bcf2SDag-Erling Smørgrav debug3("%s: channel %u: down->up: type %u", __func__, 2705ca86bcf2SDag-Erling Smørgrav downstream->self, type); 2706ca86bcf2SDag-Erling Smørgrav 2707ca86bcf2SDag-Erling Smørgrav switch (type) { 2708ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN: 2709ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2710ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 2711ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2712ca86bcf2SDag-Erling Smørgrav goto out; 2713ca86bcf2SDag-Erling Smørgrav } 2714ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || 2715ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 2716ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2717ca86bcf2SDag-Erling Smørgrav goto out; 2718ca86bcf2SDag-Erling Smørgrav } 27194f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, 2720ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, ctype, 1); 2721ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 2722ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; /* original downstream id */ 2723ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || 2724ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 2725ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 2726ca86bcf2SDag-Erling Smørgrav error("%s: compose error %s", __func__, ssh_err(r)); 27274f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2728ca86bcf2SDag-Erling Smørgrav goto out; 2729ca86bcf2SDag-Erling Smørgrav } 2730ca86bcf2SDag-Erling Smørgrav break; 2731ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2732ca86bcf2SDag-Erling Smørgrav /* 2733ca86bcf2SDag-Erling Smørgrav * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we 2734ca86bcf2SDag-Erling Smørgrav * need to parse 'remote_id' instead of 'ctype'. 2735ca86bcf2SDag-Erling Smørgrav */ 2736ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL || 2737ca86bcf2SDag-Erling Smørgrav (modified = sshbuf_new()) == NULL) { 2738ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2739ca86bcf2SDag-Erling Smørgrav goto out; 2740ca86bcf2SDag-Erling Smørgrav } 2741ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || 2742ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &id)) != 0) { 2743ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2744ca86bcf2SDag-Erling Smørgrav goto out; 2745ca86bcf2SDag-Erling Smørgrav } 27464f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, 2747ca86bcf2SDag-Erling Smørgrav -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); 2748ca86bcf2SDag-Erling Smørgrav c->mux_ctx = downstream; /* point to mux client */ 2749ca86bcf2SDag-Erling Smørgrav c->mux_downstream_id = id; 2750ca86bcf2SDag-Erling Smørgrav c->remote_id = remote_id; 27514f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 2752ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || 2753ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(modified, c->self)) != 0 || 2754ca86bcf2SDag-Erling Smørgrav (r = sshbuf_putb(modified, original)) != 0) { 2755ca86bcf2SDag-Erling Smørgrav error("%s: compose error %s", __func__, ssh_err(r)); 27564f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2757ca86bcf2SDag-Erling Smørgrav goto out; 2758ca86bcf2SDag-Erling Smørgrav } 2759ca86bcf2SDag-Erling Smørgrav break; 2760ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_GLOBAL_REQUEST: 2761ca86bcf2SDag-Erling Smørgrav if ((original = sshbuf_from(cp, have)) == NULL) { 2762ca86bcf2SDag-Erling Smørgrav error("%s: alloc", __func__); 2763ca86bcf2SDag-Erling Smørgrav goto out; 2764ca86bcf2SDag-Erling Smørgrav } 2765ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { 2766ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2767ca86bcf2SDag-Erling Smørgrav goto out; 2768ca86bcf2SDag-Erling Smørgrav } 2769ca86bcf2SDag-Erling Smørgrav if (strcmp(ctype, "tcpip-forward") != 0) { 2770ca86bcf2SDag-Erling Smørgrav error("%s: unsupported request %s", __func__, ctype); 2771ca86bcf2SDag-Erling Smørgrav goto out; 2772ca86bcf2SDag-Erling Smørgrav } 2773ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_get_u8(original, NULL)) != 0 || 2774ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || 2775ca86bcf2SDag-Erling Smørgrav (r = sshbuf_get_u32(original, &listen_port)) != 0) { 2776ca86bcf2SDag-Erling Smørgrav error("%s: parse error %s", __func__, ssh_err(r)); 2777ca86bcf2SDag-Erling Smørgrav goto out; 2778ca86bcf2SDag-Erling Smørgrav } 2779ca86bcf2SDag-Erling Smørgrav if (listen_port > 65535) { 2780ca86bcf2SDag-Erling Smørgrav error("%s: tcpip-forward for %s: bad port %u", 2781ca86bcf2SDag-Erling Smørgrav __func__, listen_host, listen_port); 2782ca86bcf2SDag-Erling Smørgrav goto out; 2783ca86bcf2SDag-Erling Smørgrav } 2784ca86bcf2SDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 2785*190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1, 27864f52dfbbSDag-Erling Smørgrav listen_host, NULL, (int)listen_port, downstream); 2787ca86bcf2SDag-Erling Smørgrav listen_host = NULL; 2788ca86bcf2SDag-Erling Smørgrav break; 2789ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2790ca86bcf2SDag-Erling Smørgrav if (have < 4) 2791ca86bcf2SDag-Erling Smørgrav break; 2792ca86bcf2SDag-Erling Smørgrav remote_id = PEEK_U32(cp); 27934f52dfbbSDag-Erling Smørgrav if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { 2794ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_RCVD) 27954f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2796ca86bcf2SDag-Erling Smørgrav else 2797ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_SENT; 2798ca86bcf2SDag-Erling Smørgrav } 2799ca86bcf2SDag-Erling Smørgrav break; 2800ca86bcf2SDag-Erling Smørgrav } 2801ca86bcf2SDag-Erling Smørgrav if (modified) { 2802ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 2803ca86bcf2SDag-Erling Smørgrav (r = sshpkt_putb(ssh, modified)) != 0 || 2804ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 2805ca86bcf2SDag-Erling Smørgrav error("%s: send %s", __func__, ssh_err(r)); 2806ca86bcf2SDag-Erling Smørgrav goto out; 2807ca86bcf2SDag-Erling Smørgrav } 2808ca86bcf2SDag-Erling Smørgrav } else { 2809ca86bcf2SDag-Erling Smørgrav if ((r = sshpkt_start(ssh, type)) != 0 || 2810ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put(ssh, cp, have)) != 0 || 2811ca86bcf2SDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) { 2812ca86bcf2SDag-Erling Smørgrav error("%s: send %s", __func__, ssh_err(r)); 2813ca86bcf2SDag-Erling Smørgrav goto out; 2814ca86bcf2SDag-Erling Smørgrav } 2815ca86bcf2SDag-Erling Smørgrav } 2816ca86bcf2SDag-Erling Smørgrav ret = 0; 2817ca86bcf2SDag-Erling Smørgrav out: 2818ca86bcf2SDag-Erling Smørgrav free(ctype); 2819ca86bcf2SDag-Erling Smørgrav free(listen_host); 2820ca86bcf2SDag-Erling Smørgrav sshbuf_free(original); 2821ca86bcf2SDag-Erling Smørgrav sshbuf_free(modified); 2822ca86bcf2SDag-Erling Smørgrav return ret; 2823ca86bcf2SDag-Erling Smørgrav } 2824ca86bcf2SDag-Erling Smørgrav 2825ca86bcf2SDag-Erling Smørgrav /* 2826ca86bcf2SDag-Erling Smørgrav * receive packets from upstream server and de-multiplex packets 2827ca86bcf2SDag-Erling Smørgrav * to correct downstream: 2828ca86bcf2SDag-Erling Smørgrav * implemented as a helper for channel input handlers, 2829ca86bcf2SDag-Erling Smørgrav * replaces local (proxy) channel ID with downstream channel ID. 2830ca86bcf2SDag-Erling Smørgrav */ 2831ca86bcf2SDag-Erling Smørgrav int 28324f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) 2833ca86bcf2SDag-Erling Smørgrav { 2834ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 2835ca86bcf2SDag-Erling Smørgrav Channel *downstream; 2836ca86bcf2SDag-Erling Smørgrav const u_char *cp = NULL; 2837ca86bcf2SDag-Erling Smørgrav size_t len; 2838ca86bcf2SDag-Erling Smørgrav int r; 2839ca86bcf2SDag-Erling Smørgrav 2840ca86bcf2SDag-Erling Smørgrav /* 2841ca86bcf2SDag-Erling Smørgrav * When receiving packets from the peer we need to check whether we 2842ca86bcf2SDag-Erling Smørgrav * need to forward the packets to the mux client. In this case we 2843*190cef3dSDag-Erling Smørgrav * restore the original channel id and keep track of CLOSE messages, 2844ca86bcf2SDag-Erling Smørgrav * so we can cleanup the channel. 2845ca86bcf2SDag-Erling Smørgrav */ 2846ca86bcf2SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) 2847ca86bcf2SDag-Erling Smørgrav return 0; 2848ca86bcf2SDag-Erling Smørgrav if ((downstream = c->mux_ctx) == NULL) 2849ca86bcf2SDag-Erling Smørgrav return 0; 2850ca86bcf2SDag-Erling Smørgrav switch (type) { 2851ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2852ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_DATA: 2853ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EOF: 2854ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_EXTENDED_DATA: 2855ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2856ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_FAILURE: 2857ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 2858ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_SUCCESS: 2859ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_FAILURE: 2860ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_REQUEST: 2861ca86bcf2SDag-Erling Smørgrav break; 2862ca86bcf2SDag-Erling Smørgrav default: 2863ca86bcf2SDag-Erling Smørgrav debug2("%s: channel %u: unsupported type %u", __func__, 2864ca86bcf2SDag-Erling Smørgrav c->self, type); 2865ca86bcf2SDag-Erling Smørgrav return 0; 2866ca86bcf2SDag-Erling Smørgrav } 2867ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 2868ca86bcf2SDag-Erling Smørgrav error("%s: alloc reply", __func__); 2869ca86bcf2SDag-Erling Smørgrav goto out; 2870ca86bcf2SDag-Erling Smørgrav } 2871ca86bcf2SDag-Erling Smørgrav /* get remaining payload (after id) */ 2872ca86bcf2SDag-Erling Smørgrav cp = sshpkt_ptr(ssh, &len); 2873ca86bcf2SDag-Erling Smørgrav if (cp == NULL) { 2874ca86bcf2SDag-Erling Smørgrav error("%s: no packet", __func__); 2875ca86bcf2SDag-Erling Smørgrav goto out; 2876ca86bcf2SDag-Erling Smørgrav } 2877ca86bcf2SDag-Erling Smørgrav /* translate id and send to muxclient */ 2878ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 2879ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, type)) != 0 || 2880ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || 2881ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put(b, cp, len)) != 0 || 28824f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(downstream->output, b)) != 0) { 2883ca86bcf2SDag-Erling Smørgrav error("%s: compose for muxclient %s", __func__, ssh_err(r)); 2884ca86bcf2SDag-Erling Smørgrav goto out; 2885ca86bcf2SDag-Erling Smørgrav } 2886ca86bcf2SDag-Erling Smørgrav /* sshbuf_dump(b, stderr); */ 2887ca86bcf2SDag-Erling Smørgrav if (ssh_packet_log_type(type)) 2888ca86bcf2SDag-Erling Smørgrav debug3("%s: channel %u: up->down: type %u", __func__, c->self, 2889ca86bcf2SDag-Erling Smørgrav type); 2890ca86bcf2SDag-Erling Smørgrav out: 2891ca86bcf2SDag-Erling Smørgrav /* update state */ 2892ca86bcf2SDag-Erling Smørgrav switch (type) { 2893ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: 2894ca86bcf2SDag-Erling Smørgrav /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ 28954f52dfbbSDag-Erling Smørgrav if (cp && len > 4) { 2896ca86bcf2SDag-Erling Smørgrav c->remote_id = PEEK_U32(cp); 28974f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 28984f52dfbbSDag-Erling Smørgrav } 2899ca86bcf2SDag-Erling Smørgrav break; 2900ca86bcf2SDag-Erling Smørgrav case SSH2_MSG_CHANNEL_CLOSE: 2901ca86bcf2SDag-Erling Smørgrav if (c->flags & CHAN_CLOSE_SENT) 29024f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 2903ca86bcf2SDag-Erling Smørgrav else 2904ca86bcf2SDag-Erling Smørgrav c->flags |= CHAN_CLOSE_RCVD; 2905ca86bcf2SDag-Erling Smørgrav break; 2906ca86bcf2SDag-Erling Smørgrav } 2907ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 2908ca86bcf2SDag-Erling Smørgrav return 1; 2909ca86bcf2SDag-Erling Smørgrav } 2910af12a3e7SDag-Erling Smørgrav 2911af12a3e7SDag-Erling Smørgrav /* -- protocol input */ 2912511b41d2SMark Murray 29134f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */ 29144f52dfbbSDag-Erling Smørgrav static int 29154f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what) 2916511b41d2SMark Murray { 29174f52dfbbSDag-Erling Smørgrav u_int32_t id; 29184f52dfbbSDag-Erling Smørgrav int r; 29194f52dfbbSDag-Erling Smørgrav 29204f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &id)) != 0) { 29214f52dfbbSDag-Erling Smørgrav error("%s: parse id: %s", where, ssh_err(r)); 29224f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s message", what); 29234f52dfbbSDag-Erling Smørgrav } 29244f52dfbbSDag-Erling Smørgrav if (id > INT_MAX) { 29254f52dfbbSDag-Erling Smørgrav error("%s: bad channel id %u: %s", where, id, ssh_err(r)); 29264f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid %s channel id", what); 29274f52dfbbSDag-Erling Smørgrav } 29284f52dfbbSDag-Erling Smørgrav return (int)id; 29294f52dfbbSDag-Erling Smørgrav } 29304f52dfbbSDag-Erling Smørgrav 29314f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */ 29324f52dfbbSDag-Erling Smørgrav static Channel * 29334f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) 29344f52dfbbSDag-Erling Smørgrav { 29354f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, where, what); 2936a04a10f8SKris Kennaway Channel *c; 2937511b41d2SMark Murray 29384f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 29394f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, 29404f52dfbbSDag-Erling Smørgrav "%s packet referred to nonexistent channel %d", what, id); 29414f52dfbbSDag-Erling Smørgrav } 29424f52dfbbSDag-Erling Smørgrav return c; 29434f52dfbbSDag-Erling Smørgrav } 29444f52dfbbSDag-Erling Smørgrav 29454f52dfbbSDag-Erling Smørgrav int 29464f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh) 29474f52dfbbSDag-Erling Smørgrav { 29484f52dfbbSDag-Erling Smørgrav const u_char *data; 29494f52dfbbSDag-Erling Smørgrav size_t data_len, win_len; 29504f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "data"); 29514f52dfbbSDag-Erling Smørgrav int r; 29524f52dfbbSDag-Erling Smørgrav 29534f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 2954ca86bcf2SDag-Erling Smørgrav return 0; 2955511b41d2SMark Murray 2956511b41d2SMark Murray /* Ignore any data for non-open channels (might happen on close) */ 2957a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN && 29584f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_OPEN && 29594f52dfbbSDag-Erling Smørgrav c->type != SSH_CHANNEL_RDYNAMIC_FINISH && 2960a04a10f8SKris Kennaway c->type != SSH_CHANNEL_X11_OPEN) 2961bc5531deSDag-Erling Smørgrav return 0; 2962511b41d2SMark Murray 2963511b41d2SMark Murray /* Get the data. */ 29644f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) 29654f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: get data: %s", __func__, 29664f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 29674f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 29684f52dfbbSDag-Erling Smørgrav 2969e2f6069cSDag-Erling Smørgrav win_len = data_len; 2970e2f6069cSDag-Erling Smørgrav if (c->datagram) 2971e2f6069cSDag-Erling Smørgrav win_len += 4; /* string length header */ 2972a04a10f8SKris Kennaway 2973476cd3b2SDag-Erling Smørgrav /* 29744f52dfbbSDag-Erling Smørgrav * The sending side reduces its window as it sends data, so we 29754f52dfbbSDag-Erling Smørgrav * must 'fake' consumption of the data in order to ensure that window 29764f52dfbbSDag-Erling Smørgrav * updates are sent back. Otherwise the connection might deadlock. 2977476cd3b2SDag-Erling Smørgrav */ 29784f52dfbbSDag-Erling Smørgrav if (c->ostate != CHAN_OUTPUT_OPEN) { 2979e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 2980e2f6069cSDag-Erling Smørgrav c->local_consumed += win_len; 2981bc5531deSDag-Erling Smørgrav return 0; 2982476cd3b2SDag-Erling Smørgrav } 2983476cd3b2SDag-Erling Smørgrav 2984e2f6069cSDag-Erling Smørgrav if (win_len > c->local_maxpacket) { 29854f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd big packet %zu, maxpack %u", 2986e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_maxpacket); 29874f52dfbbSDag-Erling Smørgrav return 0; 2988a04a10f8SKris Kennaway } 2989e2f6069cSDag-Erling Smørgrav if (win_len > c->local_window) { 29904f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much data %zu, win %u", 2991e2f6069cSDag-Erling Smørgrav c->self, win_len, c->local_window); 2992bc5531deSDag-Erling Smørgrav return 0; 2993a04a10f8SKris Kennaway } 2994e2f6069cSDag-Erling Smørgrav c->local_window -= win_len; 29954f52dfbbSDag-Erling Smørgrav 29964f52dfbbSDag-Erling Smørgrav if (c->datagram) { 29974f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) 29984f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append datagram: %s", 29994f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 30004f52dfbbSDag-Erling Smørgrav } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) 30014f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: append data: %s", 30024f52dfbbSDag-Erling Smørgrav __func__, c->self, ssh_err(r)); 30034f52dfbbSDag-Erling Smørgrav 3004bc5531deSDag-Erling Smørgrav return 0; 3005511b41d2SMark Murray } 3006af12a3e7SDag-Erling Smørgrav 3007bc5531deSDag-Erling Smørgrav int 30084f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) 3009a04a10f8SKris Kennaway { 30104f52dfbbSDag-Erling Smørgrav const u_char *data; 30114f52dfbbSDag-Erling Smørgrav size_t data_len; 30124f52dfbbSDag-Erling Smørgrav u_int32_t tcode; 30134f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); 30144f52dfbbSDag-Erling Smørgrav int r; 3015a04a10f8SKris Kennaway 30164f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3017ca86bcf2SDag-Erling Smørgrav return 0; 3018a04a10f8SKris Kennaway if (c->type != SSH_CHANNEL_OPEN) { 30194f52dfbbSDag-Erling Smørgrav logit("channel %d: ext data for non open", c->self); 3020bc5531deSDag-Erling Smørgrav return 0; 3021a04a10f8SKris Kennaway } 302280628bacSDag-Erling Smørgrav if (c->flags & CHAN_EOF_RCVD) { 302380628bacSDag-Erling Smørgrav if (datafellows & SSH_BUG_EXTEOF) 30244f52dfbbSDag-Erling Smørgrav debug("channel %d: accepting ext data after eof", 30254f52dfbbSDag-Erling Smørgrav c->self); 302680628bacSDag-Erling Smørgrav else 30274f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Received extended_data " 30284f52dfbbSDag-Erling Smørgrav "after EOF on channel %d.", c->self); 302980628bacSDag-Erling Smørgrav } 30304f52dfbbSDag-Erling Smørgrav 30314f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { 30324f52dfbbSDag-Erling Smørgrav error("%s: parse tcode: %s", __func__, ssh_err(r)); 30334f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 30344f52dfbbSDag-Erling Smørgrav } 3035a04a10f8SKris Kennaway if (c->efd == -1 || 3036a04a10f8SKris Kennaway c->extended_usage != CHAN_EXTENDED_WRITE || 3037a04a10f8SKris Kennaway tcode != SSH2_EXTENDED_DATA_STDERR) { 3038221552e4SDag-Erling Smørgrav logit("channel %d: bad ext data", c->self); 3039bc5531deSDag-Erling Smørgrav return 0; 3040a04a10f8SKris Kennaway } 30414f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) { 30424f52dfbbSDag-Erling Smørgrav error("%s: parse data: %s", __func__, ssh_err(r)); 30434f52dfbbSDag-Erling Smørgrav ssh_packet_disconnect(ssh, "Invalid extended_data message"); 30444f52dfbbSDag-Erling Smørgrav } 30454f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 30464f52dfbbSDag-Erling Smørgrav 3047a04a10f8SKris Kennaway if (data_len > c->local_window) { 30484f52dfbbSDag-Erling Smørgrav logit("channel %d: rcvd too much extended_data %zu, win %u", 3049a04a10f8SKris Kennaway c->self, data_len, c->local_window); 3050bc5531deSDag-Erling Smørgrav return 0; 3051a04a10f8SKris Kennaway } 30524f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd ext data %zu", c->self, data_len); 30534f52dfbbSDag-Erling Smørgrav /* XXX sshpkt_getb? */ 30544f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, data, data_len)) != 0) 30554f52dfbbSDag-Erling Smørgrav error("%s: append: %s", __func__, ssh_err(r)); 3056a04a10f8SKris Kennaway c->local_window -= data_len; 3057bc5531deSDag-Erling Smørgrav return 0; 3058a04a10f8SKris Kennaway } 3059a04a10f8SKris Kennaway 3060bc5531deSDag-Erling Smørgrav int 30614f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) 3062a04a10f8SKris Kennaway { 30634f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); 3064a04a10f8SKris Kennaway 30654f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 30664f52dfbbSDag-Erling Smørgrav 30674f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3068ca86bcf2SDag-Erling Smørgrav return 0; 30694f52dfbbSDag-Erling Smørgrav chan_rcvd_ieof(ssh, c); 3070af12a3e7SDag-Erling Smørgrav 3071af12a3e7SDag-Erling Smørgrav /* XXX force input close */ 3072af12a3e7SDag-Erling Smørgrav if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { 3073af12a3e7SDag-Erling Smørgrav debug("channel %d: FORCE input drain", c->self); 3074af12a3e7SDag-Erling Smørgrav c->istate = CHAN_INPUT_WAIT_DRAIN; 30754f52dfbbSDag-Erling Smørgrav if (sshbuf_len(c->input) == 0) 30764f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 3077af12a3e7SDag-Erling Smørgrav } 3078bc5531deSDag-Erling Smørgrav return 0; 3079a04a10f8SKris Kennaway } 3080511b41d2SMark Murray 3081bc5531deSDag-Erling Smørgrav int 30824f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) 3083511b41d2SMark Murray { 30844f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); 3085511b41d2SMark Murray 30864f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3087ca86bcf2SDag-Erling Smørgrav return 0; 30884f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 30894f52dfbbSDag-Erling Smørgrav chan_rcvd_oclose(ssh, c); 3090bc5531deSDag-Erling Smørgrav return 0; 3091511b41d2SMark Murray } 3092511b41d2SMark Murray 3093bc5531deSDag-Erling Smørgrav int 30944f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) 3095a04a10f8SKris Kennaway { 30964f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); 30974f52dfbbSDag-Erling Smørgrav u_int32_t remote_window, remote_maxpacket; 30984f52dfbbSDag-Erling Smørgrav int r; 3099af12a3e7SDag-Erling Smørgrav 31004f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3101ca86bcf2SDag-Erling Smørgrav return 0; 3102ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 3103a04a10f8SKris Kennaway packet_disconnect("Received open confirmation for " 31044f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 31054f52dfbbSDag-Erling Smørgrav /* 31064f52dfbbSDag-Erling Smørgrav * Record the remote channel number and mark that the channel 31074f52dfbbSDag-Erling Smørgrav * is now open. 31084f52dfbbSDag-Erling Smørgrav */ 31094f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || 31104f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || 31114f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0) { 31124f52dfbbSDag-Erling Smørgrav error("%s: window/maxpacket: %s", __func__, ssh_err(r)); 31134f52dfbbSDag-Erling Smørgrav packet_disconnect("Invalid open confirmation message"); 31144f52dfbbSDag-Erling Smørgrav } 31154f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 3116a04a10f8SKris Kennaway 31174f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 31184f52dfbbSDag-Erling Smørgrav c->remote_window = remote_window; 31194f52dfbbSDag-Erling Smørgrav c->remote_maxpacket = remote_maxpacket; 31204f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_OPEN; 3121d4af9e69SDag-Erling Smørgrav if (c->open_confirm) { 31224f52dfbbSDag-Erling Smørgrav debug2("%s: channel %d: callback start", __func__, c->self); 31234f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); 31244f52dfbbSDag-Erling Smørgrav debug2("%s: channel %d: callback done", __func__, c->self); 3125a04a10f8SKris Kennaway } 3126221552e4SDag-Erling Smørgrav debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 3127a04a10f8SKris Kennaway c->remote_window, c->remote_maxpacket); 3128bc5531deSDag-Erling Smørgrav return 0; 3129af12a3e7SDag-Erling Smørgrav } 3130af12a3e7SDag-Erling Smørgrav 3131af12a3e7SDag-Erling Smørgrav static char * 3132af12a3e7SDag-Erling Smørgrav reason2txt(int reason) 3133af12a3e7SDag-Erling Smørgrav { 3134af12a3e7SDag-Erling Smørgrav switch (reason) { 3135af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: 3136af12a3e7SDag-Erling Smørgrav return "administratively prohibited"; 3137af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_CONNECT_FAILED: 3138af12a3e7SDag-Erling Smørgrav return "connect failed"; 3139af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: 3140af12a3e7SDag-Erling Smørgrav return "unknown channel type"; 3141af12a3e7SDag-Erling Smørgrav case SSH2_OPEN_RESOURCE_SHORTAGE: 3142af12a3e7SDag-Erling Smørgrav return "resource shortage"; 3143af12a3e7SDag-Erling Smørgrav } 3144af12a3e7SDag-Erling Smørgrav return "unknown reason"; 3145a04a10f8SKris Kennaway } 3146a04a10f8SKris Kennaway 3147bc5531deSDag-Erling Smørgrav int 31484f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) 3149a04a10f8SKris Kennaway { 31504f52dfbbSDag-Erling Smørgrav Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); 31514f52dfbbSDag-Erling Smørgrav u_int32_t reason; 31524f52dfbbSDag-Erling Smørgrav char *msg = NULL; 31534f52dfbbSDag-Erling Smørgrav int r; 3154a04a10f8SKris Kennaway 31554f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3156ca86bcf2SDag-Erling Smørgrav return 0; 3157ca86bcf2SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_OPENING) 3158a04a10f8SKris Kennaway packet_disconnect("Received open failure for " 31594f52dfbbSDag-Erling Smørgrav "non-opening channel %d.", c->self); 31604f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { 31614f52dfbbSDag-Erling Smørgrav error("%s: reason: %s", __func__, ssh_err(r)); 31624f52dfbbSDag-Erling Smørgrav packet_disconnect("Invalid open failure message"); 3163ca3176e7SBrian Feldman } 31644f52dfbbSDag-Erling Smørgrav /* skip language */ 31654f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || 31664f52dfbbSDag-Erling Smørgrav (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0) { 31674f52dfbbSDag-Erling Smørgrav error("%s: message/lang: %s", __func__, ssh_err(r)); 31684f52dfbbSDag-Erling Smørgrav packet_disconnect("Invalid open failure message"); 31694f52dfbbSDag-Erling Smørgrav } 31704f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 31714f52dfbbSDag-Erling Smørgrav logit("channel %d: open failed: %s%s%s", c->self, 3172af12a3e7SDag-Erling Smørgrav reason2txt(reason), msg ? ": ": "", msg ? msg : ""); 3173e4a9863fSDag-Erling Smørgrav free(msg); 3174e2f6069cSDag-Erling Smørgrav if (c->open_confirm) { 31754f52dfbbSDag-Erling Smørgrav debug2("%s: channel %d: callback start", __func__, c->self); 31764f52dfbbSDag-Erling Smørgrav c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); 31774f52dfbbSDag-Erling Smørgrav debug2("%s: channel %d: callback done", __func__, c->self); 3178e2f6069cSDag-Erling Smørgrav } 3179cce7d346SDag-Erling Smørgrav /* Schedule the channel for cleanup/deletion. */ 31804f52dfbbSDag-Erling Smørgrav chan_mark_dead(ssh, c); 3181bc5531deSDag-Erling Smørgrav return 0; 3182a04a10f8SKris Kennaway } 3183a04a10f8SKris Kennaway 3184bc5531deSDag-Erling Smørgrav int 31854f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) 3186a04a10f8SKris Kennaway { 31874f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "window adjust"); 3188a04a10f8SKris Kennaway Channel *c; 31894f52dfbbSDag-Erling Smørgrav u_int32_t adjust; 31904f52dfbbSDag-Erling Smørgrav u_int new_rwin; 31914f52dfbbSDag-Erling Smørgrav int r; 3192a04a10f8SKris Kennaway 31934f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 3194b74df5b2SDag-Erling Smørgrav logit("Received window adjust for non-open channel %d.", id); 3195bc5531deSDag-Erling Smørgrav return 0; 3196511b41d2SMark Murray } 31974f52dfbbSDag-Erling Smørgrav 31984f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3199ca86bcf2SDag-Erling Smørgrav return 0; 32004f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &adjust)) != 0) { 32014f52dfbbSDag-Erling Smørgrav error("%s: adjust: %s", __func__, ssh_err(r)); 32024f52dfbbSDag-Erling Smørgrav packet_disconnect("Invalid window adjust message"); 32034f52dfbbSDag-Erling Smørgrav } 32044f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 32054f52dfbbSDag-Erling Smørgrav debug2("channel %d: rcvd adjust %u", c->self, adjust); 32064f52dfbbSDag-Erling Smørgrav if ((new_rwin = c->remote_window + adjust) < c->remote_window) { 3207557f75e5SDag-Erling Smørgrav fatal("channel %d: adjust %u overflows remote window %u", 32084f52dfbbSDag-Erling Smørgrav c->self, adjust, c->remote_window); 32094f52dfbbSDag-Erling Smørgrav } 32104f52dfbbSDag-Erling Smørgrav c->remote_window = new_rwin; 3211bc5531deSDag-Erling Smørgrav return 0; 3212511b41d2SMark Murray } 3213511b41d2SMark Murray 3214bc5531deSDag-Erling Smørgrav int 32154f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) 3216af12a3e7SDag-Erling Smørgrav { 32174f52dfbbSDag-Erling Smørgrav int id = channel_parse_id(ssh, __func__, "status confirm"); 3218d4af9e69SDag-Erling Smørgrav Channel *c; 3219d4af9e69SDag-Erling Smørgrav struct channel_confirm *cc; 3220d4af9e69SDag-Erling Smørgrav 3221d4af9e69SDag-Erling Smørgrav /* Reset keepalive timeout */ 32227aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 3223d4af9e69SDag-Erling Smørgrav 32244f52dfbbSDag-Erling Smørgrav debug2("%s: type %d id %d", __func__, type, id); 3225d4af9e69SDag-Erling Smørgrav 32264f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) { 32274f52dfbbSDag-Erling Smørgrav logit("%s: %d: unknown", __func__, id); 3228bc5531deSDag-Erling Smørgrav return 0; 3229d4af9e69SDag-Erling Smørgrav } 32304f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 3231ca86bcf2SDag-Erling Smørgrav return 0; 32324f52dfbbSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 3233d4af9e69SDag-Erling Smørgrav if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) 3234bc5531deSDag-Erling Smørgrav return 0; 32354f52dfbbSDag-Erling Smørgrav cc->cb(ssh, type, c, cc->ctx); 3236d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&c->status_confirms, cc, entry); 3237b83788ffSDag-Erling Smørgrav explicit_bzero(cc, sizeof(*cc)); 3238e4a9863fSDag-Erling Smørgrav free(cc); 3239bc5531deSDag-Erling Smørgrav return 0; 3240d4af9e69SDag-Erling Smørgrav } 3241af12a3e7SDag-Erling Smørgrav 3242af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */ 3243511b41d2SMark Murray 3244511b41d2SMark Murray void 32454f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af) 3246511b41d2SMark Murray { 32474f52dfbbSDag-Erling Smørgrav ssh->chanctxt->IPv4or6 = af; 3248511b41d2SMark Murray } 3249511b41d2SMark Murray 325089986192SBrooks Davis 3251462c32cbSDag-Erling Smørgrav /* 3252462c32cbSDag-Erling Smørgrav * Determine whether or not a port forward listens to loopback, the 3253462c32cbSDag-Erling Smørgrav * specified address or wildcard. On the client, a specified bind 3254462c32cbSDag-Erling Smørgrav * address will always override gateway_ports. On the server, a 3255462c32cbSDag-Erling Smørgrav * gateway_ports of 1 (``yes'') will override the client's specification 3256462c32cbSDag-Erling Smørgrav * and force a wildcard bind, whereas a value of 2 (``clientspecified'') 3257462c32cbSDag-Erling Smørgrav * will bind to whatever address the client asked for. 3258462c32cbSDag-Erling Smørgrav * 3259462c32cbSDag-Erling Smørgrav * Special-case listen_addrs are: 3260462c32cbSDag-Erling Smørgrav * 3261462c32cbSDag-Erling Smørgrav * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR 3262462c32cbSDag-Erling Smørgrav * "" (empty string), "*" -> wildcard v4/v6 3263462c32cbSDag-Erling Smørgrav * "localhost" -> loopback v4/v6 3264a0ee8cc6SDag-Erling Smørgrav * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set 3265462c32cbSDag-Erling Smørgrav */ 3266462c32cbSDag-Erling Smørgrav static const char * 3267462c32cbSDag-Erling Smørgrav channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, 3268a0ee8cc6SDag-Erling Smørgrav int is_client, struct ForwardOptions *fwd_opts) 3269462c32cbSDag-Erling Smørgrav { 3270462c32cbSDag-Erling Smørgrav const char *addr = NULL; 3271462c32cbSDag-Erling Smørgrav int wildcard = 0; 3272462c32cbSDag-Erling Smørgrav 3273462c32cbSDag-Erling Smørgrav if (listen_addr == NULL) { 3274462c32cbSDag-Erling Smørgrav /* No address specified: default to gateway_ports setting */ 3275a0ee8cc6SDag-Erling Smørgrav if (fwd_opts->gateway_ports) 3276462c32cbSDag-Erling Smørgrav wildcard = 1; 3277a0ee8cc6SDag-Erling Smørgrav } else if (fwd_opts->gateway_ports || is_client) { 3278462c32cbSDag-Erling Smørgrav if (((datafellows & SSH_OLD_FORWARD_ADDR) && 3279462c32cbSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || 3280462c32cbSDag-Erling Smørgrav *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || 3281a0ee8cc6SDag-Erling Smørgrav (!is_client && fwd_opts->gateway_ports == 1)) { 3282462c32cbSDag-Erling Smørgrav wildcard = 1; 3283f7167e0eSDag-Erling Smørgrav /* 3284f7167e0eSDag-Erling Smørgrav * Notify client if they requested a specific listen 3285f7167e0eSDag-Erling Smørgrav * address and it was overridden. 3286f7167e0eSDag-Erling Smørgrav */ 3287f7167e0eSDag-Erling Smørgrav if (*listen_addr != '\0' && 3288f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "0.0.0.0") != 0 && 3289f7167e0eSDag-Erling Smørgrav strcmp(listen_addr, "*") != 0) { 3290f7167e0eSDag-Erling Smørgrav packet_send_debug("Forwarding listen address " 3291f7167e0eSDag-Erling Smørgrav "\"%s\" overridden by server " 3292f7167e0eSDag-Erling Smørgrav "GatewayPorts", listen_addr); 3293f7167e0eSDag-Erling Smørgrav } 3294a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "localhost") != 0 || 3295a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "127.0.0.1") == 0 || 3296a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3297a0ee8cc6SDag-Erling Smørgrav /* Accept localhost address when GatewayPorts=yes */ 3298a0ee8cc6SDag-Erling Smørgrav addr = listen_addr; 3299f7167e0eSDag-Erling Smørgrav } 3300a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(listen_addr, "127.0.0.1") == 0 || 3301a0ee8cc6SDag-Erling Smørgrav strcmp(listen_addr, "::1") == 0) { 3302a0ee8cc6SDag-Erling Smørgrav /* 3303a0ee8cc6SDag-Erling Smørgrav * If a specific IPv4/IPv6 localhost address has been 3304a0ee8cc6SDag-Erling Smørgrav * requested then accept it even if gateway_ports is in 3305a0ee8cc6SDag-Erling Smørgrav * effect. This allows the client to prefer IPv4 or IPv6. 3306a0ee8cc6SDag-Erling Smørgrav */ 3307462c32cbSDag-Erling Smørgrav addr = listen_addr; 3308462c32cbSDag-Erling Smørgrav } 3309462c32cbSDag-Erling Smørgrav if (wildcardp != NULL) 3310462c32cbSDag-Erling Smørgrav *wildcardp = wildcard; 3311462c32cbSDag-Erling Smørgrav return addr; 3312462c32cbSDag-Erling Smørgrav } 3313462c32cbSDag-Erling Smørgrav 3314af12a3e7SDag-Erling Smørgrav static int 33154f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, 33164f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int *allocated_listen_port, 33174f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3318511b41d2SMark Murray { 3319af12a3e7SDag-Erling Smørgrav Channel *c; 3320b74df5b2SDag-Erling Smørgrav int sock, r, success = 0, wildcard = 0, is_client; 3321511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 3322aa49c926SDag-Erling Smørgrav const char *host, *addr; 3323af12a3e7SDag-Erling Smørgrav char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 3324cce7d346SDag-Erling Smørgrav in_port_t *lport_p; 3325511b41d2SMark Murray 3326aa49c926SDag-Erling Smørgrav is_client = (type == SSH_CHANNEL_PORT_LISTENER); 3327511b41d2SMark Murray 3328557f75e5SDag-Erling Smørgrav if (is_client && fwd->connect_path != NULL) { 3329557f75e5SDag-Erling Smørgrav host = fwd->connect_path; 3330557f75e5SDag-Erling Smørgrav } else { 3331557f75e5SDag-Erling Smørgrav host = (type == SSH_CHANNEL_RPORT_LISTENER) ? 3332557f75e5SDag-Erling Smørgrav fwd->listen_host : fwd->connect_host; 3333af12a3e7SDag-Erling Smørgrav if (host == NULL) { 3334af12a3e7SDag-Erling Smørgrav error("No forward host name."); 3335d4ecd108SDag-Erling Smørgrav return 0; 3336ca3176e7SBrian Feldman } 3337cce7d346SDag-Erling Smørgrav if (strlen(host) >= NI_MAXHOST) { 3338ca3176e7SBrian Feldman error("Forward host name too long."); 3339d4ecd108SDag-Erling Smørgrav return 0; 3340ca3176e7SBrian Feldman } 3341557f75e5SDag-Erling Smørgrav } 3342ca3176e7SBrian Feldman 3343462c32cbSDag-Erling Smørgrav /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ 3344a0ee8cc6SDag-Erling Smørgrav addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, 3345a0ee8cc6SDag-Erling Smørgrav is_client, fwd_opts); 3346a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d wildcard %d addr %s", __func__, 3347aa49c926SDag-Erling Smørgrav type, wildcard, (addr == NULL) ? "NULL" : addr); 3348aa49c926SDag-Erling Smørgrav 3349aa49c926SDag-Erling Smørgrav /* 3350511b41d2SMark Murray * getaddrinfo returns a loopback address if the hostname is 3351511b41d2SMark Murray * set to NULL and hints.ai_flags is not AI_PASSIVE 3352511b41d2SMark Murray */ 3353511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 33544f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 3355aa49c926SDag-Erling Smørgrav hints.ai_flags = wildcard ? AI_PASSIVE : 0; 3356511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 3357a0ee8cc6SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", fwd->listen_port); 3358aa49c926SDag-Erling Smørgrav if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { 3359aa49c926SDag-Erling Smørgrav if (addr == NULL) { 3360aa49c926SDag-Erling Smørgrav /* This really shouldn't happen */ 3361aa49c926SDag-Erling Smørgrav packet_disconnect("getaddrinfo: fatal error: %s", 3362d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3363aa49c926SDag-Erling Smørgrav } else { 3364a0ee8cc6SDag-Erling Smørgrav error("%s: getaddrinfo(%.64s): %s", __func__, addr, 3365d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(r)); 3366aa49c926SDag-Erling Smørgrav } 3367d4ecd108SDag-Erling Smørgrav return 0; 3368aa49c926SDag-Erling Smørgrav } 3369cce7d346SDag-Erling Smørgrav if (allocated_listen_port != NULL) 3370cce7d346SDag-Erling Smørgrav *allocated_listen_port = 0; 3371511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 3372cce7d346SDag-Erling Smørgrav switch (ai->ai_family) { 3373cce7d346SDag-Erling Smørgrav case AF_INET: 3374cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in *)ai->ai_addr)-> 3375cce7d346SDag-Erling Smørgrav sin_port; 3376cce7d346SDag-Erling Smørgrav break; 3377cce7d346SDag-Erling Smørgrav case AF_INET6: 3378cce7d346SDag-Erling Smørgrav lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> 3379cce7d346SDag-Erling Smørgrav sin6_port; 3380cce7d346SDag-Erling Smørgrav break; 3381cce7d346SDag-Erling Smørgrav default: 3382511b41d2SMark Murray continue; 3383cce7d346SDag-Erling Smørgrav } 3384cce7d346SDag-Erling Smørgrav /* 3385cce7d346SDag-Erling Smørgrav * If allocating a port for -R forwards, then use the 3386cce7d346SDag-Erling Smørgrav * same port for all address families. 3387cce7d346SDag-Erling Smørgrav */ 33884f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 33894f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && allocated_listen_port != NULL && 33904f52dfbbSDag-Erling Smørgrav *allocated_listen_port > 0) 3391cce7d346SDag-Erling Smørgrav *lport_p = htons(*allocated_listen_port); 3392cce7d346SDag-Erling Smørgrav 3393511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), 33944f52dfbbSDag-Erling Smørgrav strport, sizeof(strport), 33954f52dfbbSDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 3396a0ee8cc6SDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 3397511b41d2SMark Murray continue; 3398511b41d2SMark Murray } 3399511b41d2SMark Murray /* Create a port to listen for the host. */ 3400221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 3401511b41d2SMark Murray if (sock < 0) { 3402511b41d2SMark Murray /* this is no error since kernel may not support ipv6 */ 340347dd1d1bSDag-Erling Smørgrav verbose("socket [%s]:%s: %.100s", ntop, strport, 340447dd1d1bSDag-Erling Smørgrav strerror(errno)); 3405511b41d2SMark Murray continue; 3406511b41d2SMark Murray } 3407b74df5b2SDag-Erling Smørgrav 340847dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 3409b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 3410b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 3411f388f5efSDag-Erling Smørgrav 3412cce7d346SDag-Erling Smørgrav debug("Local forwarding listening on %s port %s.", 3413cce7d346SDag-Erling Smørgrav ntop, strport); 3414511b41d2SMark Murray 3415511b41d2SMark Murray /* Bind the socket to the address. */ 3416511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 34174f52dfbbSDag-Erling Smørgrav /* 34184f52dfbbSDag-Erling Smørgrav * address can be in if use ipv6 address is 34194f52dfbbSDag-Erling Smørgrav * already bound 34204f52dfbbSDag-Erling Smørgrav */ 3421989dd127SDag-Erling Smørgrav if (!ai->ai_next) 342247dd1d1bSDag-Erling Smørgrav error("bind [%s]:%s: %.100s", 342347dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3424989dd127SDag-Erling Smørgrav else 342547dd1d1bSDag-Erling Smørgrav verbose("bind [%s]:%s: %.100s", 342647dd1d1bSDag-Erling Smørgrav ntop, strport, strerror(errno)); 3427989dd127SDag-Erling Smørgrav 3428511b41d2SMark Murray close(sock); 3429511b41d2SMark Murray continue; 3430511b41d2SMark Murray } 3431511b41d2SMark Murray /* Start listening for connections on the socket. */ 3432476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 3433511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 343447dd1d1bSDag-Erling Smørgrav error("listen [%s]:%s: %.100s", ntop, strport, 343547dd1d1bSDag-Erling Smørgrav strerror(errno)); 3436511b41d2SMark Murray close(sock); 3437511b41d2SMark Murray continue; 3438511b41d2SMark Murray } 3439cce7d346SDag-Erling Smørgrav 3440cce7d346SDag-Erling Smørgrav /* 3441a0ee8cc6SDag-Erling Smørgrav * fwd->listen_port == 0 requests a dynamically allocated port - 3442cce7d346SDag-Erling Smørgrav * record what we got. 3443cce7d346SDag-Erling Smørgrav */ 34444f52dfbbSDag-Erling Smørgrav if (type == SSH_CHANNEL_RPORT_LISTENER && 34454f52dfbbSDag-Erling Smørgrav fwd->listen_port == 0 && 3446cce7d346SDag-Erling Smørgrav allocated_listen_port != NULL && 3447cce7d346SDag-Erling Smørgrav *allocated_listen_port == 0) { 3448076ad2f8SDag-Erling Smørgrav *allocated_listen_port = get_local_port(sock); 3449cce7d346SDag-Erling Smørgrav debug("Allocated listen port %d", 3450cce7d346SDag-Erling Smørgrav *allocated_listen_port); 3451cce7d346SDag-Erling Smørgrav } 3452cce7d346SDag-Erling Smørgrav 345360c59fadSDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 34544f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "port listener", type, sock, sock, -1, 3455a04a10f8SKris Kennaway CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3456221552e4SDag-Erling Smørgrav 0, "port listener", 1); 3457cce7d346SDag-Erling Smørgrav c->path = xstrdup(host); 3458a0ee8cc6SDag-Erling Smørgrav c->host_port = fwd->connect_port; 3459462c32cbSDag-Erling Smørgrav c->listening_addr = addr == NULL ? NULL : xstrdup(addr); 3460a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_port == 0 && allocated_listen_port != NULL && 3461462c32cbSDag-Erling Smørgrav !(datafellows & SSH_BUG_DYNAMIC_RPORT)) 3462462c32cbSDag-Erling Smørgrav c->listening_port = *allocated_listen_port; 3463462c32cbSDag-Erling Smørgrav else 3464a0ee8cc6SDag-Erling Smørgrav c->listening_port = fwd->listen_port; 3465511b41d2SMark Murray success = 1; 3466511b41d2SMark Murray } 3467511b41d2SMark Murray if (success == 0) 3468a0ee8cc6SDag-Erling Smørgrav error("%s: cannot listen to port: %d", __func__, 3469a0ee8cc6SDag-Erling Smørgrav fwd->listen_port); 3470511b41d2SMark Murray freeaddrinfo(aitop); 3471ca3176e7SBrian Feldman return success; 3472511b41d2SMark Murray } 3473511b41d2SMark Murray 3474a0ee8cc6SDag-Erling Smørgrav static int 34754f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, 34764f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 3477a0ee8cc6SDag-Erling Smørgrav { 3478a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un sunaddr; 3479a0ee8cc6SDag-Erling Smørgrav const char *path; 3480a0ee8cc6SDag-Erling Smørgrav Channel *c; 3481a0ee8cc6SDag-Erling Smørgrav int port, sock; 3482a0ee8cc6SDag-Erling Smørgrav mode_t omask; 3483a0ee8cc6SDag-Erling Smørgrav 3484a0ee8cc6SDag-Erling Smørgrav switch (type) { 3485a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_UNIX_LISTENER: 3486a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 3487a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { 3488a0ee8cc6SDag-Erling Smørgrav error("Local connecting path too long: %s", 3489a0ee8cc6SDag-Erling Smørgrav fwd->connect_path); 3490a0ee8cc6SDag-Erling Smørgrav return 0; 3491a0ee8cc6SDag-Erling Smørgrav } 3492a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_path; 3493a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3494a0ee8cc6SDag-Erling Smørgrav } else { 3495a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_host == NULL) { 3496a0ee8cc6SDag-Erling Smørgrav error("No forward host name."); 3497a0ee8cc6SDag-Erling Smørgrav return 0; 3498a0ee8cc6SDag-Erling Smørgrav } 3499a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->connect_host) >= NI_MAXHOST) { 3500a0ee8cc6SDag-Erling Smørgrav error("Forward host name too long."); 3501a0ee8cc6SDag-Erling Smørgrav return 0; 3502a0ee8cc6SDag-Erling Smørgrav } 3503a0ee8cc6SDag-Erling Smørgrav path = fwd->connect_host; 3504a0ee8cc6SDag-Erling Smørgrav port = fwd->connect_port; 3505a0ee8cc6SDag-Erling Smørgrav } 3506a0ee8cc6SDag-Erling Smørgrav break; 3507a0ee8cc6SDag-Erling Smørgrav case SSH_CHANNEL_RUNIX_LISTENER: 3508a0ee8cc6SDag-Erling Smørgrav path = fwd->listen_path; 3509a0ee8cc6SDag-Erling Smørgrav port = PORT_STREAMLOCAL; 3510a0ee8cc6SDag-Erling Smørgrav break; 3511a0ee8cc6SDag-Erling Smørgrav default: 3512a0ee8cc6SDag-Erling Smørgrav error("%s: unexpected channel type %d", __func__, type); 3513a0ee8cc6SDag-Erling Smørgrav return 0; 3514a0ee8cc6SDag-Erling Smørgrav } 3515a0ee8cc6SDag-Erling Smørgrav 3516a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path == NULL) { 3517a0ee8cc6SDag-Erling Smørgrav error("No forward path name."); 3518a0ee8cc6SDag-Erling Smørgrav return 0; 3519a0ee8cc6SDag-Erling Smørgrav } 3520a0ee8cc6SDag-Erling Smørgrav if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { 3521a0ee8cc6SDag-Erling Smørgrav error("Local listening path too long: %s", fwd->listen_path); 3522a0ee8cc6SDag-Erling Smørgrav return 0; 3523a0ee8cc6SDag-Erling Smørgrav } 3524a0ee8cc6SDag-Erling Smørgrav 3525a0ee8cc6SDag-Erling Smørgrav debug3("%s: type %d path %s", __func__, type, fwd->listen_path); 3526a0ee8cc6SDag-Erling Smørgrav 3527a0ee8cc6SDag-Erling Smørgrav /* Start a Unix domain listener. */ 3528a0ee8cc6SDag-Erling Smørgrav omask = umask(fwd_opts->streamlocal_bind_mask); 3529a0ee8cc6SDag-Erling Smørgrav sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, 3530a0ee8cc6SDag-Erling Smørgrav fwd_opts->streamlocal_bind_unlink); 3531a0ee8cc6SDag-Erling Smørgrav umask(omask); 3532a0ee8cc6SDag-Erling Smørgrav if (sock < 0) 3533a0ee8cc6SDag-Erling Smørgrav return 0; 3534a0ee8cc6SDag-Erling Smørgrav 3535a0ee8cc6SDag-Erling Smørgrav debug("Local forwarding listening on path %s.", fwd->listen_path); 3536a0ee8cc6SDag-Erling Smørgrav 3537a0ee8cc6SDag-Erling Smørgrav /* Allocate a channel number for the socket. */ 35384f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "unix listener", type, sock, sock, -1, 3539a0ee8cc6SDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 3540a0ee8cc6SDag-Erling Smørgrav 0, "unix listener", 1); 3541a0ee8cc6SDag-Erling Smørgrav c->path = xstrdup(path); 3542a0ee8cc6SDag-Erling Smørgrav c->host_port = port; 3543a0ee8cc6SDag-Erling Smørgrav c->listening_port = PORT_STREAMLOCAL; 3544a0ee8cc6SDag-Erling Smørgrav c->listening_addr = xstrdup(fwd->listen_path); 3545a0ee8cc6SDag-Erling Smørgrav return 1; 3546a0ee8cc6SDag-Erling Smørgrav } 3547a0ee8cc6SDag-Erling Smørgrav 3548a0ee8cc6SDag-Erling Smørgrav static int 35494f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh, 35504f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 355121e764dfSDag-Erling Smørgrav { 355221e764dfSDag-Erling Smørgrav u_int i; 355321e764dfSDag-Erling Smørgrav int found = 0; 355421e764dfSDag-Erling Smørgrav 35554f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 35564f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3557462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) 3558462c32cbSDag-Erling Smørgrav continue; 3559462c32cbSDag-Erling Smørgrav if (strcmp(c->path, host) == 0 && c->listening_port == port) { 3560462c32cbSDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 35614f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3562462c32cbSDag-Erling Smørgrav found = 1; 3563462c32cbSDag-Erling Smørgrav } 3564462c32cbSDag-Erling Smørgrav } 356521e764dfSDag-Erling Smørgrav 35664f52dfbbSDag-Erling Smørgrav return found; 3567462c32cbSDag-Erling Smørgrav } 3568462c32cbSDag-Erling Smørgrav 3569a0ee8cc6SDag-Erling Smørgrav static int 35704f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) 3571462c32cbSDag-Erling Smørgrav { 3572462c32cbSDag-Erling Smørgrav u_int i; 3573462c32cbSDag-Erling Smørgrav int found = 0; 3574a0ee8cc6SDag-Erling Smørgrav 35754f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 35764f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3577a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) 3578a0ee8cc6SDag-Erling Smørgrav continue; 3579a0ee8cc6SDag-Erling Smørgrav if (c->path == NULL) 3580a0ee8cc6SDag-Erling Smørgrav continue; 3581a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->path, path) == 0) { 3582a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 35834f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3584a0ee8cc6SDag-Erling Smørgrav found = 1; 3585a0ee8cc6SDag-Erling Smørgrav } 3586a0ee8cc6SDag-Erling Smørgrav } 3587a0ee8cc6SDag-Erling Smørgrav 35884f52dfbbSDag-Erling Smørgrav return found; 3589a0ee8cc6SDag-Erling Smørgrav } 3590a0ee8cc6SDag-Erling Smørgrav 3591a0ee8cc6SDag-Erling Smørgrav int 35924f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) 3593a0ee8cc6SDag-Erling Smørgrav { 35944f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 35954f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_streamlocal(ssh, 35964f52dfbbSDag-Erling Smørgrav fwd->listen_path); 35974f52dfbbSDag-Erling Smørgrav } else { 35984f52dfbbSDag-Erling Smørgrav return channel_cancel_rport_listener_tcpip(ssh, 35994f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port); 36004f52dfbbSDag-Erling Smørgrav } 3601a0ee8cc6SDag-Erling Smørgrav } 3602a0ee8cc6SDag-Erling Smørgrav 3603a0ee8cc6SDag-Erling Smørgrav static int 36044f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh, 36054f52dfbbSDag-Erling Smørgrav const char *lhost, u_short lport, int cport, 36064f52dfbbSDag-Erling Smørgrav struct ForwardOptions *fwd_opts) 3607a0ee8cc6SDag-Erling Smørgrav { 3608a0ee8cc6SDag-Erling Smørgrav u_int i; 3609a0ee8cc6SDag-Erling Smørgrav int found = 0; 3610a0ee8cc6SDag-Erling Smørgrav const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); 3611462c32cbSDag-Erling Smørgrav 36124f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 36134f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3614462c32cbSDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) 3615462c32cbSDag-Erling Smørgrav continue; 3616462c32cbSDag-Erling Smørgrav if (c->listening_port != lport) 3617462c32cbSDag-Erling Smørgrav continue; 3618462c32cbSDag-Erling Smørgrav if (cport == CHANNEL_CANCEL_PORT_STATIC) { 3619462c32cbSDag-Erling Smørgrav /* skip dynamic forwardings */ 3620462c32cbSDag-Erling Smørgrav if (c->host_port == 0) 3621462c32cbSDag-Erling Smørgrav continue; 3622462c32cbSDag-Erling Smørgrav } else { 3623462c32cbSDag-Erling Smørgrav if (c->host_port != cport) 3624462c32cbSDag-Erling Smørgrav continue; 3625462c32cbSDag-Erling Smørgrav } 3626462c32cbSDag-Erling Smørgrav if ((c->listening_addr == NULL && addr != NULL) || 3627462c32cbSDag-Erling Smørgrav (c->listening_addr != NULL && addr == NULL)) 3628462c32cbSDag-Erling Smørgrav continue; 3629462c32cbSDag-Erling Smørgrav if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { 3630aa49c926SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 36314f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 363221e764dfSDag-Erling Smørgrav found = 1; 363321e764dfSDag-Erling Smørgrav } 363421e764dfSDag-Erling Smørgrav } 363521e764dfSDag-Erling Smørgrav 36364f52dfbbSDag-Erling Smørgrav return found; 363721e764dfSDag-Erling Smørgrav } 363821e764dfSDag-Erling Smørgrav 3639a0ee8cc6SDag-Erling Smørgrav static int 36404f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) 3641a0ee8cc6SDag-Erling Smørgrav { 3642a0ee8cc6SDag-Erling Smørgrav u_int i; 3643a0ee8cc6SDag-Erling Smørgrav int found = 0; 3644a0ee8cc6SDag-Erling Smørgrav 3645a0ee8cc6SDag-Erling Smørgrav if (path == NULL) { 3646a0ee8cc6SDag-Erling Smørgrav error("%s: no path specified.", __func__); 3647a0ee8cc6SDag-Erling Smørgrav return 0; 3648a0ee8cc6SDag-Erling Smørgrav } 3649a0ee8cc6SDag-Erling Smørgrav 36504f52dfbbSDag-Erling Smørgrav for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { 36514f52dfbbSDag-Erling Smørgrav Channel *c = ssh->chanctxt->channels[i]; 3652a0ee8cc6SDag-Erling Smørgrav if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) 3653a0ee8cc6SDag-Erling Smørgrav continue; 3654a0ee8cc6SDag-Erling Smørgrav if (c->listening_addr == NULL) 3655a0ee8cc6SDag-Erling Smørgrav continue; 3656a0ee8cc6SDag-Erling Smørgrav if (strcmp(c->listening_addr, path) == 0) { 3657a0ee8cc6SDag-Erling Smørgrav debug2("%s: close channel %d", __func__, i); 36584f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 3659a0ee8cc6SDag-Erling Smørgrav found = 1; 3660a0ee8cc6SDag-Erling Smørgrav } 3661a0ee8cc6SDag-Erling Smørgrav } 3662a0ee8cc6SDag-Erling Smørgrav 36634f52dfbbSDag-Erling Smørgrav return found; 3664a0ee8cc6SDag-Erling Smørgrav } 3665a0ee8cc6SDag-Erling Smørgrav 3666a0ee8cc6SDag-Erling Smørgrav int 36674f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh, 36684f52dfbbSDag-Erling Smørgrav struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) 3669af12a3e7SDag-Erling Smørgrav { 3670a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 36714f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_streamlocal(ssh, 36724f52dfbbSDag-Erling Smørgrav fwd->listen_path); 36734f52dfbbSDag-Erling Smørgrav } else { 36744f52dfbbSDag-Erling Smørgrav return channel_cancel_lport_listener_tcpip(ssh, 36754f52dfbbSDag-Erling Smørgrav fwd->listen_host, fwd->listen_port, cport, fwd_opts); 36764f52dfbbSDag-Erling Smørgrav } 36774f52dfbbSDag-Erling Smørgrav } 36784f52dfbbSDag-Erling Smørgrav 36794f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */ 36804f52dfbbSDag-Erling Smørgrav int 36814f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh, 36824f52dfbbSDag-Erling Smørgrav struct Forward *fwd, struct ForwardOptions *fwd_opts) 36834f52dfbbSDag-Erling Smørgrav { 36844f52dfbbSDag-Erling Smørgrav if (fwd->listen_path != NULL) { 36854f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 3686a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); 3687a0ee8cc6SDag-Erling Smørgrav } else { 36884f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 36894f52dfbbSDag-Erling Smørgrav SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); 3690a0ee8cc6SDag-Erling Smørgrav } 3691af12a3e7SDag-Erling Smørgrav } 3692af12a3e7SDag-Erling Smørgrav 3693*190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */ 3694*190cef3dSDag-Erling Smørgrav static int 3695*190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd) 3696*190cef3dSDag-Erling Smørgrav { 3697*190cef3dSDag-Erling Smørgrav int ret; 3698*190cef3dSDag-Erling Smørgrav char *lhost; 3699*190cef3dSDag-Erling Smørgrav 3700*190cef3dSDag-Erling Smørgrav /* XXX add ACLs for streamlocal */ 3701*190cef3dSDag-Erling Smørgrav if (fwd->listen_path != NULL) 3702*190cef3dSDag-Erling Smørgrav return 1; 3703*190cef3dSDag-Erling Smørgrav 3704*190cef3dSDag-Erling Smørgrav if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) 3705*190cef3dSDag-Erling Smørgrav return 0; 3706*190cef3dSDag-Erling Smørgrav 3707*190cef3dSDag-Erling Smørgrav if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && 3708*190cef3dSDag-Erling Smørgrav allowed_open->listen_port != fwd->listen_port) 3709*190cef3dSDag-Erling Smørgrav return 0; 3710*190cef3dSDag-Erling Smørgrav 3711*190cef3dSDag-Erling Smørgrav /* Match hostnames case-insensitively */ 3712*190cef3dSDag-Erling Smørgrav lhost = xstrdup(fwd->listen_host); 3713*190cef3dSDag-Erling Smørgrav lowercase(lhost); 3714*190cef3dSDag-Erling Smørgrav ret = match_pattern(lhost, allowed_open->listen_host); 3715*190cef3dSDag-Erling Smørgrav free(lhost); 3716*190cef3dSDag-Erling Smørgrav 3717*190cef3dSDag-Erling Smørgrav return ret; 3718*190cef3dSDag-Erling Smørgrav } 3719*190cef3dSDag-Erling Smørgrav 3720*190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */ 3721*190cef3dSDag-Erling Smørgrav static int 3722*190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) 3723*190cef3dSDag-Erling Smørgrav { 3724*190cef3dSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3725*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->remote_perms; 3726*190cef3dSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 3727*190cef3dSDag-Erling Smørgrav struct permission *perm; 3728*190cef3dSDag-Erling Smørgrav 3729*190cef3dSDag-Erling Smørgrav /* XXX apply GatewayPorts override before checking? */ 3730*190cef3dSDag-Erling Smørgrav 3731*190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 3732*190cef3dSDag-Erling Smørgrav if (!permit) { 3733*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 3734*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 3735*190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 3736*190cef3dSDag-Erling Smørgrav permit = 1; 3737*190cef3dSDag-Erling Smørgrav break; 3738*190cef3dSDag-Erling Smørgrav } 3739*190cef3dSDag-Erling Smørgrav } 3740*190cef3dSDag-Erling Smørgrav } 3741*190cef3dSDag-Erling Smørgrav 3742*190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 3743*190cef3dSDag-Erling Smørgrav permit_adm = 0; 3744*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 3745*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 3746*190cef3dSDag-Erling Smørgrav if (remote_open_match(perm, fwd)) { 3747*190cef3dSDag-Erling Smørgrav permit_adm = 1; 3748*190cef3dSDag-Erling Smørgrav break; 3749*190cef3dSDag-Erling Smørgrav } 3750*190cef3dSDag-Erling Smørgrav } 3751*190cef3dSDag-Erling Smørgrav } 3752*190cef3dSDag-Erling Smørgrav 3753*190cef3dSDag-Erling Smørgrav return permit && permit_adm; 3754*190cef3dSDag-Erling Smørgrav } 3755*190cef3dSDag-Erling Smørgrav 3756af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */ 3757af12a3e7SDag-Erling Smørgrav int 37584f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, 3759a0ee8cc6SDag-Erling Smørgrav int *allocated_listen_port, struct ForwardOptions *fwd_opts) 3760af12a3e7SDag-Erling Smørgrav { 3761*190cef3dSDag-Erling Smørgrav if (!check_rfwd_permission(ssh, fwd)) { 3762*190cef3dSDag-Erling Smørgrav packet_send_debug("port forwarding refused"); 3763*190cef3dSDag-Erling Smørgrav return 0; 3764*190cef3dSDag-Erling Smørgrav } 3765a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 37664f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_streamlocal(ssh, 3767a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); 3768a0ee8cc6SDag-Erling Smørgrav } else { 37694f52dfbbSDag-Erling Smørgrav return channel_setup_fwd_listener_tcpip(ssh, 3770a0ee8cc6SDag-Erling Smørgrav SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, 3771a0ee8cc6SDag-Erling Smørgrav fwd_opts); 3772a0ee8cc6SDag-Erling Smørgrav } 3773af12a3e7SDag-Erling Smørgrav } 3774af12a3e7SDag-Erling Smørgrav 3775511b41d2SMark Murray /* 3776462c32cbSDag-Erling Smørgrav * Translate the requested rfwd listen host to something usable for 3777462c32cbSDag-Erling Smørgrav * this server. 3778462c32cbSDag-Erling Smørgrav */ 3779462c32cbSDag-Erling Smørgrav static const char * 3780462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host) 3781462c32cbSDag-Erling Smørgrav { 3782462c32cbSDag-Erling Smørgrav if (listen_host == NULL) { 3783462c32cbSDag-Erling Smørgrav return "localhost"; 3784462c32cbSDag-Erling Smørgrav } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { 3785462c32cbSDag-Erling Smørgrav return ""; 3786462c32cbSDag-Erling Smørgrav } else 3787462c32cbSDag-Erling Smørgrav return listen_host; 3788462c32cbSDag-Erling Smørgrav } 3789462c32cbSDag-Erling Smørgrav 3790462c32cbSDag-Erling Smørgrav /* 3791511b41d2SMark Murray * Initiate forwarding of connections to port "port" on remote host through 3792511b41d2SMark Murray * the secure channel to host:port from local side. 3793462c32cbSDag-Erling Smørgrav * Returns handle (index) for updating the dynamic listen port with 3794*190cef3dSDag-Erling Smørgrav * channel_update_permission(). 3795511b41d2SMark Murray */ 3796333ee039SDag-Erling Smørgrav int 37974f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) 3798511b41d2SMark Murray { 37994f52dfbbSDag-Erling Smørgrav int r, success = 0, idx = -1; 38004f52dfbbSDag-Erling Smørgrav char *host_to_connect, *listen_host, *listen_path; 38014f52dfbbSDag-Erling Smørgrav int port_to_connect, listen_port; 3802ca3176e7SBrian Feldman 3803511b41d2SMark Murray /* Send the forward request to the remote side. */ 3804a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 38054f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 38064f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 38074f52dfbbSDag-Erling Smørgrav "streamlocal-forward@openssh.com")) != 0 || 38084f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 38094f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || 38104f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 38114f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 38124f52dfbbSDag-Erling Smørgrav fatal("%s: request streamlocal: %s", 38134f52dfbbSDag-Erling Smørgrav __func__, ssh_err(r)); 3814a0ee8cc6SDag-Erling Smørgrav } else { 38154f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 38164f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || 38174f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ 38184f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 38194f52dfbbSDag-Erling Smørgrav channel_rfwd_bind_host(fwd->listen_host))) != 0 || 38204f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || 38214f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 38224f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 38234f52dfbbSDag-Erling Smørgrav fatal("%s: request tcpip-forward: %s", 38244f52dfbbSDag-Erling Smørgrav __func__, ssh_err(r)); 3825a0ee8cc6SDag-Erling Smørgrav } 3826ca3176e7SBrian Feldman /* Assume that server accepts the request */ 3827ca3176e7SBrian Feldman success = 1; 3828ca3176e7SBrian Feldman if (success) { 3829e2f6069cSDag-Erling Smørgrav /* Record that connection to this host/port is permitted. */ 38304f52dfbbSDag-Erling Smørgrav host_to_connect = listen_host = listen_path = NULL; 38314f52dfbbSDag-Erling Smørgrav port_to_connect = listen_port = 0; 3832a0ee8cc6SDag-Erling Smørgrav if (fwd->connect_path != NULL) { 38334f52dfbbSDag-Erling Smørgrav host_to_connect = xstrdup(fwd->connect_path); 38344f52dfbbSDag-Erling Smørgrav port_to_connect = PORT_STREAMLOCAL; 3835a0ee8cc6SDag-Erling Smørgrav } else { 38364f52dfbbSDag-Erling Smørgrav host_to_connect = xstrdup(fwd->connect_host); 38374f52dfbbSDag-Erling Smørgrav port_to_connect = fwd->connect_port; 3838a0ee8cc6SDag-Erling Smørgrav } 3839a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 38404f52dfbbSDag-Erling Smørgrav listen_path = xstrdup(fwd->listen_path); 38414f52dfbbSDag-Erling Smørgrav listen_port = PORT_STREAMLOCAL; 3842a0ee8cc6SDag-Erling Smørgrav } else { 38434f52dfbbSDag-Erling Smørgrav if (fwd->listen_host != NULL) 38444f52dfbbSDag-Erling Smørgrav listen_host = xstrdup(fwd->listen_host); 38454f52dfbbSDag-Erling Smørgrav listen_port = fwd->listen_port; 3846a0ee8cc6SDag-Erling Smørgrav } 3847*190cef3dSDag-Erling Smørgrav idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, 38484f52dfbbSDag-Erling Smørgrav host_to_connect, port_to_connect, 38494f52dfbbSDag-Erling Smørgrav listen_host, listen_path, listen_port, NULL); 3850511b41d2SMark Murray } 38514f52dfbbSDag-Erling Smørgrav return idx; 3852a04a10f8SKris Kennaway } 3853511b41d2SMark Murray 3854a0ee8cc6SDag-Erling Smørgrav static int 3855*190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost, 3856a0ee8cc6SDag-Erling Smørgrav int requestedport) 3857a0ee8cc6SDag-Erling Smørgrav { 3858a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3859a0ee8cc6SDag-Erling Smørgrav return 0; 3860a0ee8cc6SDag-Erling Smørgrav if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && 3861a0ee8cc6SDag-Erling Smørgrav allowed_open->port_to_connect != requestedport) 3862a0ee8cc6SDag-Erling Smørgrav return 0; 3863076ad2f8SDag-Erling Smørgrav if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && 3864076ad2f8SDag-Erling Smørgrav strcmp(allowed_open->host_to_connect, requestedhost) != 0) 3865a0ee8cc6SDag-Erling Smørgrav return 0; 3866a0ee8cc6SDag-Erling Smørgrav return 1; 3867a0ee8cc6SDag-Erling Smørgrav } 3868a0ee8cc6SDag-Erling Smørgrav 3869a0ee8cc6SDag-Erling Smørgrav /* 3870a0ee8cc6SDag-Erling Smørgrav * Note that in the listen host/port case 3871a0ee8cc6SDag-Erling Smørgrav * we don't support FWD_PERMIT_ANY_PORT and 3872a0ee8cc6SDag-Erling Smørgrav * need to translate between the configured-host (listen_host) 3873a0ee8cc6SDag-Erling Smørgrav * and what we've sent to the remote server (channel_rfwd_bind_host) 3874a0ee8cc6SDag-Erling Smørgrav */ 3875a0ee8cc6SDag-Erling Smørgrav static int 3876*190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open, 3877a0ee8cc6SDag-Erling Smørgrav const char *requestedhost, u_short requestedport, int translate) 3878a0ee8cc6SDag-Erling Smørgrav { 3879a0ee8cc6SDag-Erling Smørgrav const char *allowed_host; 3880a0ee8cc6SDag-Erling Smørgrav 3881a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3882a0ee8cc6SDag-Erling Smørgrav return 0; 3883a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != requestedport) 3884a0ee8cc6SDag-Erling Smørgrav return 0; 3885a0ee8cc6SDag-Erling Smørgrav if (!translate && allowed_open->listen_host == NULL && 3886a0ee8cc6SDag-Erling Smørgrav requestedhost == NULL) 3887a0ee8cc6SDag-Erling Smørgrav return 1; 3888a0ee8cc6SDag-Erling Smørgrav allowed_host = translate ? 3889a0ee8cc6SDag-Erling Smørgrav channel_rfwd_bind_host(allowed_open->listen_host) : 3890a0ee8cc6SDag-Erling Smørgrav allowed_open->listen_host; 3891*190cef3dSDag-Erling Smørgrav if (allowed_host == NULL || requestedhost == NULL || 3892a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_host, requestedhost) != 0) 3893a0ee8cc6SDag-Erling Smørgrav return 0; 3894a0ee8cc6SDag-Erling Smørgrav return 1; 3895a0ee8cc6SDag-Erling Smørgrav } 3896a0ee8cc6SDag-Erling Smørgrav 3897a0ee8cc6SDag-Erling Smørgrav static int 3898*190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open, 3899a0ee8cc6SDag-Erling Smørgrav const char *requestedpath) 3900a0ee8cc6SDag-Erling Smørgrav { 3901a0ee8cc6SDag-Erling Smørgrav if (allowed_open->host_to_connect == NULL) 3902a0ee8cc6SDag-Erling Smørgrav return 0; 3903a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_port != PORT_STREAMLOCAL) 3904a0ee8cc6SDag-Erling Smørgrav return 0; 3905a0ee8cc6SDag-Erling Smørgrav if (allowed_open->listen_path == NULL || 3906a0ee8cc6SDag-Erling Smørgrav strcmp(allowed_open->listen_path, requestedpath) != 0) 3907a0ee8cc6SDag-Erling Smørgrav return 0; 3908a0ee8cc6SDag-Erling Smørgrav return 1; 3909a0ee8cc6SDag-Erling Smørgrav } 3910a0ee8cc6SDag-Erling Smørgrav 3911511b41d2SMark Murray /* 391221e764dfSDag-Erling Smørgrav * Request cancellation of remote forwarding of connection host:port from 391321e764dfSDag-Erling Smørgrav * local side. 391421e764dfSDag-Erling Smørgrav */ 3915a0ee8cc6SDag-Erling Smørgrav static int 39164f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh, 39174f52dfbbSDag-Erling Smørgrav const char *host, u_short port) 391821e764dfSDag-Erling Smørgrav { 39194f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3920*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 39214f52dfbbSDag-Erling Smørgrav int r; 39224f52dfbbSDag-Erling Smørgrav u_int i; 3923*190cef3dSDag-Erling Smørgrav struct permission *perm; 392421e764dfSDag-Erling Smørgrav 3925*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 3926*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 3927*190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, host, port, 0)) 392821e764dfSDag-Erling Smørgrav break; 3929*190cef3dSDag-Erling Smørgrav perm = NULL; 393021e764dfSDag-Erling Smørgrav } 3931*190cef3dSDag-Erling Smørgrav if (perm == NULL) { 393221e764dfSDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3933462c32cbSDag-Erling Smørgrav return -1; 393421e764dfSDag-Erling Smørgrav } 39354f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 39364f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || 39374f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 39384f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || 39394f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, port)) != 0 || 39404f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 39414f52dfbbSDag-Erling Smørgrav fatal("%s: send cancel: %s", __func__, ssh_err(r)); 394221e764dfSDag-Erling Smørgrav 3943*190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 3944462c32cbSDag-Erling Smørgrav 3945462c32cbSDag-Erling Smørgrav return 0; 394621e764dfSDag-Erling Smørgrav } 394721e764dfSDag-Erling Smørgrav 394821e764dfSDag-Erling Smørgrav /* 3949a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of Unix domain socket 3950a0ee8cc6SDag-Erling Smørgrav * path from local side. 3951a0ee8cc6SDag-Erling Smørgrav */ 3952a0ee8cc6SDag-Erling Smørgrav static int 39534f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) 3954a0ee8cc6SDag-Erling Smørgrav { 39554f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 3956*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 39574f52dfbbSDag-Erling Smørgrav int r; 39584f52dfbbSDag-Erling Smørgrav u_int i; 3959*190cef3dSDag-Erling Smørgrav struct permission *perm; 3960a0ee8cc6SDag-Erling Smørgrav 3961*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 3962*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 3963*190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) 3964a0ee8cc6SDag-Erling Smørgrav break; 3965*190cef3dSDag-Erling Smørgrav perm = NULL; 3966a0ee8cc6SDag-Erling Smørgrav } 3967*190cef3dSDag-Erling Smørgrav if (perm == NULL) { 3968a0ee8cc6SDag-Erling Smørgrav debug("%s: requested forward not found", __func__); 3969a0ee8cc6SDag-Erling Smørgrav return -1; 3970a0ee8cc6SDag-Erling Smørgrav } 39714f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 39724f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 39734f52dfbbSDag-Erling Smørgrav "cancel-streamlocal-forward@openssh.com")) != 0 || 39744f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ 39754f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, path)) != 0 || 39764f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 39774f52dfbbSDag-Erling Smørgrav fatal("%s: send cancel: %s", __func__, ssh_err(r)); 3978a0ee8cc6SDag-Erling Smørgrav 3979*190cef3dSDag-Erling Smørgrav fwd_perm_clear(perm); /* unregister */ 3980a0ee8cc6SDag-Erling Smørgrav 3981a0ee8cc6SDag-Erling Smørgrav return 0; 3982a0ee8cc6SDag-Erling Smørgrav } 3983a0ee8cc6SDag-Erling Smørgrav 3984a0ee8cc6SDag-Erling Smørgrav /* 3985a0ee8cc6SDag-Erling Smørgrav * Request cancellation of remote forwarding of a connection from local side. 3986a0ee8cc6SDag-Erling Smørgrav */ 3987a0ee8cc6SDag-Erling Smørgrav int 39884f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) 3989a0ee8cc6SDag-Erling Smørgrav { 3990a0ee8cc6SDag-Erling Smørgrav if (fwd->listen_path != NULL) { 39914f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_streamlocal(ssh, 39924f52dfbbSDag-Erling Smørgrav fwd->listen_path); 3993a0ee8cc6SDag-Erling Smørgrav } else { 39944f52dfbbSDag-Erling Smørgrav return channel_request_rforward_cancel_tcpip(ssh, 39954f52dfbbSDag-Erling Smørgrav fwd->listen_host, 39964f52dfbbSDag-Erling Smørgrav fwd->listen_port ? fwd->listen_port : fwd->allocated_port); 3997a0ee8cc6SDag-Erling Smørgrav } 3998a0ee8cc6SDag-Erling Smørgrav } 3999a0ee8cc6SDag-Erling Smørgrav 4000a0ee8cc6SDag-Erling Smørgrav /* 4001*190cef3dSDag-Erling Smørgrav * Permits opening to any host/port if permitted_user[] is empty. This is 4002ca3176e7SBrian Feldman * usually called by the server, because the user could connect to any port 4003ca3176e7SBrian Feldman * anyway, and the server has no way to know but to trust the client anyway. 4004ca3176e7SBrian Feldman */ 4005ca3176e7SBrian Feldman void 4006*190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where) 4007ca3176e7SBrian Feldman { 4008*190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 4009*190cef3dSDag-Erling Smørgrav 4010*190cef3dSDag-Erling Smørgrav if (pset->num_permitted_user == 0) 4011*190cef3dSDag-Erling Smørgrav pset->all_permitted = 1; 4012ca3176e7SBrian Feldman } 4013ca3176e7SBrian Feldman 4014*190cef3dSDag-Erling Smørgrav /* 4015*190cef3dSDag-Erling Smørgrav * Permit the specified host/port for forwarding. 4016*190cef3dSDag-Erling Smørgrav */ 4017ca3176e7SBrian Feldman void 4018*190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where, 4019*190cef3dSDag-Erling Smørgrav char *host, int port) 4020ca3176e7SBrian Feldman { 4021*190cef3dSDag-Erling Smørgrav int local = where == FORWARD_LOCAL; 4022*190cef3dSDag-Erling Smørgrav struct permission_set *pset = permission_set_get(ssh, where); 40234f52dfbbSDag-Erling Smørgrav 4024*190cef3dSDag-Erling Smørgrav debug("allow %s forwarding to host %s port %d", 4025*190cef3dSDag-Erling Smørgrav fwd_ident(who, where), host, port); 4026*190cef3dSDag-Erling Smørgrav /* 4027*190cef3dSDag-Erling Smørgrav * Remote forwards set listen_host/port, local forwards set 4028*190cef3dSDag-Erling Smørgrav * host/port_to_connect. 4029*190cef3dSDag-Erling Smørgrav */ 4030*190cef3dSDag-Erling Smørgrav permission_set_add(ssh, who, where, 4031*190cef3dSDag-Erling Smørgrav local ? host : 0, local ? port : 0, 4032*190cef3dSDag-Erling Smørgrav local ? NULL : host, NULL, local ? 0 : port, NULL); 4033*190cef3dSDag-Erling Smørgrav pset->all_permitted = 0; 4034*190cef3dSDag-Erling Smørgrav } 4035*190cef3dSDag-Erling Smørgrav 4036*190cef3dSDag-Erling Smørgrav /* 4037*190cef3dSDag-Erling Smørgrav * Administratively disable forwarding. 4038*190cef3dSDag-Erling Smørgrav */ 4039*190cef3dSDag-Erling Smørgrav void 4040*190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where) 4041*190cef3dSDag-Erling Smørgrav { 4042*190cef3dSDag-Erling Smørgrav channel_clear_permission(ssh, FORWARD_ADM, where); 4043*190cef3dSDag-Erling Smørgrav permission_set_add(ssh, FORWARD_ADM, where, 4044*190cef3dSDag-Erling Smørgrav NULL, 0, NULL, NULL, 0, NULL); 4045*190cef3dSDag-Erling Smørgrav } 4046*190cef3dSDag-Erling Smørgrav 4047*190cef3dSDag-Erling Smørgrav /* 4048*190cef3dSDag-Erling Smørgrav * Clear a list of permitted opens. 4049*190cef3dSDag-Erling Smørgrav */ 4050*190cef3dSDag-Erling Smørgrav void 4051*190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where) 4052*190cef3dSDag-Erling Smørgrav { 4053*190cef3dSDag-Erling Smørgrav struct permission **permp; 4054*190cef3dSDag-Erling Smørgrav u_int *npermp; 4055*190cef3dSDag-Erling Smørgrav 4056*190cef3dSDag-Erling Smørgrav permission_set_get_array(ssh, who, where, &permp, &npermp); 4057*190cef3dSDag-Erling Smørgrav *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); 4058*190cef3dSDag-Erling Smørgrav *npermp = 0; 4059ca3176e7SBrian Feldman } 4060ca3176e7SBrian Feldman 4061462c32cbSDag-Erling Smørgrav /* 4062462c32cbSDag-Erling Smørgrav * Update the listen port for a dynamic remote forward, after 4063462c32cbSDag-Erling Smørgrav * the actual 'newport' has been allocated. If 'newport' < 0 is 4064462c32cbSDag-Erling Smørgrav * passed then they entry will be invalidated. 4065462c32cbSDag-Erling Smørgrav */ 4066462c32cbSDag-Erling Smørgrav void 4067*190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport) 4068462c32cbSDag-Erling Smørgrav { 4069*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &ssh->chanctxt->local_perms; 40704f52dfbbSDag-Erling Smørgrav 4071*190cef3dSDag-Erling Smørgrav if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { 4072*190cef3dSDag-Erling Smørgrav debug("%s: index out of range: %d num_permitted_user %d", 4073*190cef3dSDag-Erling Smørgrav __func__, idx, pset->num_permitted_user); 4074462c32cbSDag-Erling Smørgrav return; 4075462c32cbSDag-Erling Smørgrav } 4076462c32cbSDag-Erling Smørgrav debug("%s allowed port %d for forwarding to host %s port %d", 4077462c32cbSDag-Erling Smørgrav newport > 0 ? "Updating" : "Removing", 4078462c32cbSDag-Erling Smørgrav newport, 4079*190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].host_to_connect, 4080*190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].port_to_connect); 40814f52dfbbSDag-Erling Smørgrav if (newport <= 0) 4082*190cef3dSDag-Erling Smørgrav fwd_perm_clear(&pset->permitted_user[idx]); 40834f52dfbbSDag-Erling Smørgrav else { 4084*190cef3dSDag-Erling Smørgrav pset->permitted_user[idx].listen_port = 4085462c32cbSDag-Erling Smørgrav (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; 4086462c32cbSDag-Erling Smørgrav } 4087462c32cbSDag-Erling Smørgrav } 4088462c32cbSDag-Erling Smørgrav 4089462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ 4090462c32cbSDag-Erling Smørgrav int 4091462c32cbSDag-Erling Smørgrav permitopen_port(const char *p) 4092462c32cbSDag-Erling Smørgrav { 4093462c32cbSDag-Erling Smørgrav int port; 4094462c32cbSDag-Erling Smørgrav 4095462c32cbSDag-Erling Smørgrav if (strcmp(p, "*") == 0) 4096462c32cbSDag-Erling Smørgrav return FWD_PERMIT_ANY_PORT; 4097462c32cbSDag-Erling Smørgrav if ((port = a2port(p)) > 0) 4098462c32cbSDag-Erling Smørgrav return port; 4099462c32cbSDag-Erling Smørgrav return -1; 4100462c32cbSDag-Erling Smørgrav } 4101462c32cbSDag-Erling Smørgrav 4102d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */ 4103d4af9e69SDag-Erling Smørgrav static int 4104d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx) 4105d4af9e69SDag-Erling Smørgrav { 4106d4af9e69SDag-Erling Smørgrav int sock, saved_errno; 4107a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 41084f52dfbbSDag-Erling Smørgrav char ntop[NI_MAXHOST]; 41094f52dfbbSDag-Erling Smørgrav char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; 4110d4af9e69SDag-Erling Smørgrav 4111d4af9e69SDag-Erling Smørgrav for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { 4112a0ee8cc6SDag-Erling Smørgrav switch (cctx->ai->ai_family) { 4113a0ee8cc6SDag-Erling Smørgrav case AF_UNIX: 4114a0ee8cc6SDag-Erling Smørgrav /* unix:pathname instead of host:port */ 4115a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; 4116a0ee8cc6SDag-Erling Smørgrav strlcpy(ntop, "unix", sizeof(ntop)); 4117a0ee8cc6SDag-Erling Smørgrav strlcpy(strport, sunaddr->sun_path, sizeof(strport)); 4118a0ee8cc6SDag-Erling Smørgrav break; 4119a0ee8cc6SDag-Erling Smørgrav case AF_INET: 4120a0ee8cc6SDag-Erling Smørgrav case AF_INET6: 4121d4af9e69SDag-Erling Smørgrav if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, 4122d4af9e69SDag-Erling Smørgrav ntop, sizeof(ntop), strport, sizeof(strport), 4123d4af9e69SDag-Erling Smørgrav NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 4124d4af9e69SDag-Erling Smørgrav error("connect_next: getnameinfo failed"); 4125511b41d2SMark Murray continue; 4126511b41d2SMark Murray } 4127a0ee8cc6SDag-Erling Smørgrav break; 4128a0ee8cc6SDag-Erling Smørgrav default: 4129a0ee8cc6SDag-Erling Smørgrav continue; 4130a0ee8cc6SDag-Erling Smørgrav } 4131d4af9e69SDag-Erling Smørgrav if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, 4132d4af9e69SDag-Erling Smørgrav cctx->ai->ai_protocol)) == -1) { 4133d4af9e69SDag-Erling Smørgrav if (cctx->ai->ai_next == NULL) 4134511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4135e73e9afaSDag-Erling Smørgrav else 4136e73e9afaSDag-Erling Smørgrav verbose("socket: %.100s", strerror(errno)); 4137511b41d2SMark Murray continue; 4138511b41d2SMark Murray } 413921e764dfSDag-Erling Smørgrav if (set_nonblock(sock) == -1) 414021e764dfSDag-Erling Smørgrav fatal("%s: set_nonblock(%d)", __func__, sock); 4141d4af9e69SDag-Erling Smørgrav if (connect(sock, cctx->ai->ai_addr, 4142d4af9e69SDag-Erling Smørgrav cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { 4143d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s): " 4144d4af9e69SDag-Erling Smørgrav "%.100s", cctx->host, ntop, strport, 4145511b41d2SMark Murray strerror(errno)); 4146d4af9e69SDag-Erling Smørgrav saved_errno = errno; 4147511b41d2SMark Murray close(sock); 4148d4af9e69SDag-Erling Smørgrav errno = saved_errno; 4149511b41d2SMark Murray continue; /* fail -- try next */ 4150511b41d2SMark Murray } 4151a0ee8cc6SDag-Erling Smørgrav if (cctx->ai->ai_family != AF_UNIX) 4152a0ee8cc6SDag-Erling Smørgrav set_nodelay(sock); 4153d4af9e69SDag-Erling Smørgrav debug("connect_next: host %.100s ([%.100s]:%s) " 4154d4af9e69SDag-Erling Smørgrav "in progress, fd=%d", cctx->host, ntop, strport, sock); 4155d4af9e69SDag-Erling Smørgrav cctx->ai = cctx->ai->ai_next; 4156a04a10f8SKris Kennaway return sock; 4157a04a10f8SKris Kennaway } 4158ca3176e7SBrian Feldman return -1; 4159ca3176e7SBrian Feldman } 4160ca3176e7SBrian Feldman 4161d4af9e69SDag-Erling Smørgrav static void 4162d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx) 4163d4af9e69SDag-Erling Smørgrav { 4164e4a9863fSDag-Erling Smørgrav free(cctx->host); 4165a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop) { 4166a0ee8cc6SDag-Erling Smørgrav if (cctx->aitop->ai_family == AF_UNIX) 4167a0ee8cc6SDag-Erling Smørgrav free(cctx->aitop); 4168a0ee8cc6SDag-Erling Smørgrav else 4169d4af9e69SDag-Erling Smørgrav freeaddrinfo(cctx->aitop); 4170a0ee8cc6SDag-Erling Smørgrav } 4171b83788ffSDag-Erling Smørgrav memset(cctx, 0, sizeof(*cctx)); 4172d4af9e69SDag-Erling Smørgrav } 4173d4af9e69SDag-Erling Smørgrav 4174d93a896eSDag-Erling Smørgrav /* 41754f52dfbbSDag-Erling Smørgrav * Return connecting socket to remote host:port or local socket path, 4176d93a896eSDag-Erling Smørgrav * passing back the failure reason if appropriate. 4177d93a896eSDag-Erling Smørgrav */ 41784f52dfbbSDag-Erling Smørgrav static int 41794f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, 41804f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, struct channel_connect *cctx, 4181d93a896eSDag-Erling Smørgrav int *reason, const char **errmsg) 4182d4af9e69SDag-Erling Smørgrav { 4183d4af9e69SDag-Erling Smørgrav struct addrinfo hints; 4184d4af9e69SDag-Erling Smørgrav int gaierr; 4185d4af9e69SDag-Erling Smørgrav int sock = -1; 4186d4af9e69SDag-Erling Smørgrav char strport[NI_MAXSERV]; 4187a0ee8cc6SDag-Erling Smørgrav 4188a0ee8cc6SDag-Erling Smørgrav if (port == PORT_STREAMLOCAL) { 4189a0ee8cc6SDag-Erling Smørgrav struct sockaddr_un *sunaddr; 4190a0ee8cc6SDag-Erling Smørgrav struct addrinfo *ai; 4191a0ee8cc6SDag-Erling Smørgrav 4192a0ee8cc6SDag-Erling Smørgrav if (strlen(name) > sizeof(sunaddr->sun_path)) { 4193a0ee8cc6SDag-Erling Smørgrav error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); 41944f52dfbbSDag-Erling Smørgrav return -1; 4195a0ee8cc6SDag-Erling Smørgrav } 4196a0ee8cc6SDag-Erling Smørgrav 4197a0ee8cc6SDag-Erling Smørgrav /* 4198a0ee8cc6SDag-Erling Smørgrav * Fake up a struct addrinfo for AF_UNIX connections. 4199a0ee8cc6SDag-Erling Smørgrav * channel_connect_ctx_free() must check ai_family 4200a0ee8cc6SDag-Erling Smørgrav * and use free() not freeaddirinfo() for AF_UNIX. 4201a0ee8cc6SDag-Erling Smørgrav */ 4202a0ee8cc6SDag-Erling Smørgrav ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); 4203a0ee8cc6SDag-Erling Smørgrav memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); 4204a0ee8cc6SDag-Erling Smørgrav ai->ai_addr = (struct sockaddr *)(ai + 1); 4205a0ee8cc6SDag-Erling Smørgrav ai->ai_addrlen = sizeof(*sunaddr); 4206a0ee8cc6SDag-Erling Smørgrav ai->ai_family = AF_UNIX; 42074f52dfbbSDag-Erling Smørgrav ai->ai_socktype = socktype; 4208a0ee8cc6SDag-Erling Smørgrav ai->ai_protocol = PF_UNSPEC; 4209a0ee8cc6SDag-Erling Smørgrav sunaddr = (struct sockaddr_un *)ai->ai_addr; 4210a0ee8cc6SDag-Erling Smørgrav sunaddr->sun_family = AF_UNIX; 4211a0ee8cc6SDag-Erling Smørgrav strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); 42124f52dfbbSDag-Erling Smørgrav cctx->aitop = ai; 4213a0ee8cc6SDag-Erling Smørgrav } else { 4214d4af9e69SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 42154f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 42164f52dfbbSDag-Erling Smørgrav hints.ai_socktype = socktype; 4217d4af9e69SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 42184f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) 4219d93a896eSDag-Erling Smørgrav != 0) { 4220d93a896eSDag-Erling Smørgrav if (errmsg != NULL) 4221d93a896eSDag-Erling Smørgrav *errmsg = ssh_gai_strerror(gaierr); 4222d93a896eSDag-Erling Smørgrav if (reason != NULL) 4223d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_CONNECT_FAILED; 4224a0ee8cc6SDag-Erling Smørgrav error("connect_to %.100s: unknown host (%s)", name, 4225d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 42264f52dfbbSDag-Erling Smørgrav return -1; 4227d4af9e69SDag-Erling Smørgrav } 4228a0ee8cc6SDag-Erling Smørgrav } 4229d4af9e69SDag-Erling Smørgrav 42304f52dfbbSDag-Erling Smørgrav cctx->host = xstrdup(name); 42314f52dfbbSDag-Erling Smørgrav cctx->port = port; 42324f52dfbbSDag-Erling Smørgrav cctx->ai = cctx->aitop; 4233d4af9e69SDag-Erling Smørgrav 42344f52dfbbSDag-Erling Smørgrav if ((sock = connect_next(cctx)) == -1) { 4235d4af9e69SDag-Erling Smørgrav error("connect to %.100s port %d failed: %s", 4236a0ee8cc6SDag-Erling Smørgrav name, port, strerror(errno)); 42374f52dfbbSDag-Erling Smørgrav return -1; 4238d4af9e69SDag-Erling Smørgrav } 42394f52dfbbSDag-Erling Smørgrav 42404f52dfbbSDag-Erling Smørgrav return sock; 4241d4af9e69SDag-Erling Smørgrav } 4242d4af9e69SDag-Erling Smørgrav 4243d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */ 4244d93a896eSDag-Erling Smørgrav static Channel * 42454f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port, 42464f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4247d93a896eSDag-Erling Smørgrav { 42484f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 42494f52dfbbSDag-Erling Smørgrav Channel *c; 42504f52dfbbSDag-Erling Smørgrav int sock; 42514f52dfbbSDag-Erling Smørgrav 42524f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 42534f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 42544f52dfbbSDag-Erling Smørgrav &cctx, NULL, NULL); 42554f52dfbbSDag-Erling Smørgrav if (sock == -1) { 42564f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 42574f52dfbbSDag-Erling Smørgrav return NULL; 42584f52dfbbSDag-Erling Smørgrav } 42594f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 42604f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 42614f52dfbbSDag-Erling Smørgrav c->host_port = port; 42624f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 42634f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 42644f52dfbbSDag-Erling Smørgrav 42654f52dfbbSDag-Erling Smørgrav return c; 4266d93a896eSDag-Erling Smørgrav } 4267d93a896eSDag-Erling Smørgrav 4268ca86bcf2SDag-Erling Smørgrav /* 4269ca86bcf2SDag-Erling Smørgrav * returns either the newly connected channel or the downstream channel 4270ca86bcf2SDag-Erling Smørgrav * that needs to deal with this connection. 4271ca86bcf2SDag-Erling Smørgrav */ 4272d4af9e69SDag-Erling Smørgrav Channel * 42734f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, 4274a0ee8cc6SDag-Erling Smørgrav u_short listen_port, char *ctype, char *rname) 4275d4af9e69SDag-Erling Smørgrav { 42764f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4277*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 42784f52dfbbSDag-Erling Smørgrav u_int i; 4279*190cef3dSDag-Erling Smørgrav struct permission *perm; 4280d4af9e69SDag-Erling Smørgrav 4281*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4282*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4283*190cef3dSDag-Erling Smørgrav if (open_listen_match_tcpip(perm, 4284*190cef3dSDag-Erling Smørgrav listen_host, listen_port, 1)) { 4285*190cef3dSDag-Erling Smørgrav if (perm->downstream) 4286*190cef3dSDag-Erling Smørgrav return perm->downstream; 4287*190cef3dSDag-Erling Smørgrav if (perm->port_to_connect == 0) 42884f52dfbbSDag-Erling Smørgrav return rdynamic_connect_prepare(ssh, 42894f52dfbbSDag-Erling Smørgrav ctype, rname); 42904f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4291*190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 42924f52dfbbSDag-Erling Smørgrav ctype, rname); 4293d4af9e69SDag-Erling Smørgrav } 4294d4af9e69SDag-Erling Smørgrav } 4295d4af9e69SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown listen_port %d", 4296d4af9e69SDag-Erling Smørgrav listen_port); 4297d4af9e69SDag-Erling Smørgrav return NULL; 4298d4af9e69SDag-Erling Smørgrav } 4299d4af9e69SDag-Erling Smørgrav 4300a0ee8cc6SDag-Erling Smørgrav Channel * 43014f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path, 43024f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4303a0ee8cc6SDag-Erling Smørgrav { 43044f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4305*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 43064f52dfbbSDag-Erling Smørgrav u_int i; 4307*190cef3dSDag-Erling Smørgrav struct permission *perm; 4308a0ee8cc6SDag-Erling Smørgrav 4309*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4310*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4311*190cef3dSDag-Erling Smørgrav if (open_listen_match_streamlocal(perm, path)) { 43124f52dfbbSDag-Erling Smørgrav return connect_to(ssh, 4313*190cef3dSDag-Erling Smørgrav perm->host_to_connect, perm->port_to_connect, 43144f52dfbbSDag-Erling Smørgrav ctype, rname); 4315a0ee8cc6SDag-Erling Smørgrav } 4316a0ee8cc6SDag-Erling Smørgrav } 4317a0ee8cc6SDag-Erling Smørgrav error("WARNING: Server requests forwarding for unknown path %.100s", 4318a0ee8cc6SDag-Erling Smørgrav path); 4319a0ee8cc6SDag-Erling Smørgrav return NULL; 4320a0ee8cc6SDag-Erling Smørgrav } 4321a0ee8cc6SDag-Erling Smørgrav 4322ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */ 4323d4af9e69SDag-Erling Smørgrav Channel * 43244f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, 43254f52dfbbSDag-Erling Smørgrav char *ctype, char *rname, int *reason, const char **errmsg) 4326ca3176e7SBrian Feldman { 43274f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4328*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 43294f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 43304f52dfbbSDag-Erling Smørgrav Channel *c; 43314f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 43324f52dfbbSDag-Erling Smørgrav int sock; 4333*190cef3dSDag-Erling Smørgrav struct permission *perm; 4334ca3176e7SBrian Feldman 4335*190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4336ca3176e7SBrian Feldman if (!permit) { 4337*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4338*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4339*190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4340ca3176e7SBrian Feldman permit = 1; 4341a0ee8cc6SDag-Erling Smørgrav break; 4342a0ee8cc6SDag-Erling Smørgrav } 4343ca3176e7SBrian Feldman } 43444f52dfbbSDag-Erling Smørgrav } 4345333ee039SDag-Erling Smørgrav 4346*190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4347333ee039SDag-Erling Smørgrav permit_adm = 0; 4348*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4349*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4350*190cef3dSDag-Erling Smørgrav if (open_match(perm, host, port)) { 4351333ee039SDag-Erling Smørgrav permit_adm = 1; 4352a0ee8cc6SDag-Erling Smørgrav break; 4353a0ee8cc6SDag-Erling Smørgrav } 4354333ee039SDag-Erling Smørgrav } 43554f52dfbbSDag-Erling Smørgrav } 4356333ee039SDag-Erling Smørgrav 4357333ee039SDag-Erling Smørgrav if (!permit || !permit_adm) { 4358221552e4SDag-Erling Smørgrav logit("Received request to connect to host %.100s port %d, " 4359ca3176e7SBrian Feldman "but the request was denied.", host, port); 4360d93a896eSDag-Erling Smørgrav if (reason != NULL) 4361d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; 4362d4af9e69SDag-Erling Smørgrav return NULL; 4363ca3176e7SBrian Feldman } 43644f52dfbbSDag-Erling Smørgrav 43654f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 43664f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, 43674f52dfbbSDag-Erling Smørgrav &cctx, reason, errmsg); 43684f52dfbbSDag-Erling Smørgrav if (sock == -1) { 43694f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 43704f52dfbbSDag-Erling Smørgrav return NULL; 43714f52dfbbSDag-Erling Smørgrav } 43724f52dfbbSDag-Erling Smørgrav 43734f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, 43744f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 43754f52dfbbSDag-Erling Smørgrav c->host_port = port; 43764f52dfbbSDag-Erling Smørgrav c->path = xstrdup(host); 43774f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 43784f52dfbbSDag-Erling Smørgrav 43794f52dfbbSDag-Erling Smørgrav return c; 4380ca3176e7SBrian Feldman } 4381ca3176e7SBrian Feldman 4382a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */ 4383a0ee8cc6SDag-Erling Smørgrav Channel * 43844f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path, 43854f52dfbbSDag-Erling Smørgrav char *ctype, char *rname) 4386a0ee8cc6SDag-Erling Smørgrav { 43874f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4388*190cef3dSDag-Erling Smørgrav struct permission_set *pset = &sc->local_perms; 43894f52dfbbSDag-Erling Smørgrav u_int i, permit, permit_adm = 1; 4390*190cef3dSDag-Erling Smørgrav struct permission *perm; 4391a0ee8cc6SDag-Erling Smørgrav 4392*190cef3dSDag-Erling Smørgrav permit = pset->all_permitted; 4393a0ee8cc6SDag-Erling Smørgrav if (!permit) { 4394*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_user; i++) { 4395*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_user[i]; 4396*190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4397a0ee8cc6SDag-Erling Smørgrav permit = 1; 4398a0ee8cc6SDag-Erling Smørgrav break; 4399a0ee8cc6SDag-Erling Smørgrav } 4400a0ee8cc6SDag-Erling Smørgrav } 44014f52dfbbSDag-Erling Smørgrav } 4402a0ee8cc6SDag-Erling Smørgrav 4403*190cef3dSDag-Erling Smørgrav if (pset->num_permitted_admin > 0) { 4404a0ee8cc6SDag-Erling Smørgrav permit_adm = 0; 4405*190cef3dSDag-Erling Smørgrav for (i = 0; i < pset->num_permitted_admin; i++) { 4406*190cef3dSDag-Erling Smørgrav perm = &pset->permitted_admin[i]; 4407*190cef3dSDag-Erling Smørgrav if (open_match(perm, path, PORT_STREAMLOCAL)) { 4408a0ee8cc6SDag-Erling Smørgrav permit_adm = 1; 4409a0ee8cc6SDag-Erling Smørgrav break; 4410a0ee8cc6SDag-Erling Smørgrav } 4411a0ee8cc6SDag-Erling Smørgrav } 44124f52dfbbSDag-Erling Smørgrav } 4413a0ee8cc6SDag-Erling Smørgrav 4414a0ee8cc6SDag-Erling Smørgrav if (!permit || !permit_adm) { 4415a0ee8cc6SDag-Erling Smørgrav logit("Received request to connect to path %.100s, " 4416a0ee8cc6SDag-Erling Smørgrav "but the request was denied.", path); 4417a0ee8cc6SDag-Erling Smørgrav return NULL; 4418a0ee8cc6SDag-Erling Smørgrav } 44194f52dfbbSDag-Erling Smørgrav return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); 4420a0ee8cc6SDag-Erling Smørgrav } 4421a0ee8cc6SDag-Erling Smørgrav 442221e764dfSDag-Erling Smørgrav void 44234f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh) 442421e764dfSDag-Erling Smørgrav { 44254f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 442621e764dfSDag-Erling Smørgrav struct winsize ws; 44274f52dfbbSDag-Erling Smørgrav int r; 44284f52dfbbSDag-Erling Smørgrav u_int i; 442921e764dfSDag-Erling Smørgrav 44304f52dfbbSDag-Erling Smørgrav for (i = 0; i < sc->channels_alloc; i++) { 44314f52dfbbSDag-Erling Smørgrav if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || 44324f52dfbbSDag-Erling Smørgrav sc->channels[i]->type != SSH_CHANNEL_OPEN) 443321e764dfSDag-Erling Smørgrav continue; 44344f52dfbbSDag-Erling Smørgrav if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) < 0) 443521e764dfSDag-Erling Smørgrav continue; 44364f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, i, "window-change", 0); 44374f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 44384f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 44394f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 44404f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || 44414f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 44424f52dfbbSDag-Erling Smørgrav fatal("%s: channel %u: send window-change: %s", 44434f52dfbbSDag-Erling Smørgrav __func__, i, ssh_err(r)); 444421e764dfSDag-Erling Smørgrav } 444521e764dfSDag-Erling Smørgrav } 444621e764dfSDag-Erling Smørgrav 44474f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ 44484f52dfbbSDag-Erling Smørgrav static Channel * 44494f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) 44504f52dfbbSDag-Erling Smørgrav { 44514f52dfbbSDag-Erling Smørgrav Channel *c; 44524f52dfbbSDag-Erling Smørgrav int r; 44534f52dfbbSDag-Erling Smørgrav 44544f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, 44554f52dfbbSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); 44564f52dfbbSDag-Erling Smørgrav c->host_port = 0; 44574f52dfbbSDag-Erling Smørgrav c->path = NULL; 44584f52dfbbSDag-Erling Smørgrav 44594f52dfbbSDag-Erling Smørgrav /* 44604f52dfbbSDag-Erling Smørgrav * We need to open the channel before we have a FD, 44614f52dfbbSDag-Erling Smørgrav * so that we can get SOCKS header from peer. 44624f52dfbbSDag-Erling Smørgrav */ 44634f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 44644f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 44654f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->self)) != 0 || 44664f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 44674f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { 44684f52dfbbSDag-Erling Smørgrav fatal("%s: channel %i: confirm: %s", __func__, 44694f52dfbbSDag-Erling Smørgrav c->self, ssh_err(r)); 44704f52dfbbSDag-Erling Smørgrav } 44714f52dfbbSDag-Erling Smørgrav return c; 44724f52dfbbSDag-Erling Smørgrav } 44734f52dfbbSDag-Erling Smørgrav 44744f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */ 44754f52dfbbSDag-Erling Smørgrav static int 44764f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c) 44774f52dfbbSDag-Erling Smørgrav { 44784f52dfbbSDag-Erling Smørgrav struct channel_connect cctx; 44794f52dfbbSDag-Erling Smørgrav int sock; 44804f52dfbbSDag-Erling Smørgrav 44814f52dfbbSDag-Erling Smørgrav memset(&cctx, 0, sizeof(cctx)); 44824f52dfbbSDag-Erling Smørgrav sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, 44834f52dfbbSDag-Erling Smørgrav NULL, &cctx, NULL, NULL); 44844f52dfbbSDag-Erling Smørgrav if (sock == -1) 44854f52dfbbSDag-Erling Smørgrav channel_connect_ctx_free(&cctx); 44864f52dfbbSDag-Erling Smørgrav else { 44874f52dfbbSDag-Erling Smørgrav /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ 44884f52dfbbSDag-Erling Smørgrav c->type = SSH_CHANNEL_RDYNAMIC_FINISH; 44894f52dfbbSDag-Erling Smørgrav c->connect_ctx = cctx; 44904f52dfbbSDag-Erling Smørgrav channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); 44914f52dfbbSDag-Erling Smørgrav } 44924f52dfbbSDag-Erling Smørgrav return sock; 44934f52dfbbSDag-Erling Smørgrav } 44944f52dfbbSDag-Erling Smørgrav 4495af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */ 4496511b41d2SMark Murray 4497511b41d2SMark Murray /* 4498511b41d2SMark Murray * Creates an internet domain socket for listening for X11 connections. 4499a82e551fSDag-Erling Smørgrav * Returns 0 and a suitable display number for the DISPLAY variable 4500a82e551fSDag-Erling Smørgrav * stored in display_numberp , or -1 if an error occurs. 4501511b41d2SMark Murray */ 4502af12a3e7SDag-Erling Smørgrav int 45034f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset, 45044f52dfbbSDag-Erling Smørgrav int x11_use_localhost, int single_connection, 45054f52dfbbSDag-Erling Smørgrav u_int *display_numberp, int **chanids) 4506511b41d2SMark Murray { 4507af12a3e7SDag-Erling Smørgrav Channel *nc = NULL; 4508511b41d2SMark Murray int display_number, sock; 4509511b41d2SMark Murray u_short port; 4510511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4511511b41d2SMark Murray char strport[NI_MAXSERV]; 4512511b41d2SMark Murray int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; 4513511b41d2SMark Murray 4514b74df5b2SDag-Erling Smørgrav if (chanids == NULL) 4515b74df5b2SDag-Erling Smørgrav return -1; 4516b74df5b2SDag-Erling Smørgrav 4517511b41d2SMark Murray for (display_number = x11_display_offset; 4518511b41d2SMark Murray display_number < MAX_DISPLAYS; 4519511b41d2SMark Murray display_number++) { 4520511b41d2SMark Murray port = 6000 + display_number; 4521511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 45224f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 4523af12a3e7SDag-Erling Smørgrav hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; 4524511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4525511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 45264f52dfbbSDag-Erling Smørgrav if ((gaierr = getaddrinfo(NULL, strport, 45274f52dfbbSDag-Erling Smørgrav &hints, &aitop)) != 0) { 4528d4af9e69SDag-Erling Smørgrav error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); 4529af12a3e7SDag-Erling Smørgrav return -1; 4530511b41d2SMark Murray } 4531511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 45324f52dfbbSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 45334f52dfbbSDag-Erling Smørgrav ai->ai_family != AF_INET6) 4534511b41d2SMark Murray continue; 4535221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, 4536221552e4SDag-Erling Smørgrav ai->ai_protocol); 4537511b41d2SMark Murray if (sock < 0) { 45388ad9b54aSDag-Erling Smørgrav if ((errno != EINVAL) && (errno != EAFNOSUPPORT) 45398ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT 45408ad9b54aSDag-Erling Smørgrav && (errno != EPFNOSUPPORT) 45418ad9b54aSDag-Erling Smørgrav #endif 45428ad9b54aSDag-Erling Smørgrav ) { 4543511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 454421e764dfSDag-Erling Smørgrav freeaddrinfo(aitop); 4545af12a3e7SDag-Erling Smørgrav return -1; 4546989dd127SDag-Erling Smørgrav } else { 4547989dd127SDag-Erling Smørgrav debug("x11_create_display_inet: Socket family %d not supported", 4548989dd127SDag-Erling Smørgrav ai->ai_family); 4549989dd127SDag-Erling Smørgrav continue; 4550511b41d2SMark Murray } 4551989dd127SDag-Erling Smørgrav } 4552b15c8340SDag-Erling Smørgrav if (ai->ai_family == AF_INET6) 4553b15c8340SDag-Erling Smørgrav sock_set_v6only(sock); 4554d4af9e69SDag-Erling Smørgrav if (x11_use_localhost) 455547dd1d1bSDag-Erling Smørgrav set_reuseaddr(sock); 4556511b41d2SMark Murray if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 45574f52dfbbSDag-Erling Smørgrav debug2("%s: bind port %d: %.100s", __func__, 45584f52dfbbSDag-Erling Smørgrav port, strerror(errno)); 4559511b41d2SMark Murray close(sock); 45604f52dfbbSDag-Erling Smørgrav for (n = 0; n < num_socks; n++) 4561511b41d2SMark Murray close(socks[n]); 4562511b41d2SMark Murray num_socks = 0; 4563511b41d2SMark Murray break; 4564511b41d2SMark Murray } 4565511b41d2SMark Murray socks[num_socks++] = sock; 4566511b41d2SMark Murray if (num_socks == NUM_SOCKS) 4567511b41d2SMark Murray break; 4568511b41d2SMark Murray } 4569ca3176e7SBrian Feldman freeaddrinfo(aitop); 4570511b41d2SMark Murray if (num_socks > 0) 4571511b41d2SMark Murray break; 4572511b41d2SMark Murray } 4573511b41d2SMark Murray if (display_number >= MAX_DISPLAYS) { 4574511b41d2SMark Murray error("Failed to allocate internet-domain X11 display socket."); 4575af12a3e7SDag-Erling Smørgrav return -1; 4576511b41d2SMark Murray } 4577511b41d2SMark Murray /* Start listening for connections on the socket. */ 4578511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4579511b41d2SMark Murray sock = socks[n]; 4580476cd3b2SDag-Erling Smørgrav if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 4581511b41d2SMark Murray error("listen: %.100s", strerror(errno)); 4582511b41d2SMark Murray close(sock); 4583af12a3e7SDag-Erling Smørgrav return -1; 4584511b41d2SMark Murray } 4585511b41d2SMark Murray } 4586511b41d2SMark Murray 4587511b41d2SMark Murray /* Allocate a channel for each socket. */ 4588333ee039SDag-Erling Smørgrav *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); 4589511b41d2SMark Murray for (n = 0; n < num_socks; n++) { 4590511b41d2SMark Murray sock = socks[n]; 45914f52dfbbSDag-Erling Smørgrav nc = channel_new(ssh, "x11 listener", 4592a04a10f8SKris Kennaway SSH_CHANNEL_X11_LISTENER, sock, sock, -1, 4593a04a10f8SKris Kennaway CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 4594221552e4SDag-Erling Smørgrav 0, "X11 inet listener", 1); 4595af12a3e7SDag-Erling Smørgrav nc->single_connection = single_connection; 4596d4ecd108SDag-Erling Smørgrav (*chanids)[n] = nc->self; 4597511b41d2SMark Murray } 4598d4ecd108SDag-Erling Smørgrav (*chanids)[n] = -1; 4599511b41d2SMark Murray 4600af12a3e7SDag-Erling Smørgrav /* Return the display number for the DISPLAY environment variable. */ 4601a82e551fSDag-Erling Smørgrav *display_numberp = display_number; 46024f52dfbbSDag-Erling Smørgrav return 0; 4603511b41d2SMark Murray } 4604511b41d2SMark Murray 4605af12a3e7SDag-Erling Smørgrav static int 4606cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname) 4607511b41d2SMark Murray { 4608511b41d2SMark Murray int sock; 4609511b41d2SMark Murray struct sockaddr_un addr; 4610511b41d2SMark Murray 4611511b41d2SMark Murray sock = socket(AF_UNIX, SOCK_STREAM, 0); 4612511b41d2SMark Murray if (sock < 0) 4613511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 4614511b41d2SMark Murray memset(&addr, 0, sizeof(addr)); 4615511b41d2SMark Murray addr.sun_family = AF_UNIX; 4616cce7d346SDag-Erling Smørgrav strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); 4617511b41d2SMark Murray if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 4618511b41d2SMark Murray return sock; 4619511b41d2SMark Murray close(sock); 4620511b41d2SMark Murray error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); 4621511b41d2SMark Murray return -1; 4622511b41d2SMark Murray } 4623511b41d2SMark Murray 4624cce7d346SDag-Erling Smørgrav static int 4625cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr) 4626cce7d346SDag-Erling Smørgrav { 4627cce7d346SDag-Erling Smørgrav char buf[1024]; 4628cce7d346SDag-Erling Smørgrav snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); 4629cce7d346SDag-Erling Smørgrav return connect_local_xsocket_path(buf); 4630cce7d346SDag-Erling Smørgrav } 4631cce7d346SDag-Erling Smørgrav 4632d93a896eSDag-Erling Smørgrav #ifdef __APPLE__ 4633d93a896eSDag-Erling Smørgrav static int 4634d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen) 4635d93a896eSDag-Erling Smørgrav { 4636d93a896eSDag-Erling Smørgrav struct stat sbuf; 4637d93a896eSDag-Erling Smørgrav 4638d93a896eSDag-Erling Smørgrav if (strlcpy(path, display, pathlen) >= pathlen) { 4639d93a896eSDag-Erling Smørgrav error("%s: display path too long", __func__); 4640d93a896eSDag-Erling Smørgrav return 0; 4641d93a896eSDag-Erling Smørgrav } 4642d93a896eSDag-Erling Smørgrav if (display[0] != '/') 4643d93a896eSDag-Erling Smørgrav return 0; 4644d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 4645d93a896eSDag-Erling Smørgrav return 1; 4646d93a896eSDag-Erling Smørgrav } else { 4647d93a896eSDag-Erling Smørgrav char *dot = strrchr(path, '.'); 4648d93a896eSDag-Erling Smørgrav if (dot != NULL) { 4649d93a896eSDag-Erling Smørgrav *dot = '\0'; 4650d93a896eSDag-Erling Smørgrav if (stat(path, &sbuf) == 0) { 4651d93a896eSDag-Erling Smørgrav return 1; 4652d93a896eSDag-Erling Smørgrav } 4653d93a896eSDag-Erling Smørgrav } 4654d93a896eSDag-Erling Smørgrav } 4655d93a896eSDag-Erling Smørgrav return 0; 4656d93a896eSDag-Erling Smørgrav } 4657d93a896eSDag-Erling Smørgrav #endif 4658d93a896eSDag-Erling Smørgrav 4659a04a10f8SKris Kennaway int 46604f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh) 4661511b41d2SMark Murray { 4662333ee039SDag-Erling Smørgrav u_int display_number; 4663511b41d2SMark Murray const char *display; 4664a04a10f8SKris Kennaway char buf[1024], *cp; 4665511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 4666511b41d2SMark Murray char strport[NI_MAXSERV]; 4667333ee039SDag-Erling Smørgrav int gaierr, sock = 0; 4668511b41d2SMark Murray 4669511b41d2SMark Murray /* Try to open a socket for the local X server. */ 4670511b41d2SMark Murray display = getenv("DISPLAY"); 4671511b41d2SMark Murray if (!display) { 4672511b41d2SMark Murray error("DISPLAY not set."); 4673a04a10f8SKris Kennaway return -1; 4674511b41d2SMark Murray } 4675511b41d2SMark Murray /* 4676511b41d2SMark Murray * Now we decode the value of the DISPLAY variable and make a 4677511b41d2SMark Murray * connection to the real X server. 4678511b41d2SMark Murray */ 4679511b41d2SMark Murray 4680cce7d346SDag-Erling Smørgrav #ifdef __APPLE__ 4681d93a896eSDag-Erling Smørgrav /* Check if display is a path to a socket (as set by launchd). */ 4682d93a896eSDag-Erling Smørgrav { 4683d93a896eSDag-Erling Smørgrav char path[PATH_MAX]; 4684d93a896eSDag-Erling Smørgrav 4685d93a896eSDag-Erling Smørgrav if (is_path_to_xsocket(display, path, sizeof(path))) { 4686d93a896eSDag-Erling Smørgrav debug("x11_connect_display: $DISPLAY is launchd"); 4687d93a896eSDag-Erling Smørgrav 4688d93a896eSDag-Erling Smørgrav /* Create a socket. */ 4689d93a896eSDag-Erling Smørgrav sock = connect_local_xsocket_path(path); 4690cce7d346SDag-Erling Smørgrav if (sock < 0) 4691cce7d346SDag-Erling Smørgrav return -1; 4692cce7d346SDag-Erling Smørgrav 4693cce7d346SDag-Erling Smørgrav /* OK, we now have a connection to the display. */ 4694cce7d346SDag-Erling Smørgrav return sock; 4695cce7d346SDag-Erling Smørgrav } 4696d93a896eSDag-Erling Smørgrav } 4697cce7d346SDag-Erling Smørgrav #endif 4698511b41d2SMark Murray /* 4699511b41d2SMark Murray * Check if it is a unix domain socket. Unix domain displays are in 4700511b41d2SMark Murray * one of the following formats: unix:d[.s], :d[.s], ::d[.s] 4701511b41d2SMark Murray */ 4702511b41d2SMark Murray if (strncmp(display, "unix:", 5) == 0 || 4703511b41d2SMark Murray display[0] == ':') { 4704511b41d2SMark Murray /* Connect to the unix domain socket. */ 47054f52dfbbSDag-Erling Smørgrav if (sscanf(strrchr(display, ':') + 1, "%u", 47064f52dfbbSDag-Erling Smørgrav &display_number) != 1) { 47074f52dfbbSDag-Erling Smørgrav error("Could not parse display number from DISPLAY: " 47084f52dfbbSDag-Erling Smørgrav "%.100s", display); 4709a04a10f8SKris Kennaway return -1; 4710511b41d2SMark Murray } 4711511b41d2SMark Murray /* Create a socket. */ 4712511b41d2SMark Murray sock = connect_local_xsocket(display_number); 4713511b41d2SMark Murray if (sock < 0) 4714a04a10f8SKris Kennaway return -1; 4715511b41d2SMark Murray 4716511b41d2SMark Murray /* OK, we now have a connection to the display. */ 4717a04a10f8SKris Kennaway return sock; 4718511b41d2SMark Murray } 4719511b41d2SMark Murray /* 4720511b41d2SMark Murray * Connect to an inet socket. The DISPLAY value is supposedly 4721511b41d2SMark Murray * hostname:d[.s], where hostname may also be numeric IP address. 4722511b41d2SMark Murray */ 4723af12a3e7SDag-Erling Smørgrav strlcpy(buf, display, sizeof(buf)); 4724511b41d2SMark Murray cp = strchr(buf, ':'); 4725511b41d2SMark Murray if (!cp) { 4726511b41d2SMark Murray error("Could not find ':' in DISPLAY: %.100s", display); 4727a04a10f8SKris Kennaway return -1; 4728511b41d2SMark Murray } 4729511b41d2SMark Murray *cp = 0; 47304f52dfbbSDag-Erling Smørgrav /* 47314f52dfbbSDag-Erling Smørgrav * buf now contains the host name. But first we parse the 47324f52dfbbSDag-Erling Smørgrav * display number. 47334f52dfbbSDag-Erling Smørgrav */ 4734333ee039SDag-Erling Smørgrav if (sscanf(cp + 1, "%u", &display_number) != 1) { 4735511b41d2SMark Murray error("Could not parse display number from DISPLAY: %.100s", 4736511b41d2SMark Murray display); 4737a04a10f8SKris Kennaway return -1; 4738511b41d2SMark Murray } 4739511b41d2SMark Murray 4740511b41d2SMark Murray /* Look up the host address */ 4741511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 47424f52dfbbSDag-Erling Smørgrav hints.ai_family = ssh->chanctxt->IPv4or6; 4743511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 4744333ee039SDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", 6000 + display_number); 4745511b41d2SMark Murray if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { 4746d4af9e69SDag-Erling Smørgrav error("%.100s: unknown host. (%s)", buf, 4747d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 4748a04a10f8SKris Kennaway return -1; 4749511b41d2SMark Murray } 4750511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 4751511b41d2SMark Murray /* Create a socket. */ 4752221552e4SDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 4753511b41d2SMark Murray if (sock < 0) { 4754221552e4SDag-Erling Smørgrav debug2("socket: %.100s", strerror(errno)); 4755511b41d2SMark Murray continue; 4756511b41d2SMark Murray } 4757511b41d2SMark Murray /* Connect it to the display. */ 4758511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 4759333ee039SDag-Erling Smørgrav debug2("connect %.100s port %u: %.100s", buf, 4760a04a10f8SKris Kennaway 6000 + display_number, strerror(errno)); 4761511b41d2SMark Murray close(sock); 4762511b41d2SMark Murray continue; 4763511b41d2SMark Murray } 4764511b41d2SMark Murray /* Success */ 4765511b41d2SMark Murray break; 4766a04a10f8SKris Kennaway } 4767511b41d2SMark Murray freeaddrinfo(aitop); 4768511b41d2SMark Murray if (!ai) { 47694f52dfbbSDag-Erling Smørgrav error("connect %.100s port %u: %.100s", buf, 47704f52dfbbSDag-Erling Smørgrav 6000 + display_number, strerror(errno)); 4771a04a10f8SKris Kennaway return -1; 4772511b41d2SMark Murray } 4773af12a3e7SDag-Erling Smørgrav set_nodelay(sock); 4774a04a10f8SKris Kennaway return sock; 4775a04a10f8SKris Kennaway } 4776511b41d2SMark Murray 4777a04a10f8SKris Kennaway /* 4778511b41d2SMark Murray * Requests forwarding of X11 connections, generates fake authentication 4779511b41d2SMark Murray * data, and enables authentication spoofing. 4780af12a3e7SDag-Erling Smørgrav * This should be called in the client only. 4781511b41d2SMark Murray */ 4782511b41d2SMark Murray void 47834f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, 47844f52dfbbSDag-Erling Smørgrav const char *disp, const char *proto, const char *data, int want_reply) 4785511b41d2SMark Murray { 47864f52dfbbSDag-Erling Smørgrav struct ssh_channels *sc = ssh->chanctxt; 4787ca3176e7SBrian Feldman u_int data_len = (u_int) strlen(data) / 2; 4788d4ecd108SDag-Erling Smørgrav u_int i, value; 4789511b41d2SMark Murray const char *cp; 47904f52dfbbSDag-Erling Smørgrav char *new_data; 47914f52dfbbSDag-Erling Smørgrav int r, screen_number; 4792511b41d2SMark Murray 47934f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_display == NULL) 47944f52dfbbSDag-Erling Smørgrav sc->x11_saved_display = xstrdup(disp); 47954f52dfbbSDag-Erling Smørgrav else if (strcmp(disp, sc->x11_saved_display) != 0) { 4796d4ecd108SDag-Erling Smørgrav error("x11_request_forwarding_with_spoofing: different " 4797d4ecd108SDag-Erling Smørgrav "$DISPLAY already forwarded"); 4798d4ecd108SDag-Erling Smørgrav return; 4799d4ecd108SDag-Erling Smørgrav } 4800d4ecd108SDag-Erling Smørgrav 4801d4ecd108SDag-Erling Smørgrav cp = strchr(disp, ':'); 4802511b41d2SMark Murray if (cp) 4803511b41d2SMark Murray cp = strchr(cp, '.'); 4804511b41d2SMark Murray if (cp) 4805333ee039SDag-Erling Smørgrav screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); 4806511b41d2SMark Murray else 4807511b41d2SMark Murray screen_number = 0; 4808511b41d2SMark Murray 48094f52dfbbSDag-Erling Smørgrav if (sc->x11_saved_proto == NULL) { 4810511b41d2SMark Murray /* Save protocol name. */ 48114f52dfbbSDag-Erling Smørgrav sc->x11_saved_proto = xstrdup(proto); 4812ca86bcf2SDag-Erling Smørgrav 4813ca86bcf2SDag-Erling Smørgrav /* Extract real authentication data. */ 48144f52dfbbSDag-Erling Smørgrav sc->x11_saved_data = xmalloc(data_len); 4815511b41d2SMark Murray for (i = 0; i < data_len; i++) { 4816511b41d2SMark Murray if (sscanf(data + 2 * i, "%2x", &value) != 1) 4817d4ecd108SDag-Erling Smørgrav fatal("x11_request_forwarding: bad " 4818d4ecd108SDag-Erling Smørgrav "authentication data: %.100s", data); 48194f52dfbbSDag-Erling Smørgrav sc->x11_saved_data[i] = value; 4820511b41d2SMark Murray } 48214f52dfbbSDag-Erling Smørgrav sc->x11_saved_data_len = data_len; 4822ca86bcf2SDag-Erling Smørgrav 4823ca86bcf2SDag-Erling Smørgrav /* Generate fake data of the same length. */ 48244f52dfbbSDag-Erling Smørgrav sc->x11_fake_data = xmalloc(data_len); 48254f52dfbbSDag-Erling Smørgrav arc4random_buf(sc->x11_fake_data, data_len); 48264f52dfbbSDag-Erling Smørgrav sc->x11_fake_data_len = data_len; 4827d4ecd108SDag-Erling Smørgrav } 4828511b41d2SMark Murray 4829511b41d2SMark Murray /* Convert the fake data into hex. */ 48304f52dfbbSDag-Erling Smørgrav new_data = tohex(sc->x11_fake_data, data_len); 4831511b41d2SMark Murray 4832511b41d2SMark Murray /* Send the request packet. */ 48334f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, client_session_id, "x11-req", want_reply); 48344f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ 48354f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, proto)) != 0 || 48364f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, new_data)) != 0 || 48374f52dfbbSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, screen_number)) != 0 || 48384f52dfbbSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0 || 48394f52dfbbSDag-Erling Smørgrav (r = ssh_packet_write_wait(ssh)) != 0) 48404f52dfbbSDag-Erling Smørgrav fatal("%s: send x11-req: %s", __func__, ssh_err(r)); 4841e4a9863fSDag-Erling Smørgrav free(new_data); 4842511b41d2SMark Murray } 4843