17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi> 37c478bd9Sstevel@tonic-gate * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 47c478bd9Sstevel@tonic-gate * All rights reserved 57c478bd9Sstevel@tonic-gate * Server main loop for handling the interactive session. 67c478bd9Sstevel@tonic-gate * 77c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 87c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 97c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 107c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 117c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * SSH2 support by Markus Friedl. 147c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 157c478bd9Sstevel@tonic-gate * 167c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 177c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 187c478bd9Sstevel@tonic-gate * are met: 197c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 207c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 217c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 227c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 237c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 247c478bd9Sstevel@tonic-gate * 257c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 267c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 277c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 287c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 297c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 307c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 317c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 327c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 337c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 347c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 357c478bd9Sstevel@tonic-gate */ 367c478bd9Sstevel@tonic-gate /* 379b03ea0fSjp161948 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 387c478bd9Sstevel@tonic-gate * Use is subject to license terms. 397c478bd9Sstevel@tonic-gate */ 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #include "includes.h" 427c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: serverloop.c,v 1.104 2002/09/19 16:03:15 stevesk Exp $"); 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate #include "xmalloc.h" 477c478bd9Sstevel@tonic-gate #include "packet.h" 487c478bd9Sstevel@tonic-gate #include "buffer.h" 497c478bd9Sstevel@tonic-gate #include "log.h" 507c478bd9Sstevel@tonic-gate #include "servconf.h" 517c478bd9Sstevel@tonic-gate #include "canohost.h" 527c478bd9Sstevel@tonic-gate #include "sshpty.h" 537c478bd9Sstevel@tonic-gate #include "channels.h" 547c478bd9Sstevel@tonic-gate #include "compat.h" 557c478bd9Sstevel@tonic-gate #include "ssh1.h" 567c478bd9Sstevel@tonic-gate #include "ssh2.h" 577c478bd9Sstevel@tonic-gate #include "auth.h" 587c478bd9Sstevel@tonic-gate #include "session.h" 597c478bd9Sstevel@tonic-gate #include "dispatch.h" 607c478bd9Sstevel@tonic-gate #include "auth-options.h" 617c478bd9Sstevel@tonic-gate #include "serverloop.h" 627c478bd9Sstevel@tonic-gate #include "misc.h" 637c478bd9Sstevel@tonic-gate #include "kex.h" 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 667c478bd9Sstevel@tonic-gate #include "altprivsep.h" 677c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP*/ 687c478bd9Sstevel@tonic-gate 697c478bd9Sstevel@tonic-gate extern ServerOptions options; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* XXX */ 727c478bd9Sstevel@tonic-gate extern Kex *xxx_kex; 737c478bd9Sstevel@tonic-gate static Authctxt *xxx_authctxt; 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate static Buffer stdin_buffer; /* Buffer for stdin data. */ 767c478bd9Sstevel@tonic-gate static Buffer stdout_buffer; /* Buffer for stdout data. */ 777c478bd9Sstevel@tonic-gate static Buffer stderr_buffer; /* Buffer for stderr data. */ 787c478bd9Sstevel@tonic-gate static int fdin; /* Descriptor for stdin (for writing) */ 797c478bd9Sstevel@tonic-gate static int fdout; /* Descriptor for stdout (for reading); 807c478bd9Sstevel@tonic-gate May be same number as fdin. */ 817c478bd9Sstevel@tonic-gate static int fderr; /* Descriptor for stderr. May be -1. */ 827c478bd9Sstevel@tonic-gate static long stdin_bytes = 0; /* Number of bytes written to stdin. */ 837c478bd9Sstevel@tonic-gate static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */ 847c478bd9Sstevel@tonic-gate static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */ 857c478bd9Sstevel@tonic-gate static long fdout_bytes = 0; /* Number of stdout bytes read from program. */ 867c478bd9Sstevel@tonic-gate static int stdin_eof = 0; /* EOF message received from client. */ 877c478bd9Sstevel@tonic-gate static int fdout_eof = 0; /* EOF encountered reading from fdout. */ 887c478bd9Sstevel@tonic-gate static int fderr_eof = 0; /* EOF encountered readung from fderr. */ 897c478bd9Sstevel@tonic-gate static int fdin_is_tty = 0; /* fdin points to a tty. */ 907c478bd9Sstevel@tonic-gate static int connection_in; /* Connection to client (input). */ 917c478bd9Sstevel@tonic-gate static int connection_out; /* Connection to client (output). */ 927c478bd9Sstevel@tonic-gate static int connection_closed = 0; /* Connection to client closed. */ 937c478bd9Sstevel@tonic-gate static u_int buffer_high; /* "Soft" max buffer size. */ 947c478bd9Sstevel@tonic-gate static int client_alive_timeouts = 0; 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate /* 977c478bd9Sstevel@tonic-gate * This SIGCHLD kludge is used to detect when the child exits. The server 987c478bd9Sstevel@tonic-gate * will exit after that, as soon as forwarded connections have terminated. 997c478bd9Sstevel@tonic-gate */ 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate /* prototypes */ 1047c478bd9Sstevel@tonic-gate static void server_init_dispatch(void); 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate /* 1077c478bd9Sstevel@tonic-gate * we write to this pipe if a SIGCHLD is caught in order to avoid 1087c478bd9Sstevel@tonic-gate * the race between select() and child_terminated 1097c478bd9Sstevel@tonic-gate */ 1107c478bd9Sstevel@tonic-gate static int notify_pipe[2]; 1117c478bd9Sstevel@tonic-gate static void 1127c478bd9Sstevel@tonic-gate notify_setup(void) 1137c478bd9Sstevel@tonic-gate { 1147c478bd9Sstevel@tonic-gate if (pipe(notify_pipe) < 0) { 1157c478bd9Sstevel@tonic-gate error("pipe(notify_pipe) failed %s", strerror(errno)); 1167c478bd9Sstevel@tonic-gate } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) || 1177c478bd9Sstevel@tonic-gate (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) { 1187c478bd9Sstevel@tonic-gate error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); 1197c478bd9Sstevel@tonic-gate (void) close(notify_pipe[0]); 1207c478bd9Sstevel@tonic-gate (void) close(notify_pipe[1]); 1217c478bd9Sstevel@tonic-gate } else { 1227c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[0]); 1237c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[1]); 1247c478bd9Sstevel@tonic-gate return; 1257c478bd9Sstevel@tonic-gate } 1267c478bd9Sstevel@tonic-gate notify_pipe[0] = -1; /* read end */ 1277c478bd9Sstevel@tonic-gate notify_pipe[1] = -1; /* write end */ 1287c478bd9Sstevel@tonic-gate } 1297c478bd9Sstevel@tonic-gate static void 1307c478bd9Sstevel@tonic-gate notify_parent(void) 1317c478bd9Sstevel@tonic-gate { 1327c478bd9Sstevel@tonic-gate if (notify_pipe[1] != -1) 1337c478bd9Sstevel@tonic-gate (void) write(notify_pipe[1], "", 1); 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate static void 1367c478bd9Sstevel@tonic-gate notify_prepare(fd_set *readset) 1377c478bd9Sstevel@tonic-gate { 1387c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1) 1397c478bd9Sstevel@tonic-gate FD_SET(notify_pipe[0], readset); 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate static void 1427c478bd9Sstevel@tonic-gate notify_done(fd_set *readset) 1437c478bd9Sstevel@tonic-gate { 1447c478bd9Sstevel@tonic-gate char c; 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) 1477c478bd9Sstevel@tonic-gate while (read(notify_pipe[0], &c, 1) != -1) 1487c478bd9Sstevel@tonic-gate debug2("notify_done: reading"); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate static void 1527c478bd9Sstevel@tonic-gate sigchld_handler(int sig) 1537c478bd9Sstevel@tonic-gate { 1547c478bd9Sstevel@tonic-gate int save_errno = errno; 1557c478bd9Sstevel@tonic-gate debug("Received SIGCHLD."); 1567c478bd9Sstevel@tonic-gate child_terminated = 1; 1577c478bd9Sstevel@tonic-gate #ifndef _UNICOS 1587c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 1597c478bd9Sstevel@tonic-gate #endif 1607c478bd9Sstevel@tonic-gate notify_parent(); 1617c478bd9Sstevel@tonic-gate errno = save_errno; 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate /* 1657c478bd9Sstevel@tonic-gate * Make packets from buffered stderr data, and buffer it for sending 1667c478bd9Sstevel@tonic-gate * to the client. 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate static void 1697c478bd9Sstevel@tonic-gate make_packets_from_stderr_data(void) 1707c478bd9Sstevel@tonic-gate { 1717c478bd9Sstevel@tonic-gate int len; 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate /* Send buffered stderr data to the client. */ 1747c478bd9Sstevel@tonic-gate while (buffer_len(&stderr_buffer) > 0 && 1757c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) { 1767c478bd9Sstevel@tonic-gate len = buffer_len(&stderr_buffer); 1777c478bd9Sstevel@tonic-gate if (packet_is_interactive()) { 1787c478bd9Sstevel@tonic-gate if (len > 512) 1797c478bd9Sstevel@tonic-gate len = 512; 1807c478bd9Sstevel@tonic-gate } else { 1817c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */ 1827c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize()) 1837c478bd9Sstevel@tonic-gate len = packet_get_maxsize(); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA); 1867c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer), len); 1877c478bd9Sstevel@tonic-gate packet_send(); 1887c478bd9Sstevel@tonic-gate buffer_consume(&stderr_buffer, len); 1897c478bd9Sstevel@tonic-gate stderr_bytes += len; 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate /* 1947c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data, and buffer it for sending to the 1957c478bd9Sstevel@tonic-gate * client. 1967c478bd9Sstevel@tonic-gate */ 1977c478bd9Sstevel@tonic-gate static void 1987c478bd9Sstevel@tonic-gate make_packets_from_stdout_data(void) 1997c478bd9Sstevel@tonic-gate { 2007c478bd9Sstevel@tonic-gate int len; 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate /* Send buffered stdout data to the client. */ 2037c478bd9Sstevel@tonic-gate while (buffer_len(&stdout_buffer) > 0 && 2047c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) { 2057c478bd9Sstevel@tonic-gate len = buffer_len(&stdout_buffer); 2067c478bd9Sstevel@tonic-gate if (packet_is_interactive()) { 2077c478bd9Sstevel@tonic-gate if (len > 512) 2087c478bd9Sstevel@tonic-gate len = 512; 2097c478bd9Sstevel@tonic-gate } else { 2107c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */ 2117c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize()) 2127c478bd9Sstevel@tonic-gate len = packet_get_maxsize(); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA); 2157c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer), len); 2167c478bd9Sstevel@tonic-gate packet_send(); 2177c478bd9Sstevel@tonic-gate buffer_consume(&stdout_buffer, len); 2187c478bd9Sstevel@tonic-gate stdout_bytes += len; 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate } 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate static void 2237c478bd9Sstevel@tonic-gate client_alive_check(void) 2247c478bd9Sstevel@tonic-gate { 2257c478bd9Sstevel@tonic-gate static int had_channel = 0; 2267c478bd9Sstevel@tonic-gate int id; 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate id = channel_find_open(); 2297c478bd9Sstevel@tonic-gate if (id == -1) { 2307c478bd9Sstevel@tonic-gate if (!had_channel) 2317c478bd9Sstevel@tonic-gate return; 2327c478bd9Sstevel@tonic-gate packet_disconnect("No open channels after timeout!"); 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate had_channel = 1; 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate /* timeout, check to see how many we have had */ 2377c478bd9Sstevel@tonic-gate if (++client_alive_timeouts > options.client_alive_count_max) 2387c478bd9Sstevel@tonic-gate packet_disconnect("Timeout, your session not responding."); 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate /* 2417c478bd9Sstevel@tonic-gate * send a bogus channel request with "wantreply", 2427c478bd9Sstevel@tonic-gate * we should get back a failure 2437c478bd9Sstevel@tonic-gate */ 2447c478bd9Sstevel@tonic-gate channel_request_start(id, "keepalive@openssh.com", 1); 2457c478bd9Sstevel@tonic-gate packet_send(); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate /* 2497c478bd9Sstevel@tonic-gate * Sleep in select() until we can do something. This will initialize the 2507c478bd9Sstevel@tonic-gate * select masks. Upon return, the masks will indicate which descriptors 2517c478bd9Sstevel@tonic-gate * have data or can accept data. Optionally, a maximum time can be specified 2527c478bd9Sstevel@tonic-gate * for the duration of the wait (0 = infinite). 2537c478bd9Sstevel@tonic-gate */ 2547c478bd9Sstevel@tonic-gate static void 2557c478bd9Sstevel@tonic-gate wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 2567c478bd9Sstevel@tonic-gate int *nallocp, u_int max_time_milliseconds) 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate struct timeval tv, *tvp; 2597c478bd9Sstevel@tonic-gate int ret; 2607c478bd9Sstevel@tonic-gate int client_alive_scheduled = 0; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate /* 2637c478bd9Sstevel@tonic-gate * if using client_alive, set the max timeout accordingly, 2647c478bd9Sstevel@tonic-gate * and indicate that this particular timeout was for client 2657c478bd9Sstevel@tonic-gate * alive by setting the client_alive_scheduled flag. 2667c478bd9Sstevel@tonic-gate * 2677c478bd9Sstevel@tonic-gate * this could be randomized somewhat to make traffic 2687c478bd9Sstevel@tonic-gate * analysis more difficult, but we're not doing it yet. 2697c478bd9Sstevel@tonic-gate */ 2707c478bd9Sstevel@tonic-gate if (compat20 && 2717c478bd9Sstevel@tonic-gate max_time_milliseconds == 0 && options.client_alive_interval) { 2727c478bd9Sstevel@tonic-gate client_alive_scheduled = 1; 2737c478bd9Sstevel@tonic-gate max_time_milliseconds = options.client_alive_interval * 1000; 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate /* Allocate and update select() masks for channel descriptors. */ 2777c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0); 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate if (compat20) { 2807c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 2817c478bd9Sstevel@tonic-gate int pipe_fd; 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate if ((pipe_fd = altprivsep_get_pipe_fd()) != -1) { 2847c478bd9Sstevel@tonic-gate *maxfdp = MAX(*maxfdp, pipe_fd); 2857c478bd9Sstevel@tonic-gate FD_SET(altprivsep_get_pipe_fd(), *readsetp); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 2887c478bd9Sstevel@tonic-gate #if 0 2897c478bd9Sstevel@tonic-gate /* wrong: bad condition XXX */ 2907c478bd9Sstevel@tonic-gate if (channel_not_very_much_buffered_data()) 2917c478bd9Sstevel@tonic-gate #endif 2927c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 2937c478bd9Sstevel@tonic-gate } else { 2947c478bd9Sstevel@tonic-gate /* 2957c478bd9Sstevel@tonic-gate * Read packets from the client unless we have too much 2967c478bd9Sstevel@tonic-gate * buffered stdin or channel data. 2977c478bd9Sstevel@tonic-gate */ 2987c478bd9Sstevel@tonic-gate if (buffer_len(&stdin_buffer) < buffer_high && 2997c478bd9Sstevel@tonic-gate channel_not_very_much_buffered_data()) 3007c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 3017c478bd9Sstevel@tonic-gate /* 3027c478bd9Sstevel@tonic-gate * If there is not too much data already buffered going to 3037c478bd9Sstevel@tonic-gate * the client, try to get some more data from the program. 3047c478bd9Sstevel@tonic-gate */ 3057c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write()) { 3067c478bd9Sstevel@tonic-gate if (!fdout_eof) 3077c478bd9Sstevel@tonic-gate FD_SET(fdout, *readsetp); 3087c478bd9Sstevel@tonic-gate if (!fderr_eof) 3097c478bd9Sstevel@tonic-gate FD_SET(fderr, *readsetp); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate /* 3127c478bd9Sstevel@tonic-gate * If we have buffered data, try to write some of that data 3137c478bd9Sstevel@tonic-gate * to the program. 3147c478bd9Sstevel@tonic-gate */ 3157c478bd9Sstevel@tonic-gate if (fdin != -1 && buffer_len(&stdin_buffer) > 0) 3167c478bd9Sstevel@tonic-gate FD_SET(fdin, *writesetp); 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate notify_prepare(*readsetp); 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate /* 3217c478bd9Sstevel@tonic-gate * If we have buffered packet data going to the client, mark that 3227c478bd9Sstevel@tonic-gate * descriptor. 3237c478bd9Sstevel@tonic-gate */ 3247c478bd9Sstevel@tonic-gate if (packet_have_data_to_write()) 3257c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate /* 3287c478bd9Sstevel@tonic-gate * If child has terminated and there is enough buffer space to read 3297c478bd9Sstevel@tonic-gate * from it, then read as much as is available and exit. 3307c478bd9Sstevel@tonic-gate */ 3317c478bd9Sstevel@tonic-gate if (child_terminated && packet_not_very_much_data_to_write()) 3327c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0 || client_alive_scheduled) 3337c478bd9Sstevel@tonic-gate max_time_milliseconds = 100; 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0) 3367c478bd9Sstevel@tonic-gate tvp = NULL; 3377c478bd9Sstevel@tonic-gate else { 3387c478bd9Sstevel@tonic-gate tv.tv_sec = max_time_milliseconds / 1000; 3397c478bd9Sstevel@tonic-gate tv.tv_usec = 1000 * (max_time_milliseconds % 1000); 3407c478bd9Sstevel@tonic-gate tvp = &tv; 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */ 3447c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate if (ret == -1) { 3477c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp); 3487c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp); 3497c478bd9Sstevel@tonic-gate if (errno != EINTR) 3507c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno)); 3517c478bd9Sstevel@tonic-gate } else if (ret == 0 && client_alive_scheduled) 3527c478bd9Sstevel@tonic-gate client_alive_check(); 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate notify_done(*readsetp); 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate /* 3587c478bd9Sstevel@tonic-gate * Processes input from the client and the program. Input data is stored 3597c478bd9Sstevel@tonic-gate * in buffers and processed later. 3607c478bd9Sstevel@tonic-gate */ 3617c478bd9Sstevel@tonic-gate static void 3627c478bd9Sstevel@tonic-gate process_input(fd_set * readset) 3637c478bd9Sstevel@tonic-gate { 3647c478bd9Sstevel@tonic-gate int len; 3657c478bd9Sstevel@tonic-gate char buf[16384]; 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* Read and buffer any input data from the client. */ 3687c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_in, readset)) { 3697c478bd9Sstevel@tonic-gate len = read(connection_in, buf, sizeof(buf)); 3707c478bd9Sstevel@tonic-gate if (len == 0) { 3717c478bd9Sstevel@tonic-gate verbose("Connection closed by %.100s", 3727c478bd9Sstevel@tonic-gate get_remote_ipaddr()); 3737c478bd9Sstevel@tonic-gate connection_closed = 1; 3747c478bd9Sstevel@tonic-gate if (compat20) 3757c478bd9Sstevel@tonic-gate return; 3767c478bd9Sstevel@tonic-gate fatal_cleanup(); 3777c478bd9Sstevel@tonic-gate } else if (len < 0) { 3787c478bd9Sstevel@tonic-gate if (errno != EINTR && errno != EAGAIN) { 3797c478bd9Sstevel@tonic-gate verbose("Read error from remote host " 3807c478bd9Sstevel@tonic-gate "%.100s: %.100s", 3817c478bd9Sstevel@tonic-gate get_remote_ipaddr(), strerror(errno)); 3827c478bd9Sstevel@tonic-gate fatal_cleanup(); 3837c478bd9Sstevel@tonic-gate } 3847c478bd9Sstevel@tonic-gate } else { 3857c478bd9Sstevel@tonic-gate /* Buffer any received data. */ 3867c478bd9Sstevel@tonic-gate packet_process_incoming(buf, len); 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate if (compat20) 3907c478bd9Sstevel@tonic-gate return; 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate /* Read and buffer any available stdout data from the program. */ 3937c478bd9Sstevel@tonic-gate if (!fdout_eof && FD_ISSET(fdout, readset)) { 3947c478bd9Sstevel@tonic-gate len = read(fdout, buf, sizeof(buf)); 3957c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 3967c478bd9Sstevel@tonic-gate /* EMPTY */ 3977c478bd9Sstevel@tonic-gate } else if (len <= 0) { 3987c478bd9Sstevel@tonic-gate fdout_eof = 1; 3997c478bd9Sstevel@tonic-gate } else { 4007c478bd9Sstevel@tonic-gate buffer_append(&stdout_buffer, buf, len); 4017c478bd9Sstevel@tonic-gate fdout_bytes += len; 4027c478bd9Sstevel@tonic-gate } 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate /* Read and buffer any available stderr data from the program. */ 4057c478bd9Sstevel@tonic-gate if (!fderr_eof && FD_ISSET(fderr, readset)) { 4067c478bd9Sstevel@tonic-gate len = read(fderr, buf, sizeof(buf)); 4077c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 4087c478bd9Sstevel@tonic-gate /* EMPTY */ 4097c478bd9Sstevel@tonic-gate } else if (len <= 0) { 4107c478bd9Sstevel@tonic-gate fderr_eof = 1; 4117c478bd9Sstevel@tonic-gate } else { 4127c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, buf, len); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate /* 4187c478bd9Sstevel@tonic-gate * Sends data from internal buffers to client program stdin. 4197c478bd9Sstevel@tonic-gate */ 4207c478bd9Sstevel@tonic-gate static void 4217c478bd9Sstevel@tonic-gate process_output(fd_set * writeset) 4227c478bd9Sstevel@tonic-gate { 4237c478bd9Sstevel@tonic-gate struct termios tio; 4247c478bd9Sstevel@tonic-gate u_char *data; 4257c478bd9Sstevel@tonic-gate u_int dlen; 4267c478bd9Sstevel@tonic-gate int len; 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate /* Write buffered data to program stdin. */ 4297c478bd9Sstevel@tonic-gate if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { 4307c478bd9Sstevel@tonic-gate data = buffer_ptr(&stdin_buffer); 4317c478bd9Sstevel@tonic-gate dlen = buffer_len(&stdin_buffer); 4327c478bd9Sstevel@tonic-gate len = write(fdin, data, dlen); 4337c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 4347c478bd9Sstevel@tonic-gate /* EMPTY */ 4357c478bd9Sstevel@tonic-gate } else if (len <= 0) { 4367c478bd9Sstevel@tonic-gate if (fdin != fdout) 4377c478bd9Sstevel@tonic-gate (void) close(fdin); 4387c478bd9Sstevel@tonic-gate else 4397c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ 4407c478bd9Sstevel@tonic-gate fdin = -1; 4417c478bd9Sstevel@tonic-gate } else { 4427c478bd9Sstevel@tonic-gate /* Successful write. */ 4437c478bd9Sstevel@tonic-gate if (fdin_is_tty && dlen >= 1 && data[0] != '\r' && 4447c478bd9Sstevel@tonic-gate tcgetattr(fdin, &tio) == 0 && 4457c478bd9Sstevel@tonic-gate !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 4467c478bd9Sstevel@tonic-gate /* 4477c478bd9Sstevel@tonic-gate * Simulate echo to reduce the impact of 4487c478bd9Sstevel@tonic-gate * traffic analysis 4497c478bd9Sstevel@tonic-gate */ 4507c478bd9Sstevel@tonic-gate packet_send_ignore(len); 4517c478bd9Sstevel@tonic-gate packet_send(); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate /* Consume the data from the buffer. */ 4547c478bd9Sstevel@tonic-gate buffer_consume(&stdin_buffer, len); 4557c478bd9Sstevel@tonic-gate /* Update the count of bytes written to the program. */ 4567c478bd9Sstevel@tonic-gate stdin_bytes += len; 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate /* Send any buffered packet data to the client. */ 4607c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_out, writeset)) 4617c478bd9Sstevel@tonic-gate packet_write_poll(); 4627c478bd9Sstevel@tonic-gate } 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate /* 4657c478bd9Sstevel@tonic-gate * Wait until all buffered output has been sent to the client. 4667c478bd9Sstevel@tonic-gate * This is used when the program terminates. 4677c478bd9Sstevel@tonic-gate */ 4687c478bd9Sstevel@tonic-gate static void 4697c478bd9Sstevel@tonic-gate drain_output(void) 4707c478bd9Sstevel@tonic-gate { 4717c478bd9Sstevel@tonic-gate /* Send any buffered stdout data to the client. */ 4727c478bd9Sstevel@tonic-gate if (buffer_len(&stdout_buffer) > 0) { 4737c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA); 4747c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer), 4757c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer)); 4767c478bd9Sstevel@tonic-gate packet_send(); 4777c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */ 4787c478bd9Sstevel@tonic-gate stdout_bytes += buffer_len(&stdout_buffer); 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate /* Send any buffered stderr data to the client. */ 4817c478bd9Sstevel@tonic-gate if (buffer_len(&stderr_buffer) > 0) { 4827c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA); 4837c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer), 4847c478bd9Sstevel@tonic-gate buffer_len(&stderr_buffer)); 4857c478bd9Sstevel@tonic-gate packet_send(); 4867c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */ 4877c478bd9Sstevel@tonic-gate stderr_bytes += buffer_len(&stderr_buffer); 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate /* Wait until all buffered data has been written to the client. */ 4907c478bd9Sstevel@tonic-gate packet_write_wait(); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate static void 4947c478bd9Sstevel@tonic-gate process_buffered_input_packets(void) 4957c478bd9Sstevel@tonic-gate { 4967c478bd9Sstevel@tonic-gate dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL); 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate /* 5007c478bd9Sstevel@tonic-gate * Performs the interactive session. This handles data transmission between 5017c478bd9Sstevel@tonic-gate * the client and the program. Note that the notion of stdin, stdout, and 5027c478bd9Sstevel@tonic-gate * stderr in this function is sort of reversed: this function writes to 5037c478bd9Sstevel@tonic-gate * stdin (of the child program), and reads from stdout and stderr (of the 5047c478bd9Sstevel@tonic-gate * child program). 5057c478bd9Sstevel@tonic-gate */ 5067c478bd9Sstevel@tonic-gate void 5077c478bd9Sstevel@tonic-gate server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) 5087c478bd9Sstevel@tonic-gate { 5097c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 5107c478bd9Sstevel@tonic-gate int max_fd = 0, nalloc = 0; 5117c478bd9Sstevel@tonic-gate int wait_status; /* Status returned by wait(). */ 5127c478bd9Sstevel@tonic-gate pid_t wait_pid; /* pid returned by wait(). */ 5137c478bd9Sstevel@tonic-gate int waiting_termination = 0; /* Have displayed waiting close message. */ 5147c478bd9Sstevel@tonic-gate u_int max_time_milliseconds; 5157c478bd9Sstevel@tonic-gate u_int previous_stdout_buffer_bytes; 5167c478bd9Sstevel@tonic-gate u_int stdout_buffer_bytes; 5177c478bd9Sstevel@tonic-gate int type; 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate debug("Entering interactive session."); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate /* Initialize the SIGCHLD kludge. */ 5227c478bd9Sstevel@tonic-gate child_terminated = 0; 5237c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate /* Initialize our global variables. */ 5267c478bd9Sstevel@tonic-gate fdin = fdin_arg; 5277c478bd9Sstevel@tonic-gate fdout = fdout_arg; 5287c478bd9Sstevel@tonic-gate fderr = fderr_arg; 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate /* nonblocking IO */ 5317c478bd9Sstevel@tonic-gate set_nonblock(fdin); 5327c478bd9Sstevel@tonic-gate set_nonblock(fdout); 5337c478bd9Sstevel@tonic-gate /* we don't have stderr for interactive terminal sessions, see below */ 5347c478bd9Sstevel@tonic-gate if (fderr != -1) 5357c478bd9Sstevel@tonic-gate set_nonblock(fderr); 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) 5387c478bd9Sstevel@tonic-gate fdin_is_tty = 1; 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 5417c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate notify_setup(); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = 0; 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate /* Set approximate I/O buffer size. */ 5487c478bd9Sstevel@tonic-gate if (packet_is_interactive()) 5497c478bd9Sstevel@tonic-gate buffer_high = 4096; 5507c478bd9Sstevel@tonic-gate else 5517c478bd9Sstevel@tonic-gate buffer_high = 64 * 1024; 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate #if 0 5547c478bd9Sstevel@tonic-gate /* Initialize max_fd to the maximum of the known file descriptors. */ 5557c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 5567c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin); 5577c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout); 5587c478bd9Sstevel@tonic-gate if (fderr != -1) 5597c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr); 5607c478bd9Sstevel@tonic-gate #endif 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate /* Initialize Initialize buffers. */ 5637c478bd9Sstevel@tonic-gate buffer_init(&stdin_buffer); 5647c478bd9Sstevel@tonic-gate buffer_init(&stdout_buffer); 5657c478bd9Sstevel@tonic-gate buffer_init(&stderr_buffer); 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate /* 5687c478bd9Sstevel@tonic-gate * If we have no separate fderr (which is the case when we have a pty 5697c478bd9Sstevel@tonic-gate * - there we cannot make difference between data sent to stdout and 5707c478bd9Sstevel@tonic-gate * stderr), indicate that we have seen an EOF from stderr. This way 5717c478bd9Sstevel@tonic-gate * we don\'t need to check the descriptor everywhere. 5727c478bd9Sstevel@tonic-gate */ 5737c478bd9Sstevel@tonic-gate if (fderr == -1) 5747c478bd9Sstevel@tonic-gate fderr_eof = 1; 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate server_init_dispatch(); 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate /* Main loop of the server for the interactive session mode. */ 5797c478bd9Sstevel@tonic-gate for (;;) { 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate /* Process buffered packets from the client. */ 5827c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate /* 5857c478bd9Sstevel@tonic-gate * If we have received eof, and there is no more pending 5867c478bd9Sstevel@tonic-gate * input data, cause a real eof by closing fdin. 5877c478bd9Sstevel@tonic-gate */ 5887c478bd9Sstevel@tonic-gate if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { 5897c478bd9Sstevel@tonic-gate if (fdin != fdout) 5907c478bd9Sstevel@tonic-gate (void) close(fdin); 5917c478bd9Sstevel@tonic-gate else 5927c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ 5937c478bd9Sstevel@tonic-gate fdin = -1; 5947c478bd9Sstevel@tonic-gate } 5957c478bd9Sstevel@tonic-gate /* Make packets from buffered stderr data to send to the client. */ 5967c478bd9Sstevel@tonic-gate make_packets_from_stderr_data(); 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate /* 5997c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data to send to the 6007c478bd9Sstevel@tonic-gate * client. If there is very little to send, this arranges to 6017c478bd9Sstevel@tonic-gate * not send them now, but to wait a short while to see if we 6027c478bd9Sstevel@tonic-gate * are getting more data. This is necessary, as some systems 6037c478bd9Sstevel@tonic-gate * wake up readers from a pty after each separate character. 6047c478bd9Sstevel@tonic-gate */ 6057c478bd9Sstevel@tonic-gate max_time_milliseconds = 0; 6067c478bd9Sstevel@tonic-gate stdout_buffer_bytes = buffer_len(&stdout_buffer); 6077c478bd9Sstevel@tonic-gate if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && 6087c478bd9Sstevel@tonic-gate stdout_buffer_bytes != previous_stdout_buffer_bytes) { 6097c478bd9Sstevel@tonic-gate /* try again after a while */ 6107c478bd9Sstevel@tonic-gate max_time_milliseconds = 10; 6117c478bd9Sstevel@tonic-gate } else { 6127c478bd9Sstevel@tonic-gate /* Send it now. */ 6137c478bd9Sstevel@tonic-gate make_packets_from_stdout_data(); 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate /* Send channel data to the client. */ 6187c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write()) 6197c478bd9Sstevel@tonic-gate channel_output_poll(); 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate /* 6227c478bd9Sstevel@tonic-gate * Bail out of the loop if the program has closed its output 6237c478bd9Sstevel@tonic-gate * descriptors, and we have no more data to send to the 6247c478bd9Sstevel@tonic-gate * client, and there is no pending buffered data. 6257c478bd9Sstevel@tonic-gate */ 6267c478bd9Sstevel@tonic-gate if (fdout_eof && fderr_eof && !packet_have_data_to_write() && 6277c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { 6287c478bd9Sstevel@tonic-gate if (!channel_still_open()) 6297c478bd9Sstevel@tonic-gate break; 6307c478bd9Sstevel@tonic-gate if (!waiting_termination) { 6317c478bd9Sstevel@tonic-gate const char *s = "Waiting for forwarded connections to terminate...\r\n"; 6327c478bd9Sstevel@tonic-gate char *cp; 6337c478bd9Sstevel@tonic-gate waiting_termination = 1; 6347c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, s, strlen(s)); 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate /* Display list of open channels. */ 6377c478bd9Sstevel@tonic-gate cp = channel_open_message(); 6387c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, cp, strlen(cp)); 6397c478bd9Sstevel@tonic-gate xfree(cp); 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate } 6427c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 6437c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin); 6447c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout); 6457c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr); 6467c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate /* Sleep in select() until we can do something. */ 6497c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd, 6507c478bd9Sstevel@tonic-gate &nalloc, max_time_milliseconds); 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate /* Process any channel events. */ 6537c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset); 6547c478bd9Sstevel@tonic-gate 6557c478bd9Sstevel@tonic-gate /* Process input from the client and from program stdout/stderr. */ 6567c478bd9Sstevel@tonic-gate process_input(readset); 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate /* Process output to the client and to program stdin. */ 6597c478bd9Sstevel@tonic-gate process_output(writeset); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate if (readset) 6627c478bd9Sstevel@tonic-gate xfree(readset); 6637c478bd9Sstevel@tonic-gate if (writeset) 6647c478bd9Sstevel@tonic-gate xfree(writeset); 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate /* Cleanup and termination code. */ 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate /* Wait until all output has been sent to the client. */ 6697c478bd9Sstevel@tonic-gate drain_output(); 6707c478bd9Sstevel@tonic-gate 6717c478bd9Sstevel@tonic-gate debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", 6727c478bd9Sstevel@tonic-gate stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate /* Free and clear the buffers. */ 6757c478bd9Sstevel@tonic-gate buffer_free(&stdin_buffer); 6767c478bd9Sstevel@tonic-gate buffer_free(&stdout_buffer); 6777c478bd9Sstevel@tonic-gate buffer_free(&stderr_buffer); 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate /* Close the file descriptors. */ 6807c478bd9Sstevel@tonic-gate if (fdout != -1) 6817c478bd9Sstevel@tonic-gate (void) close(fdout); 6827c478bd9Sstevel@tonic-gate fdout = -1; 6837c478bd9Sstevel@tonic-gate fdout_eof = 1; 6847c478bd9Sstevel@tonic-gate if (fderr != -1) 6857c478bd9Sstevel@tonic-gate (void) close(fderr); 6867c478bd9Sstevel@tonic-gate fderr = -1; 6877c478bd9Sstevel@tonic-gate fderr_eof = 1; 6887c478bd9Sstevel@tonic-gate if (fdin != -1) 6897c478bd9Sstevel@tonic-gate (void) close(fdin); 6907c478bd9Sstevel@tonic-gate fdin = -1; 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate channel_free_all(); 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate /* We no longer want our SIGCHLD handler to be called. */ 6957c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, SIG_DFL); 6967c478bd9Sstevel@tonic-gate 6977c478bd9Sstevel@tonic-gate while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) 6987c478bd9Sstevel@tonic-gate if (errno != EINTR) 6997c478bd9Sstevel@tonic-gate packet_disconnect("wait: %.100s", strerror(errno)); 7007c478bd9Sstevel@tonic-gate if (wait_pid != pid) 7017c478bd9Sstevel@tonic-gate error("Strange, wait returned pid %ld, expected %ld", 7027c478bd9Sstevel@tonic-gate (long)wait_pid, (long)pid); 7037c478bd9Sstevel@tonic-gate 7047c478bd9Sstevel@tonic-gate /* Check if it exited normally. */ 7057c478bd9Sstevel@tonic-gate if (WIFEXITED(wait_status)) { 7067c478bd9Sstevel@tonic-gate /* Yes, normal exit. Get exit status and send it to the client. */ 7077c478bd9Sstevel@tonic-gate debug("Command exited with status %d.", WEXITSTATUS(wait_status)); 7087c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_EXITSTATUS); 7097c478bd9Sstevel@tonic-gate packet_put_int(WEXITSTATUS(wait_status)); 7107c478bd9Sstevel@tonic-gate packet_send(); 7117c478bd9Sstevel@tonic-gate packet_write_wait(); 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate /* 7147c478bd9Sstevel@tonic-gate * Wait for exit confirmation. Note that there might be 7157c478bd9Sstevel@tonic-gate * other packets coming before it; however, the program has 7167c478bd9Sstevel@tonic-gate * already died so we just ignore them. The client is 7177c478bd9Sstevel@tonic-gate * supposed to respond with the confirmation when it receives 7187c478bd9Sstevel@tonic-gate * the exit status. 7197c478bd9Sstevel@tonic-gate */ 7207c478bd9Sstevel@tonic-gate do { 7217c478bd9Sstevel@tonic-gate type = packet_read(); 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate while (type != SSH_CMSG_EXIT_CONFIRMATION); 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate debug("Received exit confirmation."); 7267c478bd9Sstevel@tonic-gate return; 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate /* Check if the program terminated due to a signal. */ 7297c478bd9Sstevel@tonic-gate if (WIFSIGNALED(wait_status)) 7307c478bd9Sstevel@tonic-gate packet_disconnect("Command terminated on signal %d.", 7317c478bd9Sstevel@tonic-gate WTERMSIG(wait_status)); 7327c478bd9Sstevel@tonic-gate 7337c478bd9Sstevel@tonic-gate /* Some weird exit cause. Just exit. */ 7347c478bd9Sstevel@tonic-gate packet_disconnect("wait returned status %04x.", wait_status); 7357c478bd9Sstevel@tonic-gate /* NOTREACHED */ 7367c478bd9Sstevel@tonic-gate } 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate static void 7397c478bd9Sstevel@tonic-gate collect_children(void) 7407c478bd9Sstevel@tonic-gate { 7417c478bd9Sstevel@tonic-gate pid_t pid; 7427c478bd9Sstevel@tonic-gate sigset_t oset, nset; 7437c478bd9Sstevel@tonic-gate int status; 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */ 7467c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset); 7477c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD); 7487c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset); 7497c478bd9Sstevel@tonic-gate if (child_terminated) { 7507c478bd9Sstevel@tonic-gate while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || 7517c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR)) 7527c478bd9Sstevel@tonic-gate if (pid > 0) 7537c478bd9Sstevel@tonic-gate session_close_by_pid(pid, status); 7547c478bd9Sstevel@tonic-gate child_terminated = 0; 7557c478bd9Sstevel@tonic-gate } 7567c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 7577c478bd9Sstevel@tonic-gate } 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 7607c478bd9Sstevel@tonic-gate /* 7617c478bd9Sstevel@tonic-gate * For ALTPRIVSEP the wait_until_can_do_something function is very 7627c478bd9Sstevel@tonic-gate * simple: select() on the read side of the pipe, and if there's packets 7637c478bd9Sstevel@tonic-gate * to send, on the write side, and on the read side of the SIGCHLD 7647c478bd9Sstevel@tonic-gate * handler pipe. That's it. 7657c478bd9Sstevel@tonic-gate */ 7667c478bd9Sstevel@tonic-gate static void 7677c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 7687c478bd9Sstevel@tonic-gate int *maxfdp, int *nallocp, u_int max_time_milliseconds) 7697c478bd9Sstevel@tonic-gate { 7707c478bd9Sstevel@tonic-gate int ret; 7717c478bd9Sstevel@tonic-gate 7727c478bd9Sstevel@tonic-gate /* 7737c478bd9Sstevel@tonic-gate * Use channel_prepare_select() to make the fd sets. 7747c478bd9Sstevel@tonic-gate * 7757c478bd9Sstevel@tonic-gate * This is cheating, really, since because the last argument in 7767c478bd9Sstevel@tonic-gate * this call is '1' nothing related to channels will be done -- 7777c478bd9Sstevel@tonic-gate * we're using this function only to callocate the fd sets. 7787c478bd9Sstevel@tonic-gate */ 7797c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 1); 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate if ((connection_in = packet_get_connection_in()) >= 0 && 7827c478bd9Sstevel@tonic-gate !connection_closed) 7837c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate notify_prepare(*readsetp); 7867c478bd9Sstevel@tonic-gate 7877c478bd9Sstevel@tonic-gate if ((connection_out = packet_get_connection_out()) >= 0 && 7887c478bd9Sstevel@tonic-gate packet_have_data_to_write() && !connection_closed) 7897c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp); 7907c478bd9Sstevel@tonic-gate 7917c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */ 7927c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL); 7937c478bd9Sstevel@tonic-gate 7947c478bd9Sstevel@tonic-gate if (ret == -1) { 7957c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp); 7967c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp); 7977c478bd9Sstevel@tonic-gate if (errno != EINTR) 7987c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno)); 7997c478bd9Sstevel@tonic-gate } 8007c478bd9Sstevel@tonic-gate 8017c478bd9Sstevel@tonic-gate notify_done(*readsetp); 8027c478bd9Sstevel@tonic-gate } 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate /* 8057c478bd9Sstevel@tonic-gate * Slightly different than collect_children, aps_collect_child() has 8067c478bd9Sstevel@tonic-gate * only the unprivileged sshd to wait for, no sessions, no channells, 8077c478bd9Sstevel@tonic-gate * just one process. 8087c478bd9Sstevel@tonic-gate */ 8097c478bd9Sstevel@tonic-gate static int 8107c478bd9Sstevel@tonic-gate aps_collect_child(pid_t child) 8117c478bd9Sstevel@tonic-gate { 8127c478bd9Sstevel@tonic-gate pid_t pid; 8137c478bd9Sstevel@tonic-gate sigset_t oset, nset; 8147c478bd9Sstevel@tonic-gate int status; 8157c478bd9Sstevel@tonic-gate 8167c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */ 8177c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset); 8187c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD); 8197c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset); 8207c478bd9Sstevel@tonic-gate if (child_terminated) { 8217c478bd9Sstevel@tonic-gate while ((pid = waitpid(child, &status, WNOHANG)) > 0 || 8227c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR)) 8237c478bd9Sstevel@tonic-gate if (pid == child) { 8247c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 8257c478bd9Sstevel@tonic-gate return (1); 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate child_terminated = 0; 8287c478bd9Sstevel@tonic-gate } 8297c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 8307c478bd9Sstevel@tonic-gate return (0); 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate static int killed = 0; 8347c478bd9Sstevel@tonic-gate 8357c478bd9Sstevel@tonic-gate static void 8367c478bd9Sstevel@tonic-gate aps_monitor_kill_handler(int sig) 8377c478bd9Sstevel@tonic-gate { 8387c478bd9Sstevel@tonic-gate int save_errno = errno; 8397c478bd9Sstevel@tonic-gate killed = 1; 8407c478bd9Sstevel@tonic-gate notify_parent(); 8417c478bd9Sstevel@tonic-gate mysignal(sig, aps_monitor_kill_handler); 8427c478bd9Sstevel@tonic-gate errno = save_errno; 8437c478bd9Sstevel@tonic-gate } 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate static void 8467c478bd9Sstevel@tonic-gate aps_monitor_sigchld_handler(int sig) 8477c478bd9Sstevel@tonic-gate { 8487c478bd9Sstevel@tonic-gate int save_errno = errno; 8497c478bd9Sstevel@tonic-gate debug("Monitor received SIGCHLD."); 8507c478bd9Sstevel@tonic-gate child_terminated = 1; 8517c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler); 8527c478bd9Sstevel@tonic-gate notify_parent(); 8537c478bd9Sstevel@tonic-gate errno = save_errno; 8547c478bd9Sstevel@tonic-gate } 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate void 8577c478bd9Sstevel@tonic-gate aps_monitor_loop(Authctxt *authctxt, int pipe, pid_t child_pid) 8587c478bd9Sstevel@tonic-gate { 8597c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 8607c478bd9Sstevel@tonic-gate int max_fd, nalloc = 0; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate debug("Entering monitor loop."); 8637c478bd9Sstevel@tonic-gate 8647c478bd9Sstevel@tonic-gate /* 8657c478bd9Sstevel@tonic-gate * Awful hack follows: fake compat20 == 1 to cause process_input() 8667c478bd9Sstevel@tonic-gate * and process_output() to behave as they would for SSHv2 because that's 8677c478bd9Sstevel@tonic-gate * the behaviour we need in SSHv2. 8687c478bd9Sstevel@tonic-gate * 8697c478bd9Sstevel@tonic-gate * This same hack is done in packet.c 8707c478bd9Sstevel@tonic-gate */ 8717c478bd9Sstevel@tonic-gate compat20 = 1; /* causes process_input/output() to ignore stdio */ 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate mysignal(SIGHUP, aps_monitor_kill_handler); 8747c478bd9Sstevel@tonic-gate mysignal(SIGINT, aps_monitor_kill_handler); 8757c478bd9Sstevel@tonic-gate mysignal(SIGTERM, aps_monitor_kill_handler); 8767c478bd9Sstevel@tonic-gate 8777c478bd9Sstevel@tonic-gate child_terminated = 0; 8787c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler); 8797c478bd9Sstevel@tonic-gate 8807c478bd9Sstevel@tonic-gate packet_set_monitor(pipe); 8817c478bd9Sstevel@tonic-gate 8827c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 8837c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate notify_setup(); 8867c478bd9Sstevel@tonic-gate 8877c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 8887c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 8897c478bd9Sstevel@tonic-gate 8907c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 8917c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_MAX, 8927c478bd9Sstevel@tonic-gate &dispatch_protocol_error); 8937c478bd9Sstevel@tonic-gate dispatch_set(SSH2_PRIV_MSG_ALTPRIVSEP, &aps_input_altpriv_msg); 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate for (;;) { 8967c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 8977c478bd9Sstevel@tonic-gate 8987c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(&readset, &writeset, &max_fd, 8997c478bd9Sstevel@tonic-gate &nalloc, 0); 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate if (aps_collect_child(child_pid)) 9027c478bd9Sstevel@tonic-gate break; 9037c478bd9Sstevel@tonic-gate 9047c478bd9Sstevel@tonic-gate if (killed) { 9057c478bd9Sstevel@tonic-gate /* fatal cleanups will kill child, audit logout */ 9067c478bd9Sstevel@tonic-gate log("Monitor killed; exiting"); 9077c478bd9Sstevel@tonic-gate fatal_cleanup(); 9087c478bd9Sstevel@tonic-gate } 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate /* 9117c478bd9Sstevel@tonic-gate * Unlike server_loop2() we don't care if connection_closed 9127c478bd9Sstevel@tonic-gate * since we still want to wait for the monitor's child. 9137c478bd9Sstevel@tonic-gate */ 9147c478bd9Sstevel@tonic-gate process_input(readset); 9157c478bd9Sstevel@tonic-gate process_output(writeset); 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate packet_close(); 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate void 9237c478bd9Sstevel@tonic-gate server_loop2(Authctxt *authctxt) 9247c478bd9Sstevel@tonic-gate { 9257c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 9267c478bd9Sstevel@tonic-gate int rekeying = 0, max_fd, nalloc = 0; 9277c478bd9Sstevel@tonic-gate 9287c478bd9Sstevel@tonic-gate debug("Entering interactive session for SSH2."); 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 9317c478bd9Sstevel@tonic-gate child_terminated = 0; 9327c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 9337c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 9347c478bd9Sstevel@tonic-gate 9357c478bd9Sstevel@tonic-gate notify_setup(); 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 9387c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate xxx_authctxt = authctxt; 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate server_init_dispatch(); 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate for (;;) { 9457c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 9467c478bd9Sstevel@tonic-gate 9477c478bd9Sstevel@tonic-gate rekeying = (xxx_kex != NULL && !xxx_kex->done); 9487c478bd9Sstevel@tonic-gate 9497c478bd9Sstevel@tonic-gate if (!rekeying && packet_not_very_much_data_to_write()) 9507c478bd9Sstevel@tonic-gate channel_output_poll(); 9517c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd, 9527c478bd9Sstevel@tonic-gate &nalloc, 0); 9537c478bd9Sstevel@tonic-gate 9547c478bd9Sstevel@tonic-gate collect_children(); 9557c478bd9Sstevel@tonic-gate 956*9a8058b5Sjp161948 if (!rekeying) { 9577c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset); 958*9a8058b5Sjp161948 if (packet_need_rekeying()) { 959*9a8058b5Sjp161948 debug("need rekeying"); 960*9a8058b5Sjp161948 xxx_kex->done = 0; 961*9a8058b5Sjp161948 kex_send_kexinit(xxx_kex); 962*9a8058b5Sjp161948 } 963*9a8058b5Sjp161948 } 9647c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 9657c478bd9Sstevel@tonic-gate else 9667c478bd9Sstevel@tonic-gate altprivsep_process_input(xxx_kex, readset); 9677c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate process_input(readset); 9707c478bd9Sstevel@tonic-gate if (connection_closed) 9717c478bd9Sstevel@tonic-gate break; 9727c478bd9Sstevel@tonic-gate process_output(writeset); 9737c478bd9Sstevel@tonic-gate } 9747c478bd9Sstevel@tonic-gate collect_children(); 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate if (readset) 9777c478bd9Sstevel@tonic-gate xfree(readset); 9787c478bd9Sstevel@tonic-gate if (writeset) 9797c478bd9Sstevel@tonic-gate xfree(writeset); 9807c478bd9Sstevel@tonic-gate 9817c478bd9Sstevel@tonic-gate /* free all channels, no more reads and writes */ 9827c478bd9Sstevel@tonic-gate channel_free_all(); 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate /* free remaining sessions, e.g. remove wtmp entries */ 9857c478bd9Sstevel@tonic-gate session_destroy_all(NULL); 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate static void 9897c478bd9Sstevel@tonic-gate server_input_channel_failure(int type, u_int32_t seq, void *ctxt) 9907c478bd9Sstevel@tonic-gate { 9917c478bd9Sstevel@tonic-gate debug("Got CHANNEL_FAILURE for keepalive"); 9927c478bd9Sstevel@tonic-gate /* 9937c478bd9Sstevel@tonic-gate * reset timeout, since we got a sane answer from the client. 9947c478bd9Sstevel@tonic-gate * even if this was generated by something other than 9957c478bd9Sstevel@tonic-gate * the bogus CHANNEL_REQUEST we send for keepalives. 9967c478bd9Sstevel@tonic-gate */ 9977c478bd9Sstevel@tonic-gate client_alive_timeouts = 0; 9987c478bd9Sstevel@tonic-gate } 9997c478bd9Sstevel@tonic-gate 10007c478bd9Sstevel@tonic-gate static void 10017c478bd9Sstevel@tonic-gate server_input_stdin_data(int type, u_int32_t seq, void *ctxt) 10027c478bd9Sstevel@tonic-gate { 10037c478bd9Sstevel@tonic-gate char *data; 10047c478bd9Sstevel@tonic-gate u_int data_len; 10057c478bd9Sstevel@tonic-gate 10067c478bd9Sstevel@tonic-gate /* Stdin data from the client. Append it to the buffer. */ 10077c478bd9Sstevel@tonic-gate /* Ignore any data if the client has closed stdin. */ 10087c478bd9Sstevel@tonic-gate if (fdin == -1) 10097c478bd9Sstevel@tonic-gate return; 10107c478bd9Sstevel@tonic-gate data = packet_get_string(&data_len); 10117c478bd9Sstevel@tonic-gate packet_check_eom(); 10127c478bd9Sstevel@tonic-gate buffer_append(&stdin_buffer, data, data_len); 10137c478bd9Sstevel@tonic-gate memset(data, 0, data_len); 10147c478bd9Sstevel@tonic-gate xfree(data); 10157c478bd9Sstevel@tonic-gate } 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate static void 10187c478bd9Sstevel@tonic-gate server_input_eof(int type, u_int32_t seq, void *ctxt) 10197c478bd9Sstevel@tonic-gate { 10207c478bd9Sstevel@tonic-gate /* 10217c478bd9Sstevel@tonic-gate * Eof from the client. The stdin descriptor to the 10227c478bd9Sstevel@tonic-gate * program will be closed when all buffered data has 10237c478bd9Sstevel@tonic-gate * drained. 10247c478bd9Sstevel@tonic-gate */ 10257c478bd9Sstevel@tonic-gate debug("EOF received for stdin."); 10267c478bd9Sstevel@tonic-gate packet_check_eom(); 10277c478bd9Sstevel@tonic-gate stdin_eof = 1; 10287c478bd9Sstevel@tonic-gate } 10297c478bd9Sstevel@tonic-gate 10307c478bd9Sstevel@tonic-gate static void 10317c478bd9Sstevel@tonic-gate server_input_window_size(int type, u_int32_t seq, void *ctxt) 10327c478bd9Sstevel@tonic-gate { 10337c478bd9Sstevel@tonic-gate int row = packet_get_int(); 10347c478bd9Sstevel@tonic-gate int col = packet_get_int(); 10357c478bd9Sstevel@tonic-gate int xpixel = packet_get_int(); 10367c478bd9Sstevel@tonic-gate int ypixel = packet_get_int(); 10377c478bd9Sstevel@tonic-gate 10387c478bd9Sstevel@tonic-gate debug("Window change received."); 10397c478bd9Sstevel@tonic-gate packet_check_eom(); 10407c478bd9Sstevel@tonic-gate if (fdin != -1) 10417c478bd9Sstevel@tonic-gate pty_change_window_size(fdin, row, col, xpixel, ypixel); 10427c478bd9Sstevel@tonic-gate } 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate static Channel * 10457c478bd9Sstevel@tonic-gate server_request_direct_tcpip(char *ctype) 10467c478bd9Sstevel@tonic-gate { 10477c478bd9Sstevel@tonic-gate Channel *c; 10487c478bd9Sstevel@tonic-gate int sock; 10497c478bd9Sstevel@tonic-gate char *target, *originator; 10507c478bd9Sstevel@tonic-gate int target_port, originator_port; 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate target = packet_get_string(NULL); 10537c478bd9Sstevel@tonic-gate target_port = packet_get_int(); 10547c478bd9Sstevel@tonic-gate originator = packet_get_string(NULL); 10557c478bd9Sstevel@tonic-gate originator_port = packet_get_int(); 10567c478bd9Sstevel@tonic-gate packet_check_eom(); 10577c478bd9Sstevel@tonic-gate 10587c478bd9Sstevel@tonic-gate debug("server_request_direct_tcpip: originator %s port %d, target %s port %d", 10597c478bd9Sstevel@tonic-gate originator, originator_port, target, target_port); 10607c478bd9Sstevel@tonic-gate 10617c478bd9Sstevel@tonic-gate /* XXX check permission */ 10627c478bd9Sstevel@tonic-gate sock = channel_connect_to(target, target_port); 10637c478bd9Sstevel@tonic-gate 10647c478bd9Sstevel@tonic-gate xfree(target); 10657c478bd9Sstevel@tonic-gate xfree(originator); 10667c478bd9Sstevel@tonic-gate if (sock < 0) 10677c478bd9Sstevel@tonic-gate return NULL; 10687c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_CONNECTING, 10697c478bd9Sstevel@tonic-gate sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, 10707c478bd9Sstevel@tonic-gate CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); 10717c478bd9Sstevel@tonic-gate return c; 10727c478bd9Sstevel@tonic-gate } 10737c478bd9Sstevel@tonic-gate 10747c478bd9Sstevel@tonic-gate static Channel * 10757c478bd9Sstevel@tonic-gate server_request_session(char *ctype) 10767c478bd9Sstevel@tonic-gate { 10777c478bd9Sstevel@tonic-gate Channel *c; 10787c478bd9Sstevel@tonic-gate 10797c478bd9Sstevel@tonic-gate debug("input_session_request"); 10807c478bd9Sstevel@tonic-gate packet_check_eom(); 10817c478bd9Sstevel@tonic-gate /* 10827c478bd9Sstevel@tonic-gate * A server session has no fd to read or write until a 10837c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST for a shell is made, so we set the type to 10847c478bd9Sstevel@tonic-gate * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all 10857c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST messages is registered. 10867c478bd9Sstevel@tonic-gate */ 10877c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_LARVAL, 10887c478bd9Sstevel@tonic-gate -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 10897c478bd9Sstevel@tonic-gate 0, xstrdup("server-session"), 1); 10907c478bd9Sstevel@tonic-gate if (session_open(xxx_authctxt, c->self) != 1) { 10917c478bd9Sstevel@tonic-gate debug("session open failed, free channel %d", c->self); 10927c478bd9Sstevel@tonic-gate channel_free(c); 10937c478bd9Sstevel@tonic-gate return NULL; 10947c478bd9Sstevel@tonic-gate } 10957c478bd9Sstevel@tonic-gate channel_register_cleanup(c->self, session_close_by_channel); 10967c478bd9Sstevel@tonic-gate return c; 10977c478bd9Sstevel@tonic-gate } 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate static void 11007c478bd9Sstevel@tonic-gate server_input_channel_open(int type, u_int32_t seq, void *ctxt) 11017c478bd9Sstevel@tonic-gate { 11027c478bd9Sstevel@tonic-gate Channel *c = NULL; 11037c478bd9Sstevel@tonic-gate char *ctype; 11047c478bd9Sstevel@tonic-gate int rchan; 11057c478bd9Sstevel@tonic-gate u_int rmaxpack, rwindow, len; 11067c478bd9Sstevel@tonic-gate 11077c478bd9Sstevel@tonic-gate ctype = packet_get_string(&len); 11087c478bd9Sstevel@tonic-gate rchan = packet_get_int(); 11097c478bd9Sstevel@tonic-gate rwindow = packet_get_int(); 11107c478bd9Sstevel@tonic-gate rmaxpack = packet_get_int(); 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate debug("server_input_channel_open: ctype %s rchan %d win %d max %d", 11137c478bd9Sstevel@tonic-gate ctype, rchan, rwindow, rmaxpack); 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate if (strcmp(ctype, "session") == 0) { 11167c478bd9Sstevel@tonic-gate c = server_request_session(ctype); 11177c478bd9Sstevel@tonic-gate } else if (strcmp(ctype, "direct-tcpip") == 0) { 11187c478bd9Sstevel@tonic-gate c = server_request_direct_tcpip(ctype); 11197c478bd9Sstevel@tonic-gate } 11207c478bd9Sstevel@tonic-gate if (c != NULL) { 11217c478bd9Sstevel@tonic-gate debug("server_input_channel_open: confirm %s", ctype); 11227c478bd9Sstevel@tonic-gate c->remote_id = rchan; 11237c478bd9Sstevel@tonic-gate c->remote_window = rwindow; 11247c478bd9Sstevel@tonic-gate c->remote_maxpacket = rmaxpack; 11257c478bd9Sstevel@tonic-gate if (c->type != SSH_CHANNEL_CONNECTING) { 11267c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 11277c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id); 11287c478bd9Sstevel@tonic-gate packet_put_int(c->self); 11297c478bd9Sstevel@tonic-gate packet_put_int(c->local_window); 11307c478bd9Sstevel@tonic-gate packet_put_int(c->local_maxpacket); 11317c478bd9Sstevel@tonic-gate packet_send(); 11327c478bd9Sstevel@tonic-gate } 11337c478bd9Sstevel@tonic-gate } else { 11347c478bd9Sstevel@tonic-gate debug("server_input_channel_open: failure %s", ctype); 11357c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 11367c478bd9Sstevel@tonic-gate packet_put_int(rchan); 11377c478bd9Sstevel@tonic-gate packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 11387c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_OPENFAILURE)) { 11397c478bd9Sstevel@tonic-gate packet_put_cstring("open failed"); 11407c478bd9Sstevel@tonic-gate packet_put_cstring(""); 11417c478bd9Sstevel@tonic-gate } 11427c478bd9Sstevel@tonic-gate packet_send(); 11437c478bd9Sstevel@tonic-gate } 11447c478bd9Sstevel@tonic-gate xfree(ctype); 11457c478bd9Sstevel@tonic-gate } 11467c478bd9Sstevel@tonic-gate 11477c478bd9Sstevel@tonic-gate static void 11487c478bd9Sstevel@tonic-gate server_input_global_request(int type, u_int32_t seq, void *ctxt) 11497c478bd9Sstevel@tonic-gate { 11507c478bd9Sstevel@tonic-gate char *rtype; 11517c478bd9Sstevel@tonic-gate int want_reply; 11527c478bd9Sstevel@tonic-gate int success = 0; 11537c478bd9Sstevel@tonic-gate 11547c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL); 11557c478bd9Sstevel@tonic-gate want_reply = packet_get_char(); 11567c478bd9Sstevel@tonic-gate debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); 11577c478bd9Sstevel@tonic-gate 11587c478bd9Sstevel@tonic-gate /* -R style forwarding */ 11597c478bd9Sstevel@tonic-gate if (strcmp(rtype, "tcpip-forward") == 0) { 11607c478bd9Sstevel@tonic-gate struct passwd *pw; 11617c478bd9Sstevel@tonic-gate char *listen_address; 11627c478bd9Sstevel@tonic-gate u_short listen_port; 11637c478bd9Sstevel@tonic-gate 11647c478bd9Sstevel@tonic-gate pw = auth_get_user(); 11657c478bd9Sstevel@tonic-gate if (pw == NULL) 11667c478bd9Sstevel@tonic-gate fatal("server_input_global_request: no user"); 11677c478bd9Sstevel@tonic-gate listen_address = packet_get_string(NULL); /* XXX currently ignored */ 11687c478bd9Sstevel@tonic-gate listen_port = (u_short)packet_get_int(); 11697c478bd9Sstevel@tonic-gate debug("server_input_global_request: tcpip-forward listen %s port %d", 11707c478bd9Sstevel@tonic-gate listen_address, listen_port); 11717c478bd9Sstevel@tonic-gate 11727c478bd9Sstevel@tonic-gate /* check permissions */ 11737c478bd9Sstevel@tonic-gate if (!options.allow_tcp_forwarding || 11747c478bd9Sstevel@tonic-gate no_port_forwarding_flag 11757c478bd9Sstevel@tonic-gate #ifndef NO_IPPORT_RESERVED_CONCEPT 11767c478bd9Sstevel@tonic-gate || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0) 11777c478bd9Sstevel@tonic-gate #endif 11787c478bd9Sstevel@tonic-gate ) { 11797c478bd9Sstevel@tonic-gate success = 0; 11807c478bd9Sstevel@tonic-gate packet_send_debug("Server has disabled port forwarding."); 11817c478bd9Sstevel@tonic-gate } else { 11827c478bd9Sstevel@tonic-gate /* Start listening on the port */ 11837c478bd9Sstevel@tonic-gate success = channel_setup_remote_fwd_listener( 11847c478bd9Sstevel@tonic-gate listen_address, listen_port, options.gateway_ports); 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate xfree(listen_address); 11879b03ea0fSjp161948 } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { 11889b03ea0fSjp161948 char *cancel_address; 11899b03ea0fSjp161948 u_short cancel_port; 11907c478bd9Sstevel@tonic-gate 11919b03ea0fSjp161948 cancel_address = packet_get_string(NULL); 11929b03ea0fSjp161948 cancel_port = (u_short)packet_get_int(); 11939b03ea0fSjp161948 debug("%s: cancel-tcpip-forward addr %s port %d", __func__, 11949b03ea0fSjp161948 cancel_address, cancel_port); 11959b03ea0fSjp161948 11969b03ea0fSjp161948 success = channel_cancel_rport_listener(cancel_address, 11979b03ea0fSjp161948 cancel_port); 11989b03ea0fSjp161948 xfree(cancel_address); 11999b03ea0fSjp161948 } 12007c478bd9Sstevel@tonic-gate if (want_reply) { 12017c478bd9Sstevel@tonic-gate packet_start(success ? 12027c478bd9Sstevel@tonic-gate SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 12037c478bd9Sstevel@tonic-gate packet_send(); 12047c478bd9Sstevel@tonic-gate packet_write_wait(); 12057c478bd9Sstevel@tonic-gate } 12067c478bd9Sstevel@tonic-gate xfree(rtype); 12077c478bd9Sstevel@tonic-gate } 12089b03ea0fSjp161948 12097c478bd9Sstevel@tonic-gate static void 12107c478bd9Sstevel@tonic-gate server_input_channel_req(int type, u_int32_t seq, void *ctxt) 12117c478bd9Sstevel@tonic-gate { 12127c478bd9Sstevel@tonic-gate Channel *c; 12137c478bd9Sstevel@tonic-gate int id, reply, success = 0; 12147c478bd9Sstevel@tonic-gate char *rtype; 12157c478bd9Sstevel@tonic-gate 12167c478bd9Sstevel@tonic-gate id = packet_get_int(); 12177c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL); 12187c478bd9Sstevel@tonic-gate reply = packet_get_char(); 12197c478bd9Sstevel@tonic-gate 12207c478bd9Sstevel@tonic-gate debug("server_input_channel_req: channel %d request %s reply %d", 12217c478bd9Sstevel@tonic-gate id, rtype, reply); 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate if ((c = channel_lookup(id)) == NULL) 12247c478bd9Sstevel@tonic-gate packet_disconnect("server_input_channel_req: " 12257c478bd9Sstevel@tonic-gate "unknown channel %d", id); 12267c478bd9Sstevel@tonic-gate if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) 12277c478bd9Sstevel@tonic-gate success = session_input_channel_req(c, rtype); 12287c478bd9Sstevel@tonic-gate if (reply) { 12297c478bd9Sstevel@tonic-gate packet_start(success ? 12307c478bd9Sstevel@tonic-gate SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 12317c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id); 12327c478bd9Sstevel@tonic-gate packet_send(); 12337c478bd9Sstevel@tonic-gate } 12347c478bd9Sstevel@tonic-gate xfree(rtype); 12357c478bd9Sstevel@tonic-gate } 12367c478bd9Sstevel@tonic-gate 12377c478bd9Sstevel@tonic-gate static void 12387c478bd9Sstevel@tonic-gate server_init_dispatch_20(void) 12397c478bd9Sstevel@tonic-gate { 12407c478bd9Sstevel@tonic-gate debug("server_init_dispatch_20"); 12417c478bd9Sstevel@tonic-gate dispatch_init(&dispatch_protocol_error); 12427c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 12437c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 12447c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 12457c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 12467c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); 12477c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 12487c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 12497c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); 12507c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 12517c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); 12527c478bd9Sstevel@tonic-gate /* client_alive */ 12537c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); 12547c478bd9Sstevel@tonic-gate /* rekeying */ 12557c478bd9Sstevel@tonic-gate 12567c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 12577c478bd9Sstevel@tonic-gate /* unprivileged sshd has a kex packet handler that must not be reset */ 12587c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- should we dispatch_set(KEXINIT) here? %d && !%d", 12597c478bd9Sstevel@tonic-gate packet_is_server(), packet_is_monitor()); 12607c478bd9Sstevel@tonic-gate if (packet_is_server() && !packet_is_monitor()) { 12617c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- skipping dispatch_set(KEXINIT) in unpriv proc"); 12627c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX, 12637c478bd9Sstevel@tonic-gate &altprivsep_rekey); 12647c478bd9Sstevel@tonic-gate return; 12657c478bd9Sstevel@tonic-gate } 12667c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 12677c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 12687c478bd9Sstevel@tonic-gate } 12697c478bd9Sstevel@tonic-gate static void 12707c478bd9Sstevel@tonic-gate server_init_dispatch_13(void) 12717c478bd9Sstevel@tonic-gate { 12727c478bd9Sstevel@tonic-gate debug("server_init_dispatch_13"); 12737c478bd9Sstevel@tonic-gate dispatch_init(NULL); 12747c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_EOF, &server_input_eof); 12757c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); 12767c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); 12777c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 12787c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 12797c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 12807c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 12817c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 12827c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 12837c478bd9Sstevel@tonic-gate } 12847c478bd9Sstevel@tonic-gate static void 12857c478bd9Sstevel@tonic-gate server_init_dispatch_15(void) 12867c478bd9Sstevel@tonic-gate { 12877c478bd9Sstevel@tonic-gate server_init_dispatch_13(); 12887c478bd9Sstevel@tonic-gate debug("server_init_dispatch_15"); 12897c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 12907c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); 12917c478bd9Sstevel@tonic-gate } 12927c478bd9Sstevel@tonic-gate static void 12937c478bd9Sstevel@tonic-gate server_init_dispatch(void) 12947c478bd9Sstevel@tonic-gate { 12957c478bd9Sstevel@tonic-gate if (compat20) 12967c478bd9Sstevel@tonic-gate server_init_dispatch_20(); 12977c478bd9Sstevel@tonic-gate else if (compat13) 12987c478bd9Sstevel@tonic-gate server_init_dispatch_13(); 12997c478bd9Sstevel@tonic-gate else 13007c478bd9Sstevel@tonic-gate server_init_dispatch_15(); 13017c478bd9Sstevel@tonic-gate } 1302