1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: serverloop.c,v 1.209 2018/07/27 05:13:02 dtucker Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * Server main loop for handling the interactive session. 7b66f2d16SKris Kennaway * 8b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 9b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 11b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13b66f2d16SKris Kennaway * 14a04a10f8SKris Kennaway * SSH2 support by Markus Friedl. 15af12a3e7SDag-Erling Smørgrav * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 16b66f2d16SKris Kennaway * 17b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 18b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 19b66f2d16SKris Kennaway * are met: 20b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 22b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 25b66f2d16SKris Kennaway * 26b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36a04a10f8SKris Kennaway */ 37511b41d2SMark Murray 38511b41d2SMark Murray #include "includes.h" 39333ee039SDag-Erling Smørgrav 40333ee039SDag-Erling Smørgrav #include <sys/types.h> 41333ee039SDag-Erling Smørgrav #include <sys/wait.h> 42333ee039SDag-Erling Smørgrav #include <sys/socket.h> 43333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 44333ee039SDag-Erling Smørgrav # include <sys/time.h> 45333ee039SDag-Erling Smørgrav #endif 46333ee039SDag-Erling Smørgrav 47333ee039SDag-Erling Smørgrav #include <netinet/in.h> 48333ee039SDag-Erling Smørgrav 49333ee039SDag-Erling Smørgrav #include <errno.h> 50333ee039SDag-Erling Smørgrav #include <fcntl.h> 51333ee039SDag-Erling Smørgrav #include <pwd.h> 52333ee039SDag-Erling Smørgrav #include <signal.h> 53333ee039SDag-Erling Smørgrav #include <string.h> 54333ee039SDag-Erling Smørgrav #include <termios.h> 55333ee039SDag-Erling Smørgrav #include <unistd.h> 56333ee039SDag-Erling Smørgrav #include <stdarg.h> 575b9b2fafSBrian Feldman 58d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 59511b41d2SMark Murray #include "xmalloc.h" 60511b41d2SMark Murray #include "packet.h" 61*190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 62ca3176e7SBrian Feldman #include "log.h" 63a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 64511b41d2SMark Murray #include "servconf.h" 65f388f5efSDag-Erling Smørgrav #include "canohost.h" 66ca3176e7SBrian Feldman #include "sshpty.h" 67a04a10f8SKris Kennaway #include "channels.h" 68a04a10f8SKris Kennaway #include "compat.h" 69a04a10f8SKris Kennaway #include "ssh2.h" 70*190cef3dSDag-Erling Smørgrav #include "sshkey.h" 71333ee039SDag-Erling Smørgrav #include "cipher.h" 72333ee039SDag-Erling Smørgrav #include "kex.h" 73333ee039SDag-Erling Smørgrav #include "hostfile.h" 74ca3176e7SBrian Feldman #include "auth.h" 75a04a10f8SKris Kennaway #include "session.h" 76a04a10f8SKris Kennaway #include "dispatch.h" 77b66f2d16SKris Kennaway #include "auth-options.h" 78ca3176e7SBrian Feldman #include "serverloop.h" 79bc5531deSDag-Erling Smørgrav #include "ssherr.h" 80511b41d2SMark Murray 815b9b2fafSBrian Feldman extern ServerOptions options; 825b9b2fafSBrian Feldman 83ca3176e7SBrian Feldman /* XXX */ 841ec0d754SDag-Erling Smørgrav extern Authctxt *the_authctxt; 8547dd1d1bSDag-Erling Smørgrav extern struct sshauthopt *auth_opts; 86b74df5b2SDag-Erling Smørgrav extern int use_privsep; 87ca3176e7SBrian Feldman 88d4af9e69SDag-Erling Smørgrav static int no_more_sessions = 0; /* Disallow further sessions. */ 89511b41d2SMark Murray 90511b41d2SMark Murray /* 91511b41d2SMark Murray * This SIGCHLD kludge is used to detect when the child exits. The server 92511b41d2SMark Murray * will exit after that, as soon as forwarded connections have terminated. 93511b41d2SMark Murray */ 94511b41d2SMark Murray 95af12a3e7SDag-Erling Smørgrav static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ 96511b41d2SMark Murray 97b74df5b2SDag-Erling Smørgrav /* Cleanup on signals (!use_privsep case only) */ 98b74df5b2SDag-Erling Smørgrav static volatile sig_atomic_t received_sigterm = 0; 99b74df5b2SDag-Erling Smørgrav 100af12a3e7SDag-Erling Smørgrav /* prototypes */ 101af12a3e7SDag-Erling Smørgrav static void server_init_dispatch(void); 102a04a10f8SKris Kennaway 10347dd1d1bSDag-Erling Smørgrav /* requested tunnel forwarding interface(s), shared with session.c */ 10447dd1d1bSDag-Erling Smørgrav char *tun_fwd_ifnames = NULL; 10547dd1d1bSDag-Erling Smørgrav 106*190cef3dSDag-Erling Smørgrav /* returns 1 if bind to specified port by specified user is permitted */ 107*190cef3dSDag-Erling Smørgrav static int 108*190cef3dSDag-Erling Smørgrav bind_permitted(int port, uid_t uid) 109*190cef3dSDag-Erling Smørgrav { 110*190cef3dSDag-Erling Smørgrav if (use_privsep) 111*190cef3dSDag-Erling Smørgrav return 1; /* allow system to decide */ 112*190cef3dSDag-Erling Smørgrav if (port < IPPORT_RESERVED && uid != 0) 113*190cef3dSDag-Erling Smørgrav return 0; 114*190cef3dSDag-Erling Smørgrav return 1; 115*190cef3dSDag-Erling Smørgrav } 116*190cef3dSDag-Erling Smørgrav 117af12a3e7SDag-Erling Smørgrav /* 118af12a3e7SDag-Erling Smørgrav * we write to this pipe if a SIGCHLD is caught in order to avoid 119af12a3e7SDag-Erling Smørgrav * the race between select() and child_terminated 120af12a3e7SDag-Erling Smørgrav */ 121af12a3e7SDag-Erling Smørgrav static int notify_pipe[2]; 122af12a3e7SDag-Erling Smørgrav static void 123af12a3e7SDag-Erling Smørgrav notify_setup(void) 124af12a3e7SDag-Erling Smørgrav { 125af12a3e7SDag-Erling Smørgrav if (pipe(notify_pipe) < 0) { 126af12a3e7SDag-Erling Smørgrav error("pipe(notify_pipe) failed %s", strerror(errno)); 127e146993eSDag-Erling Smørgrav } else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) || 128e146993eSDag-Erling Smørgrav (fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) { 129af12a3e7SDag-Erling Smørgrav error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); 130af12a3e7SDag-Erling Smørgrav close(notify_pipe[0]); 131af12a3e7SDag-Erling Smørgrav close(notify_pipe[1]); 132af12a3e7SDag-Erling Smørgrav } else { 133af12a3e7SDag-Erling Smørgrav set_nonblock(notify_pipe[0]); 134af12a3e7SDag-Erling Smørgrav set_nonblock(notify_pipe[1]); 135af12a3e7SDag-Erling Smørgrav return; 136af12a3e7SDag-Erling Smørgrav } 137af12a3e7SDag-Erling Smørgrav notify_pipe[0] = -1; /* read end */ 138af12a3e7SDag-Erling Smørgrav notify_pipe[1] = -1; /* write end */ 139af12a3e7SDag-Erling Smørgrav } 140af12a3e7SDag-Erling Smørgrav static void 141af12a3e7SDag-Erling Smørgrav notify_parent(void) 142af12a3e7SDag-Erling Smørgrav { 143af12a3e7SDag-Erling Smørgrav if (notify_pipe[1] != -1) 144e4a9863fSDag-Erling Smørgrav (void)write(notify_pipe[1], "", 1); 145af12a3e7SDag-Erling Smørgrav } 146af12a3e7SDag-Erling Smørgrav static void 147af12a3e7SDag-Erling Smørgrav notify_prepare(fd_set *readset) 148af12a3e7SDag-Erling Smørgrav { 149af12a3e7SDag-Erling Smørgrav if (notify_pipe[0] != -1) 150af12a3e7SDag-Erling Smørgrav FD_SET(notify_pipe[0], readset); 151af12a3e7SDag-Erling Smørgrav } 152af12a3e7SDag-Erling Smørgrav static void 153af12a3e7SDag-Erling Smørgrav notify_done(fd_set *readset) 154af12a3e7SDag-Erling Smørgrav { 155af12a3e7SDag-Erling Smørgrav char c; 156ca3176e7SBrian Feldman 157af12a3e7SDag-Erling Smørgrav if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) 158af12a3e7SDag-Erling Smørgrav while (read(notify_pipe[0], &c, 1) != -1) 159*190cef3dSDag-Erling Smørgrav debug2("%s: reading", __func__); 160af12a3e7SDag-Erling Smørgrav } 161af12a3e7SDag-Erling Smørgrav 162333ee039SDag-Erling Smørgrav /*ARGSUSED*/ 163af12a3e7SDag-Erling Smørgrav static void 164511b41d2SMark Murray sigchld_handler(int sig) 165511b41d2SMark Murray { 166511b41d2SMark Murray int save_errno = errno; 167511b41d2SMark Murray child_terminated = 1; 168af12a3e7SDag-Erling Smørgrav notify_parent(); 169a04a10f8SKris Kennaway errno = save_errno; 170511b41d2SMark Murray } 171511b41d2SMark Murray 172333ee039SDag-Erling Smørgrav /*ARGSUSED*/ 173b74df5b2SDag-Erling Smørgrav static void 174b74df5b2SDag-Erling Smørgrav sigterm_handler(int sig) 175b74df5b2SDag-Erling Smørgrav { 176b74df5b2SDag-Erling Smørgrav received_sigterm = sig; 177b74df5b2SDag-Erling Smørgrav } 178b74df5b2SDag-Erling Smørgrav 179af12a3e7SDag-Erling Smørgrav static void 1804f52dfbbSDag-Erling Smørgrav client_alive_check(struct ssh *ssh) 181af12a3e7SDag-Erling Smørgrav { 1821ec0d754SDag-Erling Smørgrav int channel_id; 18347dd1d1bSDag-Erling Smørgrav char remote_id[512]; 184af12a3e7SDag-Erling Smørgrav 185af12a3e7SDag-Erling Smørgrav /* timeout, check to see how many we have had */ 1867aee6ffeSDag-Erling Smørgrav if (packet_inc_alive_timeouts() > options.client_alive_count_max) { 18747dd1d1bSDag-Erling Smørgrav sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); 18847dd1d1bSDag-Erling Smørgrav logit("Timeout, client not responding from %s", remote_id); 18962efe23aSDag-Erling Smørgrav cleanup_exit(255); 19062efe23aSDag-Erling Smørgrav } 191af12a3e7SDag-Erling Smørgrav 192af12a3e7SDag-Erling Smørgrav /* 1931ec0d754SDag-Erling Smørgrav * send a bogus global/channel request with "wantreply", 194af12a3e7SDag-Erling Smørgrav * we should get back a failure 195af12a3e7SDag-Erling Smørgrav */ 1964f52dfbbSDag-Erling Smørgrav if ((channel_id = channel_find_open(ssh)) == -1) { 1971ec0d754SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 1981ec0d754SDag-Erling Smørgrav packet_put_cstring("keepalive@openssh.com"); 1991ec0d754SDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 2001ec0d754SDag-Erling Smørgrav } else { 2014f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, channel_id, 2024f52dfbbSDag-Erling Smørgrav "keepalive@openssh.com", 1); 2031ec0d754SDag-Erling Smørgrav } 204af12a3e7SDag-Erling Smørgrav packet_send(); 205af12a3e7SDag-Erling Smørgrav } 206af12a3e7SDag-Erling Smørgrav 207511b41d2SMark Murray /* 208511b41d2SMark Murray * Sleep in select() until we can do something. This will initialize the 209511b41d2SMark Murray * select masks. Upon return, the masks will indicate which descriptors 210511b41d2SMark Murray * have data or can accept data. Optionally, a maximum time can be specified 211511b41d2SMark Murray * for the duration of the wait (0 = infinite). 212511b41d2SMark Murray */ 213af12a3e7SDag-Erling Smørgrav static void 2144f52dfbbSDag-Erling Smørgrav wait_until_can_do_something(struct ssh *ssh, 2154f52dfbbSDag-Erling Smørgrav int connection_in, int connection_out, 216ca86bcf2SDag-Erling Smørgrav fd_set **readsetp, fd_set **writesetp, int *maxfdp, 217076ad2f8SDag-Erling Smørgrav u_int *nallocp, u_int64_t max_time_ms) 218511b41d2SMark Murray { 219511b41d2SMark Murray struct timeval tv, *tvp; 220511b41d2SMark Murray int ret; 221462c32cbSDag-Erling Smørgrav time_t minwait_secs = 0; 222ca3176e7SBrian Feldman int client_alive_scheduled = 0; 2234f52dfbbSDag-Erling Smørgrav static time_t last_client_time; 224ca3176e7SBrian Feldman 225462c32cbSDag-Erling Smørgrav /* Allocate and update select() masks for channel descriptors. */ 2264f52dfbbSDag-Erling Smørgrav channel_prepare_select(ssh, readsetp, writesetp, maxfdp, 2274f52dfbbSDag-Erling Smørgrav nallocp, &minwait_secs); 228462c32cbSDag-Erling Smørgrav 229076ad2f8SDag-Erling Smørgrav /* XXX need proper deadline system for rekey/client alive */ 230462c32cbSDag-Erling Smørgrav if (minwait_secs != 0) 231ca86bcf2SDag-Erling Smørgrav max_time_ms = MINIMUM(max_time_ms, (u_int)minwait_secs * 1000); 232462c32cbSDag-Erling Smørgrav 233ca3176e7SBrian Feldman /* 234ca3176e7SBrian Feldman * if using client_alive, set the max timeout accordingly, 235ca3176e7SBrian Feldman * and indicate that this particular timeout was for client 236ca3176e7SBrian Feldman * alive by setting the client_alive_scheduled flag. 237ca3176e7SBrian Feldman * 238ca3176e7SBrian Feldman * this could be randomized somewhat to make traffic 239ca3176e7SBrian Feldman * analysis more difficult, but we're not doing it yet. 240ca3176e7SBrian Feldman */ 241ca86bcf2SDag-Erling Smørgrav if (options.client_alive_interval) { 242076ad2f8SDag-Erling Smørgrav uint64_t keepalive_ms = 243076ad2f8SDag-Erling Smørgrav (uint64_t)options.client_alive_interval * 1000; 244076ad2f8SDag-Erling Smørgrav 245ca3176e7SBrian Feldman client_alive_scheduled = 1; 246076ad2f8SDag-Erling Smørgrav if (max_time_ms == 0 || max_time_ms > keepalive_ms) 247076ad2f8SDag-Erling Smørgrav max_time_ms = keepalive_ms; 248af12a3e7SDag-Erling Smørgrav } 249511b41d2SMark Murray 250af12a3e7SDag-Erling Smørgrav #if 0 251a04a10f8SKris Kennaway /* wrong: bad condition XXX */ 252a04a10f8SKris Kennaway if (channel_not_very_much_buffered_data()) 253af12a3e7SDag-Erling Smørgrav #endif 254ca3176e7SBrian Feldman FD_SET(connection_in, *readsetp); 255af12a3e7SDag-Erling Smørgrav notify_prepare(*readsetp); 256511b41d2SMark Murray 257511b41d2SMark Murray /* 258511b41d2SMark Murray * If we have buffered packet data going to the client, mark that 259511b41d2SMark Murray * descriptor. 260511b41d2SMark Murray */ 261511b41d2SMark Murray if (packet_have_data_to_write()) 262ca3176e7SBrian Feldman FD_SET(connection_out, *writesetp); 263511b41d2SMark Murray 264511b41d2SMark Murray /* 265511b41d2SMark Murray * If child has terminated and there is enough buffer space to read 266511b41d2SMark Murray * from it, then read as much as is available and exit. 267511b41d2SMark Murray */ 268511b41d2SMark Murray if (child_terminated && packet_not_very_much_data_to_write()) 269076ad2f8SDag-Erling Smørgrav if (max_time_ms == 0 || client_alive_scheduled) 270076ad2f8SDag-Erling Smørgrav max_time_ms = 100; 271511b41d2SMark Murray 272076ad2f8SDag-Erling Smørgrav if (max_time_ms == 0) 273511b41d2SMark Murray tvp = NULL; 274511b41d2SMark Murray else { 275076ad2f8SDag-Erling Smørgrav tv.tv_sec = max_time_ms / 1000; 276076ad2f8SDag-Erling Smørgrav tv.tv_usec = 1000 * (max_time_ms % 1000); 277511b41d2SMark Murray tvp = &tv; 278511b41d2SMark Murray } 279511b41d2SMark Murray 280511b41d2SMark Murray /* Wait for something to happen, or the timeout to expire. */ 281ca3176e7SBrian Feldman ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 282511b41d2SMark Murray 283ca3176e7SBrian Feldman if (ret == -1) { 284af12a3e7SDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 285af12a3e7SDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 286511b41d2SMark Murray if (errno != EINTR) 287511b41d2SMark Murray error("select: %.100s", strerror(errno)); 2884f52dfbbSDag-Erling Smørgrav } else if (client_alive_scheduled) { 2894f52dfbbSDag-Erling Smørgrav time_t now = monotime(); 2904f52dfbbSDag-Erling Smørgrav 2914f52dfbbSDag-Erling Smørgrav if (ret == 0) { /* timeout */ 2924f52dfbbSDag-Erling Smørgrav client_alive_check(ssh); 2934f52dfbbSDag-Erling Smørgrav } else if (FD_ISSET(connection_in, *readsetp)) { 2944f52dfbbSDag-Erling Smørgrav last_client_time = now; 2954f52dfbbSDag-Erling Smørgrav } else if (last_client_time != 0 && last_client_time + 2964f52dfbbSDag-Erling Smørgrav options.client_alive_interval <= now) { 2974f52dfbbSDag-Erling Smørgrav client_alive_check(ssh); 2984f52dfbbSDag-Erling Smørgrav last_client_time = now; 2994f52dfbbSDag-Erling Smørgrav } 3004f52dfbbSDag-Erling Smørgrav } 301ca3176e7SBrian Feldman 302af12a3e7SDag-Erling Smørgrav notify_done(*readsetp); 303511b41d2SMark Murray } 304511b41d2SMark Murray 305511b41d2SMark Murray /* 306511b41d2SMark Murray * Processes input from the client and the program. Input data is stored 307511b41d2SMark Murray * in buffers and processed later. 308511b41d2SMark Murray */ 309ca86bcf2SDag-Erling Smørgrav static int 3104f52dfbbSDag-Erling Smørgrav process_input(struct ssh *ssh, fd_set *readset, int connection_in) 311511b41d2SMark Murray { 312511b41d2SMark Murray int len; 313511b41d2SMark Murray char buf[16384]; 314511b41d2SMark Murray 315511b41d2SMark Murray /* Read and buffer any input data from the client. */ 316511b41d2SMark Murray if (FD_ISSET(connection_in, readset)) { 317acc1a9efSDag-Erling Smørgrav len = read(connection_in, buf, sizeof(buf)); 318511b41d2SMark Murray if (len == 0) { 319076ad2f8SDag-Erling Smørgrav verbose("Connection closed by %.100s port %d", 320076ad2f8SDag-Erling Smørgrav ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 321ca86bcf2SDag-Erling Smørgrav return -1; 3222632b0c8SKris Kennaway } else if (len < 0) { 323d4af9e69SDag-Erling Smørgrav if (errno != EINTR && errno != EAGAIN && 324d4af9e69SDag-Erling Smørgrav errno != EWOULDBLOCK) { 325f388f5efSDag-Erling Smørgrav verbose("Read error from remote host " 326076ad2f8SDag-Erling Smørgrav "%.100s port %d: %.100s", 327076ad2f8SDag-Erling Smørgrav ssh_remote_ipaddr(ssh), 328076ad2f8SDag-Erling Smørgrav ssh_remote_port(ssh), strerror(errno)); 3291ec0d754SDag-Erling Smørgrav cleanup_exit(255); 330511b41d2SMark Murray } 3312632b0c8SKris Kennaway } else { 332511b41d2SMark Murray /* Buffer any received data. */ 333511b41d2SMark Murray packet_process_incoming(buf, len); 334511b41d2SMark Murray } 3352632b0c8SKris Kennaway } 336ca86bcf2SDag-Erling Smørgrav return 0; 3372632b0c8SKris Kennaway } 338511b41d2SMark Murray 339511b41d2SMark Murray /* 340511b41d2SMark Murray * Sends data from internal buffers to client program stdin. 341511b41d2SMark Murray */ 342af12a3e7SDag-Erling Smørgrav static void 343ca86bcf2SDag-Erling Smørgrav process_output(fd_set *writeset, int connection_out) 344511b41d2SMark Murray { 345511b41d2SMark Murray /* Send any buffered packet data to the client. */ 346511b41d2SMark Murray if (FD_ISSET(connection_out, writeset)) 347511b41d2SMark Murray packet_write_poll(); 348511b41d2SMark Murray } 349511b41d2SMark Murray 350af12a3e7SDag-Erling Smørgrav static void 3514f52dfbbSDag-Erling Smørgrav process_buffered_input_packets(struct ssh *ssh) 352a04a10f8SKris Kennaway { 3534f52dfbbSDag-Erling Smørgrav ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, NULL); 354a04a10f8SKris Kennaway } 355a04a10f8SKris Kennaway 356af12a3e7SDag-Erling Smørgrav static void 3574f52dfbbSDag-Erling Smørgrav collect_children(struct ssh *ssh) 358af12a3e7SDag-Erling Smørgrav { 359af12a3e7SDag-Erling Smørgrav pid_t pid; 360af12a3e7SDag-Erling Smørgrav sigset_t oset, nset; 361af12a3e7SDag-Erling Smørgrav int status; 362af12a3e7SDag-Erling Smørgrav 363af12a3e7SDag-Erling Smørgrav /* block SIGCHLD while we check for dead children */ 364af12a3e7SDag-Erling Smørgrav sigemptyset(&nset); 365af12a3e7SDag-Erling Smørgrav sigaddset(&nset, SIGCHLD); 366af12a3e7SDag-Erling Smørgrav sigprocmask(SIG_BLOCK, &nset, &oset); 367af12a3e7SDag-Erling Smørgrav if (child_terminated) { 368333ee039SDag-Erling Smørgrav debug("Received SIGCHLD."); 36980628bacSDag-Erling Smørgrav while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || 37080628bacSDag-Erling Smørgrav (pid < 0 && errno == EINTR)) 37180628bacSDag-Erling Smørgrav if (pid > 0) 3724f52dfbbSDag-Erling Smørgrav session_close_by_pid(ssh, pid, status); 373af12a3e7SDag-Erling Smørgrav child_terminated = 0; 374af12a3e7SDag-Erling Smørgrav } 375af12a3e7SDag-Erling Smørgrav sigprocmask(SIG_SETMASK, &oset, NULL); 376af12a3e7SDag-Erling Smørgrav } 377af12a3e7SDag-Erling Smørgrav 378a04a10f8SKris Kennaway void 3794f52dfbbSDag-Erling Smørgrav server_loop2(struct ssh *ssh, Authctxt *authctxt) 380a04a10f8SKris Kennaway { 381ca3176e7SBrian Feldman fd_set *readset = NULL, *writeset = NULL; 382acc1a9efSDag-Erling Smørgrav int max_fd; 383ca86bcf2SDag-Erling Smørgrav u_int nalloc = 0, connection_in, connection_out; 384e4a9863fSDag-Erling Smørgrav u_int64_t rekey_timeout_ms = 0; 385a04a10f8SKris Kennaway 386a04a10f8SKris Kennaway debug("Entering interactive session for SSH2."); 387a04a10f8SKris Kennaway 38847dd1d1bSDag-Erling Smørgrav signal(SIGCHLD, sigchld_handler); 389a04a10f8SKris Kennaway child_terminated = 0; 390a04a10f8SKris Kennaway connection_in = packet_get_connection_in(); 391a04a10f8SKris Kennaway connection_out = packet_get_connection_out(); 392ca3176e7SBrian Feldman 393b74df5b2SDag-Erling Smørgrav if (!use_privsep) { 394b74df5b2SDag-Erling Smørgrav signal(SIGTERM, sigterm_handler); 395b74df5b2SDag-Erling Smørgrav signal(SIGINT, sigterm_handler); 396b74df5b2SDag-Erling Smørgrav signal(SIGQUIT, sigterm_handler); 397b74df5b2SDag-Erling Smørgrav } 398b74df5b2SDag-Erling Smørgrav 399af12a3e7SDag-Erling Smørgrav notify_setup(); 400af12a3e7SDag-Erling Smørgrav 401ca86bcf2SDag-Erling Smørgrav max_fd = MAXIMUM(connection_in, connection_out); 402ca86bcf2SDag-Erling Smørgrav max_fd = MAXIMUM(max_fd, notify_pipe[0]); 403af12a3e7SDag-Erling Smørgrav 404a04a10f8SKris Kennaway server_init_dispatch(); 405a04a10f8SKris Kennaway 406a04a10f8SKris Kennaway for (;;) { 4074f52dfbbSDag-Erling Smørgrav process_buffered_input_packets(ssh); 408ca3176e7SBrian Feldman 4094f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh) && 410acc1a9efSDag-Erling Smørgrav packet_not_very_much_data_to_write()) 4114f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 4124f52dfbbSDag-Erling Smørgrav if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) 413e4a9863fSDag-Erling Smørgrav rekey_timeout_ms = packet_get_rekey_timeout() * 1000; 414e4a9863fSDag-Erling Smørgrav else 415e4a9863fSDag-Erling Smørgrav rekey_timeout_ms = 0; 416e4a9863fSDag-Erling Smørgrav 4174f52dfbbSDag-Erling Smørgrav wait_until_can_do_something(ssh, connection_in, connection_out, 418ca86bcf2SDag-Erling Smørgrav &readset, &writeset, &max_fd, &nalloc, rekey_timeout_ms); 419af12a3e7SDag-Erling Smørgrav 420b74df5b2SDag-Erling Smørgrav if (received_sigterm) { 4216888a9beSDag-Erling Smørgrav logit("Exiting on signal %d", (int)received_sigterm); 422b74df5b2SDag-Erling Smørgrav /* Clean up sessions, utmp, etc. */ 423b74df5b2SDag-Erling Smørgrav cleanup_exit(255); 424b74df5b2SDag-Erling Smørgrav } 425b74df5b2SDag-Erling Smørgrav 4264f52dfbbSDag-Erling Smørgrav collect_children(ssh); 4274f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 4284f52dfbbSDag-Erling Smørgrav channel_after_select(ssh, readset, writeset); 4294f52dfbbSDag-Erling Smørgrav if (process_input(ssh, readset, connection_in) < 0) 430ca3176e7SBrian Feldman break; 431ca86bcf2SDag-Erling Smørgrav process_output(writeset, connection_out); 432a04a10f8SKris Kennaway } 4334f52dfbbSDag-Erling Smørgrav collect_children(ssh); 434af12a3e7SDag-Erling Smørgrav 435e4a9863fSDag-Erling Smørgrav free(readset); 436e4a9863fSDag-Erling Smørgrav free(writeset); 437ca3176e7SBrian Feldman 438af12a3e7SDag-Erling Smørgrav /* free all channels, no more reads and writes */ 4394f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 440af12a3e7SDag-Erling Smørgrav 441af12a3e7SDag-Erling Smørgrav /* free remaining sessions, e.g. remove wtmp entries */ 4424f52dfbbSDag-Erling Smørgrav session_destroy_all(ssh, NULL); 443a04a10f8SKris Kennaway } 444a04a10f8SKris Kennaway 445bc5531deSDag-Erling Smørgrav static int 4464f52dfbbSDag-Erling Smørgrav server_input_keep_alive(int type, u_int32_t seq, struct ssh *ssh) 447ca3176e7SBrian Feldman { 4481ec0d754SDag-Erling Smørgrav debug("Got %d/%u for keepalive", type, seq); 449ca3176e7SBrian Feldman /* 450ca3176e7SBrian Feldman * reset timeout, since we got a sane answer from the client. 451ca3176e7SBrian Feldman * even if this was generated by something other than 452ca3176e7SBrian Feldman * the bogus CHANNEL_REQUEST we send for keepalives. 453ca3176e7SBrian Feldman */ 4547aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 455bc5531deSDag-Erling Smørgrav return 0; 456ca3176e7SBrian Feldman } 457ca3176e7SBrian Feldman 458af12a3e7SDag-Erling Smørgrav static Channel * 4594f52dfbbSDag-Erling Smørgrav server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg) 460a04a10f8SKris Kennaway { 4616888a9beSDag-Erling Smørgrav Channel *c = NULL; 462a04a10f8SKris Kennaway char *target, *originator; 463cce7d346SDag-Erling Smørgrav u_short target_port, originator_port; 464a04a10f8SKris Kennaway 465a04a10f8SKris Kennaway target = packet_get_string(NULL); 466a04a10f8SKris Kennaway target_port = packet_get_int(); 467a04a10f8SKris Kennaway originator = packet_get_string(NULL); 468a04a10f8SKris Kennaway originator_port = packet_get_int(); 469af12a3e7SDag-Erling Smørgrav packet_check_eom(); 4702632b0c8SKris Kennaway 47147dd1d1bSDag-Erling Smørgrav debug("%s: originator %s port %d, target %s port %d", __func__, 47247dd1d1bSDag-Erling Smørgrav originator, originator_port, target, target_port); 473b66f2d16SKris Kennaway 4746888a9beSDag-Erling Smørgrav /* XXX fine grained permissions */ 4756888a9beSDag-Erling Smørgrav if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && 47647dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag && 47747dd1d1bSDag-Erling Smørgrav !options.disable_forwarding) { 4784f52dfbbSDag-Erling Smørgrav c = channel_connect_to_port(ssh, target, target_port, 479d93a896eSDag-Erling Smørgrav "direct-tcpip", "direct-tcpip", reason, errmsg); 4806888a9beSDag-Erling Smørgrav } else { 4816888a9beSDag-Erling Smørgrav logit("refused local port forward: " 4826888a9beSDag-Erling Smørgrav "originator %s port %d, target %s port %d", 4836888a9beSDag-Erling Smørgrav originator, originator_port, target, target_port); 484d93a896eSDag-Erling Smørgrav if (reason != NULL) 485d93a896eSDag-Erling Smørgrav *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; 4866888a9beSDag-Erling Smørgrav } 487d4af9e69SDag-Erling Smørgrav 488e4a9863fSDag-Erling Smørgrav free(originator); 489e4a9863fSDag-Erling Smørgrav free(target); 490d4af9e69SDag-Erling Smørgrav 491af12a3e7SDag-Erling Smørgrav return c; 492ca3176e7SBrian Feldman } 493ca3176e7SBrian Feldman 494af12a3e7SDag-Erling Smørgrav static Channel * 4954f52dfbbSDag-Erling Smørgrav server_request_direct_streamlocal(struct ssh *ssh) 496a0ee8cc6SDag-Erling Smørgrav { 497a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 498a0ee8cc6SDag-Erling Smørgrav char *target, *originator; 499a0ee8cc6SDag-Erling Smørgrav u_short originator_port; 500d93a896eSDag-Erling Smørgrav struct passwd *pw = the_authctxt->pw; 501d93a896eSDag-Erling Smørgrav 502d93a896eSDag-Erling Smørgrav if (pw == NULL || !the_authctxt->valid) 50347dd1d1bSDag-Erling Smørgrav fatal("%s: no/invalid user", __func__); 504a0ee8cc6SDag-Erling Smørgrav 505a0ee8cc6SDag-Erling Smørgrav target = packet_get_string(NULL); 506a0ee8cc6SDag-Erling Smørgrav originator = packet_get_string(NULL); 507a0ee8cc6SDag-Erling Smørgrav originator_port = packet_get_int(); 508a0ee8cc6SDag-Erling Smørgrav packet_check_eom(); 509a0ee8cc6SDag-Erling Smørgrav 51047dd1d1bSDag-Erling Smørgrav debug("%s: originator %s port %d, target %s", __func__, 511a0ee8cc6SDag-Erling Smørgrav originator, originator_port, target); 512a0ee8cc6SDag-Erling Smørgrav 513a0ee8cc6SDag-Erling Smørgrav /* XXX fine grained permissions */ 514a0ee8cc6SDag-Erling Smørgrav if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && 51547dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag && 51647dd1d1bSDag-Erling Smørgrav !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) { 5174f52dfbbSDag-Erling Smørgrav c = channel_connect_to_path(ssh, target, 518a0ee8cc6SDag-Erling Smørgrav "direct-streamlocal@openssh.com", "direct-streamlocal"); 519a0ee8cc6SDag-Erling Smørgrav } else { 520a0ee8cc6SDag-Erling Smørgrav logit("refused streamlocal port forward: " 521a0ee8cc6SDag-Erling Smørgrav "originator %s port %d, target %s", 522a0ee8cc6SDag-Erling Smørgrav originator, originator_port, target); 523a0ee8cc6SDag-Erling Smørgrav } 524a0ee8cc6SDag-Erling Smørgrav 525a0ee8cc6SDag-Erling Smørgrav free(originator); 526a0ee8cc6SDag-Erling Smørgrav free(target); 527a0ee8cc6SDag-Erling Smørgrav 528a0ee8cc6SDag-Erling Smørgrav return c; 529a0ee8cc6SDag-Erling Smørgrav } 530a0ee8cc6SDag-Erling Smørgrav 531a0ee8cc6SDag-Erling Smørgrav static Channel * 5324f52dfbbSDag-Erling Smørgrav server_request_tun(struct ssh *ssh) 533b74df5b2SDag-Erling Smørgrav { 534b74df5b2SDag-Erling Smørgrav Channel *c = NULL; 53547dd1d1bSDag-Erling Smørgrav int mode, tun, sock; 53647dd1d1bSDag-Erling Smørgrav char *tmp, *ifname = NULL; 537b74df5b2SDag-Erling Smørgrav 538b74df5b2SDag-Erling Smørgrav mode = packet_get_int(); 539b74df5b2SDag-Erling Smørgrav switch (mode) { 540b74df5b2SDag-Erling Smørgrav case SSH_TUNMODE_POINTOPOINT: 541b74df5b2SDag-Erling Smørgrav case SSH_TUNMODE_ETHERNET: 542b74df5b2SDag-Erling Smørgrav break; 543b74df5b2SDag-Erling Smørgrav default: 544b74df5b2SDag-Erling Smørgrav packet_send_debug("Unsupported tunnel device mode."); 545b74df5b2SDag-Erling Smørgrav return NULL; 546b74df5b2SDag-Erling Smørgrav } 547b74df5b2SDag-Erling Smørgrav if ((options.permit_tun & mode) == 0) { 548b74df5b2SDag-Erling Smørgrav packet_send_debug("Server has rejected tunnel device " 549b74df5b2SDag-Erling Smørgrav "forwarding"); 550b74df5b2SDag-Erling Smørgrav return NULL; 551b74df5b2SDag-Erling Smørgrav } 552b74df5b2SDag-Erling Smørgrav 553b74df5b2SDag-Erling Smørgrav tun = packet_get_int(); 55447dd1d1bSDag-Erling Smørgrav if (auth_opts->force_tun_device != -1) { 55547dd1d1bSDag-Erling Smørgrav if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun) 556b74df5b2SDag-Erling Smørgrav goto done; 55747dd1d1bSDag-Erling Smørgrav tun = auth_opts->force_tun_device; 558b74df5b2SDag-Erling Smørgrav } 55947dd1d1bSDag-Erling Smørgrav sock = tun_open(tun, mode, &ifname); 560b74df5b2SDag-Erling Smørgrav if (sock < 0) 561b74df5b2SDag-Erling Smørgrav goto done; 56247dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 56347dd1d1bSDag-Erling Smørgrav 5644f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, 56560c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 566b74df5b2SDag-Erling Smørgrav c->datagram = 1; 567b74df5b2SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 568b74df5b2SDag-Erling Smørgrav if (mode == SSH_TUNMODE_POINTOPOINT) 5694f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 570d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 571b74df5b2SDag-Erling Smørgrav #endif 572b74df5b2SDag-Erling Smørgrav 57347dd1d1bSDag-Erling Smørgrav /* 57447dd1d1bSDag-Erling Smørgrav * Update the list of names exposed to the session 57547dd1d1bSDag-Erling Smørgrav * XXX remove these if the tunnels are closed (won't matter 57647dd1d1bSDag-Erling Smørgrav * much if they are already in the environment though) 57747dd1d1bSDag-Erling Smørgrav */ 57847dd1d1bSDag-Erling Smørgrav tmp = tun_fwd_ifnames; 57947dd1d1bSDag-Erling Smørgrav xasprintf(&tun_fwd_ifnames, "%s%s%s", 58047dd1d1bSDag-Erling Smørgrav tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames, 58147dd1d1bSDag-Erling Smørgrav tun_fwd_ifnames == NULL ? "" : ",", 58247dd1d1bSDag-Erling Smørgrav ifname); 58347dd1d1bSDag-Erling Smørgrav free(tmp); 58447dd1d1bSDag-Erling Smørgrav free(ifname); 58547dd1d1bSDag-Erling Smørgrav 586b74df5b2SDag-Erling Smørgrav done: 587b74df5b2SDag-Erling Smørgrav if (c == NULL) 588b74df5b2SDag-Erling Smørgrav packet_send_debug("Failed to open the tunnel device."); 589b74df5b2SDag-Erling Smørgrav return c; 590b74df5b2SDag-Erling Smørgrav } 591b74df5b2SDag-Erling Smørgrav 592b74df5b2SDag-Erling Smørgrav static Channel * 5934f52dfbbSDag-Erling Smørgrav server_request_session(struct ssh *ssh) 594ca3176e7SBrian Feldman { 595af12a3e7SDag-Erling Smørgrav Channel *c; 596ca3176e7SBrian Feldman 597ca3176e7SBrian Feldman debug("input_session_request"); 598af12a3e7SDag-Erling Smørgrav packet_check_eom(); 599d4af9e69SDag-Erling Smørgrav 600d4af9e69SDag-Erling Smørgrav if (no_more_sessions) { 601d4af9e69SDag-Erling Smørgrav packet_disconnect("Possible attack: attempt to open a session " 602d4af9e69SDag-Erling Smørgrav "after additional sessions disabled"); 603d4af9e69SDag-Erling Smørgrav } 604d4af9e69SDag-Erling Smørgrav 605ca3176e7SBrian Feldman /* 606ca3176e7SBrian Feldman * A server session has no fd to read or write until a 607ca3176e7SBrian Feldman * CHANNEL_REQUEST for a shell is made, so we set the type to 608ca3176e7SBrian Feldman * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all 609ca3176e7SBrian Feldman * CHANNEL_REQUEST messages is registered. 610ca3176e7SBrian Feldman */ 6114f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, 612af12a3e7SDag-Erling Smørgrav -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 613cf2b5f3bSDag-Erling Smørgrav 0, "server-session", 1); 6141ec0d754SDag-Erling Smørgrav if (session_open(the_authctxt, c->self) != 1) { 615af12a3e7SDag-Erling Smørgrav debug("session open failed, free channel %d", c->self); 6164f52dfbbSDag-Erling Smørgrav channel_free(ssh, c); 617ca3176e7SBrian Feldman return NULL; 618a04a10f8SKris Kennaway } 6194f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, c->self, session_close_by_channel, 0); 620af12a3e7SDag-Erling Smørgrav return c; 621af12a3e7SDag-Erling Smørgrav } 622a04a10f8SKris Kennaway 623bc5531deSDag-Erling Smørgrav static int 6244f52dfbbSDag-Erling Smørgrav server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 625a04a10f8SKris Kennaway { 626a04a10f8SKris Kennaway Channel *c = NULL; 627a04a10f8SKris Kennaway char *ctype; 628d93a896eSDag-Erling Smørgrav const char *errmsg = NULL; 629d93a896eSDag-Erling Smørgrav int rchan, reason = SSH2_OPEN_CONNECT_FAILED; 630a82e551fSDag-Erling Smørgrav u_int rmaxpack, rwindow, len; 631a04a10f8SKris Kennaway 632a04a10f8SKris Kennaway ctype = packet_get_string(&len); 633a04a10f8SKris Kennaway rchan = packet_get_int(); 634a04a10f8SKris Kennaway rwindow = packet_get_int(); 635a04a10f8SKris Kennaway rmaxpack = packet_get_int(); 636a04a10f8SKris Kennaway 637*190cef3dSDag-Erling Smørgrav debug("%s: ctype %s rchan %d win %d max %d", __func__, 638a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 639a04a10f8SKris Kennaway 640a04a10f8SKris Kennaway if (strcmp(ctype, "session") == 0) { 6414f52dfbbSDag-Erling Smørgrav c = server_request_session(ssh); 642a04a10f8SKris Kennaway } else if (strcmp(ctype, "direct-tcpip") == 0) { 6434f52dfbbSDag-Erling Smørgrav c = server_request_direct_tcpip(ssh, &reason, &errmsg); 644a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { 6454f52dfbbSDag-Erling Smørgrav c = server_request_direct_streamlocal(ssh); 646b74df5b2SDag-Erling Smørgrav } else if (strcmp(ctype, "tun@openssh.com") == 0) { 6474f52dfbbSDag-Erling Smørgrav c = server_request_tun(ssh); 648a04a10f8SKris Kennaway } 649a04a10f8SKris Kennaway if (c != NULL) { 650*190cef3dSDag-Erling Smørgrav debug("%s: confirm %s", __func__, ctype); 651a04a10f8SKris Kennaway c->remote_id = rchan; 6524f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 653a04a10f8SKris Kennaway c->remote_window = rwindow; 654a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 655af12a3e7SDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 656a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 657a04a10f8SKris Kennaway packet_put_int(c->remote_id); 658a04a10f8SKris Kennaway packet_put_int(c->self); 659a04a10f8SKris Kennaway packet_put_int(c->local_window); 660a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 661a04a10f8SKris Kennaway packet_send(); 662af12a3e7SDag-Erling Smørgrav } 663a04a10f8SKris Kennaway } else { 664*190cef3dSDag-Erling Smørgrav debug("%s: failure %s", __func__, ctype); 665a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 666a04a10f8SKris Kennaway packet_put_int(rchan); 667d93a896eSDag-Erling Smørgrav packet_put_int(reason); 668d93a896eSDag-Erling Smørgrav packet_put_cstring(errmsg ? errmsg : "open failed"); 669a04a10f8SKris Kennaway packet_put_cstring(""); 670a04a10f8SKris Kennaway packet_send(); 671a04a10f8SKris Kennaway } 672e4a9863fSDag-Erling Smørgrav free(ctype); 673bc5531deSDag-Erling Smørgrav return 0; 674a04a10f8SKris Kennaway } 675a04a10f8SKris Kennaway 676bc5531deSDag-Erling Smørgrav static int 6774f52dfbbSDag-Erling Smørgrav server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) 678bc5531deSDag-Erling Smørgrav { 679bc5531deSDag-Erling Smørgrav struct sshbuf *resp = NULL; 680bc5531deSDag-Erling Smørgrav struct sshbuf *sigbuf = NULL; 681bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; 68247dd1d1bSDag-Erling Smørgrav int r, ndx, kexsigtype, use_kexsigtype, success = 0; 683bc5531deSDag-Erling Smørgrav const u_char *blob; 684bc5531deSDag-Erling Smørgrav u_char *sig = 0; 685bc5531deSDag-Erling Smørgrav size_t blen, slen; 686bc5531deSDag-Erling Smørgrav 687bc5531deSDag-Erling Smørgrav if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) 688bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 689bc5531deSDag-Erling Smørgrav 69047dd1d1bSDag-Erling Smørgrav kexsigtype = sshkey_type_plain( 69147dd1d1bSDag-Erling Smørgrav sshkey_type_from_name(ssh->kex->hostkey_alg)); 692bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 693bc5531deSDag-Erling Smørgrav sshkey_free(key); 694bc5531deSDag-Erling Smørgrav key = NULL; 695bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || 696bc5531deSDag-Erling Smørgrav (r = sshkey_from_blob(blob, blen, &key)) != 0) { 697bc5531deSDag-Erling Smørgrav error("%s: couldn't parse key: %s", 698bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 699bc5531deSDag-Erling Smørgrav goto out; 700bc5531deSDag-Erling Smørgrav } 701bc5531deSDag-Erling Smørgrav /* 702bc5531deSDag-Erling Smørgrav * Better check that this is actually one of our hostkeys 703bc5531deSDag-Erling Smørgrav * before attempting to sign anything with it. 704bc5531deSDag-Erling Smørgrav */ 705bc5531deSDag-Erling Smørgrav if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { 706bc5531deSDag-Erling Smørgrav error("%s: unknown host %s key", 707bc5531deSDag-Erling Smørgrav __func__, sshkey_type(key)); 708bc5531deSDag-Erling Smørgrav goto out; 709bc5531deSDag-Erling Smørgrav } 710bc5531deSDag-Erling Smørgrav /* 711bc5531deSDag-Erling Smørgrav * XXX refactor: make kex->sign just use an index rather 712bc5531deSDag-Erling Smørgrav * than passing in public and private keys 713bc5531deSDag-Erling Smørgrav */ 714bc5531deSDag-Erling Smørgrav if ((key_prv = get_hostkey_by_index(ndx)) == NULL && 715bc5531deSDag-Erling Smørgrav (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { 716bc5531deSDag-Erling Smørgrav error("%s: can't retrieve hostkey %d", __func__, ndx); 717bc5531deSDag-Erling Smørgrav goto out; 718bc5531deSDag-Erling Smørgrav } 719bc5531deSDag-Erling Smørgrav sshbuf_reset(sigbuf); 720bc5531deSDag-Erling Smørgrav free(sig); 721bc5531deSDag-Erling Smørgrav sig = NULL; 72247dd1d1bSDag-Erling Smørgrav /* 72347dd1d1bSDag-Erling Smørgrav * For RSA keys, prefer to use the signature type negotiated 72447dd1d1bSDag-Erling Smørgrav * during KEX to the default (SHA1). 72547dd1d1bSDag-Erling Smørgrav */ 72647dd1d1bSDag-Erling Smørgrav use_kexsigtype = kexsigtype == KEY_RSA && 72747dd1d1bSDag-Erling Smørgrav sshkey_type_plain(key->type) == KEY_RSA; 728bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(sigbuf, 729bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 730bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(sigbuf, 731bc5531deSDag-Erling Smørgrav ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || 732bc5531deSDag-Erling Smørgrav (r = sshkey_puts(key, sigbuf)) != 0 || 733bc5531deSDag-Erling Smørgrav (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen, 73447dd1d1bSDag-Erling Smørgrav sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), 73547dd1d1bSDag-Erling Smørgrav use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0)) != 0 || 736bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(resp, sig, slen)) != 0) { 737bc5531deSDag-Erling Smørgrav error("%s: couldn't prepare signature: %s", 738bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 739bc5531deSDag-Erling Smørgrav goto out; 740bc5531deSDag-Erling Smørgrav } 741bc5531deSDag-Erling Smørgrav } 742bc5531deSDag-Erling Smørgrav /* Success */ 743bc5531deSDag-Erling Smørgrav *respp = resp; 744bc5531deSDag-Erling Smørgrav resp = NULL; /* don't free it */ 745bc5531deSDag-Erling Smørgrav success = 1; 746bc5531deSDag-Erling Smørgrav out: 747bc5531deSDag-Erling Smørgrav free(sig); 748bc5531deSDag-Erling Smørgrav sshbuf_free(resp); 749bc5531deSDag-Erling Smørgrav sshbuf_free(sigbuf); 750bc5531deSDag-Erling Smørgrav sshkey_free(key); 751bc5531deSDag-Erling Smørgrav return success; 752bc5531deSDag-Erling Smørgrav } 753bc5531deSDag-Erling Smørgrav 754bc5531deSDag-Erling Smørgrav static int 7554f52dfbbSDag-Erling Smørgrav server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 756ca3176e7SBrian Feldman { 757ca3176e7SBrian Feldman char *rtype; 758ca3176e7SBrian Feldman int want_reply; 759bc5531deSDag-Erling Smørgrav int r, success = 0, allocated_listen_port = 0; 760bc5531deSDag-Erling Smørgrav struct sshbuf *resp = NULL; 761d93a896eSDag-Erling Smørgrav struct passwd *pw = the_authctxt->pw; 762d93a896eSDag-Erling Smørgrav 763d93a896eSDag-Erling Smørgrav if (pw == NULL || !the_authctxt->valid) 764*190cef3dSDag-Erling Smørgrav fatal("%s: no/invalid user", __func__); 765ca3176e7SBrian Feldman 766ca3176e7SBrian Feldman rtype = packet_get_string(NULL); 767ca3176e7SBrian Feldman want_reply = packet_get_char(); 768*190cef3dSDag-Erling Smørgrav debug("%s: rtype %s want_reply %d", __func__, rtype, want_reply); 769ca3176e7SBrian Feldman 770ca3176e7SBrian Feldman /* -R style forwarding */ 771ca3176e7SBrian Feldman if (strcmp(rtype, "tcpip-forward") == 0) { 772a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 773ca3176e7SBrian Feldman 774a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 775a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = packet_get_string(NULL); 776a0ee8cc6SDag-Erling Smørgrav fwd.listen_port = (u_short)packet_get_int(); 777*190cef3dSDag-Erling Smørgrav debug("%s: tcpip-forward listen %s port %d", __func__, 778a0ee8cc6SDag-Erling Smørgrav fwd.listen_host, fwd.listen_port); 779ca3176e7SBrian Feldman 780ca3176e7SBrian Feldman /* check permissions */ 7816888a9beSDag-Erling Smørgrav if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || 78247dd1d1bSDag-Erling Smørgrav !auth_opts->permit_port_forwarding_flag || 78347dd1d1bSDag-Erling Smørgrav options.disable_forwarding || 784076ad2f8SDag-Erling Smørgrav (!want_reply && fwd.listen_port == 0) || 785ca86bcf2SDag-Erling Smørgrav (fwd.listen_port != 0 && 786ca86bcf2SDag-Erling Smørgrav !bind_permitted(fwd.listen_port, pw->pw_uid))) { 787ca3176e7SBrian Feldman success = 0; 788ca3176e7SBrian Feldman packet_send_debug("Server has disabled port forwarding."); 789ca3176e7SBrian Feldman } else { 790ca3176e7SBrian Feldman /* Start listening on the port */ 7914f52dfbbSDag-Erling Smørgrav success = channel_setup_remote_fwd_listener(ssh, &fwd, 792a0ee8cc6SDag-Erling Smørgrav &allocated_listen_port, &options.fwd_opts); 793ca3176e7SBrian Feldman } 794a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_host); 795bc5531deSDag-Erling Smørgrav if ((resp = sshbuf_new()) == NULL) 796bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 797acc1a9efSDag-Erling Smørgrav if (allocated_listen_port != 0 && 798acc1a9efSDag-Erling Smørgrav (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) 799bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r)); 80021e764dfSDag-Erling Smørgrav } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { 801a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 80221e764dfSDag-Erling Smørgrav 803a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 804a0ee8cc6SDag-Erling Smørgrav fwd.listen_host = packet_get_string(NULL); 805a0ee8cc6SDag-Erling Smørgrav fwd.listen_port = (u_short)packet_get_int(); 80621e764dfSDag-Erling Smørgrav debug("%s: cancel-tcpip-forward addr %s port %d", __func__, 807a0ee8cc6SDag-Erling Smørgrav fwd.listen_host, fwd.listen_port); 80821e764dfSDag-Erling Smørgrav 8094f52dfbbSDag-Erling Smørgrav success = channel_cancel_rport_listener(ssh, &fwd); 810a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_host); 811a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { 812a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 813a0ee8cc6SDag-Erling Smørgrav 814a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 815a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = packet_get_string(NULL); 816*190cef3dSDag-Erling Smørgrav debug("%s: streamlocal-forward listen path %s", __func__, 817a0ee8cc6SDag-Erling Smørgrav fwd.listen_path); 818a0ee8cc6SDag-Erling Smørgrav 819a0ee8cc6SDag-Erling Smørgrav /* check permissions */ 820a0ee8cc6SDag-Erling Smørgrav if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 82147dd1d1bSDag-Erling Smørgrav || !auth_opts->permit_port_forwarding_flag || 82247dd1d1bSDag-Erling Smørgrav options.disable_forwarding || 823d93a896eSDag-Erling Smørgrav (pw->pw_uid != 0 && !use_privsep)) { 824a0ee8cc6SDag-Erling Smørgrav success = 0; 825d93a896eSDag-Erling Smørgrav packet_send_debug("Server has disabled " 826d93a896eSDag-Erling Smørgrav "streamlocal forwarding."); 827a0ee8cc6SDag-Erling Smørgrav } else { 828a0ee8cc6SDag-Erling Smørgrav /* Start listening on the socket */ 8294f52dfbbSDag-Erling Smørgrav success = channel_setup_remote_fwd_listener(ssh, 830a0ee8cc6SDag-Erling Smørgrav &fwd, NULL, &options.fwd_opts); 831a0ee8cc6SDag-Erling Smørgrav } 832a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 833a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { 834a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 835a0ee8cc6SDag-Erling Smørgrav 836a0ee8cc6SDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 837a0ee8cc6SDag-Erling Smørgrav fwd.listen_path = packet_get_string(NULL); 838a0ee8cc6SDag-Erling Smørgrav debug("%s: cancel-streamlocal-forward path %s", __func__, 839a0ee8cc6SDag-Erling Smørgrav fwd.listen_path); 840a0ee8cc6SDag-Erling Smørgrav 8414f52dfbbSDag-Erling Smørgrav success = channel_cancel_rport_listener(ssh, &fwd); 842a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 843d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { 844d4af9e69SDag-Erling Smørgrav no_more_sessions = 1; 845d4af9e69SDag-Erling Smørgrav success = 1; 846bc5531deSDag-Erling Smørgrav } else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) { 8474f52dfbbSDag-Erling Smørgrav success = server_input_hostkeys_prove(ssh, &resp); 848ca3176e7SBrian Feldman } 849ca3176e7SBrian Feldman if (want_reply) { 850ca3176e7SBrian Feldman packet_start(success ? 851ca3176e7SBrian Feldman SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 852bc5531deSDag-Erling Smørgrav if (success && resp != NULL) 8534f52dfbbSDag-Erling Smørgrav ssh_packet_put_raw(ssh, sshbuf_ptr(resp), 854bc5531deSDag-Erling Smørgrav sshbuf_len(resp)); 855ca3176e7SBrian Feldman packet_send(); 856ca3176e7SBrian Feldman packet_write_wait(); 857ca3176e7SBrian Feldman } 858e4a9863fSDag-Erling Smørgrav free(rtype); 859bc5531deSDag-Erling Smørgrav sshbuf_free(resp); 860bc5531deSDag-Erling Smørgrav return 0; 861ca3176e7SBrian Feldman } 862333ee039SDag-Erling Smørgrav 863bc5531deSDag-Erling Smørgrav static int 8644f52dfbbSDag-Erling Smørgrav server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 865af12a3e7SDag-Erling Smørgrav { 866af12a3e7SDag-Erling Smørgrav Channel *c; 867af12a3e7SDag-Erling Smørgrav int id, reply, success = 0; 868af12a3e7SDag-Erling Smørgrav char *rtype; 869ca3176e7SBrian Feldman 870af12a3e7SDag-Erling Smørgrav id = packet_get_int(); 871af12a3e7SDag-Erling Smørgrav rtype = packet_get_string(NULL); 872af12a3e7SDag-Erling Smørgrav reply = packet_get_char(); 873af12a3e7SDag-Erling Smørgrav 874af12a3e7SDag-Erling Smørgrav debug("server_input_channel_req: channel %d request %s reply %d", 875af12a3e7SDag-Erling Smørgrav id, rtype, reply); 876af12a3e7SDag-Erling Smørgrav 8774f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 878af12a3e7SDag-Erling Smørgrav packet_disconnect("server_input_channel_req: " 879af12a3e7SDag-Erling Smørgrav "unknown channel %d", id); 880d4af9e69SDag-Erling Smørgrav if (!strcmp(rtype, "eow@openssh.com")) { 881d4af9e69SDag-Erling Smørgrav packet_check_eom(); 8824f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 883d4af9e69SDag-Erling Smørgrav } else if ((c->type == SSH_CHANNEL_LARVAL || 884d4af9e69SDag-Erling Smørgrav c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) 8854f52dfbbSDag-Erling Smørgrav success = session_input_channel_req(ssh, c, rtype); 886a0ee8cc6SDag-Erling Smørgrav if (reply && !(c->flags & CHAN_CLOSE_SENT)) { 8874f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 8884f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: no remote_id", 8894f52dfbbSDag-Erling Smørgrav __func__, c->self); 890af12a3e7SDag-Erling Smørgrav packet_start(success ? 891af12a3e7SDag-Erling Smørgrav SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 892af12a3e7SDag-Erling Smørgrav packet_put_int(c->remote_id); 893af12a3e7SDag-Erling Smørgrav packet_send(); 894af12a3e7SDag-Erling Smørgrav } 895e4a9863fSDag-Erling Smørgrav free(rtype); 896bc5531deSDag-Erling Smørgrav return 0; 897af12a3e7SDag-Erling Smørgrav } 898af12a3e7SDag-Erling Smørgrav 899af12a3e7SDag-Erling Smørgrav static void 900ca86bcf2SDag-Erling Smørgrav server_init_dispatch(void) 901a04a10f8SKris Kennaway { 902ca86bcf2SDag-Erling Smørgrav debug("server_init_dispatch"); 903a04a10f8SKris Kennaway dispatch_init(&dispatch_protocol_error); 904a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 905a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 906a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 907a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 908a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); 909a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 910a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 911af12a3e7SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); 912a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 913ca3176e7SBrian Feldman dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); 914ca3176e7SBrian Feldman /* client_alive */ 915cce7d346SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive); 916cce7d346SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive); 9171ec0d754SDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive); 9181ec0d754SDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive); 919ca3176e7SBrian Feldman /* rekeying */ 920ca3176e7SBrian Feldman dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 921a04a10f8SKris Kennaway } 922